7

I would like to run animations in PostScript smoothly. To see what I want, let me switch to PostScript directly. Call ghostscript, and

200 dup scale .5 setgray 0 0 3 3 rectfill

We have now a gray square.

0 setgray 0 1 3 1 rectfill

With a black stripe in it. We will now fill that stripe, one time white and black in succession:

{1 setgray 0 1 3 1 rectfill 0 setgray 0 1 3 1 rectfill} loop

You will see now some flickering of black and white rectangles that are smaller than the original black stripe. Ideally, we would see the original grey square. Or almost. Where can I get such functionality today?

To see a more interesting animation sequence searching for magic squares of size 5:

wget http://www.complang.tuwien.ac.at/ulrich/gupu/l5.eps.gz
zcat l5.eps.gz | ghostscript -

A couple of years ago I did try to address these issues. But it never went into ghostscript or Xfree. See this page. Maybe there are some better ideas now?

Edit: After reading the responses so far, let me clarify one issue here. Essentially, there are two independent issues in this question:

  • How should an animation be viewed from the language level? I believe, the best way is to view each frame as a single page. With copypage incremental changes can be realized with low effort. Sadly, that semantics of copypage is only present in Level 1 and 2. In Level 3, the meaning of copypage changed to showpage. I did - many years ago - a little modification to ghostscript to delay all visible changes up to copypage or showpage. In this manner, a single XCopyArea containing the changed area is performed locally on the server (that is, the display).

  • How should actual changes on the visual display be synchronized to avoid artefacts that where not present in the graphics described? The irregular flicker you see is not a privilege of PostScript, it seems to be present in any double-buffered system I have seen so far. Just try to program this in whatever system you see fit.

Further edit:

To get the right language level, that is level 1 or 2 do (for ghostscript):

systemdict /.setlanguagelevel known {2 .setlanguagelevel} if

Edit: I am adding this comment to may attract some new contributors.

false
  • 10,264
  • 13
  • 101
  • 209
  • It seems to me if you want to get a gray "effect" from a series of white and black swatches, it needs to be sync'ed to the refresh rate of the display. At 60hz, you'd want at most 2 "frames" of each, 30hz being just above the Persistence Of Vision threshold. – luser droog Dec 10 '11 at 11:28
  • This was only a test example where it is evident what should be visible and what not. Nobody wants to paint gray that way (it was popular to do so with ATARI 25 years ago...). What is interesting to this today is to view discrete animations as the one above (l5.eps) and this without seeing artefacts that are not part of the animation. Please look at it: It shows you how a CLPFD-program finds solutions to the magic squares by enumerating all possibilities **systematically**. – false Dec 10 '11 at 17:53
  • 1
    Recent thread about [postscript and animation](https://groups.google.com/forum/#!topic/comp.lang.postscript/olXsoUut2aQ) in a *batch* mode. – luser droog Nov 17 '14 at 08:20

2 Answers2

4

We explored some of these issues in this thread on comp.lang.postscript.

Since the release of the Level 2 standard, Postscript is a garbage-collected language. You can disable collection of user objects with the fragment -2 vmreclaim, but it doesn't accomplish much. You can disable ALL ghostscript garbage collection by invoking with the -dNOGC option. This should help prevent stalling and spitting with parts of the image.

Ghostscript has a non-standard operator called flushpage, which synchronizes the rendering with the execution. This helps make sure that everything is seen before it's gone.

- flushpage -
On displays, flushes any buffered output, so that it is guaranteed to
show up on the screen; on printers, has no effect

And for timing control, there doesn't seem to be a better way than simple busy-waiting.


/smallpause {
    /flushpage where{pop flushpage}if
    50 sleep } def
/pagepause {
    /flushpage where{pop flushpage}if
    1000 sleep } def

/sleep {
    usertime add {
        10 {
            1 100000 div pop %busy
            .1 sin 257 cos atan pop %busy busy
        } repeat
        dup usertime le {exit}if
    } loop pop
} def 

The where guards around flushpage allow you to send the same code to interpreters other than ghostscript (like a printer).

If you had an actual Display Postscript server, you could use the wait operator instead of busy-waiting.

luser droog
  • 18,988
  • 3
  • 53
  • 105
  • There are two issues here. What should be seen? And how is the interaction with the display performed? You are interested in the former, I rather in the latter. I believe that `flushpage` is not a good way to go. `copypage` (that is, only Level 1 & 2 semantics) is the clean, logical way. – false Dec 06 '11 at 11:06
  • That makes sense. I have not gotten the results I wanted from `copypage`. It always does an `erasepage` which I don't want. I probably need to upgrade my ghostscript install. – luser droog Dec 06 '11 at 17:13
  • No! You have to set the language level to 1 or 2. – false Dec 06 '11 at 17:14
  • I can't find it in the docs! How do you set the language level? – luser droog Dec 06 '11 at 17:39
  • 1
    I put it at the end of my question: It used to be: `systemdict /.setlanguagelevel known {2 .setlanguagelevel} if` – false Dec 06 '11 at 17:47
3

Although I like (and upvoted) @luserdroog's answer, I don't believe Postscript should be used for animations on this way - I'd rather use some language that can run widgets or display elements that are designed for real time display and user interation - that is not the case of postscript or ghostscript.

I think it would be nice, though, use postscript for aimations for rendering purposes - just rendering a page after each modification on the image, and using an external program to assemble the different pages as animation frames. Maybe even using postscript as a rendering engine, with the process in another language calling ghostscript to render each frame in realtime. A nice and easy to use multimedia framework to do that could be for example, the Python language with the Pygame module.

Here is a short example using "pure python + pygame".

#! /usr/bin/env python
# coding: utf-8

import pygame

size = 640,480 #(in pixels)

# multiplying factor so that the whole image is 5 units across
factor = size[0] / 5.0

# Inits pygame drawing window
screen = pygame.display.set_mode(size)

# Rectangle drawing function that scales drawing using the factor above
def draw_rect(color, rect):
    new_rect = [int (r * factor) for r in rect]
    return pygame.draw.rect(screen, color, new_rect)

def main():
    draw_rect((128,128,128), (0, 0, 3, 3))
    colors = ((255,255,255), (0,0,0))
    color_index = 0
    while True:
        draw_rect(colors[color_index], (0, 1, 3, 1))
        pygame.display.flip()
        color_index = 1 - color_index
        pygame.time.delay(50) # in miliseconds

try:
    main()
finally:
    pygame.quit()

To execute this, you have to have Python and Pygame (from http://pygame.org - there are ready packages on most linux systems for that)

Pygame's drawing API is much poorer than postscript - if you need more than rectangles (like bezies, scaling, rotating and shearing the coordinate system, and so on), the way to go is to use Cairo + python + some display library (which might be pygame, GTK+ or qt) . Cairo is a 2D drawing library that inherits from the postscript way. - or, as I said above, to drive a ghostscript external process using Python, having it generating a rle compressed image file to stdout or a ramdrive, and that image read and displayed frame by frame using python + pygame.

Another option is to use HTML5's Canvas, and do everything in Javascript, viewable on a browser. HTML5's Canvas also inherits the Postscript way of drawing.

jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • 1
    I am using PostScript for animations since about 1990. So far, I have not seen a single other programming language that is capable of the same 2D functionality. BTW, have you tried to program the original program in another framework? I would be interested to see, if there is **any** system capable of displaying such graphics accurately. – false Dec 06 '11 at 12:17
  • Just to make things clear: Paint a gray square of size 3x3, draw a stripe of a rectangle 3x1 through it, change the color of that rectangle from white to black all the time. – false Dec 06 '11 at 16:25
  • I could make that in pure pygame - but I think I could come up with a layer to have ghostscript to draw the images, and pygame to display then and add interactivity - so you could keep drawing in postscript. – jsbueno Dec 07 '11 at 02:17
  • (BTW, I dislike offering another technology/language than the one asked for, but I use python/pygame so much, it came out this time) – jsbueno Dec 07 '11 at 02:17
  • Thank you! The key seems to be `pygame.display.flip()`. Could you remove the sleeping and tell me what you see then? ((I unsuccessfully tried to install pygame on karmic Ubuntu - not even from source...)) – false Dec 07 '11 at 23:50
  • On ubuntu you should try the "python-pygame" package from the repositories. – jsbueno Dec 08 '11 at 01:06
  • Without the "delay" call I see a superfast flashing of white/black/grey, in some frames with strips of black inside the white - but the effect here has more to do with me running an X11 driver without any aceleration - with the 50ms delay I get well defined blacks and whites. – jsbueno Dec 08 '11 at 01:09
  • This I feared. In theory, `pygame.display.flip()` or `copypage` could check with the X-driver if the paint operation could be done atomically now or wait until this is possible. This was done, ~10 years [ago in this XFree mod](http://www.complang.tuwien.ac.at/skral/download/eyefriendly_xfree86/) - would be nice to have it today. – false Dec 08 '11 at 01:23