
/* $Id: magicktest.q,v 1.22 2007/09/29 08:50:15 agraef Exp $
   magicktest.q: A little demonstration program, shows how to render images
   on a GGI visual. */

import ggi, magick, system;

/****************************************************************************/

/* CONFIG SECTION. Edit the following as needed. */

/* Display driver. Assume DirectX on Windows, X11 everywhere else. */

target		= "directx" if pos "mingw" sysinfo >= 0;
		= "x" otherwise;

/* Default font to use for text rendering. */

def FONT_DIRS = ["/usr/X11R6/lib/X11/fonts/truetype",
		 "/windows/fonts", "/winnt/fonts"],
  FONT = search_font "arial.ttf";

search_font FONT
= FONT where [FONT|_] = cat (map (glob.(++("/"++FONT))) FONT_DIRS);
= printf "Warning: %s could not be found -- using default font instead\n" FONT
    otherwise;

/****************************************************************************/

/* display IMG: Display the given image in a GGI visual. Returns the created
   visual. Here are some examples for you to try:

   Display an image read from a file, use defaults:

   ==> display (read_image "mozilla.png" ())

   Display raw RGBA file -- must specify the dimensions and depth here:

   ==> display (read_image "mozilla_48x48.rgba" (48,48,0,8))

   Display a white image obtained from create_image:

   ==> display (create_image (100,100,0,8) (magick_pixel "white"))

   Same using read_image with the "xc:" source specifier:

   ==> display (read_image "xc:white" (100,100,0,8))

   Now for something a little more interesting. Show some of the builtin
   ImageMagick images:

   ==> display (read_image "logo:" ())

   ==> display (read_image "netscape:" ())

   ==> display (read_image "plasma:" (320,240))

   Display a screenshot of an X11 window:

   ==> def IMG = read_image "x:" (); display IMG

   (When the cross cursor appears, click on the window to capture. You can
   also use a specification like "x:0x1e00002" to denote the target window by
   its id. See ImageMagick(1) for more details.)

   Show the same image, converted to grayscale (we first clone the image so
   that the original image is not modified):

   ==> def IMG2 = clone_image IMG; quantize IMG2 GRAY; display IMG2

   The same image quantized to the colors in "netscape:":

   ==> def IMG2 = clone_image IMG; quantize IMG2 (read_image "netscape:" ())

   ==> display IMG2

   As a final example, let's see how we can draw something on an image. We
   start out with a white canvas:

   ==> def IMG = read_image "xc:white" (320,240)

   First set the font to be used for text rendering:

   ==> set_draw_font IMG FONT

   Now draw some stuff and display the resulting image in a GGI visual:

   ==> draw IMG "stroke black fill none circle 160,120 240,120"

   ==> draw IMG "text 160,120 \"Hello, world!\""

   ==> display IMG

   It goes without saying that this method is much more convenient than using
   GGI's own, very basic, drawing primitives.

   Now if we were *real* hackers, of course we'd do the same in PostScript
   instead. ;-) Actually, we *are* real hackers, so here's how we can render
   some "inline" PostScript code in a GGI visual (note that ImageMagick needs
   ghostscript to make this work):

   ==> def PS = "%!PS-Adobe-3.0 EPSF-3.0\n\
   %%BoundingBox: 0 0 320 240\n\
   /Helvetica findfont 12 scalefont setfont \
   160 120 80 0 360 arc stroke 160 120 moveto (Hello, world!) show \
   showpage\n"

   ==> display (blob_to_image (bytestr PS) ())

   Here we used the blob_to_image function to convert the PostScript code in
   "blob" (i.e., byte string) format to an image which can then be shown in a
   GGI visual. By these means, you can employ the full power of PostScript to
   draw complicated stuff in a GGI visual. Of course, we could also employ the
   graphics.q script in the standard library to create the PostScript source
   in a convenient manner. This is left as an exercise to the interested
   reader. :) */

/* The simple case: our image is opaque. In this case we just render it on a
   screen visual: */

display IMG

= ggi_put_box VIS (0,0) (W,H) PIXELS || ggi_flush VIS || VIS

    where (W,H|_) = image_info IMG,
      VIS = ggi_open (sprintf "%s:-keepcursor" target),
      () = ggi_set_flags VIS GGI_FLAG_ASYNC ||
      ggi_set_mode VIS (sprintf "%dx%d" (W,H)),
      PIXELS = get_image_pixels IMG (0,0) (W,H)

    if is_opaque_image IMG;

/* The complicated case: our image has transparency. In this case we clad the
   visual with an alpha buffer and draw the image over a background. Actually,
   it's not *that* complicated, either: */

def BACK = magick_pixel "gray";

display IMG

= ggi_set_background VIS BACK || ggi_clear VIS ||
  ggi_put_box VIS (0,0) (W,H) PIXELS || ggi_flush VIS || VIS

    where (W,H|_) = image_info IMG,
      VIS = ggi_open (sprintf "%s:-keepcursor" target),
      () = ggi_set_flags VIS GGI_FLAG_ASYNC ||
      ggi_set_mode VIS (sprintf "%dx%d.A16" (W,H)),
      PIXELS = get_image_pixels IMG (0,0) (W,H)

    otherwise;

/****************************************************************************/

/* capture VIS: Sometimes you will also want to go the other way round, i.e.,
   grab the contents of a GGI visual and return it as an image which can then
   be manipulated and saved in a file. Here's how to do this. */

capture VIS

= create_image (W,H,0,8,MATTE) (ggi_get_box VIS (0,0) (W,H))

    where MODE = ggi_get_mode VIS, (W,H) = sscanf MODE "%dx%d",
      MATTE = ifelse (pos "A" MODE >= 0) 1 0;

/****************************************************************************/

/* animate IMGS: Animate a list of images. Shows each image for 1/FPS seconds,
   then displays the next one. You can abort the animation at any time by
   hitting a key while the mouse cursor is in the GGI window. Note that, by
   default, the frame rate FPS is set to 25 which is suitable, e.g., for MPEG
   movies; you might wish to change this value as needed.

   To have some fun, download yourself jurannessic_176x104.mpg from
   http://www.pocketmovies.net/ and watch the little caveman discover the
   cinema:

   ==> animate (read_image "jurannessic_176x104.mpg" ())

   Sit back and watch. Enjoy. :) */

def FPS = 25;

animate IMGS

= catch id (loop VIS IMGS)

    where (W,H|_) = image_info (hd IMGS),
      VIS = ggi_open (sprintf "%s:-keepcursor" target),
      () = ggi_set_flags VIS GGI_FLAG_ASYNC ||
      ggi_set_mode VIS (sprintf "%dx%d" (W,H));

loop VIS IMGS	= do (show VIS) IMGS || sleep 2 || loop VIS IMGS;

show VIS IMG

= ggi_getc VIS || throw VIS if ggi_kbhit VIS;

= ggi_put_box VIS (0,0) (W,H) PIXELS || ggi_flush VIS || sleep (T+1/FPS-time)

    where T = time, (W,H|_) = image_info IMG,
      PIXELS = get_image_pixels IMG (0,0) (W,H);

/****************************************************************************/

/* gallery IMGS SCENES: Show a thumbnail gallery of the given images SCENES (a
   list of image indexes) in the given list of images IMGS. Example:

   ==> def IMGS = read_image "jurannessic_176x104.mpg" ()

   ==> gallery IMGS [20,120,160,220,420,595,650,715,760,795] */

gallery IMGS SCENES

= ifelse (islist IMG) (map display IMG) (display IMG)

  where IMGS = map (IMGS!) SCENES, LABELS = map (sprintf "#%d") SCENES,
    _ = zipwith set_image_label IMGS LABELS,
    IMG = montage IMGS ("Gallery","5x4","120x120",(),10,true,(),FONT,12);

/****************************************************************************/

/* slideshow IMGS SCENES: Show a slide show of the given images SCENES in the
   given list of images IMGS, using morph to crossfade between the different
   images. Example:

   ==> def IMGS = read_image "jurannessic_176x104.mpg" ()

   ==> slideshow IMGS [795,20,120,160,220,420,595,650,715,760] */

slideshow IMGS SCENES

= animate IMGS

  where IMGS = map (clone_image.(IMGS!)) SCENES,
    _ = do (flip set_draw_font FONT) IMGS,
    H:Int = image_height (hd IMGS),
    CMD = sprintf "fill white text 10,%d #%%d" (H-10),
    LABELS = map (sprintf CMD) SCENES, _ = zipwith draw IMGS LABELS,
    IMGS = morph (append IMGS (hd IMGS)) 50;
