Nova Design Logo

Nova Design - Main
Tutorials - Main
IFXCurrents - Main


ifx currents banner

Tutorials for ImageFX for the Amiga



All contents are Copyright - 1999, 2000 by Nova Design, Inc. All rights reserved. These documents may not be reproduced by any means without the express, written, permission of Nova Design, Inc.

Copyright - 1999, 2000 by Nova Design, Inc.

It is often helpful to hear what other users are doing with an application. If you would like to ask other ImageFX users about techniques and uses of ImageFX you can subscribe to Nova Design's ImageFX mailing list by going HERE and following the "Membership/Subscribe" link.

The ImageFX Currents Tutorials have been put into this HTML archive by the following individuals (Listed in alphabetical order):

  • Wil Haslup
  • John Whiting
  • Kermit Woodall

Logo illustration by Kermit Woodall
Edited by John A. Whiting
HTML coded by Wil Haslup and John A. Whiting
Screencaptures by John A. Whiting


Automated Image Processing From Aladdin4D with ImageFX

By J.A. Whiting

[Editor's note: this article is also being published in Aladdin's Lamp #20 --JAW]

Not being an expert is a mixed blessing. On the one hand, you're not constrained by what others have done before, and you're always learning something new. On the other hand, you do a lot of re-invention, and you make a lot of mistakes along the way.

Nowhere has my lack of expertise dramatized this dichotomy better than when I decided to make an animated "News" button using Aladdin4D, ImageFX, and the frame script capabilities of Aladdin4D to automate the process. This required experimentation with Aladdin4D, ImageFX, Amiga OS scripts, and ARexx, in none of which can I call myself an expert.

The goal was to create a 3D animation of the word "News", scale it down (because ImageFX's scaling is better than Aladdin4D's), and compile the frames into a GIF image with a transparent background. I didn't like the way the transparent background effect looked, so I later abandoned it.

First, let's fire up Aladdin4D.

Begin at the Beginning

Under the Object/Text/Font menu item, the Block font was selected, and the word "NEWS" was generated and zoomed in upon. The word was selected and extruded .025 meters (Editor set to use meters as the units of measure) in the Y axis. Each letter was individually selected and grouped, then phong shaded with Smooth Adjacent ON and set to 89 degrees.

Letters Image

Under the Attributes menu item, the default settings for the attribute list were used. Under the Texture menu item, the slate.32 texture was applied as a color bitmap planar projection through the Y axis on EACH INDIVIDUAL LETTER. Had I simply selected the letters as a group, the bit map would have stretched itself to reach from the left side of the "N" to the right side of the "S." Under the Settings tab, Strength and Color and color were set to 100%.

A small triangular polygon was created, and cloned 3 times. These polygons were turned into paths. Each letter was individually selected and the RMB used on the Pan gadget to center the ATP to the letter, and accepted. Each path was selected and the RMB used on the Center gadget to center the path to the ATP. (This was done so that when the letters are rotated, they rotate around their own centers.)

Letters with paths
Letters, paths, and background rectangle

Swing your partner!

Each path was set to start with the letter rotated 90 degrees on the Z axis, and end rotated -90 degrees on the Z axis, with a CSpline to control rotation times and rates. Each letter takes approximately 5 frames to rotate to 0 degrees, where it holds until the last 5 frames of the animation. Each letter starts rotating 5 frames after the previous one started rotating, so that it starts rotating when the previous letter is finished. All the letters smartly rotate together in the last 5 frames to the -90 degree point.

Path CSpline
CSpline for the letter "E"

(Because the transparency feature of the GIF image didn't work as well as I had hoped, a yellow rectangle was later created and moved behind the letters to serve as a backdrop. Maybe it would have worked better if I'd had Aladdin render it as a 256 color image.)

Frame 30 was rendered as a 24 bit image and saved to be manipulated by ImageFX because all of the letters would be rotated to zero degrees at that point. This was important because I originally had no backdrop and wanted to use the Crop function's Autocrop button to generate the cropping parameters. This would guarantee that all frames would end up the same size, an important feature in most animations.

It was now time to load ImageFX.

IFX Cropped image
Using the Autocrop button in ImageFX's Crop function

I used the macro-recording facility in ImageFX to crop the rendering of frame 30 to just the part I was interested in, then scale it down. Next, I went to the Color button to convert the image to a 256 color CMAP, and save the result as a GIF. At this point, I ended the macro, shown here.


/*
 * Arexx Program created by ImageFX Release 4.1 
 */

OPTIONS RESULTS

Crop 74 117 493 148
Scale 97 29 ACCURATE
Color2CMAP 256 1 2 0
SaveBufferAs "GIF" "RAM:News.gif"  NoLace NoTransparent Delay 0 NoOptimize Overwrite


This gave me the commands that formed the heart of the operation, although I had to look up the functions in the GIF saver to edit the macro so that the frames would be compiled as an animation with a transparent background.

Now, it was time to break out the text editor and write some scripts.

Automation is our friend!

Aladdin4D comes with an example frame script, and in Aladdin's Lamp #12, Robert Lanham wrote an excellent article, "Automatic Pegging" on how to use the frame script to automatically convert Aladdin frames into JPEGs. This was back in the days before Aladdin could save JPEGs directly, but the article helped me with the concepts of using external programs to process Aladdin images.

The first and most important thing to know is that the frame script MUST be named d4dproFI (a legacy from when Aladdin was Draw4D Pro), and it must be in your S: directory. Making certain the "S" protection bit is set on the file may or may not be necessary, but it's always a good idea for script files.

Much trial and error ensued in writing the scripts. For example, d4dproFI inherits the path from Aladdin; it therefore couldn't find the RX command (in the REXX: drawer) to run my ARexx script until I changed that line explicitly to say REXX:rx.

Here you can see my version of the d4dproFI script:

.k FILENAME/a
;
;
; the .k line allows you to access the name of the file as <FILENAME>
; anywhere in this script
; NEVER "run" a command
;
;
; NOTE: This file must be named: "d4dproFI" (without the quotes) and must
;       be located in the s: directory
;
;       To enable the script, turn on the Frame Script render option.
;       Then save your animation as 24 bit individual frames.
;
;       This example script will take the 24 bit image and convert it to
;       a GIF animation through ImageFX and then delete
;       the original 24 bit image.
;
;
echo "Processing file <FILENAME>"
rexx:rx rexx:AladdinGIFgen.rexx <FILENAME>
delete <FILENAME>


The only real changes I made were to change the comments about what it does, and the next to last line, which calls my ARexx script. The vast majority is still as I found it.

My ARexx programming is very much a "cut and paste" proposition; the ARexx script AladdinGIFGen.rexx consists of several different snippets. One part of the code code was originally intended to launch AWeb automatically if it wasn't already running; this is used to prevent the script from crashing if you didn't start up ImageFX yourself. More code was lifted from a macro intended to send image files to ImageFX from Directory Opus. And finally, there is the macro I had earlier created from ImageFX to actually do the work. It's ugly, kludgey, and after a fashion, it works.

But it worked only after more trials. I learned that errors in the ARexx script that weren't enough to kill the ARexx script itself could cause d4dproFI to fail before it finished. This allowed the individual Aladdin frames to accumulate; normally, the d4dproFI script deletes them immediately after the ARexx script finishes.

Example: in ARexx, you cannot open a file for an APPEND operation if it doesn't already exist. So, processing log-file msg.txt was not being created, and attempts to append to it caused d4dproFI to fail.

There has also been some recent discussion of ImageFX's ARexx communications port you should be aware of. If you are running an ARexx script from outside of ImageFX, the port name of the first running copy is 'IMAGEFX.1'. You should also be aware that all ARexx port names are CASE SENSITIVE.

If, however, you are running an ARexx script ("macro") from ImageFX itself, it uses a different port name entirely. I don't know the programming reasons for this, but I imagine that it could be important to ImageFX to know whether the script was run by it, or from an outside agency.

I also learned that if you're trying to send commands to ImageFX from an external ARexx script, you must use the ADDRESS 'IMAGEFX.1' command to redirect commands to IFX's port. However, if you've stored the port name in a variable, as I did, you must use ADDRESS VALUE variable, where "variable" is the name of the variable you used. If you don't use the VALUE keyword, it tries to find a port with the name of your variable, instead of the name stored IN the variable.

I found some of this out from Aaron Ruscetta's previously mentioned post. In the ImageFX mailing list, Aaron "AArexx AAron" Ruscetta had this to say:

Scripts run from inside IFX are "pre-addressed" to an internal reply port. The internal port name always starts with the string "arexx_reply_port", so I came up with the following header to check which port address I should let/tell my IFX macros to talk to:

/* *** Get or Set BASE ImageFX address */

BasePort=address() /* what address are we pointed to at the start? */
 
Chk=index(BasePort,'arexx_reply_port')=1  /* Launched by IFX=1 */

if ~Chk then do  /* launched externally; have to set the address */
    Chk=show('P','IMAGEFX.1')
    if Chk then do
        BasePort='IMAGEFX.1'
        address 'IMAGEFX.1' /* or "address value BasePort" */
    end
    else do
        say "IMAGEFX.1 Arexx Port NOT FOUND... "
        say "ImageFX must be running to use this Program!"
        exit 22
    end
end

/* ***** END ****** */

If the program has to talk with other ports for a bit and then return
to IFX, just use the value key word and the BasePort variable, eg:

address value BasePort

/* do more IFX stuff now... */

Here you can see the code for AladdinGIFgen.rexx. Note the tiny part of the code that actually does the work in ImageFX! You should also note that this script uses whatever filename you used for the animation as the filename for the GIF.

/*
 *  Load supplied file, crop it, scale it, and append it to a GIF animation.
 *  If an Ifx port is found, entries are loaded into a running Ifx.
 *  Otherwise Ifx is invoked first, using appropriate stack size.
 *  
 * Created by John A. Whiting by modifying a script for Directory Opus
 * Richard Wheeldon had created by modifying an existing script for Emacs
 * by David Lübbren.
 *
 *  Intended to work with Aladdin4D's d4dproFI script and Aladdin4D's
 *  Frame Script rendering mode.  Obviously, by modifying the commands
 *  to ImageFX, almost any sort of image-processing can be performed on
 *  each and every frame of an Aladdin4D animation immediately upon rendering.
 *
 *  Call: rx rexx:AladdinGIFgen.rexx <FILENAME>
 *
 */

StackSize  = 60000
IfxPort  = 'IMAGEFX.1'
IfxCmd   = 'Run <NIL: >NIL: Imagefx4:Imagefx'


OPTIONS RESULTS

filename=WORD(ARG(1),1)
svfile=LEFT(filename,LENGTH(filename)-5)

/* Verify our message file exists; if not, create it. */

IF ~OPEN('outfile', 'ram:msg.txt', 'READ') THEN DO
  OPEN('outfile', 'ram:msg.txt', 'WRITE')
  WRITELN('outfile', 'Begin Processing.')
  WRITELN('outfile', ' ')
  CLOSE('outfile')
  END
ELSE DO
  CLOSE('outfile')
END

/* Check for running copy of ImageFX.  If not found, run it! */

IF ~SHOW('Ports', IfxPort) THEN DO
  PRAGMA('Stack', StackSize)
  PRAGMA('Directory', SourcePath)
  ADDRESS COMMAND IfxCmd 'WaitForPort' IfxPort
  RC=1
  DO i=1 TO 6 WHILE RC~=0
    ADDRESS COMMAND 'waitforport' Ifxport
  END
  ADDRESS 
  IF rc = 5 THEN DO
    message =  '"Can''t find ImageFX port '''IfxPort'''"'
    SIGNAL quitit
  END
END  

/* Actual part that does the work! */

ADDRESS VALUE IfxPort
LoadBuffer filename Force New  
Crop 74 117 493 148
Scale 97 29 ACCURATE
Color2CMAP 256 1 2 0
SaveBufferAs "GIF" svfile || ".gif"  NoLace Transparent 0 Delay 0 NoOptimize Append
KillBuffer FORCE

/* End of any real work; just clean-up and exit from here. */

message = "Frame" RIGHT(filename,4) "processed."
SIGNAL quitit


quitit:
  OPEN('outfile', 'ram:msg.txt', 'APPEND')
  WRITELN('outfile', message)
  CLOSE('outfile')
  EXIT


Are we there yet?

So, we head back to Aladdin4D now. Under the Render/Render Animation menu item, I made certain we were set for a 40 frame animation. I then hit the Render Settings button, and under the Display tab, checked the frame script button. I then 'Okay'ed everything and gave the animation the name "News." Aladdin rendered the first frame, the script sent the frame to ImageFX for modification, ImageFX saved the changed image as a GIF file, and the script then deleted the first frame and returned control to Aladdin. Aladdin then rendered the next frame in the series. It worked!

Render settings requestor

Obviously, this is a very simple application, for all the stress and strain it placed on Ye Olde Editor. But imagine this possibility: you render an animation of a starship in Aladdin and send each frame to ImageFX where a background is matted in, then space itself curls and distorts as your starship suddenly decloaks. I've done this by hand in the past, with a separate run in Aladdin and ImageFX each. How much easier and nicer it will be to do it with a modified frame script! And faster, too, because Aladdin and ImageFX do not steal CPU cycles from each other; they take turns doing the work.

Enjoy!

Animated GIF
Completed animation

***


Links: