home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: Graphics / Graphics.zip / blendgif.zip / blendgif.cmd < prev    next >
OS/2 REXX Batch file  |  1999-07-12  |  172KB  |  5,494 lines

  1. /* 22 May 1999, Daniel Hellerstein (danielh@econ.ag.gov)
  2.  
  3.                 BlendGIF ver 1.15
  4.  
  5.    BlendGIF is a program to "blend" several gif files into a
  6.    multiple-frame animated gif.  Requires rexxlib.dll, rxgdutil.dll, 
  7.    rexxutil.dll, and rxsocket.
  8.  
  9.    We recommend setting the BLENDGIF_ROOT paramater below -- all other
  10.    options can be left as is (they are just defaults).  
  11.    In fact,
  12.      a) to run this as a cgi-bin script, BLENDGIF_ROOT  MUST be set.
  13.      b) if run as a stand alone, the "current" directory is used
  14.      c) if run as an sre-http addon, the TEMPFILE_DIR directory is used
  15.  
  16. */
  17.  
  18.  
  19.  
  20. /************************ User changable parameters ***********/
  21.      /* It is recommended that you specify the BLENDGIF_ROOT parameter  */
  22.       /* (or understand what happens if you don't specify it!)          */
  23.  
  24. BLENDGIF_ROOT=''     /* default root directory for INFILEs., and for temporary files */
  25.  
  26. /*        ----- The following are general parameters ------ */
  27.  
  28.  
  29. INPUT_FILE=0        /* a fully qualififed filename, containing 1 option per line */
  30.  
  31. max_tempfiles=100 /* maximum # of temporary image files to retain */
  32.  
  33. width1=200        /* user supplied width (only used if resize_mode=2 */
  34. height1=50        /* user supplied height(only used if resize_mode=2 */
  35. resize_mode=1   /* 0=set to size of first image, 1=Set to max of h & w,2=set to specified h&w */
  36.  
  37. r_back=110      /* r,g,b values for pixel=0 */
  38. g_back=110
  39. b_back=110
  40.              
  41. ct_newlen=200     /* length of combined image color table (max is 255, since 0 is reserved for transp*/
  42.  
  43. fade_regions=16     /* number of divisions to use when creating fade ct lookup table */
  44.  
  45.  
  46. disposal=1        /* disposal type */
  47. ct_make_spec=0       /*0=most frequent colors, 1= some binary search, 2=more search  */
  48. verbose=1         /* 1= verbose output, 0= more quiet */
  49. no_transparent=0  /* if 1, do NOT have image be transparent, 2=intermediate frames not trans */
  50.  
  51. cycle=0           /* if 1, then repeat frames in reverse to return to image 1;
  52.                       same as setting infile.0=3 and infile.3=infile1 */
  53.  
  54. iterations=4       /* # of times to iterate display (of all frames */
  55.  
  56. infile.0=3         /* # of images */
  57. infile.1 ='good.gif'
  58. infile.2 ='better.gif'
  59. infile.3 ='best.gif'
  60. infile.1.!nth=1         /* nth image (if not specified, use first */
  61. infile.2.!nth=1         /* if !nth> number of images, use last image */
  62.  
  63. outfile='blendgif.gif'      /* name output file (if exists, will be overwritten) */
  64.  
  65. img_prog='NETSCAPE -l en '  /* program string for displaying images */
  66.  
  67. shrink_image=0          /* if 1, attempt to use "use prior image" disposal
  68.                            mode to shrink final image size */
  69.  
  70.  
  71. /* ----  The following are "default" parameters. They are used if      */
  72. /*         no "image-pair" specific parameters are specified.  -------*/
  73.  
  74. frames=4         /* # of "frames" between each "file" in the  animation */
  75.  
  76. stop_after=0     /* stop after this frame, 0 means "no early stop */
  77.  
  78. anim_type='balloon' /*  ADD BALLOON CURTAIN DISSOLVE FADE MASK */
  79.  
  80. /* These are used with the "MASK" anim_type: */
  81. mask.0=0                 /* # of mask files, only used if anim_type='MASK' */
  82. MASK.1='FOREVER1.GIF'
  83. MASK.2='FOREVER2.GIF'
  84. MASK.1.!THRESH=0
  85. MASK.2.!THRESH=0
  86.  
  87.  
  88. /* These are used with the BALLOON anim_type */
  89. balloon_TYPE=4      /* 1=square, 2=diamond, 3=octagon, 4=circle */
  90. centerx=0.50        /* center of ballon (x=columns, y=rows */
  91. centery=0.45
  92. balloon_push=2     /* 0=overwrite,  1=push image,
  93.                        2=squoosh original image,
  94.                        20=squoosh, vert overwrite, 10=push, vert overwrite 
  95.                        balloon_push is ignored if balloon_type<>4  */
  96.  
  97.  
  98. /* These are used with the FADE anim_type */
  99. fade_type=3      /* 0=frequency sort (default), 1=brightness sort, 
  100.                      2=color specific brightness sort, 3=best match,
  101.                      or a string containing a REXX math expression that uses
  102.                      the R G and B variables, to specify a  ct sort;
  103.                      such as: '2*R + G '*/
  104.  
  105.  
  106. /* This is used with the CURTAIN anim_type */
  107. CURTAIN_TYPE='T_B'              /* L_R, T_B, or  MIDDLE */
  108. CURTAIN_OVERWRITE="OVERWRITE"    /* CURTAIN, PUSH, or SQUOOSH */
  109.  
  110. /*This is used with the DISSOLVE anim_type */
  111. dissolve_spec='1'    /* 1= linear dissolve, 
  112.                      or a string containing values between 0 and 100,
  113.                      the thresholds will determined using a curve going 
  114.                      through these values */
  115.                      
  116. frame_delay=50        /* delay between frames, 1/100th seconds */
  117.  
  118.  
  119.  
  120. /* ------------------------------------------------------------------------- 
  121.   The following are "image-pair" specific parameters. 
  122.  
  123.   They are optional; if you don't specify a particular
  124.   parameter for a particular image pair, the default
  125.   values (set above) will be used.
  126.  
  127.   To specify a parameter, simply add a ".n" (without the
  128.   quotes) to the end of the parameter name, where "n" is 
  129.   the image pair.  Thus, .2 refers to "the animation frames
  130.   derived from infile.2  and infile.3 ".
  131.  
  132.   The following parameter can have "image=pair" specific values:
  133.         FRAMES    STOP_AFTER      ANIM_TYPE       BALLOON_TYPE 
  134.         MASK.n     MASK.n.!THRESH  MASK_LIST      BALLOON_PUSH   
  135.         CENTERX    CENTERY         FADE_TYPE
  136.         CURTAIN_TYPE CURTAIN_OVEWRITE  DISSOLVE_SPEC   FRAME_DELAY 
  137.  
  138.   Examples:
  139.       FRAMES.1=10 
  140.       FRAMES.2=5
  141.       ANIM_TYPE.1='BALLOON'
  142.       BALLOON_TYPE.1=4
  143.       BALLOON_PUS.1=0
  144.       ANIM_TYPE.2="FADE"
  145.       FADE_TYPE.2=1
  146.  
  147. The following are "image-specific" transformation options. 
  148.  
  149. They have  NO "default" equivalents.
  150.  
  151.   The image-specific tranformation parameter are: 
  152.        TRANSFORM.  XMOVE. YMOVE. ZROTATE. YROTATE XROTATE. NUWIDTH.  NUHEIGHT.
  153.   which should be specified using  a tail (a .n) with specifying which
  154.   image to transform. Note that TRANSFORM. is a flag; the other parameters
  155.   are used (for image n) ONLY when TRANSFORM.n=1.
  156.  
  157.   Examples:
  158.       TRANSFORM.2=1
  159.       NUWIDTH.1=0.7
  160.       NUHEIGHT.1=0.7
  161.       XMOVE.1=0.3
  162.       YMOVE.1=0.2
  163.       ZROTATE.1=10
  164.       BKG_TRANSPARENT.1=1
  165.  
  166.    Notes: 
  167.       NUWIDTH and NUHEIGHT should be real numbers (always include the 
  168.       decimal point) that scale the images current size.
  169.       XMOVE and YMOVE (also real numbers) specify a move, as a fraction
  170.       of the width and height of the image you are creating (as set by the
  171.       RESIZE_MODE, WIDTH1, and HEIGHT1 parameters).
  172.       ROTATE is in degrees (+ or -).  Or, you can specify a space delimited
  173.       list of three rotations; for the "z-axis", "y-axis", and "x-axis" (the
  174.       "z-axis" comes out of the screen, rotation around the z-axis is the standard
  175.       rotation of a flat image).
  176.       BKG_TRANSPARENT controls the transparency of "background pixels" (values
  177.       used for pixels the transformed image does not cover).  0 means "use
  178.       the R_BACK, G_BACK, and B_BACK colors.  1 means use "transparent",
  179.       2 is a more stringents transparent.
  180.  
  181.  
  182. -------------------------------------------------------------------------   */
  183.  
  184. /******************* end of user changeable parameters ************/
  185.  
  186.  
  187.  
  188. param_list='width1 height1 resize_mode r_back g_back b_back ct_newlen ',
  189.            'fade_regions disposal ct_make_spec verbose no_transparent ',
  190.            ' cycle iterations infile. outfile img_prog  ',
  191.            ' frames frames. stop_after stop_after. anim_type anim_type. ',
  192.            ' mask. balloon_type balloon_type. centerx centerx. centery centery. ',
  193.            ' balloon_push balloon_push. fade_type fade_type. curtain_type curtain_type. ',
  194.            ' curtain_overwrite curtain_overwrite. dissolve_spec dissolve_spec. ',
  195.            ' frame_delay frame_delay. INPUT_FILE_UPLOAD INPUT_FILE SHOWDIR SHOWFILE ',
  196.            ' TRANSFORM.  XMOVE. YMOVE. ZROTATE. YROTATE. XROTATE. ' ,
  197.            ' NUWIDTH.  NUHEIGHT. UPFILE. DOPAIR. SHRINK_IMAGE MASK_LIST. MASK_LIST ' ,
  198.            ' bkg_transparent. upfile. save_tempfile '
  199. param_list=translate(param_list)
  200.  
  201.  
  202. parse arg  ddir, tempfile, reqstrg,list,verb ,uri,user, ,
  203.           basedir ,workdir,privset,enmadd,transaction,verbose0, ,
  204.          servername,host_nickname,homedir
  205.  
  206.    signal on error name erre ; signal on syntax name erre
  207.  
  208. is_cgi=0                     /* assume it's an sre-http invocation */
  209. upfile.=0                   
  210. mask_list.=0
  211. crlf    ='0d0a'x                        /* constants */
  212. input_file_upload=0
  213. showdir=0
  214. showfile=0
  215. bkg_transparent=0
  216. nuheight.='' ;nuwidth.=''; xmove.=''; ymove.='' ; zrotate.=''
  217. yrotate.='' ; xrotate.=''
  218. img_tempfile=0          /* if 1, then save a temporary image file */
  219.  
  220. /* check for CGI-BIN call */
  221.  
  222. if verb="" then do    /* is it cgi-bin? */
  223.    method = value("REQUEST_METHOD",,'os2environment')
  224.    if method="" then do
  225.        is_cgi=2         /* command line invocation */
  226.        v1=ddir
  227.    end
  228.    else do
  229.       is_cgi=1          /* cgibin invocaiton */
  230.       if method='GET' then do
  231.           list=value("QUERY_STRING",,'os2environment')
  232.       end
  233.       else do
  234.          tlen = value("CONTENT_LENGTH",,'os2environment')
  235.          list=charin(,,tlen)
  236.       end /* do */
  237.       if blendgif_root='' then do
  238.           call dosay2 "Blendgif Setup Error: the blendgif_root directory was not specified"
  239.           return 0
  240.       end /* do */
  241.    end
  242. end
  243.  
  244. /* When here, we know what type of invocation this is */
  245.  
  246. if is_cgi<2 then do             /* called as sre addon, or cgi-bin */
  247.  
  248.   amess=' *** Creating a blended/animated GIF'crlf
  249.  
  250.   if is_cgi=0  then do 
  251.     if  blendgif_root='' then do
  252.       blendgif_root=workdir
  253.       amess=amess||'  BLENDGIF: Installation error-- the BLENDGIF_ROOT directory was not set ('blendgif_root')</pre></body></html>'
  254.       'string 'amess
  255.       exit
  256.     end
  257.     conttype=reqfield('Content-type')  /* sre-http adodn */
  258.   end /* do */
  259.  
  260.   if is_cgi<>0 then  conttype=value("CONTENT_TYPE",,'os2environment')  /* cgi */
  261.  
  262. /* Is this a multipart/form-data POST (file uploads) ? */
  263.   if abbrev(upper(conttype),'MULTIPART/FORM-DATA')=1 then do
  264.      nn=read_multipart_data(list,conttype)  /* parse into form_data. */
  265.      do mm=1 to nn
  266.          elist=translate(strip(form_data.!list.mm))
  267.          if wordpos('CONTENT-TYPE',elist)>0 then do  /* check for bad file upload */
  268.              cc='!CONTENT-TYPE'
  269.              if upper(form_data.cc.mm)<>'IMAGE/GIF' then do
  270.                 call dosay 'Ignoring upload ('form_data.!FILENAME.mm'): content type not image/gif : 'form_data.cc.mm
  271.                 iterate
  272.              end /* do */
  273.              parse var form_data.!filename.mm xx '.' ill
  274.              INFILE.ill=' your 'FORM_DATA.!filename.MM  /* ASSUME FILENAME ENTRY IS AVAILABLE */
  275.          end /* do */
  276.          if wordpos('NAME',elist)>0 then do
  277.             IF FORM_DATA.MM<>'' then DO
  278.                  yof=translate(form_data.!name.mm,'.','$')
  279.                  if valid_parameter(yof,param_list)=0 then do
  280.                      call dosay2 'BlendGIF error. No such parameter (multi-part form): 'yof
  281.                      return 0 
  282.                  end /* do */
  283.                  foo=value(yof,form_data.mm)
  284.             END
  285.          end /* do */
  286.      end                /* doing parts */
  287.   end /* do */
  288.   else do                       /* standard form */
  289.     verbose=verbose0
  290.     if  verb="GET" then do
  291.       parse var uri . '?' list   /* if srefilter addon, get purer version of request string */
  292.     end    /* else use posted list */
  293.     do forever   
  294.        if list='' then leave
  295.        parse var list a1 '&' list
  296.        parse  var a1 a1a '=' a1b
  297.        a1a=translate(strip(decodekeyval(translate(a1a,' ','+'||'09000a0d'x))))
  298.        a1a=translate(a1a,'.','$')
  299.        a1b=strip(decodekeyval(translate(a1b,' ','+'||'09000a0d'x)))
  300.        a1b=translate(a1b,'.','$')               /* since javascript don't like a.b names */
  301.        if a1b='' then iterate                   /* blank entry, ignore */    
  302.        if valid_parameter(a1a,param_list)=0 then do
  303.            call dosay2 'BlendGIF2 error. No such parameter (GET/POST): 'a1a
  304.            return 0 
  305.        end /* do */
  306.        xx=value(a1a,a1b)
  307.     end 
  308.   end           /* enctype of form */
  309. end             /* iscgi 1 */
  310.  
  311.  
  312. /*  DETECT a BLENDGIF ? command line invocation */
  313. if is_cgi=2 & v1="?" then do
  314.    say " BlendGif -- blend 2 (or more) gifs into a multi-framed animated gif"
  315.    say " To execute, enter:: "
  316.    say "     BLENDGIF outfile.gif "
  317.    say "        where outfile.gif is the output file to be created."
  318.    say "     you will then be asked for some parameter values."
  319.    say " For the details, see BLENDGIF.DOC"
  320.    exit
  321. end /* do */
  322.  
  323.  
  324.  
  325. /* -- special case: display contents of BLENDGIF_ROOT directory (or relative
  326.       directory */
  327. if showdir<>0 &showdir<>'' then do
  328.    if is_cgi=1 then do
  329.       call dosay "Sorry, image library listing not currently supported under CGI-BIN."
  330.       return 0
  331.    end /* do */
  332.  
  333.    bdoc='<html><head><title>BlendGIF image-library</title></head><body><h2>The BlendGIF image library</h2>'||crlf
  334.    if pos(':',showdir)>1 then do
  335.       call dosay2 "BlendGIF error: can not specify fully-qualified file name: "showdir
  336.       return 0
  337.    end /* do */
  338.    bb=strip(blendgif_dir) ; bb=translate(blendgif_root,'\','/')
  339.    bb=strip(bb,'t','\')||'\'
  340.    showdir=strip(decodekeyval(showdir))
  341.    showdir=translate(showdir,'\','/'); showdir=strip(showdir,,'\')||'\'
  342.    cc=bb||showdir
  343.    foo=sysfiletree(cc'*.GIF','gots','OF')
  344.    bdoc=bdoc||'<br># of Files Found: 'gots.0||crlf
  345.    if gots.0>0 then do
  346.       bdoc=bdoc||'<ul>'crlf
  347.       do ig=1 to gots.0
  348.          g1=substr(gots.ig,length(bb)+1)
  349.          bdoc=bdoc||'<li> <a href="/blendgif?showfile='g1'">'||translate(g1,'/','\')||'</a>'crlf
  350.       end /* do */
  351.       bdoc=bdoc||'</ul>'crlf
  352.    end /* do */
  353.    bdoc=bdoc||'</body></html>'
  354.    foo=sref_gos('VAR TYPE TEXT/HTML NAME BDOC ', bdoc)
  355.    return '200 '||length(bdoc)
  356. end /* do */
  357.  
  358. /* Special case: show a file in the BLENDGIF_ROOT directory */
  359. if showfile<>0 &showfile<>'' then do
  360.    if is_cgi=1 then do
  361.       call dosay "Sorry, image display not currently supported under CGI-BIN."
  362.       return 0
  363.    end /* do */
  364.    if pos(':',showdir)>1 then do
  365.       call dosay2 "BlendGIF error: can not specify fully-qualified file name: "showfile
  366.       return 0
  367.    end /* do */
  368.    
  369.    bb=strip(blendgif_dir) ; bb=translate(blendgif_root,'\','/')
  370.    bb=strip(bb,'t','\')||'\'
  371.    showfile=strip(decodekeyval(showfile))
  372.    showfile=translate(showfile,'\','/'); showfile=strip(showfile,,'\')
  373.    cc=bb||showfile
  374.    cc2=stream(cc,'c','query size')
  375.    if (cc2=0 | cc2='') then do
  376.         call dosay2 "BlendGIF error: no such file: "cc
  377.         return 0
  378.    end /* do */
  379.    foo=sref_gos('FILE type image/gif name 'cc)
  380.    return '200 '||cc2
  381. end
  382.  
  383. /* If here, we will geneerate a gif== to send as multipart form, or
  384. to save as temporary file? */
  385.  
  386. if is_cgi=0 then do
  387.   if save_tempfile=1 then do
  388.      amess=amess||'0d0a'x||'      (Status messages are displayed, '||crlf||'       after which you can download the image) '||crlf||crlf
  389.  
  390.      AMESS='<html><head><title>BlendGif</title></head><body><pre>'amess
  391.  
  392.      foo=sref_multi_send(amess,'text/HTML','1S')
  393.   end
  394.   else do
  395.      amess=amess||'0d0a'x||'      (Status messages are displayed, '||crlf||'       and then the image is automatically loaded ) '||crlf
  396.      foo=sref_multi_send(amess,'text/plain','SS')
  397.   end
  398. end
  399.  
  400.  
  401. /*  ----- read an input file? */
  402. call read_input_file
  403. if result=0 then return 0
  404.  
  405. if is_cgi<2 then do             /* called as sre addon, or cgi-bin */
  406.   call dosay "     :: Number of images used: "infile.0 
  407. end
  408.  
  409.  
  410. didp0=0
  411. doinit1: call init1            /* load some dlls */
  412.  
  413. dop0: nop
  414.  
  415. if is_cgi=2 then do                     /* command line mode */
  416.    if v1='' then v1='anim'
  417.    outfile=v1
  418.    if pos('.',outfile)=0  then outfile=outfile'.gif'
  419.   ansion=checkansi()
  420.   if ansion=1 then do
  421.      aesc='1B'x
  422.      cy_ye=aesc||'[37;46;m'
  423.      cyanon=cy_ye
  424.      normal=aesc||'[0;m'
  425.      bold=aesc||'[1;m'
  426.      re_wh=aesc||'[31;47;m'
  427.      reverse=aesc||'[7;m'
  428.    end
  429.    else do
  430.      say " Warning: Could not detect ANSI....  Install will look ugly ! "
  431.       cy_ye="" ; normal="" ; bold="" ;re_wh="" ;
  432.      reverse=""
  433.    end  /* Do */
  434.    call set_params0 didp0
  435.    didp0=1
  436.    signal on error name erre ; signal on syntax name erre
  437.  
  438.    arf= yesno(" Change/view other parameters? ",'NO YES REDO ?')
  439.    if arf=2 then signal dop0
  440.    if arf>0 then  do
  441.       call set_params arf
  442.       if result=2 then signal dop0
  443.    end
  444.  
  445.   call ask_input_file           /* ask user for input file of blendgif options */
  446.  
  447. end
  448.  
  449. call init2            /* set global parameters */
  450.  
  451. /* read files into memory */
  452. do kmm=1 to infile.0
  453.    infile.kmm=strip(infile.kmm)
  454.  
  455.    if infile.kmm='.' then do    /* empty screen */
  456.        infile.kmm='1_pixel.gif'
  457.    end
  458.  
  459.    if datatype(infile.kmm.!nth)="NUM" then mg1=infile.kmm.!nth
  460.    mg1=trunc(mg1)
  461.   if upfile.kmm<>0 then do      /* got an upload */
  462.        call dosay kmm") Reading uploaded file " infile.kmm ', frame # ' mg1
  463.        f1a=upfile.kmm
  464.    end
  465.    else do
  466.       call dosay kmm") Reading " infile.kmm ', frame # ' mg1
  467.       if is_cgi<>2 then do
  468.           if pos(':',infile.kmm)=2  then DO
  469.              call dosay2 "BlendGIF error: absolute filename not allowed: "infile.kmm
  470.              return 0
  471.           end /* do */
  472.           if abbrev(translate(infile.kmm),'HTTP://')=0 then
  473.               infile.kmm=strip(translate(strip(infile.kmm),'\','/'),'l','\')
  474.       end /* do */    
  475.       f1a=read_giffile(infile.kmm ,BLENDGIF_ROOT)
  476.       if f1a=0 then do 
  477.           call dosay2 "Error: GIF not available: "infile.kmm
  478.           return 0
  479.       end 
  480.  
  481.    end
  482.    if f1a='' then do
  483.       call dosay " Could not read "infile.kmm 
  484.       exit
  485.    end /* do */
  486.     talist=read_gif_block(f1a,1,'',1)
  487.     if abbrev(talist,'ERROR')=1 then do
  488.         call dosay2 "Problem with "infile.kmm " = "||subword(talist,2)
  489.         return 0
  490.     end /* do */
  491.  
  492. /* 1a) Get LSD and GCE for images */
  493.    ab1=read_gif_block(f1a,1,'LSD',1)               /* get logical screen descriptor */
  494.    if abbrev(ab3,'ERROR')=1 then do
  495.          call dosay2 "Error: not a valid GIF file: "infile.kmm
  496.          return 0
  497.     end /* do */
  498.  
  499.    ct_name='ct.'                          /* extract info from it */
  500.    stuff=read_lsd_block(ab1)
  501.    parse var stuff width.kmm height.kmm gcflag.kmm gcsize.kmm colres.kmm ,
  502.                    sort.kmm bkgcolor.kmm aspect.kmm
  503.  
  504. /* determine which IMG and which GCE block to read */
  505.    dogce=1 ; doimg=1
  506.    if mg1<0  then mg1=1
  507.    if mg1<>1 then do            /* make sure there's mg1 images */
  508.          imgct=0 ; iat=0
  509.          do forever
  510.             iat=wordpos('IMG',talist,iat+1)
  511.             if iat=0 then leave
  512.             imgct=imgct+1
  513.          end 
  514.          doimg=min(imgct,mg1)
  515.          if doimg<>mg1 & verbose=1 then call dosay "   Warning: using IMG block # "doimg
  516.  
  517.          iat=0; gcect=0
  518.          do forever
  519.             iat=wordpos('GCE',talist,iat+1)
  520.             if iat=0 then leave
  521.             gcect=gcect+1
  522.          end /* do */
  523.          dogce=min(gcect,mg1)
  524.          if dogce<>mg1 & verbose=1 then call dosay "   Warning: using GCE block # "dogce
  525.    end
  526.  
  527.    ab2=read_gif_block(f1a,dogce,'GCE',1)               /* get graphical control extension */
  528.   if abbrev(ab2,'ERROR')=1 then do
  529.          call dosay2 "Error: not a valid GIF file: "infile.kmm
  530.          return 0
  531.   end /* do */
  532.  
  533.    stuff=READ_GCE_BLOCK(ab2)                /* extract info from it */
  534.    parse var stuff adisposal usrinflag.kmm tcflag.kmm delay.kmm tcindex.kmm
  535.    ab3=read_gif_block(f1a,doimg,'IMG',1)
  536.    img_name='img.' ; ct_name='loct.'
  537.    stuff=read_image_block(ab3,1)
  538.    parse var stuff lpos.kmm tpos.kmm widtha.kmm heighta.kmm islct lctsize ,
  539.                    interl.kmm sort.kmm ',' imgdata.kmm
  540.    foo=cvcopy('img',imgs.kmm)
  541.    if kmm=1 & resize_mode=0 then do
  542.        width1=widtha.1 ; height1=heighta.1
  543.    end 
  544.    if resize_mode=1 then do
  545.        width1=max(width1,widtha.kmm); height1=max(height1,heighta.kmm)
  546.    end 
  547.  
  548.    if islct>0 then do
  549.       call dosay " .... using local color table "
  550.       drop ct.
  551.       gcsize.kmm=lctsize
  552.       foo=cvcopy('loct','ct')
  553.    end
  554.    foo=cvcopy('ct',cts.kmm)
  555. end
  556.  
  557. /* now, resize images (if necessary), and count pixels */
  558. do kmm=1 to infile.0
  559.  
  560.    if (width1<>widtha.kmm) | (height1<>heighta.kmm) | , 
  561.              (transform.kmm=1)   then do /*need to resize */
  562.       if verbose>0 then call dosay "Resizing "kmm ') 'infile.kmm " from " widtha.kmm 'x' heighta.kmm " to " width1 'x' height1
  563.       if transform.kmm=1  then do       /* save original for later use */
  564.           foo=cvcopy('imgs.'kmm,'origimg.'kmm)
  565.       end
  566.  
  567.       aaa=get_transforms(1,,kmm)
  568.       parse var aaa dh','dw','dx','dy','drz','dry','drx
  569.  
  570.       FOO=CVCOPY('IMGS.'KMM,'AIMG')
  571. /* make_resizedi will either scale an image to be width1xheight1, or
  572.    will trabnsform an image (using dh,etc.), and fill in non-identified
  573.    pixels with the background pixel */
  574.       foo=make_resizedi(transform.kmm,kmm,width1,height1,dh,dw,dx,dy,drz,dry,drx)
  575.       foo=cvcopy('resizedi','imgs.'kmm)
  576.       if transform.kmm=1 then foo=cvcopy('trmask','trmasks.'kmm)   /* retain transformation mask */
  577.       img_name='resizedi.'      /* re create imgdata.kmm */
  578.       aw=make_image_block(0,0,width1,height1,0,0,interl.kmm,0,0)
  579.       stuff=read_image_block(aw,0)
  580.       parse var stuff . ',' imgdata.kmm
  581.       height.kmm=height1 ; width.kmm=width1  /* save new "screen size */
  582.  
  583.    end                                  /* done resizing */
  584.  
  585.    foo=cvcopy('IMGS.'kmm,'AIMG')
  586.    IMGNAME='AIMG.'                     /* count occurences of pixel values */
  587.    if verbose>0 then call dosay " ... counting pixels in " infile.kmm
  588.    FOO=COUNT_PIXELS(cts.kmm.0)         /* RETURN AS 'nn nn nn ... ' */ 
  589.    DO MM1=1 TO WORDS(FOO)              /* this is used in ct creation */
  590.       mm2=mm1-1
  591.       parse var foo cts.kmm.!ct.mm2 foo
  592.    end /* do */
  593. end 
  594. cts.0=infile.0
  595.  
  596. call dosay " image data read (w x h = "width1 'x' height1
  597.  
  598. /*3) Create a new "combined and shrunken" color table */
  599.  
  600. aa=make_new_ctable(ct_newlen,infile.0*height1*width1,ct_make_spec)
  601.  
  602. /* 4) remap images to ctnew2
  603.    Note that 0 of ctnew2 is "transparent", and pointers to it should never
  604.    occur in img1 or img2 (with the exception of "transformed images background") */
  605. ch0=c2d(0)
  606. do icth=1 to cts.0
  607.    call dosay "Remapping pixels in image # "icth
  608.    do mm=0 to cts.icth.0-1
  609.       rr=cts.icth.!r.mm ; gg=cts.icth.!g.mm ; bb=cts.icth.!b.mm
  610.       ichk=checked2.rr.gg.bb
  611.       cts.icth.!map.mm=ichk
  612.    end /* do */
  613.    jindx=tcindex.icth                   /* map transparent to pixel 0 */
  614.    if no_transparent<>1 & tcflag.icth=1 then cts.icth.!map.jindx=0
  615.  
  616.    do ir=0 to imgs.icth.!rows-1   /* height1-1 */
  617.       arow=imgs.icth.ir
  618.       newrow=''
  619.       do mm=1 to imgs.icth.!cols  /* width1 (1..width1 characters in string ) */
  620.          ivv=c2d(substr(arow,mm,1))
  621.          if cts.icth.!map.ivv<0 then 
  622.             call dosay2 " ERROR AT FirstImage: " ir mm ivv cts.icth.!map.0
  623.  
  624.          use1=d2c(cts.icth.!map.ivv)       /* remap pixel, or use background pixel */
  625.          if transform.icth=1 then do
  626.             if substr(trmasks.icth.ir,mm,1)=ch0 then use1=ch0
  627.          end  
  628.          newrow=newrow||use1
  629.       end /* do */
  630.       imgs.icth.ir=newrow
  631.    end                          /*row ir of image icth */
  632.  
  633.  
  634. /* transform the original images? */
  635.    if transform.icth=1 then do
  636.      do ir=0 to origimg.icth.!rows-1   /* height1-1 */
  637.        arow=origimg.icth.ir
  638.        newrow=''
  639.        do mm=1 to origimg.icth.!cols-1  /* width1-1 */
  640.          ivv=c2d(substr(arow,mm,1))
  641.          if cts.icth.!map.ivv<0 then 
  642.             call dosay2 " ERROR AT FirstImage: " ir mm ivv cts.icth.!map.0
  643.          use1=d2c(cts.icth.!map.ivv)       /* remap pixel, or use background pixel */
  644.          newrow=newrow||use1
  645.        end /* do */
  646.        origimg.icth.ir=newrow
  647.      end
  648.    end                          /*row ir of image icth */
  649.  
  650. end
  651.  
  652.  
  653. ctnew2.!r.0=r_back ; ctnew2.!g.0=g_back ; ctnew2.!b.0=b_back
  654.  
  655. /* --------- NOW create the various frames of the animation */
  656.  
  657. /* 5) WRITE LSD and FIRST image */
  658.  
  659. ct_name='ctnew2.'
  660. a1=MAKE_LSD_BLOCK(width1,height1,1,7,0,bkgcolor.1,aspect.1,ctnew2.0)
  661. iii=infile.0
  662.  
  663. a2=make_comment_block('BlendGif: 'anim_type" animation from "infile.1  " and " infile.2 )
  664.  
  665. a3=make_animation_block(do_iter)
  666. tcflag1=tcflag.1
  667. if no_transparent=1 then tcflag1=0
  668. a4=MAKE_GCE_BLOCK(tcflag1,tcindex.1,frame_delay.1,adisposal0,0)
  669.  
  670. foo=cvcopy(cts.1,'ct1')
  671. ct_name='ct1.'
  672. a5=make_image_block(0,0,width.1,height.1,1,gcsize.1,interl.1,0,imgdata.1)
  673. aa=a1||a2||a3||a4||a5
  674.  
  675. /*5) And now start building the animated GIF file */
  676.  
  677. /* 5a) note that the first GIF file is the "base" upon which future
  678.       images are built, hence is not animated (although it can be transformed)
  679. */
  680. cycleaa=''
  681. use_newimg=0
  682. do kmoo=1 to infile.0-1
  683.   ido1=kmoo ; ido2=kmoo+1          
  684.   if use_newimg=1 then                  /* a trick to have cumulative additions */
  685.     foo=cvcopy('newimg','img1')
  686.   else                                  /* use next */
  687.      foo=cvcopy('imgs.'ido1,'img1')
  688.   foo=cvcopy('imgs.'ido2,'img2')
  689.   img2_trans=0
  690.   if transform.ido2=1  then do
  691.       img2_trans=1
  692.       foo=cvcopy('trmasks.'ido2,'trmask')
  693.       foo=cvcopy('origimg.'ido2,'aimg')
  694.    end /* do */
  695.    
  696.  
  697. /* 5a2) get "image-pair" specific parameters */
  698.   nframes=frames.kmoo ; anim_type=strip(translate(anim_type.kmoo))
  699.   stop_after=stop_after.kmoo  
  700.  
  701.   if stop_after=0 | stop_after='' then stop_after=110000
  702.   centerx=centerx.kmoo  ; centery=centery.kmoo 
  703.  
  704.   balloon_type=strip(translate(balloon_type.kmoo))
  705.   ktt=wordpos(balloon_type,'SQUARE DIAMOND OCTAGON CIRCLE')
  706.   if ktt>0  then balloon_type=ktt
  707.  
  708.   balloon_push=strip(translate(balloon_push.kmoo))
  709.   jtt=wordpos(balloon_push,'OVERWRITE PUSH SQUOOSH')
  710.   if jtt>0 then balloon_push=jtt-1
  711.      
  712.   fade_type=strip(translate(fade_type.kmoo))
  713.   jtt=wordpos(fade_type,'FREQUENCY BRIGHTNESS COLOR_BRIGHTNESS BEST_MATCH')
  714.   if jtt>0 then fade_type=jtt-1
  715.  
  716.   CURTAIN_TYPE=strip(translate(CURTAIN_TYPE.kmoo))
  717.   CURTAIN_OVERWRITE=strip(translate(CURTAIN_OVERWRITE.kmoo))
  718.  
  719.   dissolve_spec=dissolve_spec.kmoo 
  720.   adelay=frame_delay.kmoo  
  721.   if anim_type='MASK' then do
  722.      do il0=1 to mask.0
  723.         mask.il0=mask.il0.kmoo ; mask.il0.!thresh=mask.il0.!thresh.kmoo
  724.      end /* do */
  725. /* MASK_LIST supersedes MASKS. entreis  */
  726.     if mask_list.kmoo<>'' & mask_list.kmoo<>0 then do
  727.         do mk=1 to words(mask_list.kmoo)
  728.             awm=strip(word(mask_list.kmoo,mk))
  729.             mask.mk=awm ; mask.mk.!thresh=0
  730.         end /* do */
  731.         mask.0=words(mask_list.kmoo)
  732.     end /* do */
  733.   end
  734.  
  735.   call dosay anim_type " from " infile.kmoo  " to " infile.ido2 
  736.  
  737. /* 5b) might need to create some lookup stuff  */
  738.   if anim_type='FADE' then do     
  739.        if fade_type=2 & gotroutes=0 then do
  740.           foo=make_ctroutes()
  741.           gotroutes=1
  742.         end /* do */
  743.         if fade_type=3 & gotminfos=0 then do 
  744.               gg=make_regions(nregions)
  745.               gotminfos=1
  746.         end /* do */
  747.    end
  748.  
  749. /* 5c) Create sorted index into CTNEW2 (creates sorted_ct.)  */
  750.    if anim_type='FADE' then foo=sort_ctnew2(fade_type) 
  751.  
  752. /* 5d) do the animation!!! */
  753.    if nframes>0 | anim_type='ADD' then call do_anims anim_type   
  754.  
  755. /* 5e) add the actual 2nd image (as the "final frame" of the set of frames for this image-pair) */
  756.    use_newimg=0
  757.    if (nframes<stop_after | stop_after=0) & anim_type<>'ADD' then do
  758.       if (ido2<>infile.0) | (ido2=infile.0 & cycle=1) then do    /* finalize the idoo2 image */
  759.         if transform.ido<>1 then do
  760.            tcflag2=tcflag.ido2
  761.            if no_transparent>0 then tcflag2=0
  762.            a6=MAKE_GCE_BLOCK(tcflag2,tcindex.ido2,adelay,adisposal0,0)
  763.            foo=cvcopy(cts.ido2,'ct2')
  764.            ct_name='ct2.'
  765.            a7=make_image_block(0,0,width1,height1,1,gcsize.ido2,interl.ido2,0,imgdata.ido2)
  766.            aa=aa||a6||a7
  767.            if cycle=1 & ido2<>infile.0 then cycleaa=a6||a7||cycleaa
  768.         end             /* else, it was done in do_anim */
  769.       end
  770.     end                 /* nframes < stop_after */
  771.     else do                     /* either stop_after is active, or ADD anim_type */
  772.        use_newimg=1             /* then use current image as first image of next pair */
  773.     end /* do */
  774.  
  775. /* a few more reasons for using current image as first image in next pair */
  776.     if transform.ido2=1 & bkg_transparent.ido2>0 then use_newimg=1 
  777.     if anim_type='ADD' then use_newimg=1
  778.  
  779. end                             /* infiles loop */
  780.  
  781. /* 5f) WRITE final image (note use of imgdata instead of img. array)*/
  782. if (nframes>=stop_after  & stop_after<>0 ) | ( transform.ido2=1) then do
  783.    if cycle=1 then do
  784.        tcflag2=tcflag.1
  785.        if no_transparent=1 then tcflag2=0
  786.        a6=MAKE_GCE_BLOCK(tcflag2,tcindex.1,adelay,adisposal0,0)
  787.        foo=cvcopy(cts.1,'ct2')
  788.        ct_name='ct2.'
  789.        a7=make_image_block(0,0,width1,height1,1,gcsize.1,interl.1,0,imgdata.1)
  790.        aa=aa||cycleaa||a6||a7||make_terminator_block()
  791.     end
  792.     else do
  793.        aa=aa||make_terminator_block()
  794.     end /* do */
  795. end /* do */
  796. else do
  797.   if cycle=1 then do     /* cycle back */
  798.     ill=1
  799.     tcflag2=tcflag.ill
  800.     if no_transparent=1 then tcflag2=0
  801.     a6=MAKE_GCE_BLOCK(tcflag2,tcindex.ill,adelay,adisposal0,0)
  802.     foo=cvcopy(cts.ill,'ct2')
  803.     ct_name='ct2.'
  804.     a7=make_image_block(0,0,width1,height1,1,gcsize.ill,interl.ill,0,imgdata.ill)
  805.     aa=aa||cycleaa||a6||a7||make_terminator_block()
  806.   end
  807.   else do         
  808.     ill=infile.0
  809.     tcflag2=tcflag.ill
  810.     if no_transparent=1 then tcflag2=0
  811.     a6=MAKE_GCE_BLOCK(tcflag2,tcindex.ill,adelay,adisposal0,0)
  812.     foo=cvcopy(cts.ill,'ct2')
  813.     ct_name='ct2.'
  814.     a7=make_image_block(0,0,width1,height1,1,gcsize.ill,interl.ill,0,imgdata.ill)
  815.     aa=aa||a6||a7||make_terminator_block()
  816.   end 
  817. end
  818.  
  819. /* try and shrink image? */
  820. if  shrink_image=1 then do
  821.     call dosay "... shrinking image "
  822.      aa=do_shrink_gif(aa)
  823. end /* do */
  824.  
  825. /* 6) Write image, and all done */
  826. if is_cgi=2 then do
  827.    xx=sysfiledelete(OUTFILE)
  828.    foo=charout(outfile,aa,1)
  829.    foo2=stream(outfile,'c','close')
  830.    call dosay "New file created: " outfile
  831.    IF YESNO(' Display this image using '||img_prog) =1 then do
  832.        oo=stream(outfile,'c','query exists')
  833.        ar1=translate(oo,':','|')
  834.        ar1=translate(ar1,'/','\')
  835.        foo=img_prog' file:///'||ar1
  836.        '@start /f 'foo
  837.        say cy_ye " >>> starting "img_prog ||normal" (it might take a few seconds...)"
  838.    end                  /* display with "img_prog" */
  839.    exit
  840. end
  841. /* if here, sre-http addon or cgi */
  842. if is_cgi=0 then do
  843.   if save_tempfile<>1 then do           /* send image as final part */
  844.      foo=sref_multi_send('... finished!','text/plain','SE')
  845.      gt='image/gif'||'0d0a'x||'Content-Disposition: attachment ; filename="'||outfile||'"'
  846.      foo=sref_multi_send(aa,gt,'E') 
  847.      return '200 '||length(aa)  
  848.   end
  849.   else do                /* send a link to the image */
  850.     tname=blendgif_root
  851.     bb=strip(blendgif_dir) ; bb=translate(blendgif_root,'\','/')
  852.  
  853. /* delete some old temp files? */
  854.     bb2=strip(bb,'t','\')||'\BLND*.GIF'
  855.     foo=sysfiletree(bb2,'foos','TF')
  856.     if foos.0>max_tempfiles then do     /* too many tempfiles, delete a few */
  857.       garg=min(5,1+(max_tempfiles/3))
  858.       do io=1 to garg
  859.          call deleteold
  860.       end /* do */
  861.       call pmprintf('BlendGIF: Deleted 'garg 'old temporary files ')
  862.     end 
  863.      
  864.     bb=strip(bb,'t','\')||'\BLND????.GIF'
  865.     bb=dostempname(bb)
  866.     foo=charout(bb,aa,1)
  867.     foo2=stream(bb,'c','close') 
  868.     outf=filespec('n',bb)
  869.     toview='<a href="/blendgif?showfile='outf'">your animated GIF</a>?'
  870.     call dosay " </pre> <b>Would you like to view</b> "toview'<p>'
  871.     call dosay "</body></html>"
  872.     return '200 '  
  873.   end   
  874. end
  875.  
  876. /* if here, cgi */
  877. say 'Content-type: image/gif '
  878. say 
  879. call charout,aa
  880. exit 
  881.  
  882.  
  883. erre:
  884.  call dosay2 "Error occured at line "sigl' 'rc
  885.  exit 
  886.  
  887.  
  888.  
  889. /*************************** END OF MAIN *******************/
  890.  
  891. /* ------------------- */
  892. deleteold:              /* real primitive search */
  893.   oldest='999999999999999' ; oldid=0
  894.   do ijo=1 to foos.0
  895.      parse var foos.ijo adate . 
  896.      if adate<oldest  then do
  897.          oldest=adate ; oldid=ijo
  898.      end /* do */
  899.   end       /* io loop */
  900.   parse var foos.oldid . . . afile
  901.   idid=sysfiledelete(strip(afile))
  902.   foos.oldid='99999999999999999999'
  903.   return 0
  904.  
  905.  
  906.  
  907. /********************/
  908. /* ask user for input file (of blendgif options */
  909. ask_input_file:
  910. do forever
  911.   arf=yesno(normal||bold||" and last of all -- "normal||reverse" read options from an input file",'NO YES ?','NO')
  912.   if arf=0 then return 0
  913.  
  914.   if arf=2 then do
  915.      say " "
  916.      say "You can read options from a BlendGIF input file."
  917.      say bold"An example of such a file is: "normal
  918.      say "    ; sample input file for BlendGIF"
  919.      say "    infile.0=2 "
  920.      say "     infile.1=hello "
  921.      say "     infile.2=goodbye "
  922.      say "     anim_type=balloon"
  923.      say "     balloon_push=squoosh"
  924.      say "     balloon_type=circle "
  925.      say "For further details on BlendGIF options, see the manual (BLENDGIF.DOC)"
  926.      say " "
  927.      iterate
  928.   end /* do */
  929.  
  930. /* if here, ask for file name */
  931.   do forever
  932.       say "     "bold" File name: " normal"(BLENDGIF.IN, ?=list files):"
  933.       call charout,"     "bold"? "normal
  934.       parse pull aa ; aa=strip(aa)
  935.       if aa='' then aa='BLENDGIF.IN'
  936.       if left(aa,1)="?" then do
  937.           parse var aa . thisdir
  938.           if thisdir="" then    thisdir=directory()
  939.           say 
  940.           say reverse ' List of .IN files in: ' normal bold thisdir normal
  941.           do while queued()>0
  942.              pull .
  943.            end /* do */
  944.           '@DIR /b  '||strip(thisdir,'t','\')'\*.in | rxqueue'
  945.           foo=show_dir_queue('.IN')
  946.           say
  947.           iterate
  948.       end
  949.       if pos('.',aa)=0 then aa=aa'.in'
  950.       foo=stream(aa,'c','query size')
  951.       if foo=0 | foo='' then do
  952.          say "       a) No such file: " aa
  953.          iterate
  954.       end /* do */
  955.       input_file=aa
  956.       call read_input_file
  957.       if result=0 then iterate   /* some kind of error */
  958.       return 1                  /* got it okay */
  959.  
  960.    end                  /* read file loop */
  961.  
  962. end /* top forreer */
  963.  
  964.  
  965. /************************/
  966. /* read an input file containing blendgif parameters */
  967. read_input_file:
  968. if input_file<>0 & input_file<>'' then do  
  969.    input_file=resolve_filename(input_file,blendgif_root,'.IN')
  970.    jaa=stream(input_file,'c','query size')
  971.    if jaa=0  | jaa='' then do
  972.       call dosay2 "BlendGIF error: no such input file: "input_file
  973.       return 0
  974.    end /* do */
  975.    foo=stream(input_file,'c','open read')
  976.    input_file_upload=charin(input_file,1,jaa)
  977. end
  978. if input_file_upload<>'' & input_file_upload<>0 then do
  979.    iread=0
  980.    do forever           /* parse and use parameters in input file */
  981.       if input_file_upload='' then leave
  982.       parse var input_file_upload a1 (crlf) input_file_upload
  983.       a1=strip(a1) 
  984.       if abbrev(a1,';')=1 then iterate  /* skip comments */
  985.        parse var a1 a1a '=' a1b
  986.        a1a=strip(decodekeyval(translate(a1a,' ','+'||'09000a0d'x)))
  987.        a1b=strip(decodekeyval(translate(a1b,' ','+'||'09000a0d'x)))
  988.        a1b=translate(a1b,'.','$')               /* since javascript don't like a.b names */
  989.        if a1b='' then iterate                   /* blank entry, ignore */    
  990.  
  991.        if valid_parameter(a1a,param_list)=0 then do
  992.            call dosay2 'BlendGIF error. No such parameter (input-file): 'a1
  993.            return 0 
  994.        end /* do */
  995.       if abbrev(translate(a1a),'UPFILE')=1 then do       /* special case -- read lines, decode */
  996.           ccc=''
  997.           parse var a1a . '.' mmm
  998.           infile.mmm=' Uploaded file = ' a1b
  999.           do forever
  1000.              if input_file_upload='' then leave
  1001.              parse var input_file_upload  c0 (crlf) input_file_upload
  1002.              if c0='' then leave                /* blank line signals end of file upload */
  1003.              ccc=ccc||c0
  1004.           end /* do */
  1005.           a1b=unpack64(ccc)
  1006.        end /* do */
  1007.        xx=value(a1a,a1b)
  1008.        iread=iread+1
  1009.    end /* do */
  1010.    call dosay "# of options read from input file= " iread
  1011. end /* do */
  1012.  
  1013. return 1
  1014.  
  1015. /***************************/
  1016. /* determine transformation factors, given "nth" frame in kth 
  1017. set of nframes factors. 
  1018. Speial case: If "nth"=1 then just use first word in each factor */
  1019. get_transforms:procedure expose nuheight. nuwidth. xmove. ymove. ,
  1020.                                zrotate. yrotate. xrotate. 
  1021. parse arg nth,inlist,kmm
  1022. if nth=1 then do
  1023.  dh=word(nuheight.kmm,1) ; dw=word(nuwidth.kmm,1)
  1024.  dx=word(xmove.kmm,1) ; dy=word(ymove.kmm,1)
  1025.  drz=word(zrotate.kmm,1); dry=word(yrotate.kmm,1)
  1026.  drx=word(xrotate.kmm,1)
  1027. end
  1028. else do
  1029.  dh=get_user_scale(nth,inlist,nuheight.kmm) 
  1030.  dw=get_user_scale(nth,inlist,nuwidth.kmm)
  1031.  dx=get_user_scale(nth,inlist,xmove.kmm) 
  1032.  dy=get_user_scale(nth,inlist,ymove.kmm)
  1033.  drz=get_user_scale(nth,inlist,zrotate.kmm)
  1034.  dry=get_user_scale(nth,inlist,yrotate.kmm)
  1035.  drx=get_user_scale(nth,inlist,xrotate.kmm)
  1036. end /* do */
  1037. return dh','dw','dx','dy','drz','dry','drx
  1038.  
  1039.  
  1040. /****************************************/
  1041. /* resize/transform AIMG., returns RESIZEDI
  1042.   NEWIMG. will be height1 x width1;
  1043.   If dotransform=1, then and translate/rotate/scale, and possibly
  1044.   fill background 
  1045.   If dotransform=0, then just scale it up (or down)
  1046.   Other Arguments:  
  1047.   knn = points to some parameters arrays
  1048.   nuheight = height of full image
  1049.   nuwidth = width of full image
  1050.   myheight = image scaled to this height
  1051.   mywidth = image scaled to this width
  1052.   myxmove,myymove = move this way horiz and vertical
  1053.   zrotate, yrotate, xrotate=rotate by this angle(s)
  1054. */
  1055. make_resizedi:procedure expose resizedi. trmask. aimg. ,
  1056.                                bkg_transparent. tcflag. tcindex.
  1057.  
  1058. parse arg dotransform, knn,width1,height1,myheight,mywidth,myxmove,myymove, ,
  1059.               zrotate,yrotate,xrotate
  1060.  
  1061. drop resizedi.
  1062. resizedi.=0
  1063. /* resize to width1, height1 (no transformations?) */
  1064. if dotransform<>1 then do
  1065.   resizedi.!rows=height1 ; resizedi.!cols=width1
  1066.   wfact=(AIMG.!cols-1)/max(1,(width1-1))
  1067.   hfact=(AIMG.!rows-1)/max(1,(height1-1))
  1068.   oldir1a=-1
  1069.   do ir1=0 to height1-1
  1070.     ir1a=trunc(ir1*hfact)
  1071.     if oldir1a=ir1a then do
  1072.        irn1=ir1-1
  1073.        resizedi.ir1=resizedi.irn1
  1074.        iterate
  1075.     end /* do */
  1076.     userow=AIMG.ir1a
  1077.     new1=''
  1078.     do ic1=0 to width1-1
  1079.        ic1a=trunc(ic1*wfact)
  1080.        new1=new1||substr(userow,ic1a+1,1)
  1081.     end                         /* ic1 = 0 to width-1 */
  1082.     resizedi.ir1=new1
  1083.     oldir1a=ir1a
  1084.   end           /* ir1 .. 0 to height-1 */
  1085.   return 1
  1086. end
  1087.  
  1088.  
  1089. /* ------------ if here, this is a "transformation" */
  1090. resizedi.=0
  1091. resizedi.!cols=width1 ; resizedi.!rows=height1  /* full "window" size */
  1092.  
  1093. if zrotate='' then zrotate=0
  1094. if yrotate='' then yrotate=0
  1095. if xrotate='' then xrotate=0
  1096. BKG_TRANSPARENT=BKG_TRANSPARENT.KNN
  1097. IF wordpos(bkg_transparent,'0 1 2')=0 then bkg_transparent=0
  1098. if datatype(myheight)<>'NUM' then   myheight=AIMG.!ROWS
  1099. if datatype(mywidth)<>'NUM' then    mywidth=AIMG.!COLS
  1100. if datatype(myxmove)<>'NUM' then    myxmove=0
  1101. if datatype(myymove)<>'NUM' then    myymove=0
  1102. if datatype(zrotate)<>'NUM' then   zrotate=0
  1103. if datatype(xrotate)<>'NUM' then   xrotate=0
  1104. if datatype(yrotate)<>'NUM' then   yrotate=0
  1105.  
  1106. /* CONVERT FRACTIONS APPROPRIATELY */
  1107. IF pos('.',myheight)>0 then myheight=trunc(AIMG.!rows*myheight)
  1108. IF pos('.',mywidth)>0   then mywidth=trunc(AIMG.!cols*mywidth)
  1109. /* enforce minimums */
  1110. myheight=max(2,myheight) ; mywidth=max(2,mywidth)
  1111.  
  1112. /* scale factors */
  1113. hscale=myheight/AIMG.!rows
  1114. wscale=mywidth/AIMG.!cols
  1115.  
  1116. if pos('.',myxmove)>0 then myxmove=trunc(myxmove*width1)
  1117. if pos('.',myymove)>0 then myymove=trunc(myymove*height1)
  1118.  
  1119. blank1=copies(d2c(0),width1)
  1120. do ir1=0 to height1-1             /* initialize */ 
  1121.     resizedi.ir1=blank1          
  1122.     trmask.ir1=blank1            
  1123. end
  1124. ch1=d2c(255)
  1125.  
  1126.  
  1127. /* compute the transformation and inverse transformation matrices */
  1128.    tran_matrix='trnmtx.'; inv_tran_matrix='itrnmtx.'
  1129.    astatus=create_trans_matrix(AIMG.!cols-1,AIMG.!rows-1, ,
  1130.                             wscale,hscale,zrotate,yrotate,xrotate,myxmove,myymove)
  1131.  
  1132. /* the transformation works in reverse direction -- for each spot in
  1133. the "new image", we find where it would have come from in the old image.
  1134. As an efficiency measure, we bound the area of this "detransformation"
  1135. by transforming the corners, and finding mins and maxs */
  1136.  
  1137. tran_matrix='trnmtx.'
  1138. parse value transfrm_point(0,0,0) with x0 y0 z0
  1139. parse value transfrm_point(AIMG.!cols-1,0,0) with x1 y1 z1
  1140. parse value transfrm_point(AIMG.!cols-1,AIMG.!rows-1,0) with x2 y2 z2
  1141. parse value transfrm_point(0,AIMG.!rows-1,0) with x3 y3 z3
  1142.  
  1143. /* find bounds of where transformation will fall (on the "screen") */
  1144. xmin=max(min(x1,x2,x3,x0,width1-1),0)   /* keep it on the "screen" */
  1145. xmax=min(max(x1,x2,x3,x0,0),width1-1)
  1146. ymin=max(min(y1,y2,y3,y0,height1-1),0) 
  1147. ymax=min(max(y1,y2,y3,y0,0),height1-1)
  1148.  
  1149. /* define plane equation */
  1150. xa=x1-x0 ; ya=y1-y0 ; za=z1-z0  /* equation for line A */
  1151. xb=x2-x0 ; yb=y2-y0 ; zb=z2-z0  /* equation for line B */
  1152. xab=(ya*zb)-(za*yb);            /* AxB, x component */
  1153. yab=-( (xa*zb)-(za*xb))         /* AxB, y component */
  1154. zab=(xa*yb)-(ya*xb)             /* AxB, z component */
  1155. /* the _ab line is normal to the plane defined by lines a and b */
  1156. D=(xab*x0)+(yab*y0)+(zab*z0)    /* Ax + By + Cz = D  */
  1157.  
  1158. /* z function is:  znew = (D- ((xnew*xab)+(ynew*yab)) ) /  zab  */
  1159. ISTC=0 ; TCVAL=CH1
  1160. IF TCFLAG.KNN=1 then DO
  1161.   ISTC=1 ; TCVAL=D2C(TCINDEX.KNN)
  1162. END
  1163.  
  1164. /* resize, and make the TRmask. Note: default is "pixel is not useable"
  1165. (that it is transparent) */
  1166.  
  1167. tran_matrix='itrnmtx.'
  1168. do ir1=ymin to ymax
  1169.   do ic1=xmin to xmax
  1170.      if zab=0 then
  1171.         iz1=0
  1172.      else
  1173.         iz1= (d - ((ic1*xab)+(ir1*yab)))/ zab
  1174.      parse value transfrm_point(ic1,ir1,iz1) with ocol orow odepth
  1175.      if orow<0 | ocol<0 | orow>=AIMG.!rows | ocol>=AIMG.!cols then iterate
  1176.      pold=substr(AIMG.orow,ocol+1,1)
  1177.      resizedi.ir1=overlay(pold,resizedi.ir1,ic1+1,1)
  1178.      if (bkg_transparent=2) then do   /* mark tc pixels as background transparent? */
  1179.         if (istc=1 & pold=tcval) then iterate /* do NOT mark this as "useable */
  1180.      end /* do */
  1181.      trmask.ir1=overlay(ch1,trmask.ir1,ic1+1,1)
  1182.   end /* columns */
  1183. end /* rows */
  1184.  
  1185.  
  1186. return 1
  1187.  
  1188.  
  1189. /************************/
  1190. /* allow user to set parameters */
  1191. set_params0:
  1192. parse arg didp00
  1193. if didp00<>1 then do
  1194.   say
  1195.   say "     " cy_ye" BlendGif -- blend several GIF files into a multi-frame animated GIF" normal
  1196.   say
  1197.   say bold" Please specify a few parameters."normal
  1198.   say     "  "bold"*"normal" values in parenthesis, and "cy_ye"H"normal"ighlighted choices, are the defaults "
  1199.   say     "  "bold"*"normal" [Up] means : Hit Upper Arrow to go back to a prior question "
  1200.   say     "  "bold"*"normal" The BlendGIF manual is BlendGIF.DOC"
  1201.   if stream('BLENDGIF.DOC','c','query exists')<>'' then do
  1202.      ii=yesno(normal"      "bold"Would you like to view BLENDGIF.DOC in another window?"normal,,'N')
  1203.      if ii=1 then '@START  "The BlendGif Manual" /C /F /WIN E BLENDGIF.DOC'
  1204.   end
  1205. end
  1206. else do
  1207.   say
  1208.    say cy_ye'... please re-enter parameters...'normal
  1209.   say
  1210. end /* do */
  1211. say
  1212. do forever
  1213.  
  1214.  infile.0=ask_integer('INFILE.0'," Number of GIF files (or URLS) to blend:",infile.0,2)
  1215.  
  1216. if datatype(infile.0)<>'NUM' then signal if0
  1217. mm=0
  1218. do forever
  1219.    if mm>=infile.0 then leave
  1220.    mm0=mm+1
  1221.    say "    (INFILE."mm0" )" bold" File name (or URL): " normal"("infile.mm0 ", ?=list files, *=wildcard):"
  1222.    call charout,"     "bold"? "normal
  1223.    parse pull aa
  1224.    if left(aa,1)="?" then do
  1225.         parse var aa . thisdir
  1226.         if thisdir="" then    thisdir=directory()
  1227.         say 
  1228.         say reverse ' List of GIF files in: ' normal bold thisdir normal
  1229.         do while queued()>0
  1230.             pull .
  1231.          end /* do */
  1232.         '@DIR /b  '||strip(thisdir,'t','\')'\*.gif | rxqueue'
  1233.         foo=show_dir_queue('.GIF')
  1234.         say "  "bold"*"normal" Note: to retrieve a GIF file on the www,  "
  1235.         say "          enter it's complete URL (including the http://) "
  1236.         say
  1237.         iterate
  1238.    end
  1239.  
  1240.    if pos('*',aa)>0 then do           /* get wildcards */
  1241.          oo=sysfiletree(aa,goobs,'O')
  1242.          say " Found "goobs.0' matches to: 'aa
  1243.          do jj=1 to goobs.0
  1244.            mm=mm+1
  1245.            infile.mm=goobs.jj
  1246.            iii=get_img_Num(mm') 'infile.mm '(0 to skip)',0) 
  1247.            if iii=0 then do             /* 0 means "skip this one" */
  1248.              mm=mm-1
  1249.              iterate
  1250.            end /* do */
  1251.            infile.mm.!nth=iii
  1252.            if mm>=infile.0 then leave
  1253.         end               
  1254.         iterate
  1255.    end /* do */
  1256.  
  1257.   if aa='' then aa=infile.mm0 
  1258.   if pos('.',aa)=0 then aa=aa||'.GIF'   
  1259.   if abbrev(translate(aa),'HTTP://')=0 then do
  1260.     if resolve_filename(aa,BLENDGIF_ROOT,'.GIF')='' then do
  1261.        say "Plese reenter (no such file: "aa ')'
  1262.        iterate
  1263.     end 
  1264.   end
  1265.   mm=mm+1               /* record name, and nth image */
  1266.   infile.mm =aa
  1267.   infile.mm.!nth=get_img_Num('')
  1268. end
  1269.  
  1270. yesans.0='NO' ; yesans.1='YES'
  1271.  
  1272. qq1: cycle=yesno("(CYCLE) Cycle through images (YES= first to last to first)",,yesans.cycle)
  1273.  
  1274. frames=ask_integer('FRAMES','# of intermediate frames between images:',frames,0)
  1275.  
  1276. iterations=ask_integer('ITERATIONS'," # of iterations of animation loop: ",iterations,1)
  1277.  
  1278. frame_delay=ask_integer('FRAME_DELAY','Delay between frame display (in 1/100th seconds)',frame_delay,1)
  1279.  
  1280. shrink_image=yesno(" Attempt to shrink final image file size (by using 'retain' disposal)?")
  1281.  
  1282.  
  1283. rsans.0='F' ; rsans.1='M'; rsans.2='U'
  1284. rhere: rr=yesno("(RESIZE_MODE) How to set image size" ,'FIRST_IMG MAX USER_SET ?',rsans.resize_mode,1)
  1285. if rr=-1 then  signal qq1
  1286. resize_mode=rr
  1287. if resize_mode=2 then do
  1288.    width1=ask_integer('     WIDTH1','Width of image (in pixels) ',width1,2)
  1289.    height1=ask_integer('    HEIGHT1','Height of image (in pixels)',height1,2)
  1290. end /* do */
  1291. if resize_mode=3 then do
  1292.   say " Determine the size of the image by: "
  1293.   say bold" *"normal" FIRST_IMG = Using the size of the first image "
  1294.   say bold" *"normal"       MAX = Using the maximum width and height (across all images) "
  1295.   say bold" *"normal"  USER_SET = Specify the width and height "
  1296.   signal rhere
  1297. end
  1298.  
  1299. jumpa:aa=yesno("(ANIM_TYPE) Animation type","ADD BALLOON CURTAIN DISSOLVE FADE MASK ?",anim_type,1)
  1300. if aa=-1 then signal rhere
  1301.  
  1302. anim_type=aa+1          /* first choice is 0 */
  1303. if anim_type=7 then do
  1304.     say ' '
  1305.     say reverse'Select one of: 'normal
  1306.     say '          'bold'ADD'normal': The second image is added onto the the first '
  1307.     say '     'bold' BALLOON'normal': The first image is replaced by an expanding "balloon" '
  1308.     say '               of the second image.'
  1309.     say '     'bold'CURTAIN'normal': The second image is a curtain pulled over the first image'
  1310.     say '     'bold'DISSOLVE'normal': The first image dissolves into the second image'
  1311.     say '         'bold'FADE'normal': The first image fades into the second image.'
  1312.     say '        'bold'MASK'normal': "Mask" files are used to overlay portions of the second image'
  1313.     say '               onto the first image.'
  1314.     say ' '
  1315.     signal jumpa
  1316. end /* do */
  1317. else do
  1318.    anim_type=strip(word('ADD BALLOON CURTAIN DISSOLVE FADE MASK',anim_type))
  1319. end /* do */
  1320.  
  1321. do forever
  1322.  select
  1323.    when anim_type="FADE" then do
  1324.       call charout,'  (FADE_TYPE) 'bold' Type of fade 'normal'('fade_type', ?=help):'
  1325.       pull aa
  1326.       if aa='' then aa=fade_type
  1327.       if aa="?" then do
  1328.            say "     Standard FADE_TYPE values (or, enter a equation in R,G and B): "
  1329.            say "        0 = color frequency           :  1=Brightness "
  1330.            say "        2 = Color specific brightness :  3=Best match "
  1331.            say "        Equation example: "bold" 2*R + G "normal
  1332.            iterate
  1333.       end /* do */
  1334.       fade_type=aa
  1335.    end /* do */
  1336.  
  1337.    when anim_type="BALLOON" then do
  1338.      bans.1='S'; bans.2='D'; bans.3='O'; bans.4='C'
  1339.       bb=yesno(normal'   (BALLOON_TYPE) 'bold' Type of balloon: 'normal,'SQUARE DIAMOND OCTAGON CIRCLE',bans.balloon_type,1)
  1340.       if bb=-1 then signal jumpa
  1341.       balloon_type=bb+1
  1342.       if balloon_type=4 then do
  1343.         bans.0='0';bans.1='P'; bans.2='S';bans.10='P'; bans.20='S'
  1344.         btmp=yesno(normal'   (BALLOON_PUSH) 'bold' Circular-balloon mode :'normal,'OVERWRITE PUSH SQUOOSH ',bans.balloon_push,1)
  1345.         if btmp<0 then signal jumpa
  1346.         if btmp>0 then do
  1347.            aa='Y'; if balloon_push<10 then aa='N'
  1348.            btmp2=yesno(normal'   (BALLOON_PUSH columns)'bold' Squoosh/Push columns only? ',,aa)
  1349.            if btmp2=-1 then signal jumpa
  1350.            if btmp2=1 then btmp=btmp||'0'
  1351.         end /* do */
  1352.         balloon_push=btmp
  1353.       end
  1354.    end                  /* balloon  */
  1355.  
  1356.    when  anim_type="CURTAIN" then do
  1357.         ctype=yesno(normal'  (CURTAIN_TYPE)'bold'Direction of curtain: 'normal,'LEFT_RIGHT TOP_BOTTOM MIDDLE_DRAW',1)
  1358.         if ctype=-1 then jumpa
  1359.         curtain_type=strip(word('L_R T_B MIDDLE',ctype+1))
  1360.         ctype=yesno(normal'  (CURTAIN_OVEWRWITE)'bold'Curtain Overwrite Mode: ','OVERWRITE PUSH SQUOOSH',1)
  1361.         if ctype=-1 then jumpa
  1362.         curtain_overwrite=strip(word('OVERWRITE PUSH SQUOOSH',ctype+1))
  1363.    end                   /* CURTAIN animtype */
  1364.  
  1365.    otherwise 
  1366.  end  /* select */
  1367.  leave
  1368.  
  1369. end
  1370.  
  1371.  
  1372. return 1
  1373.  
  1374. /**************/
  1375. /* is this a valid parameter? -- return 0 if not */
  1376. valid_parameter:procedure
  1377. parse upper arg aparam,alist
  1378.  
  1379. luse=aparam
  1380. foo=pos('.',luse)       
  1381. if foo>0 then luse=left(aparam,foo)
  1382. foo=wordpos(luse,alist)
  1383. return foo
  1384.  
  1385. /************/
  1386. /* unpack64   :  astring=unpack64(string_packed_64) */
  1387.  
  1388. unpack64:procedure
  1389. char_set='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
  1390. do mm=0 to length(char_set)-1
  1391.    a.mm=substr(char_set,mm+1,1)
  1392. end /* do */
  1393.  
  1394. parse arg mess
  1395. newmess=""
  1396. do mm=1 to length(mess)
  1397.    a1=substr(mess,mm,1)
  1398.    a1a=c2d(a1)
  1399.    select
  1400.      when a1a>64 & a1a<91  then a1b=a1a-65  /* ascii 65 to 90 */
  1401.      when a1a>96 & a1a<123 then a1b=26+(a1a-97)  /* ascii 97 to 122 */
  1402.      when a1a>47 & a1a<58  then a1b=a1a+4   /* ascii 48 to 57 */
  1403.      when a1='+' then a1b=62
  1404.      when a1='/' then a1b=63
  1405.      when a1='=' then iterate
  1406.      otherwise return ""        /* error */
  1407.    end
  1408.    pp=x2b(d2x(a1b))
  1409.    if length(pp)>6 then
  1410.         pp=substr(pp,3)
  1411.    else
  1412.         pp=right(pp,6,0)
  1413.    newmess=newmess||pp
  1414. end
  1415. ilen=trunc(length(newmess)/8)*8 ; newmess=left(newmess,ilen)
  1416. newm=x2c(b2x(newmess))
  1417. return newm
  1418.  
  1419.  
  1420. /************************************************/
  1421. /* procedure from TEST-CGI.CMD by  Frankie Fan <kfan@netcom.com>  7/11/94 */
  1422. DecodeKeyVal: procedure
  1423.   parse arg Code
  1424.   Text=''
  1425.   Code=translate(Code, ' ', '+')
  1426.   rest='%'
  1427.   do while (rest\='')
  1428.      Parse var Code T '%' rest
  1429.      Text=Text || T
  1430.      if (rest\='' ) then
  1431.       do
  1432.         ch = left( rest,2)
  1433.         if verify(ch,'01234567890ABCDEF')=0 then
  1434.            c=X2C(ch)
  1435.         else
  1436.            c=ch
  1437.         Text=Text || c
  1438.         Code=substr( rest, 3)
  1439.       end
  1440.   end
  1441.   return Text
  1442.  
  1443.  
  1444. /*********/
  1445.  
  1446.  
  1447. /**********/
  1448. /* ask for an integer (min value of minval */
  1449. ask_integer:procedure expose bold normal
  1450. parse arg  varname,amess,defval,minval
  1451. if minval='' then minval=0
  1452. if amess=''  then amess=' ? '
  1453. if defval='' then defval=minval
  1454. if varname='' then varname=word(amess,1)
  1455.  
  1456. do forever
  1457.   call  charout,'('varname')'bold||amess||normal||'('||defval||'):'
  1458.   pull aa
  1459.   if aa="" then aa=defval
  1460.   if datatype(aa)<>'NUM' then do
  1461.       say " You must enter an integer greater then or equal to " minval
  1462.       iterate
  1463.   end /* do */
  1464.   if aa<minval then do
  1465.       say " You must enter an integer greater then or equal to " minval
  1466.       iterate
  1467.   end /* do */
  1468.   return aa
  1469. end
  1470.  
  1471.  
  1472.  
  1473.  
  1474.  
  1475. /**********/
  1476. /* ask for image number */
  1477. get_img_Num:procedure expose bold normal
  1478. parse arg aname,amin
  1479. if amin='' then amin=1
  1480. if length(aname)>30 then  say aname 
  1481.  
  1482. do forever
  1483.   if length(aname)<=30 then do
  1484.     if aname='' then
  1485.        call charout,'    'bold'... Which frame (in this image) 'normal'(first frame): '
  1486.     else
  1487.        call charout,aname'    'bold'... which frame (in this image) 'normal'(first frame): '
  1488.   end
  1489.   else do
  1490.        call charout,'    'bold' ... which frame (in this image) 'normal'(first): '
  1491.   end /* do */
  1492.   pull aa
  1493.   if aa='' then aa=1
  1494.   if datatype(aa)<>'NUM' then do
  1495.         say "Bad frame number -- enter a positive integer "
  1496.         iterate
  1497.    end /* do */
  1498.    return trunc(max(amin,aa))
  1499. end
  1500.  
  1501.  
  1502. /************************/
  1503. /* allow user to set parameters */
  1504. set_params:
  1505. parse arg isq
  1506. signal on error name ggg ; signal on syntax name ggg
  1507. if isq>1 then do
  1508.   call showhelp
  1509.   call charout,cy_ye'...hit any key to continue'
  1510.   foo=sysgetkey('NOECHO');say
  1511. end
  1512. say bold" Please enter parameter values. "normal
  1513. say bold"  *"normal " Enter 1 value per line. "
  1514. say bold"  *"normal " When done, don't enter anything (just hit the ENTER key)"
  1515. say bold"  *"normal " To view current parameter settings, enter "bold"? "normal
  1516. say bold"  *"normal " To re-enter parameters, enter "bold"REDO"normal
  1517. say bold"  *"normal " To exit program, enter "bold"EXIT"normal
  1518. say "  Example: " reverse "   ANIM_TYPE='DISSOLVE' " normal
  1519. do forever
  1520.    call charout,bold "? " normal
  1521.     pull ain ; ain=strip(ain)
  1522.    if ain='' then return 1
  1523.    if translate(ain)='REDO' then return 2
  1524.    if ain="?" then do
  1525.       call showhelp
  1526.       iterate
  1527.    end
  1528.    interpret ain
  1529. end /* do */
  1530.  
  1531.  
  1532. ggg:
  1533. say
  1534. say " Bad entry. Did you forget a quote? "
  1535. say
  1536. signal set_params
  1537. return
  1538.  
  1539.  
  1540. /*****************************/
  1541. /* list parameters and current values */
  1542. showhelp:
  1543.  
  1544. btype.1='square'
  1545. btype.2='diamond'
  1546. btype.3='octagon'
  1547. btype.4='circle'
  1548.  
  1549. btype2.0='overwrite' ; btype2.1='push' ; btype2.2='squoosh'
  1550. btype2.10='push columns ' ; btype2.20='squoosh columns'
  1551.  
  1552. ctms.0='most frequent colors'
  1553. ctms.1='some binary search'
  1554. ctms.2='more binary search'
  1555.  
  1556. fty.='rexx string'
  1557. fty.0='frequency sort'
  1558. fty.1='brightness sort'
  1559. fty.2='color specific brightness sort'
  1560. fty.3='best match'
  1561.  
  1562. ptype.L_R="left to right"
  1563. ptype.T_B="top to bottom"
  1564. ptype.MIDDLE='converge in middle'
  1565.  
  1566. yesnoc.0="no" ; yesnoc.1="yes"
  1567.  
  1568. say "    " cy_ye"The more important parameters, and their current values."normal
  1569. SAY " INFILE.0 (# of files)= "bold' 'infile.0||normal||'.  INFILE.n  INFILE.n.!nth ['bold'filename'normal' image#] =='
  1570. aa=''
  1571. do iii=1 to infile.0
  1572.    af=infile.iii 
  1573.    kiki=infile.iii.!nth
  1574.    if datatype(kiki)<>'NUM' then kiki=1
  1575.    af='['bold||af||normal' '||kiki||'] '
  1576.    if length(af)<18 then af=left(af,17)
  1577.    if length(aa||af)>74 then do
  1578.       say '   ' aa
  1579.       aa=af
  1580.    end
  1581.    else do
  1582.       aa=aa||af
  1583.    end /* do */
  1584. end /* do */
  1585. if aa<>'' then say '   ' aa
  1586. say normal" CT_NEWLEN (length of global color table) = " bold' ' ct_newlen' 'normal
  1587. say " CT_MAKE_SPEC (global color table method; 0,1,2) = "bold' ' ct_make_spec' ('ctms.ct_make_Spec')'normal
  1588. say " FADE_REGIONS (dimensions of FADE index) = "bold ' ' fade_regions' 'normal 
  1589.  
  1590. say " FRAMES (# frames) = "bold' ' frames' 'normal  , 
  1591.       ', FRAME_DELAY (1/100 seconds) = 'bold' ' FRAME_DELAY ' ' normal
  1592. say " CYCLE (0,1) = "bold ' ' CYCLE' ('yesnoc.cycle') 'normal ', ITERATIONS = 'bold' 'iterations ' ' normal ,
  1593.     ", STOP_AFTER = "bold||stop_after||normal
  1594. say " ANIM_TYPE (add balloon curtain dissolve fade mask) = " bold' 'anim_type' ' normal
  1595. say "    BALLOON_TYPE (1,2,3,4) = " bold' 'balloon_Type' ('btype.balloon_type') 'normal  
  1596. say '       BALLOON_PUSH (0,1,2,10,20)= 'bold' ' balloon_push' ('btype2.balloon_push') ' normal
  1597. say "       CENTERX and CENTERY = "bold' ' centerx ' ' centery' ' normal
  1598. say "    CURTAIN_TYPE (L_R T_B MIDDLE) = " bold' ' curtain_type' ('ptype.CURTAIN_TYPE')' normal
  1599. say "        CURTAIN_OVERWRITE (OVERWRITE PUSH SQUOOSH) = " bold' ' curtain_overwrite normal
  1600. say "    FADE_TYPE = " bold' ' fade_type ' ('fty.fade_type')' normal
  1601. say "    DISSOLVE_SPEC = "bold' ' dissolve_spec' ' normal
  1602. SAY "    MASK.0 (# of mask files)= "bold' 'mask.0
  1603. aa=''
  1604. do iii=1 to mask.0
  1605.    af=mask.iii ; if length(af)<15 then af=left(af,14)
  1606.    if length(aa||af)>74 then do
  1607.       say '       ' aa
  1608.       aa=af
  1609.    end
  1610.    else do
  1611.       aa=aa||af
  1612.    end /* do */
  1613. end /* do */
  1614. say '       ' aa
  1615.  
  1616. return 1
  1617.  
  1618.  
  1619.  
  1620. /*******************************************/
  1621. use_sorted_ct:procedure expose sorted_ct. is_cgi BLENDGIF_ROOT save_tempfile
  1622. parse arg ac1,ac2,pp,sort_ct
  1623.  
  1624. /* use raw (frequency) pixel values */
  1625. if sort_ct=0 | sorted_ct.!is=0 then
  1626.  return ac1+trunc((ac2-ac1)*pp)
  1627.  
  1628. i1=sorted_ct.ac1
  1629. i2=sorted_ct.ac2       /* the "brightness" levels of pixel 1 and 2 */
  1630. ip=i1+trunc((i2-i1)*pp) /* an index to an intermediate brightness */
  1631. return sorted_ct.!rev.ip  /* this index points to a ctnew2. color */
  1632.  
  1633.  
  1634. exit
  1635.  
  1636.  
  1637.  
  1638.  
  1639. /**************************/
  1640. /* construct a set of animated frames, append to the AA variable
  1641.   Requires that img1 and img2 (the begin and end images) be
  1642.   set
  1643. */
  1644.  
  1645. do_anims:
  1646. parse arg anim_type
  1647.  
  1648. newimg.=0
  1649. newimg.!rows=height1
  1650. newimg.!cols=width1
  1651.  
  1652. btype2.0='Overwrite' ; btype2.1='Push' ; btype2.2='Squoosh'
  1653. btype2.10='PushX  ' ; btype2.20='SquooshX '
  1654.  
  1655. ch0=d2c(0)
  1656. cctype=anim_type
  1657. if cctype='CURTAIN' then cctype=curtain_overwrite
  1658. /* build a shutter style animation */
  1659. select
  1660.  
  1661. when cctype="ADD" then do
  1662.      foo=cvcopy('img2','newimg')
  1663.      itt=0
  1664.      if img2_trans=1 then do
  1665.         call do_fix_trans   /* enforce transparency of "transformed" image 2 */
  1666.      end
  1667.      a4=MAKE_GCE_BLOCK(itt,0,adelay,adisposal0,0)
  1668.      img_name='newimg.'
  1669.      a5=make_image_block(0,0,width1,height1,0,0,interl.1,0,0)  /* local ct NOT specified */
  1670.      aa=aa||a4||a5
  1671.      if cycle=1 then  cycleaa=a4||a5||cycleaa
  1672.  
  1673. signal on error name eek1err ;  signal on syntax name eek1err ;
  1674.  
  1675. /* now do other transformations (if any were specified) */
  1676.     oo=ismany_transforms(ido2)
  1677.     if oo=1 then do
  1678.       do jmm=1 to min(nframes,stop_after)
  1679.         aaa=get_transforms(jmm+1,nframes+1,ido2) 
  1680.         parse var aaa dh','dw','dx','dy','drz','dry','drx
  1681.   
  1682.         CALL DOSAY '   'jmm' of 'nframes'  frame of ADD image (' aaa
  1683.         foo=make_resizedi(1,kmm,width1,height1,dh,dw,dx,dy,drz,dry,drx)
  1684.         foo=cvcopy('resizedi','NEWIMG')
  1685.         itt=0
  1686.         if img2_trans=1 then do
  1687.           call do_fix_trans   /* enforce transparency of "transformed" image 2 */
  1688.         end
  1689.         a4=MAKE_GCE_BLOCK(itt,0,adelay,adisposal0,0)
  1690.         img_name='newimg.'
  1691.         a5=make_image_block(0,0,width1,height1,0,0,interl.1,0,0)  /* local ct NOT specified */
  1692.         aa=aa||a4||a5
  1693.         if cycle=1 then  cycleaa=a4||a5||cycleaa
  1694.       end /* do */
  1695.     end
  1696. end
  1697.  
  1698.  
  1699.  
  1700.  
  1701. when cctype="OVERWRITE" & CURTAIN_TYPE="T_B" then do
  1702.   rowchunk=(height1/(nframes+1))
  1703.   do mm=1 to min(nframes,stop_after)
  1704.      m2=trunc(rowchunk*mm)
  1705.      call dosay " CURTAIN: rows to " m2
  1706.  
  1707.      do i1=0 to m2
  1708.         newimg.i1=img2.i1
  1709.      end /* do */
  1710.      do i2=m2+1 to height1-1
  1711.         newimg.i2=img1.i2
  1712.      end /* do */
  1713.  
  1714.      if img2_trans=1 then call do_fix_trans   /* enforce transparency of "transformed" image 2 */
  1715.  
  1716.      a4=MAKE_GCE_BLOCK(0,0,adelay,adisposal0,0)
  1717.      img_name='newimg.'
  1718.      a5=make_image_block(0,0,width1,height1,0,0,interl.1,0,0)  /* local ct NOT specified */
  1719.      aa=aa||a4||a5
  1720.      if cycle=1 then  cycleaa=a4||a5||cycleaa
  1721.  
  1722.   end
  1723. end /* do */
  1724.  
  1725.  
  1726.  
  1727. when  cctype="PUSH" & CURTAIN_TYPE="T_B" then do
  1728.   rowchunk=height1/(nframes+1)
  1729.   do mm=1 to min(nframes,stop_after)
  1730.      m2=trunc(rowchunk*mm)
  1731.      call dosay " PUSH: rows to " m2
  1732.      do i1=0 to m2
  1733.         i1a=i1+height1-m2
  1734.         newimg.i1=img2.i1a
  1735.      end /* do */
  1736.      do i2=m2+1 to height1-1
  1737.         i2a=i2-(m2+1)
  1738.         newimg.i2=img1.i2a
  1739.      end /* do */
  1740.  
  1741.      if img2_trans=1 then call do_fix_trans   /* enforce transparency of "transformed" image 2 */
  1742.  
  1743.      a4=MAKE_GCE_BLOCK(0,0,adelay,adisposal0,0)
  1744.      img_name='newimg.'
  1745.      a5=make_image_block(0,0,width1,height1,0,0,interl.1,0,0)  /* local ct NOT specified */
  1746.      aa=aa||a4||a5
  1747.      if cycle=1 then  cycleaa=a4||a5||cycleaa
  1748.   end
  1749. end /* do */
  1750.  
  1751.  
  1752. when cctype="SQUOOSH" & CURTAIN_TYPE="T_B" then do
  1753.   rowchunk=(height1/(nframes+1))
  1754.   do mm=1 to min(nframes,stop_after)
  1755.      m2=trunc(rowchunk*mm)
  1756.      call dosay " SQUOOSH: rows to " m2
  1757.      do i1=0 to m2
  1758.         i1a=i1+height1-m2
  1759.         newimg.i1=img2.i1a
  1760.      end /* do */
  1761.      if rleft<2 then do
  1762.         do i2a=0 to rleft-1
  1763.           newimg.i2=img1.i2a
  1764.         end /* do */
  1765.      end
  1766.      else do
  1767.        rleft=(height1-1)-(m2)
  1768.        rfact=(height1-1)/(max(1,(rleft-1)))
  1769.        do i2=1 to rleft                 /* squoosh vertically */
  1770.           i2a=trunc((i2-1)*rfact)
  1771.           i22=m2+i2
  1772.           newimg.i22=img1.i2a
  1773.        end /* do */
  1774.      end
  1775.      if img2_trans=1 then call do_fix_trans   /* enforce transparency of "transformed" image 2 */
  1776.  
  1777.      a4=MAKE_GCE_BLOCK(0,0,adelay,adisposal0,0)
  1778.      img_name='newimg.'
  1779.      a5=make_image_block(0,0,width1,height1,0,0,interl.1,0,0)  /* local ct NOT specified */
  1780.      aa=aa||a4||a5
  1781.      if cycle=1 then  cycleaa=a4||a5||cycleaa
  1782.   end
  1783. end /* do */
  1784.  
  1785.  
  1786. when cctype="BALLOON" & balloon_type<>4 then do
  1787.    ixcenter=trunc(width1*centerx)
  1788.    if ixcenter<0 | ixcenter>(width1-1) then ixcenter=trunc(width1/2)
  1789.    iycenter=trunc(height1*centery)
  1790.    if iycenter<0 | iycenter>(height1-1) then iycenter=trunc(height1/2)
  1791.    d1=dist3(ixcenter,iycenter,,balloon_type)
  1792.    d2=dist3(width1-ixcenter,iycenter,,balloon_type)
  1793.    d3=dist3(ixcenter,height1-iycenter,,balloon_type)
  1794.    d4=dist3(width1-ixcenter,height1-iycenter,,balloon_type)
  1795.    mrad=max(d1,d2,d3,d4)
  1796.    radstep=mrad/nframes
  1797.  
  1798.    do mm=1 to min(nframes,stop_after)                   /*use all of img2 with userad of ixcenter,iycenter*/
  1799.       userad=trunc(radstep*mm)
  1800.       call dosay " Drawing frame " mm " with "balname.balloon_type" radius = " userad '(center of 'ixcenter iycenter')'
  1801.       userad2=userad*userad
  1802.       do ir=0 to height1-1  
  1803.          arow1=img1.ir
  1804.          arow2=img2.ir
  1805.          dy=abs(ir-iycenter)
  1806.          doit1=0
  1807.          do ic=0 to width1-1    
  1808.             dd=dist3(ic-ixcenter,ir-iycenter,,balloon_type)    
  1809.             if dd<userad then do 
  1810.                doit1=1
  1811.                aca=substr(arow2,ic+1,1)
  1812.                arow1=overlay(aca,arow1,ic+1,1)
  1813.             end
  1814.             else do
  1815.                if doit1=1 then leave   /* no more arow2 possible */
  1816.             end /* do */
  1817.          end            /* ic loop */
  1818.          newimg.ir=arow1
  1819.       end /* do */
  1820.  
  1821.      if img2_trans=1 then call do_fix_trans   /* enforce transparency of "transformed" image 2 */
  1822.  
  1823.       a4=MAKE_GCE_BLOCK(0,0,adelay,adisposal0,0)
  1824.       img_name='newimg.'
  1825.       a5=make_image_block(0,0,width1,height1,0,0,interl.1,0,0)  /* local ct NOT specified */
  1826.       aa=aa||a4||a5
  1827.       if cycle=1 then  cycleaa=a4||a5||cycleaa
  1828.    end /* do */
  1829. end /* do */
  1830.  
  1831.  
  1832. when cctype="BALLOON" & balloon_type=4 then do
  1833.    ixcenter=trunc(width1*centerx)
  1834.    if ixcenter<0 | ixcenter>(width1-1) then ixcenter=trunc(width1/2)
  1835.    iycenter=trunc(height1*centery)
  1836.    if iycenter<0 | iycenter>(height1-1) then iycenter=trunc(height1/2)
  1837.    d1=dist3(ixcenter,iycenter,,balloon_type)
  1838.    d2=dist3(width1-ixcenter,iycenter,,balloon_type)
  1839.    d3=dist3(ixcenter,height1-iycenter,,balloon_type)
  1840.    d4=dist3(width1-ixcenter,height1-iycenter,,balloon_type)
  1841.    mrad=max(d1,d2,d3,d4)
  1842.    radstep=mrad/nframes
  1843.  
  1844.    do mm=1 to min(nframes,stop_after)                   /*use all of img2 with userad of ixcenter,iycenter*/
  1845.       userad=max(1,trunc(radstep*mm))
  1846.       call dosay btype2.balloon_push " frame " mm " with "balname.balloon_type" radius = " userad '(center of 'ixcenter iycenter')'
  1847.       userad2=userad*userad
  1848.       IF TRANSLATE(BALLOON_PUSH)='OVERWRITE' then BALLOON_PUSH=0
  1849.       if translate(balloon_push)='PUSH' then balloon_push=10
  1850.       if translate(balloon_push)='SQUOOSH' then balloon_push=20
  1851.  
  1852.       do ir=0 to (iycenter-userad)
  1853.          if balloon_push=0 | balloon_push=10 | balloon_push=20 then do
  1854.                 newimg.ir=img1.ir
  1855.                 iterate
  1856.           end
  1857.           if balloon_push=1 then do
  1858.              ir0=ir+userad
  1859.              newimg.ir=img1.ir0
  1860.           end /* do */
  1861.           if balloon_push=2 then do
  1862.              dnm=max(iycenter-userad,1)
  1863.              ki=(height1-1)/dnm  
  1864.              kii=trunc(ki*ir)
  1865.              newimg.ir=img1.kii
  1866.           end /* do */
  1867.       end /* do top */
  1868.  
  1869.       do ir=max(0,1+iycenter-userad) to min(height1-1,iycenter+userad)
  1870.          arow1=img1.ir
  1871.          arow2=img2.ir
  1872.          dy=abs(ir-iycenter)
  1873.          if balloon_push=0 then do      /* euclidean replace */
  1874.              if dy>userad then do           /* too far away in rowspace */
  1875.                 newimg.ir=arow1
  1876.                 iterate
  1877.             end 
  1878.             t1=trunc(sqrt(userad2-(dy*dy)))+1
  1879.             m1=ixcenter-t1 ; m2=ixcenter+t1
  1880.             if m1<2 & m2>=width1 then do         /* full line */
  1881.               newimg.ir=arow2
  1882.               iterate
  1883.             end /* do */
  1884.             if m1<2  then do     /* but not full line */
  1885.                newimg.ir=left(arow2,m2)||substr(arow1,m2+1)
  1886.                iterate
  1887.             end                 /* else, right end of line */
  1888.             if m1>1 & m2>=width1  then do
  1889.                newimg.ir=left(arow1,m1)||substr(arow2,m1+1)
  1890.                iterate
  1891.             end                 /* else, original image on both sides of row */
  1892.             newimg.ir=left(arow1,m1)||substr(arow2,m1+1,m2-m1)||substr(arow1,m2+1)
  1893.             iterate
  1894.          end
  1895.  
  1896.          if balloon_push=1 | balloon_push=10 then do      /* euclidean push */
  1897.             t1=trunc(sqrt(userad2-(dy*dy)))+1
  1898.             m1=ixcenter-t1 ; m2=ixcenter+t1
  1899.             if m1<2 & m2>=width1 then do         /* full line */
  1900.               newimg.ir=arow2
  1901.               iterate
  1902.             end /* do */
  1903.             if m1<2  then do     /* but not full line */
  1904.               p1=squoosh_row(arow2,m2)
  1905.               newimg.ir=left(p1||substr(arow1,ixcenter),width1)
  1906.               iterate
  1907.             end                 /* else, right end of line */
  1908.             if m1>1 & m2>=width1  then do
  1909.               p2=squoosh_row(arow2,width1-m1)
  1910.               newimg.ir=substr(arow1,ixcenter-m1,m1)||p2
  1911.               iterate
  1912.             end                 /* else, original image on both sides of row */
  1913.             p1=substr(arow1,ixcenter-m1,m1)
  1914.             p2=squoosh_row(arow2,1+m2-m1)
  1915.             p3=substr(arow1,ixcenter)
  1916.             newimg.ir=left(p1||p2||p3,width1)
  1917.             iterate
  1918.         end
  1919.  
  1920.         if  balloon_push=2 | balloon_push=20 then do      /* euclidean squoosh */
  1921.             t1=trunc(sqrt(userad2-(dy*dy)))+1
  1922.             m1=ixcenter-t1 ; m2=ixcenter+t1
  1923.             if m1<2 & m2>=width1 then do         /* full line */
  1924.               newimg.ir=arow2
  1925.               iterate
  1926.             end /* do */
  1927.             if m1<2  then do     /* but not full line */
  1928.               p1=squoosh_row(arow2,m2)
  1929.               p2=substr(arow1,ixcenter)
  1930.               newimg.ir=p1||squoosh_row(p2,width1-length(p1))
  1931.               iterate
  1932.             end                 /* else, right end of line */
  1933.             if m1>1 & m2>=width1  then do
  1934.               p2=squoosh_row(arow2,width1-m1)
  1935.               p1=squoosh_row(left(arow,ixcenter),m1)
  1936.               newimg.ir=p1||p2
  1937.               iterate
  1938.             end                 /* else, original image on both sides of row */
  1939.             p1=squoosh_row(left(arow1,ixcenter),m1)
  1940.             p2=squoosh_row(arow2,1+m2-m1)
  1941.             newimg.ir=p1||p2
  1942.             ip3=width1-(length(p1)+length(p2))
  1943.             if ip3>0 then
  1944.                newimg.ir=newimg.ir||squoosh_row(substr(arow1,ixcenter),ip3)
  1945.             iterate
  1946.         end
  1947.       end                /* iycenter-userad ... iycenter + userad */
  1948.  
  1949.       do ir=max(0,iycenter+userad+1) to height1-1  
  1950.          if balloon_push=0 | balloon_push=10 | balloon_push=20 then do
  1951.                 newimg.ir=img1.ir
  1952.                 iterate
  1953.           end
  1954.           if balloon_push=1 then do
  1955.              ir0=iycenter+(ir-(iycenter+userad+1))
  1956.              newimg.ir=img1.ir0
  1957.           end /* do */
  1958.           if balloon_push=2 then do
  1959.              ira=ir-(iycenter+userad+1)
  1960.              dnm=max((height1-1)-(iycenter+userad+1),1)
  1961.              ki=(height1-1)/(dnm)   
  1962.              kii=trunc(ki*ira)
  1963.              newimg.ir=img1.kii
  1964.           end /* do */
  1965.       end /* do bottom */
  1966.  
  1967.      if img2_trans=1 then call do_fix_trans   /* enforce transparency of "transformed" image 2 */
  1968.  
  1969.       a4=MAKE_GCE_BLOCK(0,0,adelay,adisposal0,0)
  1970.       img_name='newimg.'
  1971.       a5=make_image_block(0,0,width1,height1,0,0,interl.1,0,0)  /* local ct NOT specified */
  1972.       aa=aa||a4||a5
  1973.       if cycle=1 then  cycleaa=a4||a5||cycleaa
  1974.    end /* do */
  1975. end /* do */
  1976.  
  1977.  
  1978. when cctype="DISSOLVE" then do
  1979.    do Rmm=0 to height1-1         /* create a "DISSOLVE" mask */
  1980.       brow=''
  1981.       do nn=1 to width1
  1982.          brow=brow||d2c(random(1,100))
  1983.       end
  1984.       mask.Rmm=brow
  1985.    end
  1986.    ijump=100/(nframes+1)
  1987.    nwd=words(dissolve_spec)
  1988.    do jj=1 to min(nframes,stop_after)     
  1989.       pp=trunc(jj*ijump)
  1990.       if nwd<=1 then do
  1991.          thresh=d2c(pp)
  1992.       end
  1993.       else do           /* do a linear interpolation */
  1994.           jw=(pp/100)*(1+nwd)
  1995.           jw1=trunc(jw)
  1996.           select
  1997.             when jw1=0 then do
  1998.                 pc1=0 ; pc2=strip(word(dissolve_spec,1))
  1999.             end 
  2000.             when jw1>=NWD then do
  2001.                pc1=strip(word(dissolve_spec,NWD)) ; PC2=100
  2002.             end 
  2003.             otherwise do
  2004.                 pc1=strip(word(dissolve_spec,jw1)) 
  2005.                 pc2=strip(word(dissolve_spec,jw1+1)) 
  2006.             end
  2007.           end
  2008.           thr=pc1+((jw-jw1)*(pc2-pc1))
  2009.           thr=trunc(max(0,min(thr,100)))
  2010.           thresh=d2c(thr)
  2011.       end /* do */
  2012.       call dosay " DISSOLVE: Writing frame with " pp"% threshold "
  2013.       do ir=0 to height1-1
  2014.          arow1=img1.ir
  2015.          arow2=img2.ir
  2016.          msk1=mask.ir
  2017.          do ic=1 to width1    /*replace elements of arow1 if within userad */
  2018.             if substr(msk1,ic,1)<thresh then do
  2019.                acb=substr(arow2,ic,1)
  2020.                arow1=overlay(acb,arow1,ic,1)
  2021.             end /* do */
  2022.          end
  2023.          newimg.ir=arow1
  2024.       end /* do */
  2025.      if img2_trans=1 then call do_fix_trans   /* enforce transparency of "transformed" image 2 */
  2026.  
  2027.       a4=MAKE_GCE_BLOCK(0,0,adelay,adisposal0,0)
  2028.       img_name='newimg.'
  2029.       a5=make_image_block(0,0,width1,height1,0,0,interl.1,0,0)  /* local ct NOT specified */
  2030.       aa=aa||a4||a5
  2031.       if cycle=1 then  cycleaa=a4||a5||cycleaa
  2032.   end /* do */
  2033. end /* do */
  2034.  
  2035.  
  2036.  
  2037. when cctype="FADE" then do
  2038.    ijump=1/(nframes+1)
  2039.    do jj=1 to min(nframes,stop_after) 
  2040.       pp=ijump*jj
  2041.       call dosay " FADE: Writing frame with " trunc(100*pp)"% distance "
  2042.       do ir=0 to height1-1
  2043.          arow1=img1.ir
  2044.          arow2=img2.ir
  2045.          do ic=1 to width1    /*replace elements of arow1 if within userad */
  2046.             ac1=c2d(substr(arow1,ic,1))
  2047.             ac2=c2d(substr(arow2,ic,1))
  2048.             if ac1=ac2 then do
  2049.                 iterate                 /* no need to change */
  2050.             end
  2051.             SELECT
  2052.               when  fade_type=2 then DO
  2053.                    typ1=ctnew2.!class.ac1
  2054.                    typ2=ctnew2.!class.ac2
  2055.                    rte='!'||typ1||typ2      /* which route to look in */
  2056.                    RTE=RTES.RTE      /* GR=RG, ETC. */
  2057.                    n1=ctnew2.rte.ac1        /* position of color 1 in route */
  2058.                    n2=ctnew2.rte.ac2        /* position of color 2 */
  2059.                    dn=n1+trunc((n2-n1)*pp)  /* somewhere between, in route */
  2060.                    dd=ctrs.rte.dn           /* and the ctnew2. value of this inbetween */
  2061.               END
  2062.               WHEN fade_type=3 then DO
  2063.                     R1=CTNEW2.!r.AC1;G1=CTNEW2.!G.AC1;B1=CTNEW2.!b.AC1    
  2064.                     R2=CTNEW2.!r.AC2;G2=CTNEW2.!G.AC2;B2=CTNEW2.!b.AC2    
  2065.                     DR=PP*(R2-R1) ; DG=PP*(G2-G1) ; DB=PP*(B2-B1)
  2066.                     R3=TRUNC(R1+DR) ; G3=TRUNC(G1+DG) ; B3=TRUNC(B1+DB)
  2067.  
  2068.                     dd=d2c(get_region(r3,g3,b3,nregions))
  2069.  
  2070.                     arow1=overlay(dd,arow1,ic,1)
  2071.                     iterate
  2072.               end 
  2073.               otherwise  do       /* just use the "sorted" color table */
  2074.                   dd=use_sorted_ct(ac1,ac2,pp,fade_type) /* uses sorted_ct. */
  2075.               end
  2076.             end  /* select */
  2077.             arow1=overlay(d2c(dd),arow1,ic,1)
  2078.          end                    /* this row */
  2079.          newimg.ir=arow1
  2080.       end /* do */
  2081.      if img2_trans=1 then call do_fix_trans   /* enforce transparency of "transformed" image 2 */
  2082.  
  2083.       a4=MAKE_GCE_BLOCK(0,0,adelay,adisposal0,0)
  2084.       img_name='newimg.'
  2085.       a5=make_image_block(0,0,width1,height1,0,0,interl.1,0,0)  /* local ct NOT specified */
  2086.       aa=aa||a4||a5
  2087.       if cycle=1 then  cycleaa=a4||a5||cycleaa
  2088.   end /* do */
  2089. end /* do */
  2090.  
  2091.  
  2092. when cctype="OVERWRITE" & CURTAIN_TYPE="L_R" then do
  2093.    ijump=width1/(nframes+1)
  2094.    do jj=1 to min(nframes,stop_after)     
  2095.       pp=trunc(jj*ijump)
  2096.  
  2097.       call dosay " CURTAIN: Swiping to "pp
  2098.       do ir=0 to height1-1
  2099.          arow1=img1.ir
  2100.          arow2=img2.ir
  2101.          newimg.ir=substr(arow2,1,pp)||substr(arow1,pp+1)
  2102.       end /* do */
  2103.  
  2104.       if img2_trans=1 then call do_fix_trans   /* enforce transparency of "transformed" image 2 */
  2105.  
  2106.       a4=MAKE_GCE_BLOCK(0,0,adelay,adisposal0,0)
  2107.       img_name='newimg.'
  2108.       a5=make_image_block(0,0,width1,height1,0,0,interl.1,0,0)  /* local ct NOT specified */
  2109.       aa=aa||a4||a5
  2110.       if cycle=1 then  cycleaa=a4||a5||cycleaa
  2111.   end /* do */
  2112. end /* do */
  2113.  
  2114. when cctype="PUSH" & CURTAIN_TYPE="L_R" then do
  2115.    ijump=width1/(nframes+1)
  2116.    do jj=1 to min(nframes,stop_after)     
  2117.       pp=trunc(jj*ijump)
  2118.       call dosay  "  PUSH: Pushing column to "pp
  2119.       do ir=0 to height1-1
  2120.          arow1=img1.ir
  2121.          arow2=img2.ir
  2122.          newimg.ir=left(right(arow2,pp)||arow1,width1)
  2123.       end /* do */
  2124.  
  2125.      if img2_trans=1 then call do_fix_trans   /* enforce transparency of "transformed" image 2 */
  2126.  
  2127.       a4=MAKE_GCE_BLOCK(0,0,adelay,adisposal0,0)
  2128.       img_name='newimg.'
  2129.       a5=make_image_block(0,0,width1,height1,0,0,interl.1,0,0)  /* local ct NOT specified */
  2130.       aa=aa||a4||a5
  2131.       if cycle=1 then  cycleaa=a4||a5||cycleaa
  2132.   end /* do */
  2133. end /* do */
  2134.  
  2135. when cctype="SQUOOSH" & CURTAIN_TYPE="L_R" then do
  2136.    ijump=width1/(nframes+1)
  2137.    do jj=1 to min(nframes,stop_after)     
  2138.       pp=trunc(jj*ijump)
  2139.       call dosay " SQUOOSH: Squooshing left  "pp
  2140.       do ir=0 to height1-1
  2141.          arow1=img1.ir
  2142.          arow2=img2.ir
  2143.          p1=right(arow2,pp)
  2144.          p2=squoosh_row(arow1,width1-pp)
  2145.          newimg.ir=p1||p2
  2146.       end /* do */
  2147.  
  2148.      if img2_trans=1 then call do_fix_trans   /* enforce transparency of "transformed" image 2 */
  2149.  
  2150.       a4=MAKE_GCE_BLOCK(0,0,adelay,adisposal0,0)
  2151.       img_name='newimg.'
  2152.       a5=make_image_block(0,0,width1,height1,0,0,interl.1,0,0)  /* local ct NOT specified */
  2153.       aa=aa||a4||a5
  2154.       if cycle=1 then  cycleaa=a4||a5||cycleaa
  2155.   end /* do */
  2156. end /* do */
  2157.  
  2158.  
  2159.  
  2160. when cctype="OVERWRITE" & CURTAIN_TYPE="MIDDLE" then do
  2161.    ijump=width1/(nframes+1)
  2162.  
  2163.    do jj=1 to min(nframes,stop_after)     
  2164.       pp=trunc(jj*ijump/2)
  2165.       call dosay " CURTAIN: Curtain to "pp
  2166.       do ir=0 to height1-1
  2167.          arow1=img1.ir
  2168.          arow2=img2.ir
  2169.          p1=left(arow2,pp)
  2170.          p3=right(arow2,pp)
  2171.          p2=substr(arow1,pp+1,width1-2*pp)
  2172.          newimg.ir=p1||p2||p3
  2173.       end /* do */
  2174.  
  2175.      if img2_trans=1 then call do_fix_trans   /* enforce transparency of "transformed" image 2 */
  2176.  
  2177.       a4=MAKE_GCE_BLOCK(0,0,adelay,adisposal0,0)
  2178.  
  2179.       img_name='newimg.'
  2180.       a5=make_image_block(0,0,width1,height1,0,0,interl.1,0,0)  /* local ct NOT specified */
  2181.       aa=aa||a4||a5
  2182.       if cycle=1 then  cycleaa=a4||a5||cycleaa
  2183.   end /* do */
  2184. end /* do */
  2185.  
  2186. when cctype="SQUOOSH" & CURTAIN_TYPE="MIDDLE" then do
  2187.    ijump=width1/(nframes+1)
  2188.  
  2189.    do jj=1 to min(nframes,stop_after)     
  2190.       pp=trunc(jj*ijump/2)
  2191.       half=trunc(width1/2)
  2192.       call dosay " SQUOOSH: SQUOOSH to "pp
  2193.       do ir=0 to height1-1
  2194.          arow1=img1.ir
  2195.          arow2=img2.ir
  2196.          p1=substr(arow2,half-pp,pp)
  2197.          p3=substr(arow2,half,pp)
  2198.          p2=squoosh_row(arow1,width1-(2*pp))
  2199.          newimg.ir=p1||p2||p3
  2200.       end /* do */
  2201.  
  2202.      if img2_trans=1 then call do_fix_trans   /* enforce transparency of "transformed" image 2 */
  2203.  
  2204.       a4=MAKE_GCE_BLOCK(0,0,adelay,adisposal0,0)
  2205.       img_name='newimg.'
  2206.       a5=make_image_block(0,0,width1,height1,0,0,interl.1,0,0)  /* local ct NOT specified */
  2207.       aa=aa||a4||a5
  2208.       if cycle=1 then  cycleaa=a4||a5||cycleaa
  2209.   end /* do */
  2210. end /* do */
  2211.  
  2212. when cctype="PUSH" & CURTAIN_TYPE="MIDDLE" then do
  2213.    ijump=width1/(nframes+1)
  2214.  
  2215.    do jj=1 to min(nframes,stop_after)     
  2216.       pp=trunc(jj*ijump/2)
  2217.       half=trunc(width1/2)
  2218.       call dosay " PUSH: to "pp
  2219.       do ir=0 to height1-1
  2220.          arow1=img1.ir
  2221.          arow2=img2.ir
  2222.          p1=substr(arow2,half-pp,pp)
  2223.          p3=substr(arow2,half,pp)
  2224.  
  2225.          irem=width1-2*pp
  2226.          IREM2=TRUNC(IREM/2)
  2227.          p2=LEFT(AROW1,IREM2)||RIGHT(AROW1,IREM-IREM2)
  2228.          newimg.ir=p1||p2||p3
  2229.       end /* do */
  2230.  
  2231.      if img2_trans=1 then call do_fix_trans   /* enforce transparency of "transformed" image 2 */
  2232.  
  2233.       a4=MAKE_GCE_BLOCK(0,0,adelay,adisposal0,0)
  2234.       img_name='newimg.'
  2235.       a5=make_image_block(0,0,width1,height1,0,0,interl.1,0,0)  /* local ct NOT specified */
  2236.       aa=aa||a4||a5
  2237.       if cycle=1 then  cycleaa=a4||a5||cycleaa
  2238.   end /* do */
  2239. end /* do */
  2240.  
  2241.  
  2242.  
  2243. when cctype="MASK" then do           /*use the mask files */
  2244.    do jSj=1 to mask.0
  2245.      call dosay " MASK: Using Maskfile " mask.jsj
  2246.      acthresh=d2c(mask.jSj.!thresh)
  2247.       if is_cgi<>2 then do
  2248.           if pos(':',amask.jsj)>0 then DO
  2249.              call dosay "BlendGIF error: an absolute filename is not allowed: "amask.jsj
  2250.              return 0
  2251.           end /* do */
  2252.           amask.jsj=strip(translate(strip(amask.jsj),'\','/'),'l','\')
  2253.       end /* do */
  2254.      amask=read_giffile(mask.jSj,BLENDGIF_ROOT)
  2255.      if amask=0 then return 0
  2256.      ab=read_gif_block(amask,1,'LSD',1)        /* get logical screen descriptor */
  2257.      ct_name='lct.'                          /* extract info from it */
  2258.      stuff=read_lsd_block(ab)
  2259.      parse var stuff mwidth mheight  .
  2260.      img_name='mimg.'
  2261.      ab=read_gif_block(amask,1,'IMG',1)
  2262.      stuff=read_image_block(ab,1)
  2263.      if stuff=0 then do
  2264.          call dosay "Problem with " mask.jsj
  2265.          exit       
  2266.      end
  2267. /* might need to replicate, or shrink */
  2268.      if mheight<height1 then do         /* add rows */
  2269.         mh0=mheight
  2270.         kat=mheight
  2271.         do until kat=height1
  2272.           do kat2=0 to mh0-1
  2273.              mimg.kat=mimg.kat2
  2274.              kat=kat+1
  2275.              if kat=height1 then leave
  2276.           end
  2277.         end 
  2278.      end                /* MHEIGHT< HEIGFHT1 */
  2279.      dx=width1-mwidth             /* add or subtract columns */
  2280.      if dx<0 then dx=0
  2281.      fct=trunc(0.99+(dx/mwidth))+1
  2282.      do kr=0 to height1-1
  2283.            mimg.kr=left(copies(mimg.kr,fct),width1)
  2284.      end 
  2285.      do ir=0 to height1-1              /* now mask the image */
  2286.          arow1=img1.ir
  2287.          arow2=img2.ir
  2288.          mrow=mimg.ir
  2289.          do ic=1 to width1    /*replace elements of arow1 if msk1>mask.!thresh */
  2290.             if substr(mrow,ic,1)>acthresh then do
  2291.                aca=substr(arow2,ic,1)
  2292.                arow1=overlay(aca,arow1,ic,1)
  2293.             end 
  2294.          end            /* I=1 TO WIDTH1 */
  2295.          newimg.ir=arow1
  2296.      end                 /* IR=0 TO HEIGHT-1 */
  2297.  
  2298.      if img2_trans=1 then call do_fix_trans   /* enforce transparency of "transformed" image 2 */
  2299.  
  2300.      a4=MAKE_GCE_BLOCK(0,0,adelay,adisposal0,0)
  2301.      img_name='newimg.'
  2302.      a5=make_image_block(0,0,width1,height1,0,0,interl.1,0,0)  /* local ct NOT specified */
  2303.      aa=aa||a4||a5
  2304.      if cycle=1 then  cycleaa=a4||a5||cycleaa
  2305.   end                   /* get next mask file */     
  2306. end             /* MASK */
  2307.  
  2308.  
  2309. otherwise nop
  2310.  
  2311. end
  2312.  
  2313. /* if transformed image, and nframes< stop_after, then write original transformed image */
  2314. if cctype<>"ADD"  & img2_trans=1 & (nframes<stop_after | stop_after=0) then do
  2315.      do i1=0 to height1-1
  2316.         newimg.i1=img2.i1
  2317.      end /* do */
  2318.      itt=0
  2319.      call do_fix_trans   /* enforce transparency of "transformed" image 2 */
  2320.      tcflag3=tcflag.ido2
  2321.      if no_transparent>0 then tcflag3=0
  2322.      a4=MAKE_GCE_BLOCK(tcflag3,tcindex.ido2,adelay,adisposal0,0)
  2323.      img_name='newimg.'
  2324.  
  2325.      a5=make_image_block(0,0,width1,height1,0,0,interl.1,0,0)  /* local ct NOT specified */
  2326.      aa=aa||a4||a5
  2327.      if cycle=1 then  cycleaa=a4||a5||cycleaa
  2328. end /* do */
  2329.  
  2330.  
  2331. return 1
  2332.  
  2333. eek1err:
  2334. say " ERROR at " sigl ' ' rc
  2335. exit
  2336.  
  2337. /*************************/
  2338. /*do any of this image specific transformations have multiple parameters */
  2339. ismany_transforms:procedure expose  nuheight. nuwidth. xmove. ymove. ,
  2340.                                zrotate. yrotate. xrotate. 
  2341. parse arg ijj
  2342. if words(nuheight.ijj) >1 then return 1
  2343. if words(nuwidth.ijj) >1 then return 1
  2344. if words(xmove.ijj) >1 then return 1
  2345. if words(ymove.ijj) >1 then return 1
  2346. if words(zrotate.ijj) >1 then return 1
  2347. if words(yrotate.ijj) >1 then return 1
  2348. if words(xrotate.ijj) >1 then return 1
  2349. return 0
  2350.  
  2351.  
  2352.  
  2353. /***********************************/
  2354. /* overlay strings, using a mask */
  2355. /* The mask should have 00 and ff bytes. 
  2356.    A "00" bytes means "use string 1 byte"
  2357.    A "11" byte means "use string 2 byte"
  2358.  
  2359. mess1: string 1
  2360. mess2: string 2
  2361. cmask: the "00 / ff" mask 
  2362.  
  2363. All 3 of these MUST be the same size! 
  2364.  
  2365. */
  2366. overlay_strings:procedure
  2367. parse arg mess1,mess2,cmask
  2368.  
  2369.  
  2370. ch1=d2c(255)
  2371.  
  2372. /* if imask is 1, use mess 2 character. Else, use mess 1 character */
  2373. use1=bitand(mess2,cmask)
  2374. cmaskn=bitxor(cmask,ch1,ch1)            /* reverse of cmask (flip 1s and 0s */
  2375. use2=bitand(mess1,cmaskn)
  2376. newmess=bitor(use1,use2)
  2377. return newmess
  2378.  
  2379.  
  2380. /**********************************/
  2381. /* enforce transparency of "transformed" image2:
  2382.   a)for all pixels in newimg
  2383.   b)if corresponding pixel trmask = 0, then 
  2384.       reset newimg pixel to corresponding pixel from img1
  2385. */
  2386. do_fix_trans:
  2387.  
  2388. do ki=0 to height1-1
  2389.    msk1=trmask.ki
  2390.    old1=img1.ki
  2391.    new1=newimg.ki
  2392.    newimg.ki=overlay_strings(old1,new1,msk1)
  2393. end /* do */
  2394. return 1
  2395.  
  2396.  
  2397. /********************************/
  2398. /******** PROCEDURES USED BY FADE ANIM_TYPE *****/
  2399. /********************************/
  2400.  
  2401. /********************/
  2402. /* return a region, give a color table position */
  2403. get_region:procedure expose reglist. is_cgi BLENDGIF_ROOT save_tempfile
  2404. parse arg ar,ag,ab,nregions
  2405. regsize=trunc(256/nregions)
  2406. ir=min(1+trunc(ar/regsize),nregions)
  2407. ig=min(1+trunc(ag/regsize),nregions)
  2408. ib=min(1+trunc(ab/regsize),nregions)
  2409. nn=reglist.ir.ig.ib
  2410.  
  2411. return nn
  2412.  
  2413. /***************************/
  2414. /* find color closest to rgb (in ctnew2) */
  2415. find_color:procedure expose ctnew2. is_cgi BLENDGIF_ROOT  save_tempfile
  2416. parse arg ar,ag,ab,try1,regsize
  2417. tr=ctnew2.!r.try1
  2418. tg=ctnew2.!g.try1
  2419. tb=ctnew2.!b.try1
  2420. dst=dist3(tr-ar,tg-ag,tb-ab)
  2421. igot=try1
  2422. dstsec=0
  2423. do mm=1 to ctnew2.0-1
  2424.    if mm=try1 then iterate
  2425.    dr=abs(ctnew2.!r.mm-ar)
  2426.    dg=abs(ctnew2.!g.mm-ag)
  2427.    db=abs(ctnew2.!b.mm-ab)
  2428.    if max(dr,dg,db)>dst then iterate
  2429.    a1=max(dr,dg)
  2430.    a2=min(dr,dg)
  2431.    d1=(a1+(a2/2))
  2432.    a1=max(abs(d1),abs(db))
  2433.    a2=min(abs(d1),abs(db))
  2434.    tdst=(a1+(a2/2))
  2435.    if tdst<dst then do
  2436.        dstsec=dst-tdst
  2437.        dst=tdst ;  igot=mm
  2438.        if dst<regsize then return igot regsize
  2439.    end
  2440. end
  2441. return igot dstsec
  2442.  
  2443. /***************************/
  2444. /* create match array for rgb regions */
  2445. make_regions:procedure expose ctnew2. reglist. is_cgi BLENDGIF_ROOT  save_tempfile
  2446. parse arg nregions
  2447.  
  2448. reglist.=0
  2449. gg=1
  2450. regsize=trunc(256/nregions)
  2451. add1=trunc(regsize/2)
  2452. do ir=1 to nregions
  2453.    call dosay "  " ir " of "nregions " reference layers (fade lookup table)"
  2454.    do ig=1 to nregions
  2455.       distsec=0
  2456.       do ib=1 to nregions
  2457.             if distsec>regsize then do
  2458.                reglist.ir.ig.ib=gg
  2459.                distsec=distsec-regsize
  2460.             end /* do */
  2461.             else
  2462.             ur=(ir-1)*regsize+add1
  2463.             ug=(ig-1)*regsize+add1
  2464.             ub=(ib-1)*regsize+add1
  2465.             agg=find_color(ur,ug,ub,gg,regsize)
  2466.             parse var agg gg distsec
  2467.             reglist.ir.ig.ib=gg
  2468.        end /* do */
  2469.    end
  2470. end
  2471. return 1
  2472.  
  2473.  
  2474.  
  2475. /***************************/
  2476. /* assign "close colors" to each 32x32x32 cell of color table space */
  2477. /* this has been superseded by make_regions */
  2478. make_matchinfos:procedure expose minfos. ctnew2. verbose is_cgi BLENDGIF_ROOT  save_tempfile
  2479.  
  2480. csize=dist3(8,8,8)
  2481. csize2=csize/2
  2482. ir0=0
  2483. a80=60
  2484.  
  2485. do ir=8 to 255 by 16          
  2486.  ir0=1+ir0 ; ig0=0
  2487.  do ig=8 to 255 by 16
  2488.   ig0=1+ig0 ; ib0=0
  2489.   do ib=8 to 255 by 16
  2490.     ib0=1+ib0
  2491.     drop act.; act.0=0
  2492.     do mm=1 to ctnew2.0-1
  2493.       dr=abs(ctnew2.!r.mm-ir) ; dg=abs(ctnew2.!g.mm-ig) ;db=abs(ctnew2.!b.mm-ib)
  2494.       if max(dr,dg,db)>a80 then iterate
  2495.       adist=trunc(dist3(dr,dg,db))
  2496.       if adist<csize2 then do           /* close enough */
  2497.          minfos.ir0.ig0.ib0=mm
  2498.          iterate ib
  2499.       end
  2500.       jact=act.0+1
  2501.       act.jact=left(mm,5)||left(adist,8)
  2502.       act.0=jact
  2503.     end 
  2504.     if act.0=0 then do  /* nothing close, do again no restrictiosn */
  2505.       do mm=1 to ctnew2.0-1
  2506.         a80=a80+5
  2507.         dr=abs(ctnew2.!r.mm-ir) ; dg=abs(ctnew2.!g.mm-ig) ;db=abs(ctnew2.!b.mm-ib)
  2508.         adist=trunc(dist3(dr,dg,db))
  2509.         act.mm=left(mm,5)||left(adist,8)
  2510.         act.0=act.0+1
  2511.       end 
  2512.     end
  2513.     foo=arraysort(act,1,,6,8,'A','N')
  2514.     parse var act.1 jmm ascor
  2515.     keepers=jmm 
  2516.     athresh=ascor+csize
  2517.     do mm=2 to act.0
  2518.       parse var act.mm jmm ascor
  2519.       if ascor>athresh then leave
  2520.       keepers=keepers' 'jmm
  2521.     end
  2522.     minfos.ir0.ig0.ib0=keepers
  2523.   end /* do */
  2524.  end /* do */
  2525.  call dosay "   Building pre-search ctable for " ir
  2526. end /* do */
  2527. return 1
  2528.  
  2529.  
  2530. /***************************/
  2531. /* classify a color into one of:
  2532.   W (black and white)
  2533.   R G B (red green blue)
  2534.   C M O (cyan (gb), magenta (rb), orange (rg)
  2535. */
  2536. classify_color:procedure 
  2537. parse arg ar,ag,ab
  2538.  
  2539. /* a grey scale */
  2540. if max(abs(ar-ab),abs(ar-ag),abs(ab-ag))<10 then return 'W'  
  2541.  
  2542. if ar> 0.8*(ag+ab)  then return 'R'
  2543. if ag> 0.8*(ar+ab)  then return 'G'
  2544. if ab> 0.8*(ar+ag)  then return 'B'
  2545.  
  2546. if ag>ar & ab>ar  then return 'C'
  2547. if ar>ag & ab>ag  then return 'M'
  2548.  
  2549. return 'O'            /* ar>ab & ag>ab (O) is the default */
  2550.  
  2551.  
  2552. /********************************/
  2553. /* make  ctrs. (ct-routes), and pointers from ctnew2. to ctrs. */
  2554. make_ctroutes:procedure expose ctrs. ctnew2. RTES.
  2555.  
  2556. combos='WW RR GG BB CC MM OO '|| ,
  2557.        '   WR WG WB WC WM WO '|| ,
  2558.        '      RG RB RC RM RO '|| ,
  2559.        '         GB GC GM GO '|| ,
  2560.        '            BC BM BO '|| ,
  2561.        '               CM CO '|| ,
  2562.        '                  MO '
  2563. isum=0
  2564.  
  2565. DO MM=1 TO WORDS(COMBOS)
  2566.    AW=STRIP(WORD(COMBOS,MM))
  2567.    AW2='!'||AW
  2568.    RTES.AW2=AW2
  2569.    AW3='!'||REVERSE(AW)
  2570.    RTES.AW3=AW2
  2571. END
  2572.  
  2573. ctrs.=0
  2574. ctnew2.!class.0='W'
  2575. DO mm=1 TO CTNEW2.0-1           /* classify each color */
  2576.    ar=ctnew2.!r.mm ; ag=ctnew2.!g.mm ; ab=ctnew2.!b.mm
  2577.    CTNEW2.!class.mm=classify_color(ar,ag,ab)
  2578. end /* do */
  2579.  
  2580. wnc=words(combos)
  2581. do Rii=1 to wnc
  2582.    a1=strip(word(combos,Rii))
  2583.  
  2584.    a1a=left(a1,1); a1b=right(a1,1)
  2585. /* find all colors that classify into a1a or a1b */
  2586.    drop tct.
  2587.    tct.0=0 ; a1as=0; a1bs=0
  2588.    do mm=0 to ctnew2.0-1
  2589.       ttype=ctnew2.!class.mm
  2590.       ar=ctnew2.!r.mm ; ag=ctnew2.!g.mm ; ab=ctnew2.!b.mm
  2591.       if ttype=A1A & a1a<>a1b then do      /* - value for very much a1a */
  2592.          scor=-fig_color_score(ttype,ar,ag,ab)
  2593.          itct=tct.0+1
  2594.          tct.itct=left(mm,5)||left(scor,8)
  2595.          tct.0=itct
  2596.          a1as=a1as+1
  2597.        end
  2598.        if ttype=A1B  then do
  2599.          scor=fig_color_score(ttype,ar,ag,ab)
  2600.          itct=tct.0+1
  2601.          tct.itct=left(mm,5)||left(scor,8)
  2602.          tct.0=itct
  2603.          a1bs=a1bs+1
  2604.        end
  2605.     end                 /* grabbing candidate colors */
  2606.     if a1as+a1bs=0 then iterate      /* neither of either color */
  2607.     if a1a<>a1b & a1as*a1bs=0 then iterate /* one of 2 colors is missing */
  2608.     ta1='!'||a1         /* fill these tails */
  2609.     ta2='!'||a1b||a1a
  2610.  
  2611.     foo=arraysort(tct,1,,6,8,'A','N')
  2612.     if foo=0 then do 
  2613.           call dosay "ARRAYSORT failure "
  2614.           return 0
  2615.     end /* do */
  2616. /* normalize scores between 0 and 100 */
  2617.  
  2618.     tnn=tct.0
  2619.     if tnn<=1 then iterate   /* 1 element, no need for table */
  2620.     parse var tct.1 . smin
  2621.     parse var tct.tnn . smax
  2622.     dif=smax-smin
  2623.     do nn=1 to tct.0                    /* creating 1..101 ctable */
  2624.        parse var tct.nn jmm asco
  2625.        tct.nn=jmm
  2626.        if asco-smin=0 then
  2627.            kikw=1
  2628.        else
  2629.           kikw=trunc(100*(asco-smin)/dif)+1  /* 1 to 101 */
  2630.        tct.nn.!sc=kikw
  2631.        ctnew2.ta2.jmm=tct.nn.!sc
  2632.        if ta1<>ta2 then ctnew2.ta1.jmm=tct.nn.!sc
  2633.     end 
  2634.  
  2635.     atct.=0
  2636.     do nn=1 to tct.0            /* fill know values  */
  2637.        nn2=trunc(tct.nn.!sc)
  2638.        atct.nn2=tct.nn 
  2639.     end
  2640.  
  2641.     iwas=atct.1                 /*now fill in gaps */
  2642.     do nn=1 to 101
  2643.        if atct.nn=0 then 
  2644.           atct.nn=iwas
  2645.        else
  2646.           iwas=atct.nn
  2647.     end /* do */ 
  2648.     do nn=1 to 101                      /* now record this normalized/expanded ct */
  2649.           jmm=atct.nn
  2650.           ctrs.ta1.nn=jmm
  2651.           if a1a<>a1b then  ctrs.ta2.nn=jmm
  2652.     end /* do */
  2653.  
  2654. end /* do */
  2655. return 1
  2656.  
  2657. /**************************/
  2658. /* compute a color intensity score */
  2659. fig_color_score:procedure
  2660. parse arg ttype,rr,gg,bb
  2661.  
  2662. select
  2663.    when ttype='W' then return (rr+gg+bb)/3   
  2664.    when ttype='R' then return rr
  2665.    when ttype='G' then return GG
  2666.    when ttype='B' then return bb
  2667.    when ttype='C' then return (bb+gg)/2
  2668.    when ttype='M' then return (rr+bb)/2
  2669.    otherwise return (gg+rr)/2                   /* O is the default */
  2670. end
  2671.  
  2672.  
  2673. /*****************************/
  2674. /* FIND COLOR CLOSE TO R,G,B; USING MINFOS. ARRAY TO SPEED THINGS UP */
  2675. FIND_CLOSEST:procedure expose minfos. ctnew2.
  2676. parse arg ar,ag,ab
  2677.  
  2678. ir=min(1+trunc(ar/16),16)
  2679. ig=min(1+trunc(ab/16),16)
  2680. ib=min(1+trunc(ag/16),16)
  2681.  
  2682. ilook=minfos.ir.ig.ib
  2683. if words(ilook)=1 then return ilook
  2684. adist=111111
  2685. do mm=1 to words(ilook)
  2686.    jmm=strip(word(ilook,mm))
  2687.    br=ctnew2.!r.jmm ; bg=ctnew2.!g.jmm ; bb=ctnew2.!b.jmm
  2688.    adist2=dist3(ar-br,ag-bg,ab-bb)
  2689.    if adist2<adist then do
  2690.       adist=adist2 ; iuse=jmm
  2691.    end
  2692. end
  2693. return iuse
  2694.  
  2695.  
  2696.  
  2697. /********************************/
  2698. /****** CT CREATION PROCEDURES  ***************/
  2699. /********************************/
  2700.  
  2701. /****************************************/
  2702. /* Combine and shrink cts.  Returns checked2. and ctnew2.  */
  2703.  
  2704. make_new_ctable:procedure expose ctnew2. checked2. verbose cts. is_cgi BLENDGIF_ROOT verbose ,
  2705.                                 r_back g_back b_back   save_tempfile
  2706.  
  2707. parse arg ct_newlen,npixels,usesrch
  2708.  
  2709. rmin=0;gmin=0;bmin=0;rmax=255;bmax=255;gmax=255 /* search bounds */
  2710.  
  2711. /*a) combine the several color tables, discard unused colors (create ctnew.)*/
  2712.  
  2713. call combine_cts
  2714. call dosay " total used & unique colors " ctnew.0 
  2715. if ctnew.0+1 <= ct_newlen then do
  2716.    usesrch=0
  2717.    ct_newlen=ctnew.0+1
  2718. end /* do */
  2719.  
  2720.  
  2721. /*b) shrink combined color table to  ct_newlen 
  2722.        ctnew2. = the new "shrunken" color table
  2723.        checked2. = points from rgb to newcolor table 
  2724. */
  2725.  
  2726. ctnew2.!r.0=r_back ; ctnew2.!g.0=g_back ; ctnew2.!b.0=b_back       /* 0 is for transparent color */
  2727. ctnew2.0=1
  2728. checked2.=0                     /* setup pointer from rgb to ctnew2 */
  2729.  
  2730. /*c) find most frequent pixel value, that will be the #1 color */
  2731. pmax=find_frequent(1,0)
  2732. if verbose>0 then call dosay "  Most freq color occurs " pmax  " times "
  2733.  
  2734. unacc=npixels-pmax             /* unaccounted for pixels */
  2735.  
  2736. select
  2737.  when usesrch=0 then mx1=ct_newlen-1
  2738.  when usesrch=1 then mx1=trunc(.66*ct_newlen)
  2739.  otherwise mx1=trunc(ct_newlen*0.40)
  2740. end
  2741.  
  2742. /* 3b.ii) now, fill up to 1/2 of ctnew2 with "frequent" colors */
  2743. do nc=2 to mx1
  2744.   if usesrch=2 then
  2745.      n2=find_frequent(nc,unacc/(ct_newlen-nc))
  2746.   else
  2747.      n2=find_frequent(nc,0)
  2748.   if n2=0 then do
  2749.        leave            /* no winner */
  2750.     end
  2751.   unacc=unacc-n2
  2752. end
  2753.  
  2754. if verbose>0 then 
  2755.    call dosay "  "ctnew2.0-1 " frequent colors (unaccounted for pixels= " unacc
  2756.  
  2757. /* d) Initialize distances of ctnew colors */
  2758. foo=reset_dist(ctnew2.!r.1,ctnew2.!g.1,ctnew2.!b.1,1,1)
  2759.  
  2760. /* e) For  remaining colors in ctnew2, compute the "set of miniumum distances"
  2761.    of colors in ctnew */
  2762.  
  2763. totre=0
  2764. do mm=3 to ctnew2.0-1
  2765.    totre=totre+reset_dist(ctnew2.!r.mm,ctnew2.!g.mm,ctnew2.!b.mm,mm)
  2766. end /* do */
  2767. if verbose>0 then call dosay "  Total number of distance resets " totre
  2768.  
  2769.  
  2770. /* f) search for 3-color values that remove the most distance  */
  2771.  
  2772. totdist=0
  2773. do mm0=0 to ctnew.0-1
  2774.      totdist=totdist+(ctnew.!ct.mm0*ctnew.!dist.mm0)
  2775. end /* do */
  2776. if verbose>0 then call dosay "Total distance to explain: " totdist
  2777.  
  2778. athresh=0
  2779. if usesrch=1 then athresh=40
  2780. ijf=ctnew2.0
  2781. do iat=ijf to ct_newlen-1
  2782.   saves=make_colors_search(128,128,128,128,iat,athresh)
  2783.   if saves=0 then leave                         /* couldn't improve */
  2784. end
  2785.  
  2786. /* g) try frequent colors again (or all, if usesrch=1*/
  2787. unacc=0
  2788. do kk=0 to ctnew.0-1
  2789.    ir=ctnew.!r.kk ; ig=ctnew.!g.kk ; ib=ctnew.!b.kk
  2790.    if checked2.ir.ig.ib=0 then do
  2791.        unacc=unacc+ctnew.!ct.kk
  2792.    end
  2793. end /* do */
  2794. if verbose>0 then call dosay " Currently unaccounted for = " unacc
  2795.  
  2796. iz=ctnew2.0
  2797. do nc=iz to ct_newlen-1
  2798.   if usesrch=1 then
  2799.      n2=find_frequent(nc,0)
  2800.   else
  2801.      n2=find_frequent(nc,unacc/(ct_newlen-nc))
  2802.   unacc=unacc-n2
  2803.   if n2=0 then leave            /* no winner */
  2804.   lastdid=nc
  2805. end
  2806.  
  2807. if verbose>0 then call dosay "Colors in new table = " ctnew2.0
  2808.  
  2809. /* reset distance again */
  2810. totre=0
  2811. do mm=iz to ctnew2.0-1
  2812.    totre=totre+reset_dist(ctnew2.!r.mm,ctnew2.!g.mm,ctnew2.!b.mm,mm)
  2813. end /* do */
  2814. if verbose>0 then call dosay " Total number of distance resets " totre
  2815.  
  2816. /* h) binary search again */
  2817. iz=ctnew2.0
  2818. do iat=iz to ct_newlen-1
  2819.   axx=make_colors_search(120,120,120,128,iat)
  2820.   if axx=0 then leave
  2821. end 
  2822.  
  2823. /* i) fill up with frequents */
  2824. iz=ctnew2.0
  2825. do nc=iz to ct_newlen-1
  2826.   n2=find_frequent(nc,0)
  2827.   unacc=unacc-n2
  2828. end
  2829.  
  2830. /* reset distance again */
  2831. totre=0
  2832. do mm=iz to ctnew2.0-1
  2833.    totre=totre+reset_dist(ctnew2.!r.mm,ctnew2.!g.mm,ctnew2.!b.mm,mm)
  2834. end /* do */
  2835.  
  2836. /* CHECKED2. will contain cntains pointers from rgb values
  2837.    to CTNEW2 (the new color table) */
  2838.  
  2839. /* i) define REMAPS */
  2840. DO MM=0 TO CTNEW.0-1
  2841.    INEW=CTNEW.!NEW.MM
  2842.    kr=ctnew.!r.mm ; kg=ctnew.!g.mm ; kb=ctnew.!b.mm
  2843.    checked2.kr.kg.kb=inew    
  2844. end /* do */
  2845.  
  2846. return 1
  2847.  
  2848. exit
  2849.  
  2850. /**************************/
  2851. /* use binary search to find best colors */
  2852. make_colors_search:procedure expose ctnew. ctnew2. checked2. rmin rmax gmin gmax bmin bmax verbose is_cgi BLENDGIF_ROOT  save_tempfile
  2853. parse arg r1,g1,b1,size,iat,athresh
  2854.  
  2855. if athresh='' then athresh=0
  2856.  
  2857. saves=-1; 
  2858. drop checked.
  2859. checked.=0
  2860.  
  2861. do iii=1 to 8           /* 2**8 = 256 */
  2862.    foo=points_8(r1,g1,b1,size)            /* 8 points in 8 quadrants */
  2863.  
  2864. /* compute "savings" from each of these points */
  2865.    do mm=1 to 8
  2866.        ee=comp_savings(points.!r.mm, points.!g.mm,points.!b.mm)  
  2867.        if ee>saves then do 
  2868.          r1=points.!r.mm; g1=points.!g.mm ; b1=points.!b.mm ;saves=ee
  2869.        end /* do */
  2870.    end
  2871.    size=size/2 
  2872. end
  2873.  
  2874. if saves<=athresh then return 0   /* can't find a useful enough rgb, give up */
  2875.  
  2876. ctnew2.!r.iat=r1 ; ctnew2.!g.iat=g1 ; ctnew2.!b.iat=b1
  2877. ctnew2.0=iat+1
  2878. if verbose>0 then call dosay " Using color " r1 g1 b1 " explains " saves 
  2879. aa=reset_dist(r1,g1,b1,iat)
  2880. checked2.r1.g1.b1=iat
  2881. return saves
  2882.  
  2883.  
  2884.  
  2885. /******************************/
  2886. /* find frequent pixels, and assign them to the ctnew2.  color table
  2887.  nc= spot in ctnew2. to (possibly) set
  2888.  thresh = .!ct must exceed thresh 
  2889. */
  2890.  
  2891. find_frequent:procedure expose ctnew. ctnew2. checked2. is_cgi BLENDGIF_ROOT  save_tempfile
  2892. parse arg nc,thresh                     
  2893.  
  2894. thresh=max(1,thresh)
  2895. amax=0
  2896. do nc2=0 to ctnew.0-1                  /* check each used color */
  2897.    rr=ctnew.!r.nc2 ; gg=ctnew.!g.nc2 ; bb=ctnew.!b.nc2
  2898.    if checked2.rr.gg.bb>0 then iterate   /* skip if already matched */
  2899.    if ctnew.!ct.nc2>amax then do
  2900.          amax=ctnew.!ct.nc2 
  2901.          rr1=rr; bb1=bb; gg1=gg ; ipt=nc2
  2902.    end /* do */
  2903. end
  2904.  
  2905. if amax>=thresh then do                /* the max is > thresh, record it */
  2906.      ctnew2.!r.nc=rr1
  2907.      ctnew2.!g.nc=gg1
  2908.      ctnew2.!b.nc=bb1
  2909.      checked2.rr1.gg1.bb1=nc
  2910.      ctnew.!new.ipt=nc
  2911.      ctnew.!dist.ipt=0
  2912.      ctnew2.0=nc+1                      /* account for 0th color */
  2913.      return amax
  2914. end
  2915. return 0                      /* no winner */
  2916.  
  2917.  
  2918.  
  2919. /****************************************/
  2920. /* 1) create ctnew from cts  */
  2921. combine_cts:procedure expose  ctnew. cts. is_cgi BLENDGIF_ROOT  save_tempfile
  2922.  
  2923. ctref.=0 ; ctnew.=0
  2924. do ithc=1 to cts.0
  2925.   foo=cvcopy(cts.ithc,'ct1')
  2926.   do mm=0 to ct1.0-1
  2927.     atail=d2c(ct1.!r.mm)||d2c(ct1.!g.mm)||d2c(ct1.!b.mm)
  2928.     if ctref.atail=0 then do
  2929.       i0=ctnew.0
  2930.       ctnew.!r.i0=ct1.!r.mm; ctnew.!g.i0=ct1.!g.mm; ctnew.!b.i0=ct1.!b.mm
  2931.       ctnew.0=i0+1
  2932.       ctref.atail=i0
  2933.     end /* do */
  2934.     i0=ctref.atail
  2935.     ctnew.!ct.i0=ctnew.!ct.i0+ct1.!ct.mm
  2936.   end 
  2937. end
  2938.  
  2939. /* Discard unused colors */
  2940. itt=0
  2941. do mm=0 to ctnew.0-1
  2942.   if ctnew.!ct.mm>0 then do
  2943.       ctemp.!r.itt=ctnew.!r.mm ; ctemp.!g.itt=ctnew.!g.mm 
  2944.       ctemp.!b.itt=ctnew.!b.mm ; 
  2945.       ctemp.!ct.itt=ctnew.!ct.mm ;  ctemp.!dist.itt=-1 ; 
  2946.       CTEMP.!NEW.ITT=-1
  2947.       itt=itt+1
  2948.       ctemp.0=itt
  2949.   end /* do */
  2950. end
  2951. drop ctnew.
  2952. foo=cvcopy('ctemp','ctnew')
  2953.  
  2954. return 1
  2955.  
  2956.  
  2957.  
  2958. /**********************/
  2959. /* reset .!dist for ctnew. */
  2960. reset_dist:procedure expose ctnew. checked2. is_cgi BLENDGIF_ROOT  save_tempfile
  2961. parse arg r0,g0,b0,icc,istart
  2962.  
  2963. ree=0
  2964. do mm=0 to ctnew.0-1
  2965.    ar=ctnew.!r.mm ; ag=ctnew.!g.mm ; ab=ctnew.!b.mm
  2966.    if checked2.ar.ag.ab>0 then do
  2967.        ctnew.!new.mm=checked2.ar.ag.ab
  2968.        ctnew.!dist.mm=0
  2969.        iterate
  2970.    end
  2971.    isdist=ctnew.!dist.mm
  2972.    adist=dist3(r0-ar,g0-ag,b0-ab)
  2973.    if (adist< isdist) | (istart=1) then do
  2974.       ree=ree+1
  2975.       ctnew.!dist.mm=adist 
  2976.       CTNEW.!NEW.MM=ICC
  2977.       if adist=0 then checked2.ar.ag.ab=icc
  2978.    end /* do */
  2979. end /* do */
  2980. return ree
  2981.  
  2982. /**********************/
  2983. /* given a candidate color, compute how much this "saves" (for colors
  2984. in ctnew */
  2985. comp_savings:procedure expose ctnew. checked. rmin rmax gmin gmax bmin bmax checked2. is_cgi BLENDGIF_ROOT  save_tempfile
  2986. parse arg r0,g0,b0
  2987.  
  2988. if checked.r0.g0.b0=1 then do
  2989.    return 0  /* checked this round */
  2990. end
  2991.  
  2992. if checked2.r0.g0.b0>0 then do
  2993.    return 0  /* useing this color */
  2994. end
  2995.  
  2996. if r0<rmin | r0>rmax | g0<gmin | g0>gmax | b0<bmin | b0>bmax then do
  2997.    return 0
  2998. end
  2999.  
  3000. checked.r0.g0.b0=1
  3001. saves=0
  3002. do mm=0 to ctnew.0-1
  3003.    ar=ctnew.!r.mm ; ag=ctnew.!g.mm ; ab=ctnew.!b.mm
  3004.    adist=dist3(r0-ar,g0-ag,b0-ab)
  3005.    if adist< ctnew.!dist.mm then saves=saves+((ctnew.!dist.mm-adist)*ctnew.!ct.mm)
  3006.  
  3007. end /* do */
  3008. return saves
  3009.  
  3010. /**********************/
  3011. /* define 8 quadrants in color cube, as defined by centers */
  3012. points_8:procedure expose points.
  3013. parse arg rc,gc,bc,isize
  3014. is2=max(1,trunc(isize/2))
  3015. points.!r.1=rc-is2 ;   points.!g.1=gc-is2 ;   points.!b.1=bc-is2
  3016. points.!r.2=rc+is2 ;   points.!g.2=gc-is2 ;   points.!b.2=bc-is2
  3017. points.!r.3=rc-is2 ;   points.!g.3=gc+is2 ;   points.!b.3=bc-is2
  3018. points.!r.4=rc+is2 ;   points.!g.4=gc+is2 ;   points.!b.4=bc-is2
  3019. points.!r.5=rc-is2 ;   points.!g.5=gc-is2 ;   points.!b.5=bc+is2
  3020. points.!r.6=rc+is2 ;   points.!g.6=gc-is2 ;   points.!b.6=bc+is2
  3021. points.!r.7=rc-is2 ;   points.!g.7=gc+is2 ;   points.!b.7=bc+is2
  3022. points.!r.8=rc+is2 ;   points.!g.8=gc+is2 ;   points.!b.8=bc+is2
  3023.  
  3024. return 1
  3025.  
  3026.  
  3027.  
  3028. /***************************************/
  3029. /* count occurences of a pixel value in an image stem array
  3030.   return in a space delimted list (nth word corresponding to nth+1 pixel value)
  3031. */
  3032. count_pixels:procedure expose (imgname) is_cgi BLENDGIF_ROOT  save_tempfile
  3033. parse arg ncs
  3034. cts.=0
  3035. irows=value(imgname||'!rows')
  3036. icols=value(imgname||'!cols')
  3037. do ir=0 to irows-1
  3038.    arow=value(imgname||ir)
  3039.    do ic=1 to icols
  3040.       ipx=c2d(substr(arow,ic,1))
  3041.       if ipx>(ncs-1) then do
  3042.          call dosay "ERROR: pixel value greater then # colors: " ir ic ipx ncs irows icols
  3043.          exit
  3044.       end /* do */
  3045.       cts.ipx=cts.ipx+1
  3046.    end /* do */
  3047. end /* do */
  3048. goo=''
  3049. do ii=0 to ncs-1
  3050.   goo=goo||cts.ii' '
  3051. end /* do */
  3052. return goo
  3053.  
  3054.  
  3055. /**************************/
  3056. /* created sorted_ct. -- a sort index into ctnew2. 
  3057. */
  3058.  
  3059. sorT_ctnew2:procedure expose ctnew2. sorted_ct. is_cgi BLENDGIF_ROOT  save_tempfile
  3060. parse arg ttype
  3061.  
  3062. if ttype=0 | ttype=2 | ttype=3 then return 0 /* don't need to sort ct */
  3063.  
  3064. /* otherwise, score and sort colors */
  3065. bri.0=ctnew2.0-1
  3066. do mm=1 to ctnew2.0-1        /* always leave 0th unchanged */
  3067.       r=ctnew2.!r.mm ; g=ctnew2.!g.mm ; b=ctnew2.!b.mm
  3068.       if ttype=1 then do
  3069.          scor=r+g+b
  3070.       end /* do */
  3071.       else do
  3072.          ff='scor='||ttype
  3073.          interpret ff
  3074.       end /* do */
  3075.       bri.mm=left(mm,5)||left(scor,8)
  3076. end /* do */
  3077. foo=arraysort(bri,1,,6,8,'A','N')
  3078. if foo=0 then do 
  3079.        call dosay "ARRAYSORT failure "
  3080.        return 0
  3081. end /* do */
  3082. do mm=1 to bri.0
  3083.    parse var bri.mm jmm .
  3084.    sorted_ct.jmm=mm      /* ctnew2.jmm is the mm'th "brightest" color */
  3085.    sorted_ct.!rev.mm=jmm  /* the mm'th brightest color is ctnew2.jmm */
  3086. end /* do */
  3087. return 1
  3088.  
  3089.  
  3090. /****** END OF CT CREATION PROCEDURE  ***************/
  3091.  
  3092. /********************************/
  3093. /******** GENERALLY USEFUL PROCEDURES (and INITIALIZATION PROCEDURE ****/
  3094. /********************************/
  3095.  
  3096. /**********************/
  3097. /* temp file in gif_dir_dir */
  3098. systempfilename2:procedure expose BLENDGIF_ROOT  save_tempfile
  3099. parse arg aname
  3100. aa=resolve_filename(aname,BLENDGIF_ROOT,,1)
  3101.  
  3102. bb=systempfilename(aa)
  3103.  
  3104. return bb
  3105.  
  3106.  
  3107.  
  3108. /* -------------------- */
  3109. /* choose between 3 alternatives (by default,a yes or no ), 
  3110. return 1 if yes (or 0,1,2 for chosen altenative ) */
  3111.  
  3112. yesno:procedure expose normal reverse bold cy_ye
  3113. parse arg amessage , altans,def,arrowok
  3114. aynn=' '
  3115. if def='' then 
  3116.  defans=''
  3117. else
  3118.  defans=translate(left(strip(def),1))
  3119. if altans='' then altans='No Yes'
  3120.  
  3121. w.0=words(altans)
  3122. do iw0=1 to w.0
  3123.      w.iw0=strip(word(altans,iw0))
  3124.      a.iw0=translate(left(w.iw0,1))
  3125.      aa.iw0=substr(w.iw0,2)
  3126.      aynn=aynn||bold
  3127.      if  a.iw0=defans then aynn=aynn||cy_ye
  3128.      aynn=aynn||a.iw0||normal||aa.iw0
  3129.      if iw0<w.0 then aynn=aynn'|'
  3130. end
  3131. if arrowok=1 then aynn=aynn||' [UP]'
  3132. do forever
  3133.  foo1=normal||reverse||amessage||normal||aynn||' 'normal
  3134.  call charout,foo1
  3135.  anans=translate(sysgetkey('echo'))
  3136.  ianans=c2d(anans)
  3137.  if anans='' | ianans=13 | ianans=10 then  anans=defans
  3138.  
  3139.  if arrowok=1 & ianans=0 then do
  3140.      ians=c2d(sysgetkey('noecho'))
  3141.      if ians=72 then  do
  3142.            say ;say
  3143.            return -1  /* -1 : up key */
  3144.      end
  3145.  end /* do */
  3146.  
  3147.  do ijj=1 to w.0
  3148.     if abbrev(anans,a.ijj)=1 then do
  3149.         say
  3150.         return Ijj-1
  3151.     end
  3152.  end /* do */
  3153.  call charout,'0d'x
  3154. end
  3155.  
  3156.  
  3157.  
  3158.  
  3159. /* ------------------------------------------------------------------ */
  3160.  /* function: Check if ANSI is activated                               */
  3161.  /*                                                                    */
  3162.  /* call:     CheckAnsi                                                */
  3163.  /*                                                                    */
  3164.  /* where:    -                                                        */
  3165.  /*                                                                    */
  3166.  /* returns:  1 - ANSI support detected                                */
  3167.  /*           0 - no ANSI support available                            */
  3168.  /*          -1 - error detecting ansi                                 */
  3169.  /*                                                                    */
  3170.  /* note:     Tested with the German and the US version of OS/2 3.0    */
  3171.  /*                                                                    */
  3172.  /*                                                                    */
  3173.  CheckAnsi: PROCEDURE
  3174.    thisRC = -1
  3175.  
  3176.    trace off
  3177.                          /* install a local error handler              */
  3178.    SIGNAL ON ERROR Name InitAnsiEnd
  3179.  
  3180.    "@ANSI 2>NUL | rxqueue 2>NUL"
  3181.  
  3182.    thisRC = 0
  3183.  
  3184.    do while queued() <> 0
  3185.      queueLine = lineIN( "QUEUE:" )
  3186.      if pos( " on.", queueLine ) <> 0 | ,                       /* USA */
  3187.         pos( " (ON).", queueLine ) <> 0 then                    /* GER */
  3188.        thisRC = 1
  3189.    end /* do while queued() <> 0 */
  3190.  
  3191.  InitAnsiEnd:
  3192.  signal off error
  3193.  RETURN thisRC
  3194.  
  3195. /********/
  3196. /* say or "push" a message -- but don't do nothing if cgi-bin! */
  3197. dosay:procedure expose is_cgi BLENDGIF_ROOT save_tempfile
  3198. parse arg amess
  3199. if is_cgi=2 then say amess
  3200. if is_cgi=0 then do
  3201.    if save_tempfile=1 then
  3202.       foo=sref_multi_send(amess||'0d0a'x,'text/plain','1A')
  3203.    else
  3204.       foo=sref_multi_send(amess||'0d0a'x,'text/plain','A')
  3205.    if foo<0 then do
  3206.        call pmprintf(" connection broken in blendgif")
  3207.        exit
  3208.    end /* do */
  3209. end
  3210. return 0
  3211.  
  3212.  
  3213. /********/
  3214. /* say or "push" an error message -- if cgi-bin, first write content-type line
  3215.    open as a text file */
  3216. dosay2:procedure expose is_cgi BLENDGIF_ROOT save_tempfile
  3217. parse arg amess
  3218. if is_cgi=2 then say amess              /* from command prompt */
  3219. if is_cgi=0 then do                     /* sre-http addon */
  3220.    if save_tempfile=1 then
  3221.       foo=sref_multi_send(amess||'</body></html>'||'0d0a'x,,'1E')
  3222.    else
  3223.       foo=sref_multi_send(amess||'0d0a'x,'text/plain','A')
  3224.    if foo<0 then do
  3225.        call pmprintf(" connection broken in blendgif")
  3226.        exit
  3227.    end /* do */
  3228. end
  3229. if is_cgi=1 then do                     /* cgi-bin */
  3230.    say 'Content-type: text/plain'
  3231.    say
  3232.    say amess
  3233. end /* do */
  3234. return 0
  3235.  
  3236.  
  3237. /*********/
  3238. /* show stuff in queue as a list */
  3239. show_dir_queue:procedure expose qlist.
  3240. parse arg lookfor
  3241.     ibs=0 ;mxlen=0
  3242.     if lookfor<>1 then
  3243.        nq=queued()
  3244.      else
  3245.         nq=qlist.0
  3246.     do ii=1 to nq
  3247.        if lookfor=1 then do
  3248.           aa=qlist.ii
  3249.           ii2=lastpos('\',aa) ; anam=substr(aa,ii2+1)
  3250.        end /* do */
  3251.        else do
  3252.           pull aa
  3253.           if pos(lookfor,aa)=0 then iterate
  3254.           parse var aa anam (lookfor) .
  3255.           if strip(anam)='.' | strip(anam)='..' then iterate
  3256.        end
  3257.        ibs=ibs+1
  3258.        blist.ibs=anam
  3259.        mxlen=max(length(anam),mxlen)
  3260.     end /* do */
  3261. arf=""
  3262. do il=1 to ibs
  3263.    anam=blist.il
  3264.    arf=arf||left(anam,mxlen+2)
  3265.    if length(arf)+mxlen+2>75  then do
  3266.         say arf
  3267.         arf=""
  3268.    end /* do */
  3269. end /* do */
  3270. if length(arf)>1 then say arf
  3271. say
  3272. return 1
  3273.  
  3274.  
  3275. /********************************/
  3276. /* determine a user scale, given ith of Ilen position, and 
  3277.    list of "user_scales". We assume user_scales is a space delimited list
  3278.   of numbers. 
  3279.  
  3280. The algorithim: determine relative position of ilen in the 1...LLEN list
  3281. of integer values.  Then map this relative position to a relative
  3282. position in the implicit graph determined by the points listed in the
  3283. user_scales array; and read off the value at this position 
  3284. */
  3285.  
  3286. get_user_scale:procedure 
  3287. parse arg ith,ilen,user_scales
  3288.  
  3289. if user_scales="" then return ''  /* AN ARIBRARY DEFAULT */
  3290.  
  3291. igoo=words(user_scales)
  3292.  
  3293. if igoo=1 then return user_scales  /* a trivial case */
  3294.  
  3295. /* More trivial, "ends", cases */
  3296. if ith=1 then return word(user_scales,1)
  3297. if ith>=ilen then return word(user_scales,igoo)
  3298.  
  3299. /* middle position -- determine relative position */
  3300. frac=(ith-1)/(ilen-1)    /* where in scale list is it (steps from first position*/
  3301. spot=1+ ((igoo-1)*frac)
  3302. ifrac=trunc(spot)
  3303. afrac=spot-ifrac
  3304.  
  3305. /* exact match (no interpolation needed */
  3306. if afrac=0 then return word(user_scales,ifrac)
  3307.  
  3308. /* otherwise, interpolate */
  3309. ii=ifrac+1
  3310. a1=word(user_scales,ii)
  3311. a2=word(user_scales,ifrac)
  3312.  
  3313. diff=a1-a2
  3314. aaa=(a2+(diff*afrac))
  3315. if pos('.',a1)+pos('.',a2)=0 then aaa=trunc(aaa)
  3316. return aaa
  3317.  
  3318. /************************************/
  3319. /* load dlls, etc */
  3320. init1:
  3321.  
  3322. foo=rxfuncquery('sysloadfuncs')     /* load rexxutil library */
  3323. if foo=1 then do
  3324.   call RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
  3325.   call SysLoadFuncs
  3326. end
  3327.  
  3328. foo=rxfuncquery('rxgdloadfuncs')
  3329. if foo=1 then do
  3330.   Call RxFuncAdd 'RxgdLoadFuncs', 'RXGDUTIL', 'RxgdLoadFuncs'
  3331.   Call RxgdLoadFuncs
  3332. end
  3333. foo=rxfuncquery('rxgdloadfuncs')
  3334. if foo=1 then do
  3335.    if verb="" then do
  3336.         STRING "Sorry: RXGDUTIL.DLL is not available! Did you copy it to your LIBPATH? "
  3337.         return ' '
  3338.    end /* do */
  3339.    call dosay 'Sorry: RXGDUTIL.DLL is not available! Did you copy it to your LIBPATH? '
  3340.    exit
  3341. end /* do */
  3342.  
  3343.  
  3344. /* Load up advanced REXX functions */
  3345. foo=rxfuncquery('rexxlibregister')
  3346. if foo=1 then do
  3347.  call rxfuncadd 'rexxlibregister','rexxlib', 'rexxlibregister'
  3348.  call rexxlibregister
  3349. end
  3350. foo=rxfuncquery('rexxlibregister')
  3351. if foo=1 then do
  3352.     say "Sorry: REXXLIB is not available. Did you copy it to your LIBPATH?"
  3353.     exit
  3354. end /* do */
  3355. return 0
  3356.  
  3357.  
  3358. /****************/
  3359. /* initialize parameters */
  3360. init2:
  3361. nregions=fade_regions 
  3362. nframes=frames
  3363.  
  3364. balname.1='Square'
  3365. balname.2='Diamond'
  3366. balname.3='Octagon'
  3367. balname.4='Circle'
  3368.  
  3369. adisposal=disposal
  3370. ADISPOSAL0=DISPOSAL
  3371. do_iter=iterations
  3372.  
  3373. gotroutes=0; gotminfos=0
  3374.  
  3375.  
  3376. /* assign defaults to unspecified .n parameers */
  3377. pnames='FRAMES STOP_AFTER ANIM_TYPE BALLOON_TYPE BALLOON_PUSH MASK_LIST  '|| ,
  3378.        'CENTERX CENTERY FADE_TYPE CURTAIN_OVERWRITE CURTAIN_TYPE DISSOLVE_SPEC FRAME_DELAY'
  3379.  
  3380. do nmm=1 to infile.0-1
  3381.    do njj=1 to words(pnames)
  3382.       aw=strip(word(pnames,njj))
  3383.       if symbol(aw'.'nmm)<>'VAR' | DOPAIR.Nmm<>1 then do
  3384.          arf=aw'.'nmm'='aw
  3385.          interpret arf
  3386.       end /* do */
  3387.    end /* do */
  3388.    do njj=1 to mask.0   
  3389.       if symbol('MASK.'njj'.'nmm)<>'VAR'   | DOPAIR.mm<>1 then
  3390.         mask.njj.nmm=mask.njj
  3391.       if symbol('MASK.'njj'.!thresh.'nmm)<>'VAR'   | DOPAIR.mm<>1 then
  3392.         mask.njj.!thresh.nmm=mask.njj.!thresh
  3393.    end /* do */
  3394. end /* do */
  3395.  
  3396.  
  3397. if resize_mode<>2 then do
  3398.    height1=0 ;width1=0
  3399. end /* do */
  3400.  
  3401. if datatype(r_back)<>'NUM' then r_back=110
  3402. if datatype(g_back)<>'NUM' then g_back=110
  3403. if datatype(b_back)<>'NUM' then b_back=110
  3404.  
  3405. r_back=min(max(0,r_back),255)
  3406. g_back=min(max(0,g_back),255)
  3407. b_back=min(max(0,b_back),255)
  3408.  
  3409. return 0
  3410.  
  3411.  
  3412. /*********************/
  3413. /* read a gif file into memory  -- possibly use socket calls to get
  3414. a gif file from da web */
  3415. read_giffile:procedure expose is_cgi BLENDGIF_ROOT  save_tempfile
  3416. parse arg giffile, BLENDGIF_ROOT
  3417. giffile=strip(giffile)
  3418. if giffile='' then do
  3419.   call charout, " Enter gif file(s): "
  3420.   pull giffile
  3421. end
  3422. if giffile='' then exit
  3423. if pos('.',giffile)=0 then giffile=giffile'.gif'  /* add the extension? */
  3424.  
  3425. hh=strip(translate(giffile))
  3426. if abbrev(hh,'HTTP://')=1 then do   /* get from the web */
  3427.   gifcontents=go_get_url(giffile,,verbose)
  3428.   if gifcontents='' then do
  3429.      return 0
  3430.   end /* do */
  3431. /* parse it, check content-type, then return body */
  3432.   parse var gifcontents lin1 '0d0a'x gifcontents
  3433.   parse var lin1 ap istat .
  3434.   if left(istat,1)<>'2' then return 0  /* not a 200 response */
  3435. /* scan through headers, check for content-type */
  3436.   isimg=0
  3437.   do forever
  3438.      parse var gifcontents lin1 '0d0a'x gifcontents
  3439.      if lin1='' then do
  3440.         if isimg=0 then return 0       /* no image/gif header found */
  3441.         return gifcontents
  3442.      end /* do */
  3443.      parse  upper var lin1 amime ':' amime2
  3444.      if strip(amime)='CONTENT-TYPE'  then do
  3445.         if strip(amime2)<>'IMAGE/GIF' then return 0  /* not a gif file */
  3446.         isimg=1
  3447.      end
  3448.      if gifcontents='' then return 0
  3449.   end /* do */
  3450. end /* do */
  3451.  
  3452.  
  3453. /* else, read a file into memory */
  3454. gfn=giffile
  3455. if BLENDGIF_ROOT<>'' then do
  3456.    gfn=resolve_filename(giffile,BLENDGIF_ROOT,'.GIF')
  3457. end
  3458. if gfn='' then do
  3459.    call dosay "b) no such file "||giffile
  3460.    return 0
  3461. end /* do */
  3462.  
  3463. igs=stream(gfn,'c','query size')
  3464. if igs=0 | igs='' then do
  3465.    call dosay "c) no such file "||giffile
  3466.    return 0
  3467. end /* do */
  3468. gifcontents=charin(gfn,1,igs)
  3469. foo=stream(gfn,'c','close')
  3470. return gifcontents 
  3471.  
  3472.  
  3473. /* ---------------------------------------------*/
  3474. /* Return a file name; given a file name and
  3475.    a root directory.  Will resolve file name, if it's
  3476.    not fully qualified
  3477. Called as:  newname=resolve_file(afile,adir,nocheck)
  3478.    afile: the file name to resolve, might be relative
  3479.    adir : root directory. Use it's drive and path, with
  3480.              afile, to determine newfilename
  3481.              If not specified,  then current directory is used
  3482.   defext   : default extension to add, if no .ext exists
  3483.              if '', then ignore
  3484.    check: if =1, then do NOT check for existence of this file
  3485. returns
  3486.   Newname, if it exists of if nocheck=1
  3487.   '' -- if it doesn't exist and nocheck<>1
  3488.  
  3489. Note: afile and adir will be "stripped" of spaces -- which
  3490. limits the range of non 8.3 names that can be used */
  3491.  
  3492. resolve_filename:procedure
  3493.  
  3494. parse arg afile,adir,defext,nocheck
  3495. afile=strip(afile) ; adir=strip(adir)
  3496.  
  3497. curdir0=directory()
  3498. curdir=curdir0'\'
  3499.  
  3500. if adir='' then adir=curdir     /* no adir specified, use current */
  3501.  
  3502. if right(adir,1)<>'\' & right(adir,1)<>':' then adir=adir'\'
  3503.  
  3504. usedrive=filespec('D',adir)
  3505. usedrive0=usedrive
  3506.  
  3507. if usedrive='' then usedrive=filespec('D',curdir) /* no drive in adir, use current*/
  3508.  
  3509. usepath=filespec('P',adir)
  3510. if left(usepath,1)<>'\' then do    /* relative to current usedrive path */
  3511.    foo=directory(usedrive)'\'
  3512.    foo2=directory(curdir0)
  3513.    usepath=filespec('p',foo)||usepath
  3514. end /* do */
  3515. oldfile=filespec('n',afile)
  3516.  
  3517. select
  3518.   when substr(afile,2,2)=":\" then do /* if 2-3 = :\, then use afile as is */
  3519.      usefile=afile
  3520.   end /* do */
  3521.  
  3522.   when substr(afile,2,1)=':' then do    /* relative file name on drive */
  3523.  
  3524.       if usedrive0='' then do            /* perhaps use usepath? */
  3525.           usefile=left(afile,2)||usepath||oldfile
  3526.       end               /* otherwise, use afile as is */
  3527.       else do
  3528.          usefile=afile
  3529.       end /* do */
  3530.   end
  3531.   when left(afile,1,1)='\' then do      /* attach adir drive */
  3532.       usefile=usedrive||afile
  3533.   end
  3534.   otherwise do
  3535.       usefile=usedrive||usepath||afile
  3536.   end
  3537. end
  3538.  
  3539. if pos('.',afile)=0 & defext<>'' then usefile=usefile||'.'||strip(defext,'l','.')
  3540.  
  3541. if nocheck=1 then return usefile
  3542.  
  3543. afile=stream(usefile,'c','query exists')
  3544. return afile
  3545.  
  3546.  
  3547. /* ---------------------------------------------*/
  3548. /* get a fully qualified url from some site, return first
  3549. maxchar characters (if maxchar missing, get 10million (the whole thing?) */
  3550. /* ---------------------------------------------*/
  3551.  
  3552. go_get_url:procedure expose is_cgi BLENDGIF_ROOT  save_tempfile
  3553. parse arg aurl,maxchar,verbose,headers
  3554.  
  3555. if maxchar="" then maxchar=10000000
  3556.  
  3557. got=""
  3558. if abbrev(translate(aurl),'HTTP://')=1 then do
  3559.    aurl=substr(aurl,8)
  3560. end
  3561. else do
  3562.      return ''     /* must be fully qualified url */
  3563. end /* do */
  3564. parse var aurl server '/' request
  3565.  
  3566. /* now get the url.  It requires the RxSock.DLL be in your LIBPATH. */
  3567.  
  3568. /* Load RxSock */
  3569.     if \RxFuncQuery("SockLoadFuncs") then nop
  3570.     else do
  3571.        call RxFuncAdd "SockLoadFuncs","rxSock","SockLoadFuncs"
  3572.        call SockLoadFuncs
  3573.     end
  3574.  
  3575.     crlf    ='0d0a'x                        /* constants */
  3576.     family  ='AF_INET'
  3577.     httpport=80
  3578.  
  3579.    if verify(server,'1234567890.')>0 then 
  3580.        rc=sockgethostbyname(server, "serv.0")  /* get dotaddress of server */
  3581.    else
  3582.       serv.0addr=strip(server)
  3583.  
  3584.     if rc=0 then do
  3585.         call dosay 'Unable to resolve "'server'"'
  3586.         return ''
  3587.     end
  3588.     dotserver=serv.0addr                    /* .. */
  3589.     gosaddr.0family=family                  /* set up address */
  3590.     gosaddr.0port  =httpport
  3591.     gosaddr.0addr  =dotserver
  3592.  
  3593.     gosock = SockSocket(family, "SOCK_STREAM", "IPPROTO_TCP")
  3594.  
  3595.     /* Set up request */
  3596.     message="GET /"request' HTTP/1.0 'crlf
  3597.     if length(headers)>2 then do
  3598.        if right(headers,2)=crlf then headers=left(headers,length(headers)-2)
  3599.     end
  3600.     if headers<>'' then message=message||headers||crlf
  3601.     message=message||'Host: 'server||crlf
  3602.  
  3603.     message=message||crlf
  3604.  
  3605.   if verbose>0 then call dosay "   Retrieving " request " from " dotserver 
  3606.     got=''
  3607.     rc = SockConnect(gosock,"gosaddr.0")
  3608.     if rc<0 then do
  3609.         call dosay ' Unable to connect to "'server'"'
  3610.         return ' '
  3611.     end
  3612.     rc = SockSend(gosock, message)
  3613.  
  3614.  /* Now wait for the response */
  3615.  
  3616.    do r=1 by 1
  3617.      rc = SockRecv(gosock, "response", 1000)
  3618.      got=got||response
  3619.      if rc<=0 then leave
  3620.      tmplen=length(got)
  3621.      if tmplen> maxchar then leave
  3622.   end r
  3623.  
  3624.   rc = SockClose(gosock)
  3625.  
  3626. return got
  3627.  
  3628.  
  3629.  
  3630. /*********************************/
  3631. /* Create a transformation matrix, and it's inverse; given:
  3632.    width (WIDTH) and height (HEIGHT) (of image) in pixels
  3633.    width (WSCALE) and height (HSCALE scale (eg; 0.5=one half, 1.0=no change, 2.0=doubling)
  3634.    rotation (ROTATE)(in degrees)
  3635.    column (XMOVE) and row (YMOVE) translation.
  3636.  
  3637. The transformation matrix assumes the following operatiosn:
  3638.    move center of image to 0,0 origin 
  3639.    scale the image
  3640.    rotate the image
  3641.    move the image (which also accounts for the "move to center")
  3642.  
  3643. call as:
  3644.    tran_matrix='a_stem.'; inv_tran_matrix='b_stem.'
  3645.    astatus=create_trans_mtx(width,height,wscale,hscale,zrotate,yrotate,xrotate,xmove,ymove)
  3646. where:
  3647.    the arguments are as defined above, and where
  3648.    tranmtx is a 4x4 matrix which will transform any pixel in the image.
  3649.    tran_matrix should be set to a stem name into which transformation 
  3650.    matrix will be written (be SURE to include the trailing period).
  3651.  
  3652. To compute a transformation on a point at XOLD,YOLD (column,row), 
  3653. perform the matrix multiplication:
  3654.   xnew= xold*trnmtx.1.1 + yold*trnmtx.2.1 + tranmtx.4.1
  3655.   yxnew=xold*trnmtx.1.2 + yold*trnmtx.2.2 + tranmtx.4.2
  3656. where:
  3657.   TRNMTX is the value of TRAN_MATRIX that is, TRAN_MATRIX='TRANMTX.'
  3658.  
  3659. And, to transform xnew, ynew back to xold,yold:
  3660.   xold= xnew*itrnmtx.1.1 + ynew*itrnmtx.2.1 + itranmtx.4.1
  3661.   yold= xnew*itrnmtx.1.2 + ynew*itrnmtx.2.2 + itranmtx.4.2
  3662.  
  3663. where:  ITRNMTX is the value of INV_TRAN_MATRIX 
  3664.  
  3665.  
  3666. *******/
  3667. create_trans_matrix:procedure expose (tran_matrix) (inv_tran_matrix)
  3668. parse arg width,height,wscale,hscale,zdeg,ydeg,xdeg,xmove,ymove
  3669.  
  3670. mtx1.=0
  3671. mtx1.1.1=1; mtx1.2.2=1 ; mtx1.3.3=1 ; mtx1.4.4=1
  3672. mtx1.4.1=-width/2
  3673. mtx1.4.2=-height/2
  3674.  
  3675. /* 1b) scale matrix */
  3676. mtx2.=0
  3677. mtx2.1.1=wscale
  3678. mtx2.2.2=hscale
  3679. mtx2.3.3=1
  3680. mtx2.4.4=1
  3681.  
  3682. /* 1c) multipliy origin-translation * scaler */
  3683. newmtx='mtx1.'
  3684. foo=mtx_mult(4)
  3685.  
  3686. /* 1d) rotate z */
  3687. mtx2.=0
  3688. ztheta=(2*3.1416)*(zdeg/360)
  3689. csi=cos(ztheta) ;ssi=sin(ztheta)
  3690. mtx2.1.1=csi ; mtx2.1.2=ssi
  3691. mtx2.2.1=-ssi ; mtx2.2.2=csi
  3692. mtx2.3.3=1
  3693. mtx2.4.4=1
  3694.  
  3695. /* 1e) multiply 1c * rotater */
  3696. newmtx='mtx1.'
  3697. foo=mtx_mult(4)
  3698.  
  3699.  
  3700. /* 1da) rotate y */
  3701. mtx2.=0
  3702. ytheta=(2*3.1416)*(ydeg/360)
  3703. csi=cos(ytheta) ;ssi=sin(ytheta)
  3704. mtx2.1.1=csi ; mtx2.1.3=ssi
  3705. mtx2.3.1=-ssi ; mtx2.3.3=csi
  3706. mtx2.2.2=1
  3707. mtx2.4.4=1
  3708.  
  3709. /* 1ea) multiply 1d * 1da * rotater */
  3710. newmtx='mtx1.'
  3711. foo=mtx_mult(4)
  3712.  
  3713.  
  3714. /* 1db) rotate x */
  3715. mtx2.=0
  3716. xtheta=(2*3.1416)*(xdeg/360)
  3717. csi=cos(xtheta) ;ssi=sin(xtheta)
  3718. mtx2.2.2=csi ; mtx2.2.3=ssi
  3719. mtx2.3.2=-ssi ; mtx2.3.3=csi
  3720. mtx2.1.1=1
  3721. mtx2.4.4=1
  3722.  
  3723. /* 1ea) multiply 1db * 1da */
  3724. newmtx='mtx1.'
  3725. foo=mtx_mult(4)
  3726.  
  3727.  
  3728. /* 1f) translate + de-originizer */
  3729. mtx2.=0
  3730. mtx2.1.1=1; mtx2.2.2=1 ; mtx2.3.3=1 ; mtx2.4.4=1
  3731. mtx2.4.1=(width/2)+xmove
  3732. mtx2.4.2=(height/2)+ymove
  3733.  
  3734.  
  3735. /* 1g) multiply 1e*  translater+de-origin-translation */
  3736. newmtx='mtx3.'
  3737. foo=mtx_mult(4)
  3738.  
  3739. do ii=1 to 4
  3740.    do jj=1 to 4
  3741.       foo=value(tran_matrix||ii||'.'||jj,mtx3.ii.jj)
  3742.    end /* do */
  3743. end /* do */
  3744.  
  3745. if value(inv_tran_mtx)='' then return 1
  3746.  
  3747. /* now create de-transformer */
  3748. /* 2a) inv(translate + de-originizer) */
  3749. mtx1.=0
  3750. mtx1.1.1=1; mtx1.2.2=1 ; mtx1.3.3=1 ; mtx1.4.4=1
  3751. mtx1.4.1=-((width/2)+xmove)
  3752. mtx1.4.2=-((height/2)+ymove)
  3753.  
  3754.  
  3755. /* 2b1) derotate x */
  3756. mtx2.=0
  3757. xtheta=-(2*3.1416)*(xdeg/360)
  3758. csi=cos(xtheta) ;ssi=sin(xtheta)
  3759. mtx2.2.2=csi ; mtx2.2.3=ssi
  3760. mtx2.3.2=-ssi ; mtx2.3.3=csi
  3761. mtx2.1.1=1
  3762. mtx2.4.4=1
  3763.  
  3764. newmtx='mtx1.'
  3765. foo=mtx_mult(4)
  3766.  
  3767. /* 2b2) derotate y */
  3768. mtx2.=0
  3769. ytheta=-(2*3.1416)*(ydeg/360)
  3770. csi=cos(ytheta) ;ssi=sin(ytheta)
  3771. mtx2.1.1=csi ; mtx2.1.3=ssi
  3772. mtx2.3.1=-ssi ; mtx2.3.3=csi
  3773. mtx2.2.2=1
  3774. mtx2.4.4=1
  3775.  
  3776. newmtx='mtx1.'
  3777. foo=mtx_mult(4)
  3778.  
  3779.  
  3780. /* 2b3)de rotate z */
  3781. mtx2.=0
  3782. ztheta=-(2*3.1416)*(zdeg/360)
  3783. csi=cos(ztheta) ;ssi=sin(ztheta)
  3784. mtx2.1.1=csi ; mtx2.1.2=ssi
  3785. mtx2.2.1=-ssi ; mtx2.2.2=csi
  3786. mtx2.3.3=1
  3787. mtx2.4.4=1
  3788.  
  3789. newmtx='mtx1.'
  3790. foo=mtx_mult(4)
  3791.  
  3792. /* 2c)inv scale matrix */
  3793. mtx2.=0
  3794. mtx2.1.1=1/wscale
  3795. mtx2.2.2=1/hscale
  3796. mtx2.3.3=1
  3797. mtx2.4.4=1
  3798.  
  3799. newmtx='mtx1.'
  3800. foo=mtx_mult(4)
  3801.  
  3802.  
  3803. /* 2d) inv originizer */
  3804. mtx2.=0
  3805. mtx2.1.1=1; mtx2.2.2=1 ; mtx2.3.3=1 ;mtx2.4.4=1
  3806. mtx2.4.1=width/2
  3807. mtx2.4.2=height/2
  3808.  
  3809. newmtx='mtx3.'
  3810. foo=mtx_mult(4)
  3811.  
  3812. do ii=1 to 4            /* copy to the inverse transformation matrix */
  3813.    do jj=1 to 4
  3814.       foo=value(inv_tran_matrix||ii||'.'||jj,mtx3.ii.jj)
  3815.    end /* do */
  3816. end /* do */
  3817.  
  3818. return 1
  3819.  
  3820.  
  3821. /* multiply mtx1 by mtx2, return as mtx3 */
  3822. mtx_mult:procedure expose  mtx1. mtx2. (newmtx)
  3823. parse arg ndim
  3824.  
  3825. mtx3.=0
  3826. do rr=1 to NDIM
  3827.    do cc=1 to ndim
  3828.      do ii=1 to ndim
  3829.         mtx3.rr.cc=mtx3.rr.cc+(mtx1.rr.ii*mtx2.ii.cc)
  3830.      end /* do */
  3831.    end /* do */
  3832. end /* do */
  3833. do cc=1 to ndim
  3834.   do rr=1 to ndim
  3835.     foo=value(newmtx||cc||'.'||rr,mtx3.cc.rr)
  3836.   end /* do */
  3837. end /* do */
  3838.  
  3839. return 2
  3840.  
  3841.  
  3842.  
  3843. /**************/
  3844. /* transform a point (at x0 y0) using the "tran_matrix")*/
  3845. transfrm_point:procedure expose (tran_matrix)
  3846. parse arg x0,y0,z0
  3847. if z0='' then z0=0
  3848.  
  3849. t11=value(tran_matrix||1'.'1)
  3850. t21=value(tran_matrix||2'.'1)
  3851. t31=value(tran_matrix||3'.'1)
  3852. t41=value(tran_matrix||4'.'1)
  3853.  
  3854. t12=value(tran_matrix||1'.'2)
  3855. t22=value(tran_matrix||2'.'2)
  3856. t32=value(tran_matrix||3'.'2)
  3857. t42=value(tran_matrix||4'.'2)
  3858.  
  3859. t13=value(tran_matrix||1'.'3)
  3860. t23=value(tran_matrix||2'.'3)
  3861. t33=value(tran_matrix||3'.'3)
  3862. t43=value(tran_matrix||4'.'3)
  3863.  
  3864. xn= x0*t11 + y0*t21 + z0*t31 + t41
  3865. yn= x0*t12 + y0*t22 + z0*t32 + t42
  3866. zn= x0*t13 + y0*t23 + z0*t33 + t43
  3867. return trunc(xn)' 'trunc(yn)' 'trunc(zn)
  3868.  
  3869. /***************************/
  3870. /* quasi euclidean distance of a point in 3 space (to origin) */
  3871. dist3:procedure
  3872. parse arg dx,dy,dz,dtype
  3873. if dz='' then dz=0
  3874. if dtype='' then dtype=3
  3875.  
  3876. if dtype=1 then return max(abs(dx),abs(dy),abs(dz))  /* square */
  3877.  
  3878. if dtype=2 then return (abs(dx)+abs(dy)+abs(dz))  /* diamond */
  3879.  
  3880. if dtype=3 then do          /* octagonal */
  3881.   a1=max(abs(dx),abs(dy))
  3882.   a2=min(abs(dx),abs(dy))
  3883.   d1=(a1+(a2/2))
  3884.   if dz=0 then return d1
  3885.   a1=max(abs(d1),abs(dz))
  3886.   a2=min(abs(d1),abs(dz))
  3887.   return (a1+(a2/2))
  3888. end
  3889.  
  3890. /* otherwise, euclidean circle */
  3891. aa=(dx*dx)+(dy*dy)+(dz*dz)
  3892. return sqrt(aa)
  3893.  
  3894.  
  3895. /**********************************/
  3896. /* squoosh a row */
  3897. squoosh_row:procedure
  3898. parse arg arow,newlen
  3899.  
  3900. oldlen=lengtH(arow)
  3901. if newlen<=2 then return left(arow,newlen)
  3902. tfact=oldlen/(newlen-1)
  3903. pp=''
  3904. do mm=1 to newlen
  3905.    jmm=min(1+trunc(tfact*(mm-1)),oldlen)
  3906.    pp=pp||substr(arow,jmm,1)
  3907. end /* do */
  3908. return pp
  3909.  
  3910.  
  3911. /**********************************/
  3912. /* shrink a gif file by looking for areas of non-changing pixels */
  3913. do_shrink_gif:procedure expose is_cgi blendgif_root  save_tempfile
  3914. parse arg gifcontents
  3915.  
  3916. signal on error name foo2err ;signal on syntax name foo2err 
  3917.  
  3918. oldsize=length(gifcontents)
  3919. /* 0) ascertain block structure of the gif file */
  3920. talist=read_gif_block(gifcontents,1,'',1)
  3921.  
  3922. /* 1) get stuff up to and including first image */
  3923. newimage=''
  3924. cts.=0
  3925. curgce=''
  3926. do forever
  3927.    parse upper var talist ainfo talist
  3928.    aa='!'ainfo
  3929.    ii=cts.aa+1
  3930.    cts.aa=ii
  3931.    ab=read_gif_block(gifcontents,ii,ainfo,1)
  3932.    if ainfo='GCE' then DO        /* MAKE SURE TO RETAIN IMAGE */
  3933.       stuff=READ_GCE_BLOCK(ab)
  3934.       parse var STUFF disposal usrinflag tcflag delay tcindex
  3935.       ab=MAKE_GCE_BLOCK(tcflag,tcindex,delay,1,useinFlag)
  3936.    end /* do */
  3937.    newimage=newimage||ab
  3938.    if ainfo='IMG' then leave  /* got first image, now use it ... */
  3939. end
  3940.  
  3941. /* 2) Initialize "current image" matrix (using img block in ab */
  3942. ct_name='CT.'
  3943. img_name='CURIMG.'
  3944. curimg.=0
  3945. ct.=0
  3946. stuff=read_image_block(ab,1)           /* 0= do NOT looad IMG. matrix */
  3947. parse var stuff lpos tpos width0 height0 lct lctsize interl sort ',' imgdata
  3948.  
  3949. do forever      /*  do the images in the file */
  3950.  
  3951. /* 3) start examining other blocks, determining what portions to retain */
  3952. do forever
  3953.    if talist='' then do
  3954.       call dosay "   Image shrunk from "oldsize " to " length(newimage)" bytes."
  3955.       return newimage   /* all done */
  3956.    end
  3957.    parse upper var talist ainfo talist
  3958.    aa='!'ainfo
  3959.  
  3960.    ii=cts.aa+1
  3961.    cts.aa=ii
  3962.    ab=read_gif_block(gifcontents,ii,ainfo,1)
  3963.    if ainfo='GCE' then do 
  3964.       stuff=READ_GCE_BLOCK(ab)
  3965.       parse var STUFF disposal usrinflag tcflag delay tcindex
  3966.       curgceold=MAKE_GCE_BLOCK(tcflag,tcindex,delay,1,useinFlag)
  3967.       curgcenu=MAKE_GCE_BLOCK(tcflag,tcindex,delay,3,useinFlag)
  3968.       iterate
  3969.    end /* do */
  3970.    if ainfo="IMG" then leave    /* will use AB below */
  3971.    if ainfo='TRM' then 
  3972.        newimage=newimage||'3b'x
  3973.    ELSE
  3974.        newimage=newimage||ab
  3975. end
  3976.  
  3977. /* 4) got an image, and a a gce block. See what changes. .. */
  3978.  
  3979. ct_name='CT2.'
  3980. img_name='IMG.'
  3981. img.=0
  3982. ct.=0
  3983. stuff=read_image_block(ab,1)           /* 0= do NOT looad IMG. matrix */
  3984. parse var stuff lpos tpos width height lct lctsize interl sort ',' imgdata
  3985.  
  3986. /* compare IMG. to CURIMG. --- note xmin xmax ymin ymax (of where changes are */
  3987. xmin=11111 ; xmax=0 ; ymin='' ; ymax=height ;allsame=1
  3988. do irow=0 to height0-1
  3989.    arowcur=curimg.irow
  3990.    arownew=img.irow
  3991.    imatch1=compare(arowcur,arownew) 
  3992.    if imatch1=0 then iterate /* perfect match, so skip */
  3993.    allsame=0
  3994.    imatch2=compare(reverse(arowcur),reverse(arownew))
  3995.    if ymin='' then ymin=irow
  3996.    ymax=irow
  3997.    xmin=min(xmin,imatch1)
  3998.    xmax=max(xmax,1+length(arowcur)-imatch2)
  3999.    curimg.irow=arownew         /* new image becomes the current image */
  4000. end
  4001.  
  4002. if allsame=1 then do
  4003.    newimage=newimage||curgceold||ab 
  4004. end /* do */
  4005. else do
  4006. /*  determine mins/maxs */
  4007.   xmin=max(0,xmin-1); xmax=min(width,xmax+1)
  4008.   ymin=max(0,ymin-1); ymax=min(height,ymax+1)
  4009. /* if everything changed, then just current stuff */
  4010.   if ymin<3 & xmin<3 & xmax>(width0-2) & ymax>(height0-2) then do
  4011.       newimage=newimage||curgceold||ab
  4012.   end
  4013.   else do                 /* just write a section */
  4014.      call dosay "   Shrinking image to" (1+xmax-xmin) 'x' (1+ymax-ymin)
  4015.      width2=1+xmax-xmin
  4016.      height2=1+ymax-ymin
  4017.      lpos=xmin
  4018.      tpos=ymin
  4019.      do irow2=ymin to ymax
  4020.        irr=irow2-ymin
  4021.        nuimg.irr=substr(img.irow2,xmin+1,1+xmax-xmin)
  4022.      end 
  4023.      nuimg.!rows=1+ymax-ymin
  4024.      nuimg.!cols=1+xmax-xmin
  4025.      ct_name='ct2.' ; img_name='nuimg.'
  4026.      ablock=MAKE_IMAGE_BLOCK(lpos,tpos,width2,height2,lct,lctsize,interl,sort,0)
  4027.      NEWIMAGE=NEWIMAGE||curgcenu||ABLOCK
  4028.   end
  4029. end
  4030.  
  4031. end             /* mondo huge oop */
  4032.  
  4033.  
  4034. foo2err:
  4035. say " error at "sigl ' 'rc
  4036. /************************************************************/
  4037. /************************************************************/
  4038. /************************************************************
  4039.  
  4040.                             PARSEGIF
  4041.  
  4042.             Procedures to extract information from a  gif file.
  4043.  
  4044. Notes: 
  4045.  * In the descriptions below:
  4046.    > ABLOCK is an actual string of bytes; as pulled from gif file,
  4047.        or suitable for writing to a gif file.
  4048.    > CT_NAME is a string containing the name of the "matrix of 
  4049.        color table values" stem variable.
  4050.        You MUST set it's value before calling procedures that
  4051.        use it.  For example:   ct_name='MY_CT.'
  4052.        (note that you MUST include the . at the end of the stem name)
  4053.    > STUFF is a space or comma delimited list of variables returned
  4054.        by one of these procedures.
  4055.    > IMG_NAME is a string containing the name of a "matrix of pixels"
  4056.        stem avariable.
  4057.        You MUST set it's value before calling procedures that
  4058.        use it.  For example:   imgt_name='IMG_NAME.'
  4059.        (note that you MUST include the . at the end of the stem name)
  4060.  
  4061.  *  Use read_gif_block to read  various "blocks" from a GIF file, 
  4062.     these blocks may then be used as input to the other 
  4063.     For example: 
  4064.                  ablock=read_gif_block(a_gif_file,1,'LSD')
  4065.                  ablock=read_gif_block(a_gif_file,3,"IMG")
  4066.                  ablock=read_gif_block(gifstring,imgnum,'GCE',1)
  4067.     
  4068.  * Several of these procedures work with color tables. Color tables
  4069.    are stored in stem variables, which have the structure:
  4070.         ct.0 = # of colors
  4071.         ct.!r.n = red value for color n
  4072.         ct.!g.n = green value for color n
  4073.         ct.!b.n = blue value for color n
  4074.     where n =0 ... (ctable.0-1), and ct is the "color table name".
  4075.    
  4076.     Prior to calling a color table using/returning procedure,
  4077.     the "color table name" must be defined. 
  4078.     To do this, just set:
  4079.          CT_NAME='a_color_table_name.'
  4080.     For example:
  4081.          CT_NAME='MY_CT.'
  4082.          MY_CT.=0
  4083.     Note that you MUST include the . after the actual name. Use of MY_CT.=0
  4084.     (to set the default value of the MY_CT. "tail" values) is strictly optional.
  4085.     
  4086.     Example:
  4087.          CT_NAME='IMG3_CT.'
  4088.          IMG_NAME='IMG_PIX.'
  4089.          ablock=read_gif_block(gif_file,3,'IMG')
  4090.          stuff=READ_IMAGE_BLOCK(ablock,0)
  4091.          (the IMG3_CT. stem variable will contain the local color table
  4092.           for the 3rd image of gif_file, assuming one exists).
  4093.  
  4094.  *  Several of these procedures work with a matrix of pixel values.
  4095.     As with color tables, these are stored in stem variables, which
  4096.     requires one to assign a value to the IMG_NAME variable. For
  4097.     example:
  4098.           IMG_NAME='img1.'
  4099.           img1.=0
  4100.     Note that you MUST include the . after the actual name. 
  4101.  
  4102.    The structure of this stem variable is (assuming a stem name of img1):
  4103.       img1.!rows = # rows
  4104.       img1.!cols = # cols
  4105.    and each row of the image is in:
  4106.       img1.0
  4107.         ...
  4108.       img1.nrr
  4109.    where:
  4110.       nrr=# rows-1  
  4111.       and each "row" is a string of length img1.!cols.  
  4112.           Each character in this string corresponds (is the d2c) for
  4113.           a pixel value.  
  4114.     Thus, to get the pixel value of the 5 column of the 10th row:
  4115.                 avalue=c2d(substr(img1.10,5,1)) 
  4116.  
  4117. List of Procedures:
  4118.  ablock=READ_GIF_BLOCK(giffile,imgnum,infotype,is_string)
  4119.  ablock=MAKE_ANIMATION_BLOCK(iter) 
  4120.   niter=READ_ANIMATION_BLOCK(ablock)
  4121.  ablock=MAKE_COMMENT_BLOCK(a_comment)
  4122.   stuff=READ_COMMENT_BLOCK(ablock)
  4123.  ablock=MAKE_GCE_BLOCK(tcflag,tcindex,delay,disposal,useinlag)
  4124.   stuff=READ_GCE_BLOCK(ablock)
  4125.  ablock=MAKE_IMAGE_BLOCK(lpos,tpos,wid,hei,lct,lctsize,inter,sort,imgdata)
  4126.   stuff=READ_IMAGE_BLOCK(ablock,to_matrix)
  4127.  ablock=MAKE_LSD_BLOCK(width,height,gcflag,colres,sort,bkgcolor,aspect,gcsize)
  4128.   stuff=READ_LSD_BLOCK(ablock)
  4129.  ablock=MAKE_PTE_BLOCK(tgleft,tgtop,tgwidth,tgheight,ccwidth,ccheight,tfore,tback,amess)
  4130.   stuff=READ_PTE_BLOCK(ablock)
  4131.  ablock=MAKE_TERMINATOR_BLOCK()
  4132.  
  4133.  
  4134. Description of procedures:
  4135.  
  4136. ablock=read_gif_block(giffile,imgnum,infotype,is_string)
  4137.     Pull a "block" from a gif file.
  4138.  
  4139.    Where:
  4140.         giffile : A file name OR a string containing the contents of a gif file
  4141.         nth  : Get block associated with this image, comment, or app block.
  4142.         infotype : Type of block to get
  4143.         is_string: If 1, then GIFFILE argument is a string, otherwise it's
  4144.                    a file name (which read_gif_block will read)
  4145.    Values of infotype:
  4146.         IMG  -- get the nth "image descriptor" of the imgnum image.
  4147.                 To examine: use READ_IMG_BLOCK
  4148.         CMT  -- get the nth "comment extension". 
  4149.                 To examine: use READ_COMMENT_BLOCK
  4150.         ACE  -- get the "application control extension" for the nth application.
  4151.                 To examine: use READ_ANIMATION_BLOCK  -- but this is only
  4152.                 useful if it's an "animation" block.
  4153.         LSD  -- get the "logical control descriptor", including the "GIF89a"
  4154.                 (or "GIF87a") header (nth is ignored -- there is only one
  4155.                 LSD per file). Note that the LSD is REQUIRED -- all gif files
  4156.                 must have start with an LSD. 
  4157.                 To examine: use  READ_LSD_BLOCK.
  4158.         GCE  -- get the nth "graphic control extension". 
  4159.                 To examine: use READ_GCE_BLOCK.
  4160.         PTE --  get the nth "plain text extension".
  4161.         LST  -- return a spaced delimited list of INFOTYPE codes.
  4162.  
  4163.   Note that LST is different -- it returns a string. 
  4164.   Several additional codes may appear in this "LST" of blocks.
  4165.        00 = a '00'x block (a harmless error)
  4166.       TRM = terminator -- should ALWAYS be the last code in LST
  4167.  
  4168.   Note: if an error occurs, ablock will be a string starting with "ERROR",
  4169.         and followed by a short error message.
  4170.  
  4171. ablock=MAKE_ANIMATION_BLOCK(iter) 
  4172.       Create an "animation" applications block.
  4173.  
  4174.       Where:
  4175.           iter= # of iterations
  4176.  
  4177.  
  4178. stuff=READ_ANIMATION_BLOCK(ablock(
  4179.      Extract # iterations from a "netscape" animation applications 
  4180.      control extension (ACE) block.
  4181.  
  4182.      You can parse stuff with:
  4183.         parse var stuff appname','niters
  4184.      Where
  4185.         appname = name of applicaton block
  4186.         niters  = if "NETSCAPE" is the appname, then this is the # of iterations
  4187.                   Otherwise, niters=''                                                        
  4188.  
  4189. ablock=MAKE_COMMENT_BLOCK(a_comment)
  4190.    Make a comment block.
  4191.  
  4192.    Where:
  4193.         a_comment = A string containing your comment. Can be any length,
  4194.                     and contain CRLFs.
  4195.  
  4196. stuff=READ_COMMENT_BLOCK(ablock)
  4197.    Extract comment from a comment block.
  4198.  
  4199.    The comment is the only item returned in stuff.
  4200.  
  4201. ablock=MAKE_GCE_BLOCK(tcflag,tcindex,delay,disposal,useinlag)
  4202.    Make a "graphics control extension" block
  4203.  
  4204.    Where:
  4205.         tcflag  = transparent color index flag. If not 1, transparent
  4206.                   color still written (Tcindex), but will be ignored by
  4207.                   image dipslay programs.
  4208.         tcindex = index of the transparent color.
  4209.         delay = Delay time (1/100 ths seconds) -- wait this time AFTER
  4210.                 displaying image
  4211.         dispoal = Disposal method (after delay is over, or userinput taken)
  4212.                     0=no action, 1=retain image
  4213.                     2=set to background  3=restore to previous
  4214.         useinflag = User input flag (1=yes)
  4215.  
  4216. stuff=READ_GCE_BLOCK(ablock)
  4217.   Obtain information from a graphics control extension block.
  4218.  
  4219.   To get the actual variables, use:
  4220.      parse var stuff  disposal usrinflag tcflag delay tcindex
  4221.  
  4222.   Where the variables are as defined in MAKE_GCE_BLOCK.
  4223.  
  4224. ablock=MAKE_IMAGE_BLOCK(lpos,tpos,wid,hei,lct,lctsize,inter,sort,imgdata)
  4225.     Create an "image descriptor" box.
  4226.  
  4227.     Where:
  4228.         lpos = column number of the left edge of the image (wrt to
  4229.                logical screen)
  4230.         tpos = row number of the right edge of the image 
  4231.         wid= image width in pixels
  4232.         hei= image height in pixels
  4233.         lct = local color table flag -- set to 1 if a color table 
  4234.               to create a local color table
  4235.               If LCT=1, then you must "setup the ct_name color table"
  4236.               before calling MAKE_IMAGE_BLOCK
  4237.       lctsize= size of local color table. if no specified, ct_name.0 is used.
  4238.                 If LCT=0, lctsize will still be written (even though
  4239.                 no color table is written). This is sort of pointless,
  4240.                 but does seem to be a sop.
  4241.          inter = interlace flag 
  4242.           sort = if 1, indicates that the color table is sorted, with most
  4243.                  used color at top. 
  4244.       imgdata= If specifed, this should contain:
  4245.                    the actual lzw-compressed image data, (including the 
  4246.                    "lzw" starting byte)
  4247.                If not specified, or if equal to 0, then
  4248.                     MAKE_IMAGE_BLOCK will use the contents of the stem variable
  4249.                     declared by the IMG_NAME variable (see description above)
  4250.                     
  4251.     Note: when using a stem variable as the contents of the gif
  4252.           image (when imgdata=0), the !cols and !rows "tails" will
  4253.           NOT be used -- instead, the width and height variables (specified
  4254.           in the argument list) are used. 
  4255.           Of course, one would typically make sure that these were equal...
  4256.  
  4257. stuff=READ_IMAGE_BLOCK(ablock,to_matrix)
  4258.     Pull information out of an "image descriptor" block
  4259.  
  4260.     Where:
  4261.        ablock =an image descriptor block; say as retrieved with read_gif_block
  4262.        to_matrix =  If missing or 0, then
  4263.                      ignore
  4264.                    If 1, then  
  4265.                       write the pixel values of the image to "IMG_NAME"
  4266.                       stem variable (see the introductory notes for details).
  4267.                       A temporary file, with a name like $TMPnnnn.TMP, 
  4268.                       will be temporarily created.
  4269.                    If a file name, then
  4270.                       Same as 1, but use this filename (instead of a
  4271.                       $TMPnnnn.TMP file name) for the temporary file.
  4272.               
  4273.     The actual information is then obtained by using:
  4274.          parse var stuff lpos tpos width hei lct interl sort ',' imgdata
  4275.         (see MAKE_IMAGE_BLOCK for a description of these variables).
  4276.      and (if to_matrix is appropriately specified)
  4277.         by examining the stem variable named by IMG_NAME.
  4278.  
  4279.     Notes:
  4280.          * be SURE to include a ',' before the imgdata (in the parse)
  4281.          * if there is any chance the image block includes a local color
  4282.            table, be sure to set the value of the CT_NAME variable
  4283.            before calling READ_IMAGE_BLOCK
  4284.          * if you specify to_matrix, be sure to set the value of the
  4285.            IMG_NAME variable before calling READ_IMAGE_BLOCK.
  4286.  
  4287.  
  4288. ablock=MAKE_LSD_BLOCK(width,height,gcflag,colres,sort,bkgcolor,aspect,gcsize)
  4289.       Make a logical screen descriptor  block -- including the "GIF89a"
  4290.       header (the first 6 six characters in a gif file).
  4291.  
  4292.       Where:
  4293.           width = "logical screen" width (in pixels)
  4294.           height= "logical screen" height (in pixels)
  4295.           gcflag= set to 1 if a global color table is to be created.
  4296.                  If GCFLAG=1, then you must "setup the ct_name color table"
  4297.                  beforecalling MAKE_LSD_BLOCK
  4298.           colres=2**(colres+1)= color resolution of image creater(rarely used)
  4299.           sort = if 1, indicates that the color table is sorted, with most
  4300.                  used color at top. 
  4301.           bkgcolor = background color index (rarely used)
  4302.           aspect = height to width aspect (rarely used)
  4303.           gcsize= size of color table. if no specified, ct_name.0 is used.
  4304.                  
  4305.  
  4306. stuff=READ_LSD_BLOCK(ablock)
  4307.   Pull information from an logical screen descriptor block
  4308.  
  4309.     Ablock is an logical screen descriptor block; say as 
  4310.     retrieved with read_gif_block.
  4311.      
  4312.     The actual information is then obtained by using:
  4313.         parse var st width height gcflag colres sort bkgcolor aspect
  4314.  
  4315.      Where the variables are as defined in MAKE_LSD_BLOCK
  4316.  
  4317. ablock=MAKE_PTE_BLOCK(tgleft,tgtop,tgwidth,tgheight,ccwidth,ccheight,tfore,tback,amess)
  4318.    Create a "plain text" extensions block
  4319.  
  4320.    Where:
  4321.         tgleft = pixel column number of left of text grid
  4322.         tgtop  = pixel row number of top of text grid
  4323.        tgwidth = width of text grid in pixels
  4324.       tgheight = height of text grid in pixels
  4325.       ccwidth  = width of each cell in pixels
  4326.       ccheight = height of each cell in pixels
  4327.       tfore    = text foreground color table index (into global color table)
  4328.       tback    = text background color table index (into global color table)
  4329.        amess   = message string
  4330.  
  4331. stuff=READ_PTE_BLOCK(ablock)
  4332.    Pull information from a plain text extension block.
  4333.  
  4334.    The actual information can be obtained using:
  4335.       parse stuff  tgleft tgtop tgwidth tgheight ccwidth ccheight tfore tback ',' ptext
  4336.    Where the variables are as defined in MAKE_PTE_BLOCK
  4337.         
  4338.  
  4339. ablock=MAKE_TERMINATOR_BLOCK()
  4340.    Create a "terminator" block.
  4341.    No arguments are required (this is simple a constant equal to '3b'x.
  4342.  
  4343.   
  4344. **********************************************************************/
  4345.  
  4346.  
  4347.  
  4348. /*******************/
  4349. /* make an image block (note use of img_name and ct_name )
  4350. Example: 
  4351.   ct_name='ct1.' ; img_name='img1.'
  4352.   stuff2=make_image_block(lpos,tpos,wid,hei,lct,lcsize,inter,sort,imgdata)
  4353. */
  4354.  
  4355.  
  4356. make_image_block:procedure expose (ct_name) (img_name) is_cgi BLENDGIF_ROOT  save_tempfile
  4357.  
  4358. parse arg lpos,tpos,width,height,lctflag,lcsize,interlace,sortflag,imgdata
  4359.  
  4360. astuff='2c'x
  4361.  
  4362. astuff=astuff||dd2c(lpos,2)
  4363. astuff=astuff||dd2c(tpos,2)
  4364. astuff=astuff||dd2c(width,2)
  4365. astuff=astuff||dd2c(height,2)
  4366.  
  4367. /* create a byte containg several flags */
  4368.  
  4369. if interlace<>1 then interlace=0
  4370. if sortflag<>1 then sortflag=0
  4371. if lctflag<>1 then lctflag=0
  4372.  
  4373. ct0=value(ct_name'0')
  4374. if lcsize='' | datatype(lcsize)<>'NUM' then
  4375.    isizect=ct0
  4376. else 
  4377.    isizect=lcsize
  4378.  
  4379. select          /* 3 bit rep of 2**(sizect+1), rounded up */
  4380.    when isizect>128 then do 
  4381.          sizect='111' ; is2=256 ;end
  4382.    when isizect>64  then do
  4383.          sizect='110' ; is2=128 ; end 
  4384.    when isizect>32  then do 
  4385.         sizect='101' ; is2=64 ; end
  4386.    when isizect>16  then do 
  4387.         sizect='100' ; is2=32 ;end 
  4388.    when isizect>8   then do 
  4389.         sizect='011' ; is2=16 ; end ;
  4390.    when isizect>4   then do
  4391.          sizect='010' ; is2=8 ; end
  4392.    when isizect>2   then do 
  4393.         sizect='001' ; is2=4 ; end
  4394.    otherwise do
  4395.         sizect='000' ; is2=2 ; end
  4396. end
  4397.  
  4398. lc=lctflag||interlace||sortflag||'00'||sizect
  4399. aa=x2c(b2x(lc))
  4400.  
  4401. astuff=astuff||aa
  4402.  
  4403. /* add color table info */
  4404. if lctflag=1 then do
  4405.   lsd=''
  4406.   do mm=0 to min(isizect,ct0)-1
  4407.      ii=value(ct_name'!r.'mm)
  4408.      lsd=lsd||d2c(ii)
  4409.      ii=value(ct_name'!g.'mm)
  4410.      lsd=lsd||d2c(ii)
  4411.      ii=value(ct_name'!b.'mm)
  4412.      lsd=lsd||d2c(ii)
  4413.    end /* do */
  4414.    if isizect<is2 then do   /* pack the color table */
  4415.      do isizect+1 to is2
  4416.        lsd=lsd||'000000'x
  4417.     end /* do */
  4418.   end
  4419.   astuff=astuff||lsd
  4420. end
  4421.  
  4422. if imgdata<>'' & imgdata<>'0' then do
  4423.   astuff=astuff||imgdata
  4424.   return astuff
  4425. end
  4426.  
  4427. /* else, create lzw comppressed image from img_name stem */
  4428.  
  4429. tempname=imgdata
  4430.  
  4431. if tempname=1 then do
  4432.    usename=systempfilename2('$TM1????.TMP')
  4433.    if usename='' then do
  4434.       call dosay2 "BlendGIF error. Unable to create temporary file (perhaps a setup error)"
  4435.       exit
  4436.    end /* do */
  4437. end
  4438. else do
  4439.    if pos('?',tempname)>0 then do
  4440.       usename=systempfilename2(tempname)
  4441.       if usename='' then do
  4442.         call dosay2 "BlendGIF error. Unable to create temporary file (perhaps a setup error)"
  4443.         exit
  4444.       end /* do */
  4445.    end
  4446.    else do
  4447.       usename=TEMPNAME
  4448.    end
  4449. end
  4450.  
  4451. ncols=width
  4452. nrows=height
  4453. messim=rxgdimagecreate(ncols,nrows)
  4454. if messim<2 then do
  4455.   call dosay2 "Error Could not create temporary gif image "
  4456.   return ''
  4457. end
  4458.  
  4459. pxs.=0
  4460. do mr=0 to nrows-1              /* FROM STEM ARRAY TO IMAGE */
  4461.    alin=value(img_name||mr)
  4462.    do mc=0 to ncols-1
  4463.      PXS.MC=c2d(substr(alin,mc+1,1))
  4464.    end /* do */
  4465.    styled=RxgdImageSetStyle(messim, pxs, ncols)  
  4466.    rc=RxgdImageLine(messim, 0,mr,ncols-1,mr,styled)
  4467. end
  4468.  
  4469. DO III=0 TO 255
  4470.    FOO=RXGDIMAGECOLORALLOCATE(MESSIM,III,255-III,0)
  4471. end /* do */
  4472. foo=rxgdimageinterlace(messim,interlace)
  4473. foo=rxgdimagegif(messim,usename)
  4474. foo=rxgdimagedestroy(messim)
  4475.  
  4476. oof=charin(usename,1,chars(usename))
  4477. if oof="" then  do
  4478.  call dosay2 "Error retrieving temporary gif file: "usename
  4479.  return ""
  4480. end
  4481. foo=stream(USENAME,'c','close')
  4482. foo=sysfiledelete(usename)
  4483.  
  4484. OOF2=read_gif_block(OOF,1,'IMG',1)
  4485.  
  4486. ct_name='ctmp.'
  4487. stuff2=read_image_block(oof2,0)
  4488. parse var stuff2 . ',' imgdata
  4489. return astuff||imgdata
  4490.  
  4491.  
  4492. /*******************/
  4493. /* read an image_block
  4494.  
  4495. Example:
  4496.   ct_name="CT3."
  4497.   ct3.=0 ; img_name='img1.'
  4498.   ablock=read_gif_block(giffile,1,'IMG')
  4499.   stuff=read_image_block(ablock,0)
  4500.   parse var stuff leftpos toppos width height lctflag interlaceflag sortflag ','||imgdata
  4501.   say " Left top at "leftpos toppos
  4502.   say " Width height = " width height
  4503.   say " Interlace:" interlaceflag 
  4504.   say ' local ct = 'lctflag ' ( sorted = 'sortflag
  4505.   if lctflag=1 then do
  4506.      say " # colors in lct = " ct3.0 ct3.!r.1 ct3.!g.1 ct3.!b.1
  4507.   end
  4508.   say " Imgsize = " length(imgdata)
  4509.  
  4510. and if tomtx is specified (=1 , or equal to a filename), then also
  4511. create the IMG_NAME stem variable "matrix of pixel values"
  4512.  
  4513. */
  4514.  
  4515. read_image_block:procedure expose (ct_name) (IMG_NAME) is_cgi BLENDGIF_ROOT  save_tempfile
  4516.  
  4517. parse arg ablock,tomtx
  4518.  
  4519. il=substr(ablock,2,2)
  4520. lpos=c2d(reverse(il))
  4521. it=substr(ablock,4,2)
  4522. tpos=c2d(reverse(it))
  4523. iw=substr(ablock,6,2)
  4524. width=c2d(reverse(iw))
  4525. ih=substr(ablock,8,2)
  4526. height=c2d(reverse(ih))
  4527.  
  4528. pf=substr(ablock,10,1)
  4529.  
  4530. pf2= x2b(c2x(pf))
  4531. lctflag=substr(pf2,1,1)
  4532. interlace=substr(pf2,2,1)
  4533. sortflag=substr(pf2,3,1)
  4534.  
  4535. lctsize=right(pf2,3)
  4536. t=right(lctsize,8,0)
  4537.  
  4538. lctsize= x2d(b2x(t))
  4539.  
  4540. lctsize=2**(lctsize+1)
  4541. imgat=11
  4542.  
  4543. if lctflag=1 then do
  4544.    ith=0
  4545.    do m0=1 to (lctsize*3) by 3
  4546.       mm=m0+10
  4547.       aa=value(ct_name'!r.'ith,c2d(substr(ablock,mm,1)))
  4548.       aa=value(ct_name'!g.'ith,c2d(substr(ablock,mm+1,1)))
  4549.       aa=value(ct_name'!b.'ith,c2d(substr(ablock,mm+2,1)))
  4550.       ith=ith+1
  4551.    end
  4552.    imgat=mm+1
  4553. end
  4554.  
  4555. aa=value(ct_name'0',lctsize)
  4556.  
  4557. daimage=substr(ablock,imgat)    /* get rest of stuff in image descriptor block */
  4558.  
  4559. /* note: color table in exposed stem */
  4560. if tomtx="" | tomtx=0 then
  4561.    return lpos tpos width height lctflag lctsize interlace sortflag ','||daimage
  4562.  
  4563. /* else, create the img_name stem var */
  4564.  
  4565. tempname=tomtx
  4566.  
  4567. if tempname=1 then do
  4568.    usename=systempfilename2('$TM2????.TMP')
  4569.    if usename='' then do
  4570.       call dosay2 "BlendGIF error. Unable to create temporary file (perhaps a setup error)"
  4571.       exit
  4572.    end /* do */
  4573. end
  4574. else do
  4575.    if pos('?',tempname)>0 then do
  4576.      usename=systempfilename2(tempname)
  4577.      if usename='' then do
  4578.         call dosay2 "BlendGIF error. Unable to create temporary file (perhaps a setup error)"
  4579.         exit
  4580.      end /* do */
  4581.    end
  4582.    else do
  4583.       usename=tempname 
  4584.    end
  4585. end
  4586.  
  4587. /* make the gif file in memory (very simple version) */
  4588.  
  4589. /*rse arg width,height,gcflag,colres,sort,bkgcolor,aspect,gcsize*/
  4590.  
  4591.  
  4592. aa=MAKE_LSD_BLOCK(width,height,0,0,0,0,,)
  4593. aa=aa||ablock||make_terminator_block()
  4594.  
  4595. arf=charout(usename,aa,1)
  4596. if arf<>0 then do  
  4597.    call dosay2  "Error writing temporary gif file:" usename
  4598.    return 0
  4599. end
  4600. foo=stream(usename,'c','close')
  4601. /* now read with rxgd */
  4602. dim= RxgdImageCreateFromGIF(usename)
  4603. if dim<=1 then do
  4604.   call dosay2 " Error reading temporary gif file: " usename
  4605. exit
  4606.   oo=sysfiledelete(usename)
  4607.   return 0
  4608. end
  4609.  
  4610.   
  4611. nrows=RxgdImageSY(dim)
  4612. ncols=rxgdimageSx(dim)
  4613. foo=value(img_name'!ROWS',nrows)
  4614. foo=value(img_name'!COLS',ncols)
  4615.  
  4616.  
  4617. ndid=0
  4618. do ny=0 to nrows-1              /* FROM IMAGE TO STEM ARRAY */
  4619.  
  4620.   foo=rxgdimagegetrowpixels(dim,ny,pxels)
  4621.   alin=''
  4622.   do nx=1 to ncols
  4623.      alin=alin||d2c(pxels.nx)
  4624.   end
  4625.   foo=value(img_name||ny,alin)
  4626. end
  4627. foo=rxgdimagedestroy(dim)
  4628. foo=stream(usename,'c','close')
  4629. oo=sysfiledelete(usename)
  4630.  
  4631.  
  4632. return lpos tpos width height lctflag lctsize interlace sortflag ','||daimage
  4633.  
  4634. exit
  4635.  
  4636.  
  4637.  
  4638.  
  4639.  
  4640. /*******************/
  4641. /* make a netscape app block, for animated images, with niter iterations */
  4642.  
  4643. Example:
  4644.   niter=50
  4645.   nu_anim_block=make_animation_block(niter) 
  4646.  
  4647. */
  4648.  
  4649. make_animation_block:procedure
  4650. parse arg niter
  4651. if niter="" then niter=0
  4652. if niter<0 then niter=0
  4653. if niter>65535 then niter=65334
  4654.  
  4655. ablock='21ff0b'x
  4656. ablock=ablock||'NETSCAPE2.0'
  4657. ablock=ablock||'03'x
  4658. ablock=ablock||'01'x
  4659. aiter=dd2c(niter,2)
  4660. ablock=ablock||aiter
  4661. ablock=ablock||'00'x
  4662. return ablock
  4663.  
  4664. /*******************/
  4665. /* read a netscape app block, for animated images, with niter iterations 
  4666.  
  4667. Example:
  4668.  aa=read_animation_block(ablock)
  4669.  
  4670.  You can parse aa with:
  4671.     parse var aa apname','niter
  4672.   
  4673.  If apname='NETSCAPE' then niter will be the iteration count.
  4674.  Otherwise, niter will = ''
  4675.  (that is, if not an animation block, niter='')
  4676. */
  4677.  
  4678. read_animation_block:procedure
  4679. parse arg ablock
  4680.  
  4681. apname=substr(ablock,4,8)
  4682. apauth=substr(ablock,12,3)
  4683. foo=apname||apauth
  4684. if foo<>'NETSCAPE2.0' then return apname
  4685. aiter=substr(ablock,17,2)
  4686. niter=c2d(reverse(aiter))
  4687. return apname','niter
  4688.  
  4689.  
  4690.  
  4691.  
  4692. /*******************/
  4693. /* create a graphics control extension block.
  4694.  
  4695. Example:
  4696.   nu_gce_block=make_gce_block(tcflag,tcindex,delay,disposal,userinputflag)
  4697.  
  4698. */
  4699.  
  4700. make_gce_block:procedure
  4701. parse arg tcflag,tcindex,delay,disposal,userinput
  4702.  
  4703. ablk='21f904'x
  4704.  
  4705. l3='000'
  4706. if disposal='' then disposal=0
  4707. ii= x2b(d2x(disposal))
  4708. ii=right(ii,8,0)
  4709. ii=right(ii,3)
  4710. l3=l3||ii
  4711.  
  4712. if userinput=1 then
  4713.   l3=l3||'1'
  4714. else
  4715.   l3=l3||'0'
  4716.  
  4717. if tcflag<>1 then
  4718.    tcflag='0'
  4719. else
  4720.    tcflag='1'
  4721. l3=l3||tcflag
  4722.  
  4723. l3a=x2c(b2x(l3))
  4724.  
  4725. ablk=ablk||l3a
  4726.  
  4727. if delay='' then delay=0
  4728. delay=dd2c(delay,2)
  4729.  
  4730. if tcindex='' then tcindex=0
  4731. tcindex=dd2c(tcindex,1)
  4732. ablk=ablk||delay||tcindex||'00'x
  4733.  
  4734. return ablk
  4735.  
  4736.  
  4737. /*******************/
  4738. /* make logical screen descriptor 
  4739. Example: (ct2. is a stem containing a color table )
  4740.   ct_name='CT2.'
  4741.   lsd_block=make_lsd_block(width,height,gcflag,colres,sort,bkgcolor,aspect)
  4742.  
  4743. */
  4744.  
  4745. make_lsd_block:procedure expose (ct_name) is_cgi BLENDGIF_ROOT  save_tempfile
  4746. parse arg width,height,gcflag,colres,sort,bkgcolor,aspect,gcsize
  4747.  
  4748. /* organized as:
  4749.  hd= 'GIFxxx' (1-6)
  4750.  width = 2 bytes (7-8)
  4751.  height=  2 bytes (9-10)
  4752. packed = 1 byte (11) -- gcflag (1) colres (3) sort (1) sizect (3)
  4753. bkgcolor =1 byte (12)
  4754. aspect = 1 byte (13)
  4755. colortable = 14 ... 13+ 2**(sizect+1)  bytes (rgbrgbrgb....)
  4756. */
  4757.  
  4758. LSD='GIF89a'
  4759.  
  4760. A2=dD2C(WIDTH,2)
  4761. A3=Dd2C(HEIGHT,2)
  4762.  
  4763. lsd=lsd||A2||A3
  4764.  
  4765. if gcflag=0 | gcflag='' then
  4766.   l3='0'
  4767. else
  4768.   l3='1'
  4769.  
  4770. if colres='' then do
  4771.   colres='111'
  4772. end
  4773. else do
  4774.   colres=x2b(d2x(colres))
  4775.   colres=right(colres,8,0)
  4776.   colres=right(colres,3)
  4777. end
  4778.  
  4779. l3=l3||colres
  4780.  
  4781. if sort='' | sort=0 then
  4782.     l3=l3||'0'
  4783. else
  4784.     l3=l3||'1'
  4785.  
  4786.  
  4787. ct0=value(ct_name'0')
  4788. if gcsize='' | datatype(gcsize)<>'NUM' then
  4789.   isizect=ct0
  4790. else
  4791.   isizect=gcsize
  4792. select          /* 3 bit rep of 2**(sizect+1), rounded up */
  4793.    when isizect>128 then do 
  4794.          sizect='111' ; is2=256 ;end
  4795.    when isizect>64  then do
  4796.          sizect='110' ; is2=128 ; end 
  4797.    when isizect>32  then do 
  4798.         sizect='101' ; is2=64 ; end
  4799.    when isizect>16  then do 
  4800.         sizect='100' ; is2=32 ;end 
  4801.    when isizect>8   then do 
  4802.         sizect='011' ; is2=16 ; end ;
  4803.    when isizect>4   then do
  4804.          sizect='010' ; is2=8 ; end
  4805.    when isizect>2   then do 
  4806.         sizect='001' ; is2=4 ; end
  4807.    otherwise do
  4808.         sizect='000' ; is2=2 ; end
  4809. end
  4810. l3=l3||sizect
  4811.  
  4812. l3a=x2c(b2x(l3))
  4813.  
  4814. lsd=lsd||l3a
  4815.  
  4816. if bkgcolor='' then 
  4817.    lsd=lsd||'00'x
  4818. else
  4819.    lsd=lsd||dd2c(bkgcolor,1)
  4820.  
  4821. if aspect='' then
  4822.    lsd=lsd||d2c(0)
  4823. else
  4824.    lsd=lsd||dd2c(aspect,1)
  4825.  
  4826. /* add color table info */
  4827. if gcflag=1 then do
  4828.   do mm=0 to isizect-1
  4829.     ii=value(ct_name'!r.'mm)
  4830.     lsd=lsd||d2c(ii)
  4831.     ii=value(ct_name'!g.'mm)
  4832.     lsd=lsd||d2c(ii)
  4833.     ii=value(ct_name'!b.'mm)
  4834.     lsd=lsd||d2c(ii)
  4835.   end /* do */
  4836.   if isizect<is2 then do
  4837.      do kkk=isizect+1 to is2
  4838.         lsd=lsd||'000000'x
  4839.      end /* do */
  4840.   end
  4841. end
  4842.  
  4843. return lsd
  4844.  
  4845. /*******************/
  4846. /* make a comment block
  4847. Example:
  4848.   cmt="this is my comment on "||date()
  4849.   nu_cmt_block=make_comment_block(cmt)
  4850. */
  4851.  
  4852. make_comment_block:procedure
  4853. parse arg acomment
  4854. aa='21fe'x
  4855. aa=aa||chunkit(acomment)
  4856. return aa
  4857.  
  4858.  
  4859. /*********/
  4860. read_comment_block:procedure
  4861. parse arg ablock
  4862.  iat=2
  4863.  lena=length(ain)
  4864.  amess=''
  4865.  do forever       
  4866.     if iat>lena then return ""   /* no block terminator -- error */
  4867.     iat=iat+1      /* size of block */
  4868.     ii=substr(ain,iat,1) ; ii=c2d(ii)
  4869.     if ii=0 then return amess 
  4870.     iat=iat+1
  4871.     amess=amess||substr(ain,iat,ii)
  4872.     iat=iat+ii-1
  4873.  end /* do */
  4874.  
  4875. /*******************/
  4876. /* plain text stuff */
  4877. read_pte_block;procedure
  4878. parse arg ain
  4879.  
  4880.   l1=substr(ain,1,2)
  4881. tgleft=c2d(reverse(l1))
  4882.   l2=substr(ain,3,2)
  4883. tgtop=c2d(reverse(l2))
  4884.  
  4885.    l1=substr(ain,5,2)
  4886. tgwidth=c2d(reverse(l1))
  4887.    l2=substr(ain,7,2)
  4888. tgheight=c2d(reverse(l2))
  4889.  
  4890.    l1=susbtr(ain,9,1)
  4891.  ccwidth=c2d(l1)
  4892.    l2=substr(ain,10,1)
  4893.  ccheight=c2d(l2)
  4894.  
  4895.   l1=substr(ain,11,1)
  4896.     tfore=c2d(l1)
  4897.   l2=substr(ain,12,1)
  4898.     tback=c1d(l2)
  4899.  
  4900. lena=length(ain);amess=''
  4901.  do forever       
  4902.     if iat>lena then return ""   /* no block terminator -- error */
  4903.     iat=iat+1      /* size of block */
  4904.     ii=substr(ain,iat,1) ; ii=c2d(ii)
  4905.     if ii=0 then leave
  4906.     iat=iat+1
  4907.     amess=amess||substr(ain,iat,ii)
  4908.     iat=iat+ii-1
  4909.  end /* do */
  4910.  
  4911. return  tgleft tgtop tgwidth tgheight ccwidth ccheight tfore tback ','||amess
  4912.  
  4913.  
  4914. /*******************/
  4915. /* plain text stuff */
  4916. make_pte_block;procedure
  4917. parse arg tgleft,tgtop,tgwidth,tgheight,ccwidth,ccheight,tfore,tback,amess
  4918.  
  4919.   ab='2101'x
  4920.   ab=ab||d2c(12)
  4921.   ab=ab||dd2c(tgleft,2)
  4922.   ab=ab||dd2c(tgtop,2)
  4923.   ab=ab||dd2c(tgwidth,2)
  4924.   ab=ab||dd2c(tgheight,2)
  4925.   ab=ab||dd2c(ccwidth,1)
  4926.   ab=ab||dd2c(ccheight,1)
  4927.   ab=ab||dd2c(tfore,1)
  4928.   ab=ab||dd2c(tback,1)
  4929.   ab=ab||chunkit(amess)
  4930.  
  4931.   return ab
  4932.  
  4933. /*************/
  4934. /* convert integer to character, using nb bytes */
  4935. dd2c:procedure
  4936. parse arg ival,nb
  4937. if nb='' then nb=2
  4938. a1=reverse(d2c(ival))
  4939. if length(a1)<nb then do 
  4940.    a1=a1||copies('00'x,nb-length(a1))
  4941. end /* do */
  4942. return left(a1,nb)
  4943.  
  4944.  
  4945. /****************/
  4946. /* convert character to interger */
  4947.  
  4948. /*******************/
  4949.  
  4950. /* make a terminator block -- no arguments needed
  4951. Example:
  4952.   my_trm_block=make_terminator_block()
  4953.  
  4954. */
  4955. make_terminator_block:procedure
  4956.  
  4957. return '3b'x
  4958.  
  4959.  
  4960. /*********************/
  4961. /* parse a graphics control extension block (gce). 
  4962.   Note: Use read_gif_block to get the gce.
  4963.  
  4964. Example:
  4965.   imgnum=1
  4966.   ablock=read_gif_block(giffile,imgnum,'GCE')
  4967.   stu=read_gce_block(ablock)
  4968.   parse var stu disposal userinputflag tcflag delay tcindex
  4969.   say " disposal =  " disposal
  4970.   say " userinput flag = " userinputflag
  4971.   say " transparent color flag = " tcflag
  4972.   say " Delay = " delay
  4973.   say " transparent color index = " tcindex
  4974.  
  4975. */
  4976.  
  4977. read_gce_block:procedure
  4978. parse arg ablock
  4979.  
  4980. l3=substr(ablock,4,1)
  4981. l3=x2b(c2x(l3))
  4982. reserved=left(l3,3)
  4983. disposal=right(substr(l3,4,3),8,0)
  4984. disposal=x2d(b2x(disposal))
  4985. userinputflag=substr(l3,7,1)
  4986. tcflag=substr(l3,8,1)
  4987.  
  4988. delay=c2d(reverse(substr(ablock,5,2)))
  4989.  
  4990. tcindex=c2d(substr(ablock,7,1))
  4991.  
  4992. return  disposal userinputflag tcflag delay tcindex
  4993.  
  4994.  
  4995. /*****************/
  4996. /* read lsd (including global color table), from string containing 
  4997.    logical screen descriptor (lsd)
  4998.    Note: use read_gif_block to get the lsd
  4999.  
  5000. Example of use:
  5001.   ct2.=0
  5002.   ct_name='CT2.'
  5003.   st=read_lsd_block(gifcontents)
  5004.   parse var st width height gcflag colres sort bkgcolor aspect
  5005.   SAY "  # COLORS :" CT2.0
  5006.   say " width " width
  5007.   say " height " height
  5008.   say " gcflag " gcflag
  5009.   say " colres " colres
  5010.   say " sort " sort
  5011.   say " bkgcolor " bkgcolor
  5012.   say " aspect " ASPECT
  5013.   say " # colors = " ct_name.0 
  5014.   do mm=0 to ct_name.0-1
  5015.      say " Color " mm " ct_name.!r.mm ct_name.!g.mm ct_name.!b.mm
  5016.   end 
  5017.  
  5018. */
  5019.  
  5020. read_lsd_block:procedure expose (ct_name)
  5021. parse arg ain
  5022.  
  5023. /* organized as:
  5024.  hd= 'GIFxxx' (1-6)
  5025.  width = 2 bytes (7-8)
  5026.  height=  2 bytes (9-10)
  5027.  packed = 1 byte (11) -- gcflag (1) colres (3) sort (1) sizect (3)
  5028.  bkgcolor =1 byte (12)
  5029.  aspect = 1 byte (13)
  5030.  colortable = 14 ... 13+ 2**(sizect+1)  bytes (rgbrgbrgb....)
  5031. */
  5032.  
  5033. gifver=left(ain,6)
  5034.  
  5035. if abbrev(translate(gifver),'GIF8')=0 then do
  5036.   return 'ERROR bad gif identifier: ' gifver
  5037. end
  5038.  
  5039. l1=substr(ain,7,2)
  5040. width=c2d(reverse(l1))
  5041. l2=substr(ain,9,2)
  5042. height=c2d(reverse(l2))
  5043.  
  5044. l3=substr(ain,11,1)  /* packed fields, used below */
  5045.  
  5046. bkg_color=c2d(substr(ain,12,1))
  5047. aspect=c2d(substr(ain,13,1))
  5048.  
  5049. ctable0=x2b(c2x(l3))
  5050.  
  5051. global_color_flag=left(ctable0,1)
  5052.  
  5053. colres=substr(ctable0,2,3)
  5054. colres=right(colres,8,0)
  5055. colres=x2d(b2x(colres))
  5056.  
  5057. sort=substr(ctable0,5,1)
  5058. ct1=right(ctable0,3)
  5059.  
  5060. ct1=right(ct1,8,0)
  5061. ct1=x2d(b2x(ct1))
  5062. numcolors=2**(ct1+1)
  5063.  
  5064. gcolortable=''
  5065. if global_color_flag=1 then do
  5066.    dcolortable=substr(ain,14,3*numcolors)
  5067.    ith=0
  5068.    do mm=1 to (numcolors*3) by 3
  5069.       aa=value(ct_name'!r.'ith,c2d(substr(dcolortable,mm,1)))
  5070.       aa=value(ct_name'!g.'ith,c2d(substr(dcolortable,mm+1,1)))
  5071.       aa=value(ct_name'!b.'ith,c2d(substr(dcolortable,mm+2,1)))
  5072.       ith=ith+1
  5073.    end
  5074. end
  5075. aa=value(ct_name'0',numcolors)
  5076. return  width height global_color_flag numcolors colres sort bkg_color aspect
  5077.  
  5078.  
  5079. /**************************
  5080. read_gif_block is called as:
  5081.  
  5082.      stuff=read_gif_block(gif_file,imgnum,infotype,is_string)
  5083.  
  5084. Parameters:
  5085.  
  5086.     GIF_FILE: Required. A fully qualified file name.
  5087.                   OR
  5088.               The contents of a gif_file (say, as read with 
  5089.                   gif_file=charin(afile,1,chars(afile))
  5090.  
  5091.          nth: # of image, etc. to get information about. If not specified,
  5092.               a value of 1 is assumed.
  5093.  
  5094.     infotype: Which type "descriptor block" to read (may be image specific)
  5095.               Actually, get the "nth" occurence of this infotype.
  5096.               Valid INFOTYPES are: LSD (nth will be ignored), GCE, IMG, PTE
  5097.               ACE, and CMT
  5098.  
  5099.     is_string: if 1,then gif_file is the "string" containing a gif file,
  5100.                 otherwise, gif_file is a file name.
  5101.  
  5102. Returns:
  5103.   A block from the gif file; or a string beginning with ERROR.
  5104.   Or, if infotype='', a list ob blocks in the gif_file.
  5105.  
  5106. Technical info:  For gif89a specs, please see
  5107.                  http://member.aol.com/royalef/gif89a.txt
  5108.  
  5109. */
  5110.  
  5111. read_gif_block:procedure expose is_cgi BLENDGIF_ROOT  save_tempfile
  5112. parse  arg afile,nth,infotype,is_string
  5113.  
  5114. infotype=translate(infotype)
  5115.  
  5116. if nth='' then nth=1
  5117.  
  5118. archy='LSD'    /* list of blocks found -- first is ALWAYS LSD block */
  5119. chewerr=0     /* flag set when error in chew_data */
  5120.  
  5121. /* read gif file ? */
  5122. if is_string<>1 then do
  5123.  fqn=stream(afile,'c','query exists')
  5124.  if fqn='' then do
  5125.     if chkerr=0 then return ''
  5126.     return 'ERROR no such file: ' afile
  5127.  end
  5128.  oo=stream(afile,'c','close')
  5129.  filesize=chars(afile)
  5130.  ain=charin(fqn,1,filesize)
  5131.  oo=stream(afile,'c','close')
  5132. end
  5133. else do         /* string provided */
  5134.    ain=afile
  5135. end
  5136.  
  5137. /* check for proper header */
  5138. gifver=left(ain,6)
  5139. if abbrev(translate(gifver),'GIF8')=0 then do
  5140.   if chkerr=0 then return ''
  5141.   return 'ERROR bad gif identifier: ' gifver
  5142. end
  5143.  
  5144.  
  5145. /* is there a global color table? */
  5146. l3=substr(ain,11,1)
  5147. ctable0=x2b(c2x(l3))
  5148. global_color_flag=left(ctable0,1)
  5149. ct1=right(ctable0,3)
  5150. ct1=right(ct1,8,0)
  5151. ct1=x2d(b2x(ct1))
  5152. numcolors=2**(ct1+1)
  5153.  
  5154. iat=13          /* 11 bytes used for intro info */
  5155.  
  5156. if global_color_flag=1 then do
  5157.    iat=iat+(3*numcolors)  /* iat is the Last byte used */
  5158. end
  5159.  
  5160. if infotype='LSD' then return substr(ain,1,iat)
  5161.  
  5162.  
  5163. /* if here, we need top scan file looking for other blocks */
  5164.  
  5165. desc.1='2c'x   /*'image' */
  5166. desc.2='21'x  /*'extension'*/
  5167. desc.3='3b'x   /*trailer' */
  5168.  
  5169. ext.1='f9'x ; /*graphic control'*/
  5170. ext.2='fe'x ; /*'comment'*/
  5171. ext.3='01'x ; /*'plain text'*/
  5172. ext.4='ff'x ; /*application'*/
  5173.  
  5174. nimgs=0         /* set counters */
  5175. ngcs=0
  5176. ncmts=0
  5177. napps=0
  5178. nptxts=0
  5179.  
  5180. lengif=length(ain)
  5181.  
  5182. do forever              /* ------------ scan the gif file */
  5183. iat=iat+1       
  5184.  
  5185. if iat>lengif then leave /* end of file contents (should not happen)*/
  5186.  
  5187. blockid=substr(ain,iat,1)       /* get next block type */
  5188. iat_b=iat               /* iat at beginning of this block */
  5189.  
  5190. select
  5191.  
  5192.    when blockid='00'x then do  /* ignore this relatively harmless error */
  5193.        ares=''
  5194.        ARCHY=archy' 00'
  5195.    end /* do */
  5196.  
  5197.    when blockid=desc.1 then do  /* it's an image */
  5198.       nimgs=nimgs+1
  5199.       call do_image
  5200.       ares=result
  5201.       archy=archy' IMG'
  5202.       if nimgs=nth  & infotype='IMG' then 
  5203.            return substr(ain,iat_b,(1+iat-iat_b))
  5204.    end
  5205.  
  5206.    when blockid=desc.2 then  do      /* extension */
  5207.        iat=iat+1                /* get extention type */
  5208.        extype=substr(ain,iat,1)
  5209.  
  5210.        select                   /* several types of "extensions */
  5211.  
  5212.           when extype=ext.1 then do     /*graphics control */
  5213.             ngcs=ngcs+1
  5214.             call graphics_control
  5215.             ares=result
  5216.             archy=archy' GCE'
  5217.             if infotype='GCE' & nth=ngcs then 
  5218.                return substr(ain,iat_b,(1+iat-iat_b))
  5219.           end
  5220.  
  5221.           when extype=ext.3  then do    /*plain text */
  5222.               nptxts=nptxts+1
  5223.               call plain_text  
  5224.               ares=result
  5225.               archy=archy' PTE'
  5226.               if nptxts=nth & infotype='PTE' then       /* check this image */
  5227.                     return substr(ain,iat_b,(1+iat-iat_b))
  5228.           end /* plain text */
  5229.  
  5230.           when extype=ext.2 then do     /*comment */
  5231.              ncmts=ncmts+1
  5232.              call is_comment
  5233.              ares=result
  5234.              archy=archy' CMT'
  5235.              if ncmts=nth &  infotype='CMT' then
  5236.                 return substr(ain,iat_b,(1+iat-iat_b))
  5237.           end
  5238.  
  5239.           when extype=ext.4 then do     /* application */
  5240.              napps=napps+1
  5241.              call application_block
  5242.              ares=result
  5243.              archy=archy' ACE'
  5244.              if nth=napps & infotype='ACE' then 
  5245.                 return substr(ain,iat_b,(1+iat-iat_b))
  5246.           end /* do */
  5247.  
  5248.           otherwise  do
  5249.              return 'ERROR Bad extension code: '||c2x(extype)
  5250.           end
  5251.        end      /* extype select */
  5252.    end          /* extention descriptor */
  5253.  
  5254.    when blockid=desc.3 then do
  5255.       archy=ARCHY' TRM'
  5256.       leave      /* terminator -- must be end of real gif stuff */
  5257.    end
  5258.  
  5259.    otherwise do
  5260.       return 'ERROR Bad descriptor code: ' blockid
  5261.    end
  5262.  
  5263. end  /* select */
  5264.  
  5265. if ares<>'' then do     /* ERROR DETECTED */
  5266.    if chkerr=0 then return ''
  5267.    return 'ERROR 'ares
  5268. end
  5269.  
  5270. end     /* forever */
  5271.  
  5272. /* if here, end of file and either nothing found, or found list of blocks */
  5273. if infotype='' then return archy
  5274. return ''                       /* blank means " not found " */
  5275.  
  5276.  
  5277. /************/
  5278. do_image:                             
  5279.       l3=substr(ain,iat+9,1)
  5280.       ctable0=x2b(c2x(l3))
  5281.       lcl_ct_flag=left(ctable0,1)
  5282.       t1=right(ctable0,3) ; t1=right(t1,8,0)
  5283.       lcl_ct_size=x2d(b2x(t1)) ; lcl_ct_size=2**(lcl_ct_size+1)
  5284.  
  5285.       skip=lcl_ct_flag*lcl_ct_size*3
  5286.       iat=iat+9+skip    /* iat is now just before the table based image */
  5287.  
  5288. /* chew up the data block */
  5289.        iat=iat+1        /* skip the lzw bits variable */
  5290.        img_data=chew_data()
  5291.        if imgdata="" then return 'ERROR Bad Image Data '
  5292.        return ""
  5293.  
  5294. /*********/
  5295. graphics_control:
  5296.        iat=iat+6
  5297.        term=x2d(c2x(substr(ain,iat,1)))
  5298.        if term<>0 then return 'Bad Graphics Control Extension '
  5299. return ""
  5300.  
  5301. /*********/
  5302. application_block:
  5303. iat=iat+1
  5304. app_blocksize=x2d(c2x(substr(ain,iat,1)))
  5305. if app_blocksize<>11 then do
  5306.     return 'Bad application block size '
  5307. end /* do */
  5308.  
  5309. iat=iat+11
  5310. app_data=chew_data()
  5311. if app_data="" then return 'Bad application block data '
  5312.  
  5313. return ""
  5314.  
  5315. /***********/
  5316. plain_text:
  5317. iat=iat+1
  5318. pt_data=''
  5319. app_blocksize=x2d(c2x(substr(ain,iat,1)))
  5320. if ptextblocksize<>12 then do
  5321.     return 'Bad Plain Text Block Size '
  5322. end /* do */
  5323.  
  5324. iat=iat+13
  5325. pt_data=chew_data()
  5326. if pt_data="" then return 'Bad Plain Text Data '
  5327. return ""
  5328.  
  5329. /*********/
  5330. is_comment:
  5331. cmt_data=chew_data()
  5332. if chewerr=1 then return 'Bad Comment Data '
  5333. return ""
  5334.  
  5335. /*********/
  5336. chew_data:procedure expose iat ain amess filesize chewerr
  5337. parse arg averbose
  5338.        chewerr=1
  5339.        amess=''
  5340.        do forever       /* data blocks */
  5341.          if iat>filesize then do
  5342.              return ""
  5343.          end /* do */
  5344.          iat=iat+1      /* size of block */
  5345.          ii=substr(ain,iat,1) ; ii=c2d(ii)
  5346.          if ii=0 then do 
  5347.              leave
  5348.          end /* do */
  5349.          iat=iat+1
  5350.          amess=amess||substr(ain,iat,ii)
  5351.          iat=iat+ii-1
  5352.        end /* do */
  5353. chewerr=0
  5354. return amess 
  5355.  
  5356. /***********/
  5357. /* make a chewable chunk of data */
  5358. chunkit:procedure
  5359. parse arg astr,klen
  5360. if klen='' then klen=250
  5361.  
  5362. mkit=''
  5363. lenstr=length(astr)
  5364. do mm=1 to lenstr by 250 
  5365.    iget=min(250,1+lenstr-mm)
  5366.    a1=substr(astr,mm,iget)
  5367.    a0=d2c(iget)
  5368.    mkit=mkit||a0||a1
  5369. end
  5370. mkit=mkit||'00'x   
  5371. return mkit
  5372.  
  5373.  
  5374.  
  5375. /**************************************/
  5376. /* read data sent back by an html FORM declared with:
  5377.    enctype="multipart/form-data" method="POST"
  5378.  
  5379. Calling syntax:
  5380.    nentries=read_multipart(stuff,content_type)
  5381.   where
  5382.      stuff == the body of a POST request (i.e.; the 4th argument sent to
  5383.               sre-http addons)
  5384.      nentries == the number of entries found. If error, nentries=0
  5385.     
  5386.   and also the expose variable FORM_DATA is constructed.
  5387.  
  5388. The structure of FORM_DATA is:
  5389.   FORM_DATA.0 = # of entries (in this multipart submission)
  5390.   FORM_DATA.!list.j = space delimited list of "variable names" in part
  5391.                        j (j=1.. FORM_DATA.0)
  5392.      For each word in FORM_DATA.!list.j, there is FORM_DATA. tail.
  5393.      In particular, FORM_DATA.!aword.j, where !aword is an ! prepended
  5394.      to a word form the FORM_DATA.!list.j list.
  5395.      For example, in almost all cases, one of these words will be "NAME".
  5396.        Thus, FORM_DATA.!NAME.j = the "name" of this variable
  5397.   FORM_DATA.j  - the actual value of this part.
  5398.  
  5399.   Basically, a typical entry  will contain:
  5400.     FORM_DATA.!NAME.j and FORM_DATA.j
  5401.   which can be interpreted as the "name" of the variable and it's "value".
  5402.   However, sometimes other variables will be mentioned in the
  5403.   FORM_DATA.!LIST. In particular, file uploads will often have a
  5404.   FORM_DATA.!FILENAME.j, which is often the local name of 
  5405.   the file the client is uploading.
  5406.  
  5407.  
  5408.   Notes:
  5409.     * if an error occurs, a 0 is returmed, and FORM_DATA.!ERROR
  5410.       will contain an error message
  5411.     * a content-disposition entry, if found, is NOT included in FORM_DATA
  5412.  
  5413. */
  5414. read_multipart_data:procedure expose form_data.
  5415. parse arg abody,atype
  5416.  
  5417. drop form_data.
  5418.  
  5419. crlf='0d0a'x
  5420.  
  5421. /* is there a content-type request header ? */
  5422. if atype="" then do
  5423.    form_data.!error=" No  content-type  request header"
  5424.    return 0
  5425. end
  5426.  
  5427. parse var atype thetype ";" boog 'boundary=' abound    /* get the type */
  5428.  
  5429. if translate(thetype)<>"MULTIPART/FORM-DATA" then do
  5430.   form_data.!error="No  multipart/form-data in Content-type "
  5431.   return 0
  5432. end
  5433.  
  5434. if translate(thetype)<>"MULTIPART/FORM-DATA" then do
  5435.   form_data.!error=" BlendGif upload error: No boundary in multipart/form-data header "
  5436.   return 0
  5437. end
  5438.  
  5439. abound="--"||abound   /* since boundaries always start with -- */
  5440.  
  5441. abd2=abound||crlf
  5442. /* loop through message, pulling out blocks and storing in stem var bigstuff. */
  5443.  
  5444. /* Now parse the various parts.*/
  5445.  
  5446. parse var abody foo1 (abd2) abody    /* move beyond first boundary and it's crlf */
  5447. /* check for netscape 2.0 incorrect format */
  5448. if pos(abound,abody)=0 then do   /* no ending boundary, so add one */
  5449.    abody=abody||crlf||abound||" -- "
  5450. end
  5451.  
  5452. mm=0
  5453. do until abody=""
  5454.   parse var abody thestuff (abound) abody        /* get a  boundary defined block */
  5455.   if strip(left(thestuff,4))="--" then leave        /* -- signals no more */
  5456.   if abody="" then leave
  5457.   mm=mm+1
  5458.   form_data.!list.mm='' ; form_data.mm=''
  5459.   do forever            /* get block headers.  Stop when hit a blank line */
  5460.      parse var thestuff anarg (crlf) thestuff
  5461.      if anarg="" then do
  5462.            leave
  5463.      end
  5464.      else do                    /* extract the arguments on this line */
  5465.          do until anarg=""
  5466.               parse var anarg anarg1 ";" anarg
  5467.               boob1=pos(':',anarg1) ; boob2=pos('=',anarg1)
  5468.               if boob1=0 then nixon=boob2
  5469.               if boob2=0 then nixon=boob1
  5470.               if boob1>0 & boob2>0 then nixon=min(boob1,boob2)
  5471.               t1=translate(strip(strip(substr(anarg1,1,nixon-1)),,'"'))
  5472.               t2=strip(strip(substr(anarg1,nixon+1)),,'"')
  5473.               if t1="CONTENT-DISPOSITION" then iterate /* don't bother retaining this */
  5474.               form_data.!list.mm=form_data.!list.mm' 't1
  5475.               nm1='!'||t1
  5476.               form_data.nm1.mm=t2
  5477.           end     /* exract arguments */
  5478.      end        /* extract args on this line */
  5479.   end                    /* get a line */
  5480.   if thestuff<>"" then do
  5481.     form_data.mm=left(thestuff,length(thestuff)-2)  /* strip off ending crlf */
  5482.     parse var abody foo (crlf) abody   /* jump past extra crlf */
  5483.   end
  5484.   else do
  5485.      form_data.body.mm=""
  5486.   end
  5487. end
  5488.  
  5489. return mm
  5490.  
  5491.  
  5492. /***********************/
  5493.  
  5494.