about summary refs log tree commit diff
path: root/converter/ppm
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2006-08-19 03:12:28 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2006-08-19 03:12:28 +0000
commit1fd361a1ea06e44286c213ca1f814f49306fdc43 (patch)
tree64c8c96cf54d8718847339a403e5e67b922e8c3f /converter/ppm
downloadnetpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.gz
netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.xz
netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.zip
Create Subversion repository
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'converter/ppm')
-rw-r--r--converter/ppm/411toppm.c261
-rw-r--r--converter/ppm/Makefile49
-rw-r--r--converter/ppm/autocad.h64
-rw-r--r--converter/ppm/eyuvtoppm.c338
-rw-r--r--converter/ppm/gouldtoppm.c122
-rw-r--r--converter/ppm/hpcdtoppm/Makefile25
-rw-r--r--converter/ppm/hpcdtoppm/README20
-rwxr-xr-xconverter/ppm/hpcdtoppm/hpcdtoppm25
-rwxr-xr-xconverter/ppm/hpcdtoppm/pcdovtoppm214
-rw-r--r--converter/ppm/ilbm.h256
-rw-r--r--converter/ppm/ilbmtoppm.c2381
-rw-r--r--converter/ppm/imgtoppm.c142
-rw-r--r--converter/ppm/leaftoppm.c216
-rw-r--r--converter/ppm/mitsu.h97
-rw-r--r--converter/ppm/mtvtoppm.c68
-rw-r--r--converter/ppm/neotoppm.c104
-rw-r--r--converter/ppm/pc1toppm.c233
-rw-r--r--converter/ppm/pcxstd.ppm19
-rw-r--r--converter/ppm/pcxtoppm.c710
-rw-r--r--converter/ppm/pi1toppm.c92
-rw-r--r--converter/ppm/picttoppm.c3788
-rw-r--r--converter/ppm/pjtoppm.c253
-rw-r--r--converter/ppm/ppmtoacad.c394
-rw-r--r--converter/ppm/ppmtoarbtxt.c505
-rw-r--r--converter/ppm/ppmtobmp.c795
-rw-r--r--converter/ppm/ppmtoeyuv.c396
-rw-r--r--converter/ppm/ppmtogif.c1681
-rw-r--r--converter/ppm/ppmtoicr.c320
-rw-r--r--converter/ppm/ppmtoilbm.c2362
-rw-r--r--converter/ppm/ppmtoleaf.c250
-rw-r--r--converter/ppm/ppmtolj.c297
-rw-r--r--converter/ppm/ppmtomitsu.c604
-rw-r--r--converter/ppm/ppmtompeg/BUGS43
-rw-r--r--converter/ppm/ppmtompeg/CHANGES180
-rw-r--r--converter/ppm/ppmtompeg/COPYRIGHT20
-rw-r--r--converter/ppm/ppmtompeg/HISTORY313
-rw-r--r--converter/ppm/ppmtompeg/LOGIC126
-rw-r--r--converter/ppm/ppmtompeg/Makefile140
-rw-r--r--converter/ppm/ppmtompeg/bframe.c1347
-rw-r--r--converter/ppm/ppmtompeg/bitio.c536
-rw-r--r--converter/ppm/ppmtompeg/block.c1045
-rw-r--r--converter/ppm/ppmtompeg/bsearch.c1088
-rw-r--r--converter/ppm/ppmtompeg/combine.c357
-rw-r--r--converter/ppm/ppmtompeg/docs/EXTENSIONS41
-rw-r--r--converter/ppm/ppmtompeg/docs/INPUT.FORMAT79
-rw-r--r--converter/ppm/ppmtompeg/docs/parallel.param142
-rw-r--r--converter/ppm/ppmtompeg/docs/param-summary14
-rw-r--r--converter/ppm/ppmtompeg/docs/template.param154
-rw-r--r--converter/ppm/ppmtompeg/docs/users-guide.ps3233
-rw-r--r--converter/ppm/ppmtompeg/examples/basicspeed.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/decoded.test81
-rw-r--r--converter/ppm/ppmtompeg/examples/decoded2.test81
-rw-r--r--converter/ppm/ppmtompeg/examples/decorig.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/default.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/fastspeed.param46
-rw-r--r--converter/ppm/ppmtompeg/examples/foobar.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/frame.test37
-rw-r--r--converter/ppm/ppmtompeg/examples/gop.combine11
-rw-r--r--converter/ppm/ppmtompeg/examples/gop.test43
-rw-r--r--converter/ppm/ppmtompeg/examples/gop1.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/hello.param60
-rw-r--r--converter/ppm/ppmtompeg/examples/i.test37
-rw-r--r--converter/ppm/ppmtompeg/examples/ispeed.jpeg.param48
-rw-r--r--converter/ppm/ppmtompeg/examples/ispeed.param45
-rw-r--r--converter/ppm/ppmtompeg/examples/jpeg.test66
-rw-r--r--converter/ppm/ppmtompeg/examples/jpeg19.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/jpegout19.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/localdisk.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/localremote.test80
-rw-r--r--converter/ppm/ppmtompeg/examples/luxo.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/luxo18.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/luxo19.5.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/luxo19.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/luxo2.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/luxo48.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/luxo49.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/luxo50.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/luxo52.5.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/luxo52.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/luxo53.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/luxobug.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/luxoskip.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/med.param36
-rw-r--r--converter/ppm/ppmtompeg/examples/med18.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/medspeed.param38
-rw-r--r--converter/ppm/ppmtompeg/examples/nametest.param60
-rw-r--r--converter/ppm/ppmtompeg/examples/nosearch.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/par3.param43
-rw-r--r--converter/ppm/ppmtompeg/examples/par4.param45
-rw-r--r--converter/ppm/ppmtompeg/examples/par5.param47
-rw-r--r--converter/ppm/ppmtompeg/examples/parallel.287
-rw-r--r--converter/ppm/ppmtompeg/examples/parallel.param141
-rw-r--r--converter/ppm/ppmtompeg/examples/parallel.test85
-rw-r--r--converter/ppm/ppmtompeg/examples/param.template107
-rw-r--r--converter/ppm/ppmtompeg/examples/payam.param37
-rw-r--r--converter/ppm/ppmtompeg/examples/payam18.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/pnm.param37
-rw-r--r--converter/ppm/ppmtompeg/examples/ppm.param37
-rw-r--r--converter/ppm/ppmtompeg/examples/prof.b.param48
-rw-r--r--converter/ppm/ppmtompeg/examples/prof.bhalf.param48
-rw-r--r--converter/ppm/ppmtompeg/examples/prof.param36
-rw-r--r--converter/ppm/ppmtompeg/examples/prof.ss.param48
-rw-r--r--converter/ppm/ppmtompeg/examples/prof2.param36
-rw-r--r--converter/ppm/ppmtompeg/examples/prof3.param36
-rw-r--r--converter/ppm/ppmtompeg/examples/prof4.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/remote.test85
-rw-r--r--converter/ppm/ppmtompeg/examples/search1.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search10.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search11.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search12.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search13.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search14.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search15.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search16.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search17.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search18.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search19.5.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/search19.param42
-rw-r--r--converter/ppm/ppmtompeg/examples/search2.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search20.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search21.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search22.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search23.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search24.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search25.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search26.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search27.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search28.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search29.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search3.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search30.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search31.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search32.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search33.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search34.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search35.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search36.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search37.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search38.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search39.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search4.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search40.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search41.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search42.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search43.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search44.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search45.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search46.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search47.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search48.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/search49.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/search5.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search50.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/search51.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search52.5.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/search52.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/search53.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/search6.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search7.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search8.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/search9.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/seq.param36
-rw-r--r--converter/ppm/ppmtompeg/examples/sequoia.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/sequoia19.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/sequoia2.param36
-rw-r--r--converter/ppm/ppmtompeg/examples/simple.param40
-rw-r--r--converter/ppm/ppmtompeg/examples/slice.param47
-rw-r--r--converter/ppm/ppmtompeg/examples/slimed.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/slowspeed.param38
-rw-r--r--converter/ppm/ppmtompeg/examples/stanford.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/subtest.param34
-rw-r--r--converter/ppm/ppmtompeg/examples/temp.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/template.param154
-rw-r--r--converter/ppm/ppmtompeg/examples/tennis.param37
-rw-r--r--converter/ppm/ppmtompeg/examples/tennis18.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/tennis19.5.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/tennis19.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/tennis48.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/tennis49.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/tennis50.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/tennis52.5.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/tennis52.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/tennis53.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/tennisbug.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/testp.param45
-rw-r--r--converter/ppm/ppmtompeg/examples/walk.param61
-rw-r--r--converter/ppm/ppmtompeg/examples/walk1.param61
-rw-r--r--converter/ppm/ppmtompeg/examples/walk18.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/walk19.5.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/walk19.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/walk2.param61
-rw-r--r--converter/ppm/ppmtompeg/examples/walk3.param61
-rw-r--r--converter/ppm/ppmtompeg/examples/walk4.param61
-rw-r--r--converter/ppm/ppmtompeg/examples/walk48.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/walk49.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/walk5.param62
-rw-r--r--converter/ppm/ppmtompeg/examples/walk50.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/walk52.5.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/walk52.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/walk53.param35
-rw-r--r--converter/ppm/ppmtompeg/examples/walk93.param62
-rw-r--r--converter/ppm/ppmtompeg/file.c322
-rw-r--r--converter/ppm/ppmtompeg/frame.c836
-rw-r--r--converter/ppm/ppmtompeg/frametype.c365
-rw-r--r--converter/ppm/ppmtompeg/fsize.c134
-rw-r--r--converter/ppm/ppmtompeg/gethostname.c26
-rw-r--r--converter/ppm/ppmtompeg/gethostname.h7
-rw-r--r--converter/ppm/ppmtompeg/headers/all.h95
-rw-r--r--converter/ppm/ppmtompeg/headers/ansi.h76
-rw-r--r--converter/ppm/ppmtompeg/headers/bitio.h140
-rw-r--r--converter/ppm/ppmtompeg/headers/block.h53
-rw-r--r--converter/ppm/ppmtompeg/headers/byteorder.h77
-rw-r--r--converter/ppm/ppmtompeg/headers/combine.h20
-rw-r--r--converter/ppm/ppmtompeg/headers/dct.h79
-rw-r--r--converter/ppm/ppmtompeg/headers/frame.h147
-rw-r--r--converter/ppm/ppmtompeg/headers/frames.h487
-rw-r--r--converter/ppm/ppmtompeg/headers/frametype.h17
-rw-r--r--converter/ppm/ppmtompeg/headers/fsize.h49
-rw-r--r--converter/ppm/ppmtompeg/headers/general.h174
-rw-r--r--converter/ppm/ppmtompeg/headers/huff.h34
-rw-r--r--converter/ppm/ppmtompeg/headers/input.h70
-rw-r--r--converter/ppm/ppmtompeg/headers/jpeg.h12
-rw-r--r--converter/ppm/ppmtompeg/headers/mheaders.h114
-rw-r--r--converter/ppm/ppmtompeg/headers/motion_search.h154
-rw-r--r--converter/ppm/ppmtompeg/headers/mpeg.h116
-rw-r--r--converter/ppm/ppmtompeg/headers/mproto.h132
-rw-r--r--converter/ppm/ppmtompeg/headers/mtypes.h109
-rw-r--r--converter/ppm/ppmtompeg/headers/opts.h125
-rw-r--r--converter/ppm/ppmtompeg/headers/parallel.h135
-rw-r--r--converter/ppm/ppmtompeg/headers/param.h87
-rw-r--r--converter/ppm/ppmtompeg/headers/postdct.h40
-rw-r--r--converter/ppm/ppmtompeg/headers/prototypes.h78
-rw-r--r--converter/ppm/ppmtompeg/headers/psocket.h41
-rw-r--r--converter/ppm/ppmtompeg/headers/rate.h204
-rw-r--r--converter/ppm/ppmtompeg/headers/readframe.h69
-rw-r--r--converter/ppm/ppmtompeg/headers/rgbtoycc.h39
-rw-r--r--converter/ppm/ppmtompeg/headers/specifics.h36
-rw-r--r--converter/ppm/ppmtompeg/huff.c131
-rw-r--r--converter/ppm/ppmtompeg/huff.h34
-rw-r--r--converter/ppm/ppmtompeg/huff.table172
-rw-r--r--converter/ppm/ppmtompeg/iframe.c1062
-rw-r--r--converter/ppm/ppmtompeg/input.c515
-rw-r--r--converter/ppm/ppmtompeg/jpeg.c634
-rw-r--r--converter/ppm/ppmtompeg/jrevdct.c1278
-rw-r--r--converter/ppm/ppmtompeg/memory.c71
-rw-r--r--converter/ppm/ppmtompeg/mfwddct.c393
-rw-r--r--converter/ppm/ppmtompeg/mheaders.c1192
-rw-r--r--converter/ppm/ppmtompeg/moutput.c442
-rw-r--r--converter/ppm/ppmtompeg/mpeg.c1717
-rw-r--r--converter/ppm/ppmtompeg/mquant.c44
-rw-r--r--converter/ppm/ppmtompeg/nojpeg.c61
-rw-r--r--converter/ppm/ppmtompeg/noparallel.c229
-rw-r--r--converter/ppm/ppmtompeg/opts.c536
-rw-r--r--converter/ppm/ppmtompeg/parallel.c2278
-rw-r--r--converter/ppm/ppmtompeg/param.c1077
-rw-r--r--converter/ppm/ppmtompeg/parse_huff.pl198
-rw-r--r--converter/ppm/ppmtompeg/pframe.c1072
-rw-r--r--converter/ppm/ppmtompeg/postdct.c626
-rw-r--r--converter/ppm/ppmtompeg/ppmtompeg.c722
-rw-r--r--converter/ppm/ppmtompeg/psearch.c989
-rw-r--r--converter/ppm/ppmtompeg/psocket.c452
-rw-r--r--converter/ppm/ppmtompeg/qtest.c63
-rw-r--r--converter/ppm/ppmtompeg/rate.c986
-rw-r--r--converter/ppm/ppmtompeg/readframe.c1016
-rw-r--r--converter/ppm/ppmtompeg/rgbtoycc.c204
-rw-r--r--converter/ppm/ppmtompeg/specifics.c688
-rw-r--r--converter/ppm/ppmtompeg/subsample.c280
-rw-r--r--converter/ppm/ppmtompeg/tst/README30
-rw-r--r--converter/ppm/ppmtompeg/tst/gop.param30
-rw-r--r--converter/ppm/ppmtompeg/tst/par3.param43
-rw-r--r--converter/ppm/ppmtompeg/tst/short.param31
-rwxr-xr-xconverter/ppm/ppmtompeg/tst/test_all19
-rw-r--r--converter/ppm/ppmtompeg/tst/ts.mpgbin0 -> 40978 bytes
-rw-r--r--converter/ppm/ppmtompeg/tst/ts.param29
-rw-r--r--converter/ppm/ppmtompeg/tst/ts.stat52
-rw-r--r--converter/ppm/ppmtompeg/tst/ts/stennis.30.jpgbin0 -> 19080 bytes
-rw-r--r--converter/ppm/ppmtompeg/tst/ts/stennis.31.jpgbin0 -> 18678 bytes
-rw-r--r--converter/ppm/ppmtompeg/tst/ts/stennis.32.jpgbin0 -> 18379 bytes
-rw-r--r--converter/ppm/ppmtompeg/tst/ts/stennis.33.jpgbin0 -> 18012 bytes
-rw-r--r--converter/ppm/ppmtompeg/tst/ts/stennis.34.jpgbin0 -> 17871 bytes
-rw-r--r--converter/ppm/ppmtompeg/tst/ts/stennis.35.jpgbin0 -> 17398 bytes
-rw-r--r--converter/ppm/ppmtompeg/tst/ts/stennis.36.jpgbin0 -> 17085 bytes
-rw-r--r--converter/ppm/ppmtompeg/tst/ts/stennis.37.jpgbin0 -> 16806 bytes
-rw-r--r--converter/ppm/ppmtompeg/tst/ts/stennis.38.jpgbin0 -> 16522 bytes
-rw-r--r--converter/ppm/ppmtompeg/tst/ts/stennis.39.jpgbin0 -> 16291 bytes
-rw-r--r--converter/ppm/ppmtompeg/tst/ts2.param33
-rw-r--r--converter/ppm/ppmtompeg/tst/ts2.stat52
-rw-r--r--converter/ppm/ppmtompeg/tst/ts3.param32
-rw-r--r--converter/ppm/ppmtompeg/tst/ts4.param56
-rw-r--r--converter/ppm/ppmtompeg/tst/tsd.param32
-rw-r--r--converter/ppm/ppmtompeg/tst/tsd.stat52
-rw-r--r--converter/ppm/ppmtompeg/tst/tstl.param31
-rw-r--r--converter/ppm/ppmtoneo.c123
-rw-r--r--converter/ppm/ppmtopcx.c906
-rw-r--r--converter/ppm/ppmtopi1.c120
-rw-r--r--converter/ppm/ppmtopict.c481
-rw-r--r--converter/ppm/ppmtopj.c256
-rw-r--r--converter/ppm/ppmtopjxl.c423
-rw-r--r--converter/ppm/ppmtoppm.c44
-rw-r--r--converter/ppm/ppmtopuzz.c96
-rw-r--r--converter/ppm/ppmtorgb3.c99
-rw-r--r--converter/ppm/ppmtosixel.c215
-rw-r--r--converter/ppm/ppmtoterm.c173
-rw-r--r--converter/ppm/ppmtowinicon.c881
-rw-r--r--converter/ppm/ppmtoxpm.c659
-rw-r--r--converter/ppm/ppmtoyuv.c97
-rw-r--r--converter/ppm/ppmtoyuvsplit.c197
-rw-r--r--converter/ppm/qrttoppm.c69
-rw-r--r--converter/ppm/rawtoppm.c244
-rw-r--r--converter/ppm/rgb3toppm.c88
-rw-r--r--converter/ppm/sldtoppm.c650
-rw-r--r--converter/ppm/spctoppm.c212
-rw-r--r--converter/ppm/sputoppm.c108
-rw-r--r--converter/ppm/tgatoppm.c462
-rw-r--r--converter/ppm/vidtoppm.c270
-rw-r--r--converter/ppm/winico.h88
-rw-r--r--converter/ppm/winico.html701
-rw-r--r--converter/ppm/winicontoppm.c899
-rw-r--r--converter/ppm/xim.h125
-rw-r--r--converter/ppm/ximtoppm.c438
-rw-r--r--converter/ppm/xpmtoppm.README69
-rw-r--r--converter/ppm/xpmtoppm.c832
-rw-r--r--converter/ppm/xvminitoppm.c207
-rw-r--r--converter/ppm/yuvsplittoppm.c251
-rw-r--r--converter/ppm/yuvtoppm.c115
325 files changed, 67176 insertions, 0 deletions
diff --git a/converter/ppm/411toppm.c b/converter/ppm/411toppm.c
new file mode 100644
index 00000000..a5b25ac1
--- /dev/null
+++ b/converter/ppm/411toppm.c
@@ -0,0 +1,261 @@
+/*
+   I recently had a visit from my mom who owns a Sony Mavica camera.
+   This camera produces standard MPEG and JPEG files, but it also
+   creates 64x48 pixel thumbnails for preview/index on its own tiny
+   LCD screen.  These files are named with an extension that is
+   ".411".
+
+   Sony appears not to want to document the ".411" file format, but it
+   is clear from various web pages that it is a variant of the
+   CCIR.601 standard YUV encoding used in MPEG.  The name indicates
+   that the file content consists of chunks of 6 bytes: 4 bytes of
+   image Y values, followed by 1 bytes of U and one byte of V values
+   that apply to the previous 4 Y pixel values.
+
+   There appear to be some commercial 411 file readers on the net, and
+   there is the Java-based Javica program, but I prefer Open Source
+   command-line utilities.  So, I grabbed a copy of netpbm-9.11 from
+   SourceForge and hacked the eyuvtoppm.c file so that it looks like
+   this.  While this may not be exactly the right thing to do, it
+   produces results which are close enough for me.
+
+   There are all sorts of user-interface gotchas possible in here that
+   I'm not going to bother changing -- especially not without actual
+   documentation from Sony about what they intend to do with ".411"
+   files in the future.  I place my modifications into the public
+   domain, but I ask that my name & e-mail be mentioned in the
+   commentary of any derived version.
+
+   Steve Allen <sla@alumni.caltech.edu>, 2001-03-01
+
+   Bryan Henderson reworked the program to use the Netpbm libraries to
+   create the PPM output and follow some other Netpbm conventions.
+   2001-03-03.  Bryan's contribution is public domain.  
+*/
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.  */
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ppm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+
+typedef unsigned char uint8;
+
+#define CHOP(x)     ((x < 0) ? 0 : ((x > 255) ? 255 : x))
+
+struct cmdline_info {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *input_filespec;  /* Filespec of input file */
+    int width;
+    int height;
+};
+
+
+
+static void
+parse_command_line(int argc, char ** argv,
+                   struct cmdline_info *cmdline_p) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+
+    optStruct *option_def = malloc(100*sizeof(optStruct));
+        /* Instructions to OptParseOptions2 on how to parse our options.
+         */
+    optStruct2 opt;
+
+    unsigned int option_def_index;
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENTRY(0,   "width",      OPT_INT,    &cmdline_p->width,          0);
+    OPTENTRY(0,   "height",     OPT_INT,    &cmdline_p->height,         0);
+
+    /* Set the defaults */
+    cmdline_p->width = 64;
+    cmdline_p->height = 48;
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+    
+    optParseOptions2(&argc, argv, opt, 0);
+        /* Uses and sets argc, argv, and some of *cmdline_p and others. */
+
+    if (cmdline_p->width <= 0)
+        pm_error("-width must be positive.");
+    if (cmdline_p->height <= 0)
+        pm_error("-height must be positive.");
+
+    if (argc > 2)
+        pm_error("There is at most 1 argument: the input file spec.  "
+                 "You supplied %d", argc-1);
+    else {
+        if (argc > 1)
+            cmdline_p->input_filespec = argv[1];
+        else
+            cmdline_p->input_filespec = "-";
+    }
+}
+
+
+
+static void 
+ReadYUV(FILE *fpointer, const int width, const int height, 
+        uint8 ** const orig_y, uint8 ** const orig_cb, 
+        uint8 ** const orig_cr) {
+
+    int y, x, i;
+
+    for (y = 0; y < height; y++) {
+        for (x = 0, i = 0; x < width; x+=4, i++) {
+            fread(orig_y[y]+x , 1, 4, fpointer);
+            fread(orig_cb[y]+i, 1, 1, fpointer);
+            fread(orig_cr[y]+i, 1, 1, fpointer);
+        }
+    }
+}
+
+
+
+static void
+AllocYCC(const int width, const int height,
+         uint8 *** const orig_yP, uint8 *** const orig_crP, 
+         uint8 *** const orig_cbP) {
+    int y;
+
+    MALLOCARRAY_NOFAIL(*orig_yP, height);
+    for (y = 0; y < height; y++) 
+        MALLOCARRAY_NOFAIL((*orig_yP)[y], width);
+
+    MALLOCARRAY_NOFAIL(*orig_crP, height);
+    for (y = 0; y < height; y++) 
+        MALLOCARRAY_NOFAIL((*orig_crP)[y], width/4);
+
+    MALLOCARRAY_NOFAIL(*orig_cbP, height);
+    for (y = 0; y < height; y++) 
+        MALLOCARRAY_NOFAIL((*orig_cbP)[y], width/4);
+}
+
+
+
+static void 
+YUVtoPPM(const int width, const int height, 
+         uint8 ** const orig_y, 
+         uint8 ** const orig_cb,
+         uint8 ** const orig_cr,
+         pixel ** const ppm_image
+         ) {
+    int     **Y, **U, **V;
+    int    y;
+
+    /* first, allocate tons of memory */
+
+    MALLOCARRAY_NOFAIL(Y, height);
+    for (y = 0; y < height; y++) 
+        MALLOCARRAY_NOFAIL(Y[y], width);
+    
+    MALLOCARRAY_NOFAIL(U, height);
+    for (y = 0; y < height; y++) 
+        MALLOCARRAY_NOFAIL(U[y], width / 4);
+
+    MALLOCARRAY_NOFAIL(V, height);
+    for (y = 0; y < height; y++) 
+        MALLOCARRAY_NOFAIL(V[y], width/4);
+
+	for ( y = 0; y < height; y ++ ) {
+        int x;
+	    for ( x = 0; x < width/4; x ++ ) {
+            U[y][x] = orig_cb[y][x] - 128;
+            V[y][x] = orig_cr[y][x] - 128;
+	    }
+    }
+	for ( y = 0; y < height; y ++ ) {
+        int x;
+	    for ( x = 0; x < width; x ++ )
+	    {
+            Y[y][x] = orig_y[y][x] - 16;
+	    }
+    }
+    for ( y = 0; y < height; y++ ) {
+        int x;
+        for ( x = 0; x < width; x++ ) {
+            pixval r, g, b;
+            long   tempR, tempG, tempB;
+            /* look at yuvtoppm source for explanation */
+            
+            tempR = 104635*V[y][x/4];
+            tempG = -25690*U[y][x/4] + -53294 * V[y][x/4];
+            tempB = 132278*U[y][x/4];
+            
+            tempR += (Y[y][x]*76310);
+            tempG += (Y[y][x]*76310);
+            tempB += (Y[y][x]*76310);
+            
+            r = CHOP((int)(tempR >> 16));
+            g = CHOP((int)(tempG >> 16));
+            b = CHOP((int)(tempB >> 16));
+
+            PPM_ASSIGN(ppm_image[y][x], r, g, b);
+        }
+    }
+    /* We really should free the Y, U, and V arrays now */
+}
+
+
+
+int
+main(int argc, char **argv) {
+    FILE *infile;
+    struct cmdline_info cmdline;
+    uint8 **orig_y, **orig_cb, **orig_cr;
+    pixel **ppm_image;
+
+    ppm_init(&argc, argv);
+
+    parse_command_line(argc, argv, &cmdline);
+
+    AllocYCC(cmdline.width, cmdline.height, &orig_y, &orig_cr, &orig_cb);
+    ppm_image = ppm_allocarray(cmdline.width, cmdline.height);
+
+    pm_message("Reading (%dx%d):  %s\n", cmdline.width, cmdline.height, 
+               cmdline.input_filespec);
+    infile = pm_openr(cmdline.input_filespec);
+    ReadYUV(infile, cmdline.width, cmdline.height, orig_y, orig_cb, orig_cr);
+    pm_close(infile);
+
+    YUVtoPPM(cmdline.width, cmdline.height, orig_y, orig_cb, orig_cr, 
+             ppm_image);
+
+    ppm_writeppm(stdout, ppm_image, cmdline.width, cmdline.height, 255, 0);
+
+    ppm_freearray(ppm_image, cmdline.height);
+
+    return 0;
+}
+
diff --git a/converter/ppm/Makefile b/converter/ppm/Makefile
new file mode 100644
index 00000000..31ca826b
--- /dev/null
+++ b/converter/ppm/Makefile
@@ -0,0 +1,49 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/ppm
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+SUBDIRS = hpcdtoppm ppmtompeg
+
+PORTBINARIES =	411toppm eyuvtoppm gouldtoppm ilbmtoppm imgtoppm \
+		leaftoppm mtvtoppm neotoppm \
+		pcxtoppm pc1toppm pi1toppm picttoppm pjtoppm \
+		ppmtoacad ppmtoarbtxt \
+		ppmtobmp ppmtoeyuv ppmtogif ppmtoicr ppmtoilbm \
+		ppmtoleaf ppmtolj ppmtomitsu ppmtoneo \
+		ppmtopcx ppmtopi1 ppmtopict ppmtopj \
+		ppmtopjxl ppmtoppm ppmtopuzz ppmtorgb3 ppmtosixel ppmtoterm \
+		ppmtowinicon ppmtoxpm ppmtoyuv ppmtoyuvsplit \
+		qrttoppm rawtoppm rgb3toppm spctoppm \
+		sputoppm tgatoppm winicontoppm ximtoppm xpmtoppm xvminitoppm \
+		yuvtoppm yuvsplittoppm
+
+MATHBINARIES = sldtoppm 
+
+# We don't build vidtoppm by default, because it requires special libraries
+# and there is no known requirement for vidtoppm.
+
+
+MERGEBINARIES = $(PORTBINARIES) $(MATHBINARIES)
+NOMERGEBINARIES =
+
+BINARIES = $(MERGEBINARIES) $(NOMERGEBINARIES) 
+
+DATAFILES = pcxstd.ppm
+
+OBJECTS = $(BINARIES:%=%.o)
+
+MERGE_OBJECTS = $(BINARIES:%=%.o2)
+
+.PHONY: all
+all: $(BINARIES) $(SUBDIRS:%=%/all)
+
+include $(SRCDIR)/Makefile.common
+
+ppmtobmp.o ppmtobmp.o2: bmp.h
+
+tgatoppm.o tgatoppm.o2: tga.h
diff --git a/converter/ppm/autocad.h b/converter/ppm/autocad.h
new file mode 100644
index 00000000..3005782b
--- /dev/null
+++ b/converter/ppm/autocad.h
@@ -0,0 +1,64 @@
+#ifndef AUTOCAD_H_INCLUDED
+#define AUTOCAD_H_INCLUDED
+/* The following table maps the  256 standard AutoCAD colors into RGB
+   values with a Maxval of 255.  These colors are actually derived in
+   an  algorithmic way from the HLS color system, but it's easier and
+   faster  to  just provide a table than to compute the RGB components
+   on the fly from the color index. */
+
+static unsigned char acadcol[256][3] = {
+    {0, 0, 0}, {255, 0, 0}, {255, 255, 0}, {0, 255, 0}, {0, 255, 255},
+    {0, 0, 255}, {255, 0, 255}, {255, 255, 255}, {255, 255, 255},
+    {255, 255, 255}, {255, 0, 0}, {255, 127, 127}, {165, 0, 0}, {165, 82, 82},
+    {127, 0, 0}, {127, 63, 63}, {76, 0, 0}, {76, 38, 38}, {38, 0, 0},
+    {38, 19, 19}, {255, 63, 0}, {255, 159, 127}, {165, 41, 0}, {165, 103, 82},
+    {127, 31, 0}, {127, 79, 63}, {76, 19, 0}, {76, 47, 38}, {38, 9, 0},
+    {38, 23, 19}, {255, 127, 0}, {255, 191, 127}, {165, 82, 0}, {165, 124, 82},
+    {127, 63, 0}, {127, 95, 63}, {76, 38, 0}, {76, 57, 38}, {38, 19, 0},
+    {38, 28, 19}, {255, 191, 0}, {255, 223, 127}, {165, 124, 0},
+    {165, 145, 82}, {127, 95, 0}, {127, 111, 63}, {76, 57, 0}, {76, 66, 38},
+    {38, 28, 0}, {38, 33, 19}, {255, 255, 0}, {255, 255, 127}, {165, 165, 0},
+    {165, 165, 82}, {127, 127, 0}, {127, 127, 63}, {76, 76, 0}, {76, 76, 38},
+    {38, 38, 0}, {38, 38, 19}, {191, 255, 0}, {223, 255, 127},
+    {124, 165, 0}, {145, 165, 82}, {95, 127, 0}, {111, 127, 63}, {57, 76, 0},
+    {66, 76, 38}, {28, 38, 0}, {33, 38, 19}, {127, 255, 0}, {191, 255, 127},
+    {82, 165, 0}, {124, 165, 82}, {63, 127, 0}, {95, 127, 63}, {38, 76, 0},
+    {57, 76, 38}, {19, 38, 0}, {28, 38, 19}, {63, 255, 0}, {159, 255, 127},
+    {41, 165, 0}, {103, 165, 82}, {31, 127, 0}, {79, 127, 63}, {19, 76, 0},
+    {47, 76, 38}, {9, 38, 0}, {23, 38, 19}, {0, 255, 0}, {127, 255, 127},
+    {0, 165, 0}, {82, 165, 82}, {0, 127, 0}, {63, 127, 63}, {0, 76, 0},
+    {38, 76, 38}, {0, 38, 0}, {19, 38, 19}, {0, 255, 63}, {127, 255, 159},
+    {0, 165, 41}, {82, 165, 103}, {0, 127, 31}, {63, 127, 79}, {0, 76, 19},
+    {38, 76, 47}, {0, 38, 9}, {19, 38, 23}, {0, 255, 127}, {127, 255, 191},
+    {0, 165, 82}, {82, 165, 124}, {0, 127, 63}, {63, 127, 95}, {0, 76, 38},
+    {38, 76, 57}, {0, 38, 19}, {19, 38, 28}, {0, 255, 191}, {127, 255, 223},
+    {0, 165, 124}, {82, 165, 145}, {0, 127, 95}, {63, 127, 111}, {0, 76, 57},
+    {38, 76, 66}, {0, 38, 28}, {19, 38, 33}, {0, 255, 255}, {127, 255, 255},
+    {0, 165, 165}, {82, 165, 165}, {0, 127, 127}, {63, 127, 127}, {0, 76, 76},
+    {38, 76, 76}, {0, 38, 38}, {19, 38, 38}, {0, 191, 255}, {127, 223, 255},
+    {0, 124, 165}, {82, 145, 165}, {0, 95, 127}, {63, 111, 127}, {0, 57, 76},
+    {38, 66, 76}, {0, 28, 38}, {19, 33, 38}, {0, 127, 255}, {127, 191, 255},
+    {0, 82, 165}, {82, 124, 165}, {0, 63, 127}, {63, 95, 127}, {0, 38, 76},
+    {38, 57, 76}, {0, 19, 38}, {19, 28, 38}, {0, 63, 255}, {127, 159, 255},
+    {0, 41, 165}, {82, 103, 165}, {0, 31, 127}, {63, 79, 127}, {0, 19, 76},
+    {38, 47, 76}, {0, 9, 38}, {19, 23, 38}, {0, 0, 255}, {127, 127, 255},
+    {0, 0, 165}, {82, 82, 165}, {0, 0, 127}, {63, 63, 127}, {0, 0, 76},
+    {38, 38, 76}, {0, 0, 38}, {19, 19, 38}, {63, 0, 255}, {159, 127, 255},
+    {41, 0, 165}, {103, 82, 165}, {31, 0, 127}, {79, 63, 127}, {19, 0, 76},
+    {47, 38, 76}, {9, 0, 38}, {23, 19, 38}, {127, 0, 255}, {191, 127, 255},
+    {82, 0, 165}, {124, 82, 165}, {63, 0, 127}, {95, 63, 127}, {38, 0, 76},
+    {57, 38, 76}, {19, 0, 38}, {28, 19, 38}, {191, 0, 255}, {223, 127, 255},
+    {124, 0, 165}, {145, 82, 165}, {95, 0, 127}, {111, 63, 127}, {57, 0, 76},
+    {66, 38, 76}, {28, 0, 38}, {33, 19, 38}, {255, 0, 255}, {255, 127, 255},
+    {165, 0, 165}, {165, 82, 165}, {127, 0, 127}, {127, 63, 127}, {76, 0, 76},
+    {76, 38, 76}, {38, 0, 38}, {38, 19, 38}, {255, 0, 191}, {255, 127, 223},
+    {165, 0, 124}, {165, 82, 145}, {127, 0, 95}, {127, 63, 111}, {76, 0, 57},
+    {76, 38, 66}, {38, 0, 28}, {38, 19, 33}, {255, 0, 127}, {255, 127, 191},
+    {165, 0, 82}, {165, 82, 124}, {127, 0, 63}, {127, 63, 95}, {76, 0, 38},
+    {76, 38, 57}, {38, 0, 19}, {38, 19, 28}, {255, 0, 63}, {255, 127, 159},
+    {165, 0, 41}, {165, 82, 103}, {127, 0, 31}, {127, 63, 79},	{76, 0, 19},
+    {76, 38, 47}, {38, 0, 9}, {38, 19, 23}, {84, 84, 84}, {118, 118, 118},
+    {152, 152, 152}, {186, 186, 186}, {220, 220, 220}, {255, 255, 255}
+};
+
+#endif
diff --git a/converter/ppm/eyuvtoppm.c b/converter/ppm/eyuvtoppm.c
new file mode 100644
index 00000000..dcbb9547
--- /dev/null
+++ b/converter/ppm/eyuvtoppm.c
@@ -0,0 +1,338 @@
+/* Bryan got this from mm.ftp-cs.berkeley.edu from the package
+   mpeg-encode-1.5b-src under the name eyuvtoppm.c on March 30, 2000.  
+   The file was dated April 14, 1995.  
+
+   Bryan rewrote the program entirely to match Netpbm coding style,
+   use the Netpbm libraries and also to output to stdout and ignore
+   any specification of an output file on the command line and not
+   segfault when called with no arguments.
+
+   There was no attached documentation except for this:  Encoder/Berkeley
+   YUV format is merely the concatenation of Y, U, and V data in order.
+   Compare with Abekda YUV, which interlaces Y, U, and V data.  */
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "ppm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+
+typedef unsigned char uint8;
+
+#define CHOP(x)     ((x < 0) ? 0 : ((x > 255) ? 255 : x))
+
+
+
+struct cmdline_info {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *input_filespec;  /* Filespecs of input file */
+    unsigned int width;
+    unsigned int height;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdline_info *cmdlineP) {
+
+    optStruct3 opt;   /* Set by OPTENT3 */
+    unsigned int option_def_index;
+    optEntry *option_def = malloc(100*sizeof(optEntry));
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3('w', "width",     OPT_UINT,  &cmdlineP->width,   NULL,         0);
+    OPTENT3('h', "height",    OPT_UINT,  &cmdlineP->height,  NULL,         0);
+    
+    /* DEFAULTS */
+    cmdlineP->width = 352;
+    cmdlineP->height = 240;
+
+    opt.opt_table = option_def;
+    opt.short_allowed = TRUE;
+    opt.allowNegNum = FALSE;
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+
+    if (cmdlineP->width == 0)
+        pm_error("The width cannot be zero.");
+    if (cmdlineP->width % 2 != 0)
+        pm_error("The width of an eyuv image must be an even number.  "
+                 "You specified %u.", cmdlineP->width);
+    if (cmdlineP->height == 0)
+        pm_error("The height cannot be zero.");
+    if (cmdlineP->height % 2 != 0)
+        pm_error("The height of an eyuv image must be an even number.  "
+                 "You specified %u.", cmdlineP->height);
+
+
+    if (argc-1 == 0) 
+        cmdlineP->input_filespec = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
+    else
+        cmdlineP->input_filespec = argv[1];
+
+}
+
+
+
+static uint8 ** 
+AllocUint8Array(unsigned int const cols, unsigned int const rows) {
+
+    uint8 **retval;
+    unsigned int y;
+
+    MALLOCARRAY(retval, rows);
+    if (retval == NULL)
+        pm_error("Unable to allocate storage for %d x %d byte array.",
+                 cols, rows);
+
+    for (y = 0; y < rows; y++) {
+        MALLOCARRAY(retval[y], cols);
+        if (retval[y] == NULL)
+            pm_error("Unable to allocate storage for %d x %d byte array.",
+                     cols, rows);
+    }
+    return retval;
+}
+
+
+
+static int ** 
+AllocIntArray(unsigned int const cols, unsigned int const rows) {
+
+    int **retval;
+    unsigned int y;
+
+    MALLOCARRAY(retval, rows);
+    if (retval == NULL)
+        pm_error("Unable to allocate storage for %d x %d byte array.",
+                 cols, rows);
+
+    for (y = 0; y < rows; y++) {
+        MALLOCARRAY(retval[y], cols);
+        if (retval[y] == NULL)
+            pm_error("Unable to allocate storage for %d x %d byte array.",
+                     cols, rows);
+    }
+    return retval;
+}
+
+
+
+static void
+allocateStorage(int const cols, int const rows,
+                int *** const YP, int *** const UP, int *** const VP,
+                pixel *** const pixelsP, 
+                uint8 *** const orig_yP, uint8 *** const orig_cbP,
+                uint8 *** const orig_crP) {
+
+    *YP = AllocIntArray(cols, rows);
+    *UP = AllocIntArray(cols, rows);
+    *VP = AllocIntArray(cols, rows);
+
+    *pixelsP = ppm_allocarray(cols, rows);
+
+    *orig_yP  = AllocUint8Array(cols, rows);
+    *orig_cbP = AllocUint8Array(cols, rows);
+    *orig_crP = AllocUint8Array(cols, rows);
+}
+
+
+
+static void 
+FreeArray(void ** const array, unsigned int const rows) {
+
+    unsigned int y;
+
+    for (y = 0; y < rows; y++)
+        free(array[y]);
+    free(array);
+}
+
+
+
+static void
+freeStorage(int const rows,
+            int ** const Y, int ** const U, int ** const V,
+            pixel ** const pixels, 
+            uint8 ** const orig_y, uint8 ** const orig_cb,
+            uint8 ** const orig_cr) {
+    
+    FreeArray((void**) orig_y, rows); 
+    FreeArray((void**) orig_cb, rows); 
+    FreeArray((void**) orig_cr, rows);
+
+    ppm_freearray(pixels, rows);
+
+    FreeArray((void**) Y, rows);
+    FreeArray((void**) U, rows);
+    FreeArray((void**) V, rows);
+}
+
+
+
+static void 
+YUVtoPPM(unsigned int const cols, unsigned int const rows,
+         uint8 ** const orig_y, uint8 ** const orig_cb, uint8 ** const orig_cr,
+         pixel ** const pixels, 
+         int ** const Y, int ** const U, int ** const V) {
+/*----------------------------------------------------------------------------
+   Convert the YUV image in arrays orig_y[][], orig_cb[][], and orig_cr[][]
+   to a PPM image in the array (already allocated) pixels[][].
+
+   Use the preallocated areas Y[][], U[][], and V[][] for working space.
+-----------------------------------------------------------------------------*/
+    
+    int y;
+
+    for ( y = 0; y < rows/2; y ++ ) {
+        int x;
+        for ( x = 0; x < cols/2; x ++ ) {
+            U[y][x] = orig_cb[y][x] - 128;
+            V[y][x] = orig_cr[y][x] - 128;
+        }
+    }
+
+    for ( y = 0; y < rows; y ++ ) {
+        int x;
+        for ( x = 0; x < cols; x ++ ) 
+            Y[y][x] = orig_y[y][x] - 16;
+    }
+
+    for ( y = 0; y < rows; y++ ) {
+        int x;
+        for ( x = 0; x < cols; x++ ) {
+            long   tempR, tempG, tempB;
+            int     r, g, b;
+            /* look at yuvtoppm source for explanation */
+
+            tempR = 104635*V[y/2][x/2];
+            tempG = -25690*U[y/2][x/2] + -53294 * V[y/2][x/2];
+            tempB = 132278*U[y/2][x/2];
+
+            tempR += (Y[y][x]*76310);
+            tempG += (Y[y][x]*76310);
+            tempB += (Y[y][x]*76310);
+            
+            r = CHOP((int)(tempR >> 16));
+            g = CHOP((int)(tempG >> 16));
+            b = CHOP((int)(tempB >> 16));
+            
+            PPM_ASSIGN(pixels[y][x], r, g, b);
+        }
+    }
+}
+
+
+
+static void 
+ReadYUV(FILE * const yuvfile,
+        unsigned int const cols, unsigned int const rows,
+        uint8 ** const orig_y, 
+        uint8 ** const orig_cb, 
+        uint8 ** const orig_cr,
+        bool * const eofP) {
+
+    unsigned int y;
+    int c;
+
+    c = fgetc(yuvfile);
+    if (c < 0)
+        *eofP = TRUE;
+    else {
+        *eofP = FALSE;
+        ungetc(c, yuvfile);
+    }
+    if (!*eofP) {
+        for (y = 0; y < rows; y++)            /* Y */
+            fread(orig_y[y], 1, cols, yuvfile);
+        
+        for (y = 0; y < rows / 2; y++)            /* U */
+            fread(orig_cb[y], 1, cols / 2, yuvfile);
+        
+        for (y = 0; y < rows / 2; y++)            /* V */
+            fread(orig_cr[y], 1, cols / 2, yuvfile);
+        if (feof(yuvfile))
+            pm_error("Premature end of file reading EYUV input file");
+    }
+}
+
+
+
+int
+main(int argc, char **argv) {
+
+    FILE *ifp;
+    struct cmdline_info cmdline;
+    unsigned int frameSeq;
+
+    /* The following are addresses of malloc'ed storage areas for use by
+       subroutines.
+    */
+    int ** Y;
+    int ** U;
+    int ** V;
+    uint8 **orig_y, **orig_cb, **orig_cr;
+    pixel ** pixels;
+
+    ppm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    /* Allocate all the storage once, to save time. */
+    allocateStorage(cmdline.width, cmdline.height,
+                    &Y, &U, &V, &pixels, &orig_y, &orig_cb, &orig_cr);
+
+    ifp = pm_openr(cmdline.input_filespec);
+
+    for (frameSeq = 0; !feof(ifp); frameSeq++) {
+        bool eof;
+
+        ReadYUV(ifp, cmdline.width, cmdline.height, 
+                orig_y, orig_cb, orig_cr, &eof);
+        if (!eof) {
+            pm_message("Converting Frame %u", frameSeq);
+
+            YUVtoPPM(cmdline.width, cmdline.height, orig_y, orig_cb, orig_cr, 
+                     pixels, Y, U, V);
+            ppm_writeppm(stdout, pixels, cmdline.width, cmdline.height, 
+                         255, FALSE);
+        }
+    }
+
+    freeStorage(cmdline.height, Y, U, V, pixels, orig_y, orig_cb, orig_cr);
+
+    pm_close(ifp);
+    exit(0);
+}
+
+
+
+
+
diff --git a/converter/ppm/gouldtoppm.c b/converter/ppm/gouldtoppm.c
new file mode 100644
index 00000000..5db7c51a
--- /dev/null
+++ b/converter/ppm/gouldtoppm.c
@@ -0,0 +1,122 @@
+/* gouldtoppm.c - read GOULD imaging system files and write a portable pixmap
+**
+** Copyright (C) 1990  Stephen P. Lesniewski
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <stdio.h>
+#include "ppm.h"
+
+#define MAXVAL 255
+
+static void getgouldheader ARGS(( FILE *infile, unsigned long *cols, unsigned long *nlines, unsigned long *bytesperline, unsigned long *bitsperpixel, unsigned long *NB ));
+
+int
+main( argc, argv )
+int argc;
+char *argv[];
+{
+	FILE *ifp;
+	pixel *pixrow;
+
+	unsigned long cols, nlines, bytesperline, bitsperpixel, NB, x, y;
+	unsigned char pbuffer[4];
+
+	int color_type;
+
+
+	ppm_init( &argc, argv );
+
+	if ( argc > 2 )
+		pm_usage( "[gouldfile]" );
+
+	if ( argc == 2 )
+		ifp = pm_openr( argv[1] );
+	else
+		ifp = stdin;
+
+	getgouldheader( ifp, &cols, &nlines, &bytesperline, &bitsperpixel, &NB);
+
+	ppm_writeppminit(stdout, cols, nlines, MAXVAL, 0);
+
+	color_type = bitsperpixel/8;
+	if (color_type == 0) color_type = NB;
+
+	pixrow = ppm_allocrow(cols);
+
+	for (y = 0; y < nlines; ++y)
+	{
+		for (x = 0; x < cols; ++x)
+		{
+			switch (color_type)
+			{
+			case 0:
+				pm_error("incorrect color type" );
+
+			case 1:
+				fread(pbuffer, 1, 1, ifp);
+				PPM_ASSIGN( pixrow[x], pbuffer[0], pbuffer[0], pbuffer[0]);
+				break;
+
+			case 2:
+				fread(pbuffer, 2, 1, ifp);
+				PPM_ASSIGN( pixrow[x], pbuffer[0], pbuffer[1], pbuffer[1]);
+				break;
+
+			case 3:
+				fread(pbuffer, 3, 1, ifp);
+				PPM_ASSIGN( pixrow[x], pbuffer[0], pbuffer[1], pbuffer[2]);
+				break;
+
+			default :
+				fread(pbuffer, 3, 1, ifp);
+				PPM_ASSIGN( pixrow[x], pbuffer[0], pbuffer[1], pbuffer[2]);
+				break;
+
+			} /* switch */
+		}
+		ppm_writeppmrow(stdout, pixrow, cols, MAXVAL, 0);
+	}
+
+	pm_close(ifp);
+	pm_close(stdout);
+
+	exit(0);
+}
+
+static void
+getgouldheader( infile, cols, nlines, bytesperline, bitsperpixel, NB)
+FILE *infile;
+unsigned long *cols, *nlines, *bytesperline, *bitsperpixel, *NB;
+{
+	unsigned long nlines_new, bytesperline_new, numheaderrec;
+	unsigned char headerblk[512];
+	int i;
+
+	if (fread(headerblk, 512, 1, infile) == 0)
+		pm_error("cannot read gould header" );
+
+	*nlines       = (headerblk[3]<<8) | headerblk[2];
+	*bytesperline = (headerblk[5]<<8) | headerblk[4];
+	*bitsperpixel = (headerblk[7]<<8) | headerblk[6];
+	numheaderrec  = (headerblk[9]<<8) | headerblk[8];
+	nlines_new    = (headerblk[15]<<24)| (headerblk[14]<<16) | (headerblk[13]<<8) | (headerblk[12]);
+	bytesperline_new = (headerblk[19]<<24)| (headerblk[18]<<16) | (headerblk[17]<<8) | (headerblk[16]);
+	*NB           = (headerblk[23]<<24)| (headerblk[22]<<16) | (headerblk[21]<<8) | (headerblk[20]);
+
+	if (numheaderrec > 1)
+		for (i = 1 ; i <numheaderrec; ++i)
+			if (fread(headerblk, 512, 1, infile) == 0)
+				pm_error("cannot read gould header(2nd)" );
+
+	if (*nlines==0) *nlines=nlines_new;
+	if (*bytesperline==0) *bytesperline=bytesperline_new;
+
+	*cols = (*bytesperline)*8 / (*bitsperpixel);
+}
diff --git a/converter/ppm/hpcdtoppm/Makefile b/converter/ppm/hpcdtoppm/Makefile
new file mode 100644
index 00000000..9a7c67d0
--- /dev/null
+++ b/converter/ppm/hpcdtoppm/Makefile
@@ -0,0 +1,25 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/ppm/hpcdtoppm
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+all: hpcdtoppm
+
+SCRIPTS = hpcdtoppm pcdovtoppm
+MERGE_OBJECTS =
+
+include $(SRCDIR)/Makefile.common
+
+install: install.bin.local
+.PHONY: install.bin.local
+install.bin.local: $(PKGDIR)/bin
+# In June 2002, pcdovtoppm replaced pcdindex
+	cd $(PKGDIR)/bin ; \
+	$(SYMLINK) pcdindex$(EXE) pcdovtoppm
+
+
+FORCE:
diff --git a/converter/ppm/hpcdtoppm/README b/converter/ppm/hpcdtoppm/README
new file mode 100644
index 00000000..a1c7c8c2
--- /dev/null
+++ b/converter/ppm/hpcdtoppm/README
@@ -0,0 +1,20 @@
+This 'hpcdtoppm' directory is really just a placeholder for the real
+Hpcdtoppm source code.  
+
+The real Hpcdtoppm source code cannot be distributed on Sourceforge
+because a copyright holder does not license it in a way open enough to
+meet Sourceforge's requirements.
+
+Therefore, the Netpbm maintainer distributes Hpcdtoppm via another channel
+and distributes this dummy directory in the main Netpbm source tree on 
+Sourceforge.  When you run the program named 'hpcdtoppm' in this directory,
+it tells you how to get the real Hpcdtoppm.
+
+When you get the real Hpcdtoppm tarball, just unpack it and replace
+this entire 'hpcdtoppm' directory with its contents.  Then build
+Netpbm normally.
+
+Bear in mind when you get the real Hpcdtoppm, that it is not as openly
+licensed as what's in the rest of the Netpbm source tree.  As you'll
+see in the license that comes with Hpcdtoppm, you may _not_ sell it to
+someone else.)
diff --git a/converter/ppm/hpcdtoppm/hpcdtoppm b/converter/ppm/hpcdtoppm/hpcdtoppm
new file mode 100755
index 00000000..cb6d084b
--- /dev/null
+++ b/converter/ppm/hpcdtoppm/hpcdtoppm
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+cat <<EOF 1>&2
+You are running a program named 'hpcdtoppm' that stands in for the
+real program by that name.
+
+The real 'hpcdtoppm' is a program that converts an image from Photo CD
+format to PPM format.  The program you are running now just issues the
+message you are reading.
+
+Please get a copy of Hpcdtoppm from 
+
+  ftp://ibiblio.org/pub/linux/graphics/convert
+
+and replace this stand-in program with the real one.
+
+The point of this is that this stand-in 'hpcdtoppm' is distributed
+with Netpbm on Sourceforge.  Hpcdtoppm does not meet the requirements
+to be distributed on Sourceforge because a copyright holder does not
+permit people to sell copies.  In the past the real Hpcdtoppm was
+distributed with Netpbm on Sourceforge by mistake.
+
+EOF
+
+exit 1
\ No newline at end of file
diff --git a/converter/ppm/hpcdtoppm/pcdovtoppm b/converter/ppm/hpcdtoppm/pcdovtoppm
new file mode 100755
index 00000000..1f8b3006
--- /dev/null
+++ b/converter/ppm/hpcdtoppm/pcdovtoppm
@@ -0,0 +1,214 @@
+#!/bin/sh
+#
+# pcdovtoppm - generate a single PPM file from a PCD overview file
+#
+# Based on pnmindex (PBMPLUS), which was written by Jef Poskanzer,
+# this script makes also use of hpcdtoppm, written by Hadmut Danisch.
+#
+# Formerly called Pcdindex.
+#
+# A similar result can be achieved by using "hpcdtoppm -Overview"
+# followed by "pnmindex -black" on the generated PPM images.
+# This shell just makes it more convenient and transparent to
+# convert from one PCD to one PPM overview file.
+#
+# Additional options (compared to pnmindex) are -maxwidth and
+# -font <font>. See "man pbmtext" on how to create your own font.
+#
+# Pieter S. van der Meulen, 1992.
+# Rewritten in sh by Steve McIntyre <93sam@debian.org>, 2001
+
+# You may want to change the default values in the next 6 lines:
+maxwidth=1152   # maximum width of the index image
+size=192                # make the images about this big
+across=6                # show this many images per row
+colors="noquant"        # maximum amount of colors or noquant (no quantization)
+back="-black"   # default background color
+font=" "                # default font or none (pbmtext's internal font)
+
+usage ()
+{
+    echo "Usage: $0 [-m W] [-s S] [-a A] [-c N|n] [-f F] [-b|-w] <overview.pcd>"
+    echo " with"
+    echo "      W = maximum width of the result image   (default: $maxwidth)"
+    echo "      S = maximum size of each of the images  (default: $size)"
+    echo "      A = maximum number of images across     (default: $across)"
+    echo "      N = maximum number of colors or noquant (default: $colors)"
+    echo -n "   F = font to be used for annotation      (default: "
+    if [ "$font" = " " ] ; then
+                echo "internal font)"
+    else
+                echo "$font)"
+    fi
+    echo "      -b/-w = black/white background color    (default: $back)"
+    echo " "
+    echo " e.g.: $0 -m 768 -s 96 -f smallfont.pbm overview.pcd > overview.ppm"
+    echo " or  : $0 /cdrom/photo_cd/overview.pcd | ppmtojpeg > overview.jpg"
+    exit 1
+}
+
+# Parse the options
+while :; do
+    case "$1" in 
+        -m*)
+             if [ $# -lt 2 ] ; then usage; fi
+             maxwidth="$2"
+             shift
+             shift
+             ;;
+
+        -s*)
+             if [ $# -lt 2 ] ; then usage; fi
+             size="$2"
+             shift
+             shift
+             ;;
+
+            -a*)
+                if [ $# -lt 2 ] ; then usage; fi
+                across="$2"
+                shift
+                shift
+                ;;
+
+            -c*)
+                if [ $# -lt 2 ] ; then usage; fi
+                colors="$2"
+                shift
+                shift
+                ;;
+
+            -f*)
+                if [ $# -lt 2 ] ; then usage; fi
+                font="-font $2"
+                shift
+                shift
+                ;;
+
+            -b*)
+                back="-black"
+                shift
+                ;;
+
+            -w*)
+                back="-white"
+                shift
+                ;;
+
+            -*)
+                echo "$0 : Unknown option $1"
+                echo " "
+                usage
+                ;;
+
+            *)
+                break
+                ;;
+
+    esac
+done
+
+if [ $# = 0 ]; then
+    usage
+fi
+
+tmpfile=`tempfile -p pi -m 600`
+
+rowfiles=()
+imagefiles=()
+row=1
+col=1
+width=$size
+
+# Convert the PCD overview file to many PPM images
+if [ -f $1 ] ; then
+    hpcdtoppm -Overview $1 $tmpfile
+else
+    echo "$0 : Could not access $1"
+    echo " "
+    usage
+fi
+
+for i in "$tmpfile"* 
+do
+    if [ -f $i ] ; then
+        description=`pnmfile $i`
+        if [ "${description[4]}" -le $size -a \
+             "${description[6]}" -le $size ] ; then
+            cat $i > $tmpfile
+                else
+            if [ "$colors" = "n" ] ; then
+                pnmscale -quiet -xysize $size $size $i > $tmpfile
+            else
+                pnmscale -quiet -xysize $size $size $i | \
+                ppmquant -quiet $colors > $tmpfile
+            fi
+        fi
+    fi
+    imagefile=pi.${row}.${col}.$$
+    rm -f $imagefile
+    ttext="$i:t"
+
+    if [ "$back" = "-white" ] ; then
+        pbmtext $font "$ttext" | pnmcrop -quiet | pnmmargin -white 2| \
+        pnmcat $back -tb $tmpfile - > $imagefile
+    else
+        pbmtext $font "$ttext" | pnmcrop -quiet | pnmmargin -white 2 | \
+        pnminvert | pnmcat $back -tb $tmpfile - > $imagefile
+    fi
+
+    rm -f $tmpfile
+    description=`pnmfile $imagefile`
+    width=$(( $width + ${description[4]} ))
+    imagefiles="$imagefiles $imagefile"
+
+    if [ $col -ge $across -o $width -gt $maxwidth ] ; then
+        rowfile=pi.${row}.$$
+        rm -f $rowfile
+    if [ "$colors" = "n" ] ; then
+        pnmcat $back -lr -jbottom $imagefiles > $rowfile
+    else
+        pnmcat $back -lr -jbottom $imagefiles | \
+        ppmquant -quiet $colors > $rowfile
+    fi
+    rm -f $imagefiles
+    imagefiles=()
+    rowfiles="$rowfiles $rowfile"
+    col=1
+    row=$(( $row + 1 ))
+    width=$size
+    else
+        col=$(( $col + 1 ))
+    fi
+done
+
+if [ ${#imagefiles[*]} -gt 0 ] ; then
+    rowfile=pi.${row}.$$
+    rm -f $rowfile
+    if [ "$colors" = "n" ] ; then
+        pnmcat $back -lr -jbottom $imagefiles > $rowfile
+    else
+        pnmcat $back -lr -jbottom $imagefiles | \
+        ppmquant -quiet $colors > $rowfile
+    fi
+    rm -f $imagefiles
+    rowfiles="$rowfiles $rowfile"
+fi
+
+if [ ${#rowfiles[*]} == 1 ]; then
+    cat $rowfiles
+else
+    if [ "$colors" = "n" ] ; then
+        pnmcat $back -tb $rowfiles
+    else
+        pnmcat $back -tb $rowfiles | ppmquant -quiet $colors
+    fi
+fi
+
+rm -f $rowfiles
+
+exit 0
+
+
+
+
diff --git a/converter/ppm/ilbm.h b/converter/ppm/ilbm.h
new file mode 100644
index 00000000..68657956
--- /dev/null
+++ b/converter/ppm/ilbm.h
@@ -0,0 +1,256 @@
+#ifndef ILBM_H_INCLUDED
+#define ILBM_H_INCLUDED
+
+/* ilbm.h - definitions for IFF ILBM files */
+
+#define RowBytes(cols)          ((((cols) + 15) / 16) * 2)
+
+
+/* definitions for BMHD */
+
+typedef struct {
+    unsigned short w, h;
+    short x, y;
+    unsigned char nPlanes, masking, compression, flags;
+    unsigned short transparentColor;
+    unsigned char xAspect, yAspect;
+    short pageWidth, pageHeight;
+} BitMapHeader;
+#define BitMapHeaderSize    20
+
+#define BMHD_FLAGS_CMAPOK       (1<<7)      /* valid 8bit colormap */
+
+#define mskNone                 0
+#define mskHasMask              1
+#define mskHasTransparentColor  2
+#define mskLasso                3       /* not supported */
+#define mskMAXKNOWN             mskLasso
+static const char * mskNAME[] = { 
+    "none", "mask plane", "transparent color", "lasso" 
+};
+
+#define cmpNone                 0
+#define cmpByteRun1             1
+#define cmpMAXKNOWN             cmpByteRun1
+static const char * cmpNAME[] = { "none", "byterun1" };
+
+
+/* definitions for CMAP */
+
+#if 0   /* not used */
+typedef struct {
+    unsigned char r, g, b;
+} ColorRegister;
+#endif
+
+
+/* definitions for CAMG */
+
+#define CAMGChunkSize       4
+
+#define vmLACE              0x0004
+#define vmEXTRA_HALFBRITE   0x0080
+#define vmHAM               0x0800
+#define vmHIRES             0x8000
+
+#define HAMCODE_CMAP      0     /* look up color in colormap */
+#define HAMCODE_BLUE      1     /* new blue component */
+#define HAMCODE_RED       2     /* new red component */
+#define HAMCODE_GREEN     3     /* new green component */
+
+
+/* multipalette PCHG chunk definitions */
+
+/* get number of longwords in line mask from PCHG.LineCount */
+#define MaskLongWords(x)    (((x) + 31) / 32)
+
+typedef struct {
+    unsigned short  Compression;
+    unsigned short  Flags;
+    short           StartLine;      /* may be negative */
+    unsigned short  LineCount;
+    unsigned short  ChangedLines;
+    unsigned short  MinReg;
+    unsigned short  MaxReg;
+    unsigned short  MaxChanges;
+    unsigned long   TotalChanges;
+} PCHGHeader;
+#define PCHGHeaderSize      20
+
+/* Compression modes */
+#define PCHG_COMP_NONE      0
+#define PCHG_COMP_HUFFMAN   1
+
+/* Flags */
+#define PCHGF_12BIT         (1 << 0)    /* use SmallLineChanges */
+#define PCHGF_32BIT         (1 << 1)    /* use BigLineChanges */
+#define PCHGF_USE_ALPHA     (1 << 2)    /* meaningful only if PCHG_32BIT is on:
+                                           use the Alpha channel info */
+typedef struct {
+    unsigned long   CompInfoSize;
+    unsigned long   OriginalDataSize;
+} PCHGCompHeader;
+#define PCHGCompHeaderSize  8
+
+#if 0   /* not used */
+typedef struct {
+    unsigned char   ChangeCount16;
+    unsigned char   ChangeCount32;
+    unsigned short  *PaletteChange;
+} SmallLineChanges;
+
+typedef struct {
+    unsigned short  Register;
+    unsigned char   Alpha, Red, Blue, Green;    /* ARBG, not ARGB */
+} BigPaletteChange;
+
+typedef struct {
+    unsigned short      ChangeCount;
+    BigPaletteChange    *PaletteChange;
+} BigLineChanges;
+#endif /* 0 */
+
+
+/* definitions for CLUT */
+
+#if 0 /* not used */
+typedef struct {
+    unsigned long type;
+    unsigned long reserved0;
+    unsigned char lut[256];
+} ColorLUT;
+#endif /* 0 */
+#define CLUTSize    (256+4+4)
+
+/* types */
+#define CLUT_MONO   0
+#define CLUT_RED    1
+#define CLUT_GREEN  2
+#define CLUT_BLUE   3
+#define CLUT_HUE    4   /* not supported */
+#define CLUT_SAT    5   /* not supported */
+
+
+/* unofficial DCOL chunk for direct-color */
+
+typedef struct {
+    unsigned char r, g, b, pad1;
+} DirectColor;
+#define DirectColorSize     4
+
+
+
+/* IFF chunk IDs */
+
+typedef unsigned long   IFF_ID;
+
+#define MAKE_ID(a, b, c, d) \
+    ((IFF_ID)(a)<<24 | (IFF_ID)(b)<<16 | (IFF_ID)(c)<<8 | (IFF_ID)(d))
+
+#define ID_FORM     MAKE_ID('F', 'O', 'R', 'M')     
+    /* EA IFF 85 group identifier */
+#define ID_CAT      MAKE_ID('C', 'A', 'T', ' ')     
+    /* EA IFF 85 group identifier */
+#define ID_LIST     MAKE_ID('L', 'I', 'S', 'T')     
+    /* EA IFF 85 group identifier */
+#define ID_PROP     MAKE_ID('P', 'R', 'O', 'P')     
+    /* EA IFF 85 group identifier */
+#define ID_END      MAKE_ID('E', 'N', 'D', ' ')     
+    /* unofficial END-of-FORM identifier (see Amiga RKM Devices Ed.3
+       page 376) */
+#define ID_ILBM     MAKE_ID('I', 'L', 'B', 'M')     
+    /* EA IFF 85 raster bitmap form */
+#define ID_DEEP     MAKE_ID('D', 'E', 'E', 'P')     
+    /* Chunky pixel image files (Used in TV Paint) */
+#define ID_RGB8     MAKE_ID('R', 'G', 'B', '8')     
+    /* RGB image forms, Turbo Silver (Impulse) */
+#define ID_RGBN     MAKE_ID('R', 'G', 'B', 'N')     
+    /* RGB image forms, Turbo Silver (Impulse) */
+#define ID_PBM      MAKE_ID('P', 'B', 'M', ' ')     
+    /* 256-color chunky format (DPaint 2 ?) */
+#define ID_ACBM     MAKE_ID('A', 'C', 'B', 'M')     
+    /* Amiga Contiguous Bitmap (AmigaBasic) */
+
+/* generic */
+
+#define ID_FVER     MAKE_ID('F', 'V', 'E', 'R')     
+    /* AmigaOS version string */
+#define ID_JUNK     MAKE_ID('J', 'U', 'N', 'K')     
+    /* always ignore this chunk */
+#define ID_ANNO     MAKE_ID('A', 'N', 'N', 'O')     
+    /* EA IFF 85 Generic Annotation chunk */
+#define ID_AUTH     MAKE_ID('A', 'U', 'T', 'H')     
+    /* EA IFF 85 Generic Author chunk */
+#define ID_CHRS     MAKE_ID('C', 'H', 'R', 'S')     
+    /* EA IFF 85 Generic character string chunk */
+#define ID_NAME     MAKE_ID('N', 'A', 'M', 'E')     
+    /* EA IFF 85 Generic Name of art, music, etc. chunk */
+#define ID_TEXT     MAKE_ID('T', 'E', 'X', 'T')     
+    /* EA IFF 85 Generic unformatted ASCII text chunk */
+#define ID_copy     MAKE_ID('(', 'c', ')', ' ')     
+/* EA IFF 85 Generic Copyright text chunk */
+
+/* ILBM chunks */
+
+#define ID_BMHD     MAKE_ID('B', 'M', 'H', 'D')     
+    /* ILBM BitmapHeader */
+#define ID_CMAP     MAKE_ID('C', 'M', 'A', 'P')     
+    /* ILBM 8bit RGB colormap */
+#define ID_GRAB     MAKE_ID('G', 'R', 'A', 'B')     
+    /* ILBM "hotspot" coordiantes */
+#define ID_DEST     MAKE_ID('D', 'E', 'S', 'T')     
+    /* ILBM destination image info */
+#define ID_SPRT     MAKE_ID('S', 'P', 'R', 'T')     
+    /* ILBM sprite identifier */
+#define ID_CAMG     MAKE_ID('C', 'A', 'M', 'G')     
+    /* Amiga viewportmodes */
+#define ID_BODY     MAKE_ID('B', 'O', 'D', 'Y')     
+    /* ILBM image data */
+#define ID_CRNG     MAKE_ID('C', 'R', 'N', 'G')     
+    /* color cycling */
+#define ID_CCRT     MAKE_ID('C', 'C', 'R', 'T')     
+    /* color cycling */
+#define ID_CLUT     MAKE_ID('C', 'L', 'U', 'T')     
+    /* Color Lookup Table chunk */
+#define ID_DPI      MAKE_ID('D', 'P', 'I', ' ')     
+    /* Dots per inch chunk */
+#define ID_DPPV     MAKE_ID('D', 'P', 'P', 'V')     
+    /* DPaint perspective chunk (EA) */
+#define ID_DRNG     MAKE_ID('D', 'R', 'N', 'G')     
+    /* DPaint IV enhanced color cycle chunk (EA) */
+#define ID_EPSF     MAKE_ID('E', 'P', 'S', 'F')     
+    /* Encapsulated Postscript chunk */
+#define ID_CMYK     MAKE_ID('C', 'M', 'Y', 'K')     
+    /* Cyan, Magenta, Yellow, & Black color map (Soft-Logik) */
+#define ID_CNAM     MAKE_ID('C', 'N', 'A', 'M')     
+    /* Color naming chunk (Soft-Logik) */
+#define ID_PCHG     MAKE_ID('P', 'C', 'H', 'G')     
+    /* Line by line palette control information (Sebastiano Vigna) */
+#define ID_PRVW     MAKE_ID('P', 'R', 'V', 'W')     
+    /* A mini duplicate ILBM used for preview (Gary Bonham) */
+#define ID_XBMI     MAKE_ID('X', 'B', 'M', 'I')     
+    /* eXtended BitMap Information (Soft-Logik) */
+#define ID_CTBL     MAKE_ID('C', 'T', 'B', 'L')     
+    /* Newtek Dynamic Ham color chunk */
+#define ID_DYCP     MAKE_ID('D', 'Y', 'C', 'P')     
+    /* Newtek Dynamic Ham chunk */
+#define ID_SHAM     MAKE_ID('S', 'H', 'A', 'M')     
+    /* Sliced HAM color chunk */
+#define ID_ABIT     MAKE_ID('A', 'B', 'I', 'T')     
+    /* ACBM body chunk */
+#define ID_DCOL     MAKE_ID('D', 'C', 'O', 'L')     
+    /* unofficial direct color */
+#define ID_DPPS     MAKE_ID('D', 'P', 'P', 'S')
+    /* ? */
+#define ID_TINY     MAKE_ID('T', 'I', 'N', 'Y')
+    /* ? */
+
+/* other stuff */
+
+#define MAXPLANES       16
+typedef unsigned short  rawtype;
+
+#define MAXCMAPCOLORS   (1 << MAXPLANES)
+#define MAXCOLVAL       255     /* max value of color component */
+
+#endif
diff --git a/converter/ppm/ilbmtoppm.c b/converter/ppm/ilbmtoppm.c
new file mode 100644
index 00000000..f9f9bac3
--- /dev/null
+++ b/converter/ppm/ilbmtoppm.c
@@ -0,0 +1,2381 @@
+/* ilbmtoppm.c - read an IFF ILBM file and produce a portable pixmap
+**
+** Copyright (C) 1989 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+**
+** Modified by Mark Thompson on 10/4/90 to accomodate 24-bit IFF files
+** as used by ASDG, NewTek, etc.
+**
+** Modified by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
+**  20/Jun/93:
+**  - row-by-row operation
+**  - better de-interleave algorithm
+**  - colormap files
+**  - direct color
+**  04/Oct/93:
+**  - multipalette capability (PCHG chunk)
+**  - options -ignore, -isham, -isehb and -adjustcolors
+**  22/May/94:
+**  - minor change: check first for 24 planes, then for HAM
+**  21/Sep/94:
+**  - write mask plane to a file if -maskfile option used
+**  - write colormap file
+**  - added sliced HAM/dynamic HAM/dynamic Hires multipalette formats (SHAM, CTBL chunk)
+**  - added color lookup tables (CLUT chunk)
+**  - major rework of colormap/multipalette handling
+**  - now uses numeric IFF IDs
+**  24/Oct/94:
+**  - transparentColor capability
+**  - added RGBN/RGB8 image types
+**  - 24-bit & direct color modified to n-bit deep ILBM
+**  22/Feb/95:
+**  - direct color (DCOL) reimplemented
+**  29/Mar/95
+**  - added IFF-PBM format
+*/
+
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "ppm.h"
+#include "ilbm.h"
+#include "mallocvar.h"
+
+/*#define DEBUG*/
+
+typedef struct {
+    int reg;            /* color register to change */
+    pixval r, g, b;     /* new colors for register */
+} PaletteChange;
+
+typedef struct {
+    pixel *color;
+    int    ncolors;
+    /* lookup tables */
+    unsigned char *redlut;
+    unsigned char *greenlut;
+    unsigned char *bluelut;
+    unsigned char *monolut;
+    /* multipalette stuff */
+    PaletteChange *mp_init;
+    PaletteChange **mp_change;
+    int mp_rows;                /* # of rows in change array */
+    int mp_type;                /* see below, higher types preferred */
+    int mp_flags;
+    IFF_ID  mp_id;
+} ColorMap;
+
+#define HAS_COLORMAP(cmap)      ((cmap) && (cmap)->color)
+#define HAS_COLORLUT(cmap)      ((cmap) && ((cmap)->redlut || (cmap)->greenlut || (cmap)->bluelut))
+#define HAS_MONOLUT(cmap)       ((cmap) && (cmap)->monolut)
+#define HAS_MULTIPALETTE(cmap)  (HAS_COLORMAP(cmap) && (cmap)->mp_type)
+#define MP_TYPE_SHAM        1
+#define MP_TYPE_CTBL        2
+#define MP_TYPE_PCHG        3
+#define MP_REG_IGNORE       -1
+#define MP_REG_END          -2
+#define MP_FLAGS_SKIPLACED   (1<<0)
+
+#define FACTOR_4BIT     17      /* scale factor maxval 15 -> maxval 255 */
+
+static short verbose = 0;
+static short adjustcolors = 0;
+static unsigned char *ilbmrow;
+static pixel *pixelrow;
+static FILE *maskfile = NULL;
+static bit *maskrow = NULL;
+static short wrotemask = 0;
+static IFF_ID typeid;       /* ID_ILBM, ID_RGBN, ID_RGB8 */
+
+static pixel *transpColor = NULL;       /* transparent color */
+static short transpIndex = -1;
+static char *transpName = NULL;
+
+static bool debug = FALSE;
+
+
+
+static char *
+ID2string(id)
+    IFF_ID id;
+{
+    static char str[] = "abcd";
+
+    str[0] = (char)(id >> 24 & 0xff);
+    str[1] = (char)(id >> 16 & 0xff);
+    str[2] = (char)(id >>  8 & 0xff);
+    str[3] = (char)(id >>  0 & 0xff);
+
+    return str;
+}
+
+
+/****************************************************************************
+ Memory allocation
+ ****************************************************************************/
+static ColorMap *
+alloc_cmap(void) {
+
+    ColorMap * cmap;
+
+    MALLOCVAR_NOFAIL(cmap);
+
+    cmap->color     = NULL;
+    cmap->ncolors   = 0;
+    cmap->monolut   = NULL;
+    cmap->redlut    = NULL;
+    cmap->greenlut  = NULL;
+    cmap->bluelut   = NULL;
+    cmap->mp_init   = NULL;
+    cmap->mp_change = NULL;
+    cmap->mp_rows   = 0;
+    cmap->mp_type   = 0;
+    cmap->mp_flags  = 0;
+
+    return cmap;
+}
+
+
+
+static rawtype *
+alloc_rawrow(cols)
+    int cols;
+{
+    rawtype *r;
+    int i;
+
+    MALLOCARRAY_NOFAIL(r, cols);
+
+    for( i = 0; i < cols; i++ )
+        r[i] = 0;
+
+    return r;
+}
+
+
+/****************************************************************************
+ Basic I/O functions
+ ****************************************************************************/
+
+static void
+readerr(f, iffid)
+    FILE *f;
+    IFF_ID iffid;
+{
+    if( ferror(f) )
+        pm_error("read error");
+    else
+        pm_error("premature EOF in %s chunk", ID2string(iffid));
+}
+
+
+static void
+read_bytes(FILE *          const ifP,
+           int             const bytes,
+           unsigned char * const buffer,
+           IFF_ID          const iffid,
+           unsigned long * const counterP) {
+
+    if (counterP) {
+        if (*counterP < bytes)
+            pm_error("insufficient data in %s chunk", ID2string(iffid));
+        *counterP -= bytes;
+    }
+    if (fread(buffer, 1, bytes, ifP) != bytes)
+        readerr(ifP, iffid);
+}
+
+
+
+static unsigned char
+get_byte(ifp, iffid, counter)
+    FILE* ifp;
+    IFF_ID iffid;
+    long *counter;
+{
+    int i;
+
+    if( counter ) {
+        if( *counter == 0 )
+            pm_error("insufficient data in %s chunk", ID2string(iffid));
+        --(*counter);
+    }
+    i = getc(ifp);
+    if( i == EOF )
+        readerr(ifp, iffid);
+
+    return (unsigned char) i;
+}
+
+static long
+get_big_long(FILE *          const ifP,
+             IFF_ID          const iffid,
+             unsigned long * const counterP) {
+
+    long l;
+    
+    if (counterP) {
+        if (*counterP < 4)
+            pm_error("insufficient data in %s chunk", ID2string(iffid));
+        *counterP -= 4;
+    }
+    if (pm_readbiglong(ifP, &l) == -1)
+        readerr(ifP, iffid);
+
+    return l;
+}
+
+
+
+static short
+get_big_short(FILE *          const ifP,
+              IFF_ID          const iffid,
+              unsigned long * const counterP) {
+
+    short s;
+
+    if (counterP) {
+        if (*counterP < 2)
+            pm_error("insufficient data in %s chunk", ID2string(iffid));
+        *counterP -= 2;
+    }
+    if (pm_readbigshort(ifP, &s) == -1)
+        readerr(ifP, iffid);
+
+    return s;
+}
+
+
+
+/****************************************************************************
+ Chunk reader
+ ****************************************************************************/
+
+static void
+chunk_end(FILE *        const ifP,
+          IFF_ID        const iffid,
+          unsigned long const chunksize) {
+
+    if (chunksize > 0) {
+        unsigned long remainingChunksize;
+        pm_message("warning - %ld extraneous byte%s in %s chunk",
+                    chunksize, (chunksize == 1 ? "" : "s"), ID2string(iffid));
+        remainingChunksize = chunksize;  /* initial value */
+        while (remainingChunksize > 0)
+            get_byte(ifP, iffid, &remainingChunksize);
+    }
+}
+
+
+
+static void
+skip_chunk(FILE *        const ifP,
+           IFF_ID        const iffid,
+           unsigned long const chunksize) {
+    unsigned long remainingChunksize;
+
+    remainingChunksize = chunksize;  /* initial value */
+
+    while (remainingChunksize > 0)
+        get_byte(ifP, iffid, &remainingChunksize);
+}
+
+
+
+static void
+display_chunk(FILE *        const ifP,
+              IFF_ID        const iffid,
+              unsigned long const chunksize) {
+
+    int byte;
+    unsigned long remainingChunksize;
+
+    pm_message("contents of %s chunk:", ID2string(iffid));
+
+    remainingChunksize = chunksize;  /* initial value */
+    byte = '\0';
+
+    while (remainingChunksize > 0) {
+        byte = get_byte(ifP, iffid, &remainingChunksize);
+        if (fputc(byte, stderr) == EOF)
+            pm_error("write error");
+    }
+    if (byte != '\n')
+        if (fputc('\n', stderr) == EOF)
+            pm_error("write error");
+}
+
+
+static void
+read_cmap(FILE *     const ifp,
+          IFF_ID     const iffid,
+          long       const chunksize,
+          ColorMap * const cmap) {
+
+    long colors;
+
+    colors = chunksize / 3;
+    if( colors == 0 ) {
+        pm_error("warning - empty %s colormap", ID2string(iffid));
+        skip_chunk(ifp, iffid, chunksize);
+    } else {
+        unsigned int i;
+        if( cmap->color )               /* prefer CMAP-chunk over CMYK-chunk */
+            ppm_freerow(cmap->color);
+        cmap->color = ppm_allocrow(colors);
+        cmap->ncolors = colors;
+        
+        for( i = 0; i < colors; ++i ) {
+            int r, g, b;
+            r = get_byte(ifp, iffid, &chunksize);
+            g = get_byte(ifp, iffid, &chunksize);
+            b = get_byte(ifp, iffid, &chunksize);
+            PPM_ASSIGN(cmap->color[i], r, g, b);
+        }
+        chunk_end(ifp, iffid, chunksize);
+    }
+}
+
+
+
+static void
+read_cmyk(FILE *     const ifp,
+          IFF_ID     const iffid,
+          long       const chunksize,
+          ColorMap * const cmap) {
+
+    if( HAS_COLORMAP(cmap) ) {      /* prefer RGB color map */
+        skip_chunk(ifp, iffid, chunksize);
+    } else {
+        long const colors = chunksize/4;
+        if( colors == 0 ) {
+            pm_error("warning - empty %s colormap", ID2string(iffid));
+            skip_chunk(ifp, iffid, chunksize);
+        } else {
+            unsigned int i;
+            cmap->color = ppm_allocrow(colors);
+            cmap->ncolors = colors;
+            
+            for( i = 0; i < colors; ++i ) {
+                int c, m, y, k;
+                c = get_byte(ifp, iffid, &chunksize);
+                m = get_byte(ifp, iffid, &chunksize);
+                y = get_byte(ifp, iffid, &chunksize);
+                k = get_byte(ifp, iffid, &chunksize);
+
+                {
+                    pixval const red = 
+                        MAXCOLVAL - MIN(MAXCOLVAL,
+                                        c*(MAXCOLVAL-k)/MAXCOLVAL+k); 
+                    pixval const green = 
+                        MAXCOLVAL - MIN(MAXCOLVAL,
+                                        m*(MAXCOLVAL-k)/MAXCOLVAL+k);
+                    pixval const blue = 
+                        MAXCOLVAL - MIN(MAXCOLVAL,
+                                        y*(MAXCOLVAL-k)/MAXCOLVAL+k);
+
+                    PPM_ASSIGN(cmap->color[i], red, green, blue);
+                }
+            }
+            chunk_end(ifp, iffid, chunksize);
+        }
+    }
+}
+
+
+
+static void
+read_clut(FILE *        const ifp,
+          IFF_ID        const iffid,
+          unsigned long const chunksize,
+          ColorMap *    const cmap) {
+
+    if (chunksize != CLUTSize) {
+        pm_message("invalid size for %s chunk - skipping it", 
+                   ID2string(iffid));
+        skip_chunk(ifp, iffid, chunksize);
+    } else {
+        long type;
+        unsigned char * lut;
+        unsigned long remainingChunksize;
+        unsigned int i;
+
+        type = get_big_long(ifp, iffid, &remainingChunksize);
+        get_big_long(ifp, iffid, &remainingChunksize); /* skip reserved fld */
+
+        MALLOCARRAY_NOFAIL(lut, 256);
+        for( i = 0; i < 256; ++i )
+            lut[i] = get_byte(ifp, iffid, &remainingChunksize);
+
+        switch( type ) {
+        case CLUT_MONO:
+            cmap->monolut  = lut;
+            break;
+        case CLUT_RED:
+            cmap->redlut   = lut;
+            break;
+        case CLUT_GREEN:
+            cmap->greenlut = lut;
+            break;
+        case CLUT_BLUE:
+            cmap->bluelut  = lut;
+            break;
+        default:
+            pm_message("warning - %s type %ld not recognized",
+                       ID2string(iffid), type);
+            free(lut);
+        }
+    }
+}
+
+
+
+static BitMapHeader *
+read_bmhd(FILE *        const ifP,
+          IFF_ID        const iffid,
+          unsigned long const chunksize) {
+
+    BitMapHeader * bmhdP;
+
+    if( chunksize != BitMapHeaderSize ) {
+        pm_message("invalid size for %s chunk - skipping it", 
+                   ID2string(iffid));
+        skip_chunk(ifP, iffid, chunksize);
+        bmhdP = NULL;
+    } else {
+        unsigned long remainingChunksize;
+
+        MALLOCVAR_NOFAIL(bmhdP);
+
+        remainingChunksize = chunksize;  /* initial value */
+        
+        bmhdP->w = get_big_short(ifP, iffid, &remainingChunksize);
+        bmhdP->h = get_big_short(ifP, iffid, &remainingChunksize);
+        bmhdP->x = get_big_short(ifP, iffid, &remainingChunksize);
+        bmhdP->y = get_big_short(ifP, iffid, &remainingChunksize);
+        bmhdP->nPlanes = get_byte(ifP, iffid, &remainingChunksize);
+        bmhdP->masking = get_byte(ifP, iffid, &remainingChunksize);
+        bmhdP->compression = get_byte(ifP, iffid, &remainingChunksize);
+        bmhdP->flags = get_byte(ifP, iffid, &remainingChunksize);
+        bmhdP->transparentColor =
+            get_big_short(ifP, iffid, &remainingChunksize);
+        bmhdP->xAspect = get_byte(ifP, iffid, &remainingChunksize);
+        bmhdP->yAspect = get_byte(ifP, iffid, &remainingChunksize);
+        bmhdP->pageWidth  = get_big_short(ifP, iffid, &remainingChunksize);
+        bmhdP->pageHeight = get_big_short(ifP, iffid, &remainingChunksize);
+
+        if( verbose ) {
+            if( typeid == ID_ILBM )
+                pm_message("dimensions: %dx%d, %d planes", 
+                           bmhdP->w, bmhdP->h, bmhdP->nPlanes);
+            else
+                pm_message("dimensions: %dx%d", bmhdP->w, bmhdP->h);
+
+            if( typeid == ID_ILBM || typeid == ID_PBM ) {
+                pm_message("compression: %s",
+                           bmhdP->compression <= cmpMAXKNOWN ?
+                           cmpNAME[bmhdP->compression] : "unknown");
+
+                switch( bmhdP->masking ) {
+                case mskNone:
+                    break;
+                case mskHasMask:
+                case mskHasTransparentColor:
+                    if( !maskfile )
+                        pm_message("use '-maskfile <filename>' "
+                                   "to generate a PBM mask file from %s", 
+                                   mskNAME[bmhdP->masking]);
+                    break;
+                case mskLasso:
+                    pm_message("warning - masking type '%s' not recognized", 
+                               mskNAME[bmhdP->masking]);
+                    break;
+                default:
+                    pm_error("unknown masking type %d", bmhdP->masking);
+                }
+            }
+            else    /* RGBN/RGB8 */
+                if( !maskfile )
+                    pm_message("use '-maskfile <filename>' "
+                               "to generate a PBM mask file "
+                               "from genlock bits");
+        }
+
+        /* fix aspect ratio */
+        if( bmhdP->xAspect == 0 || bmhdP->yAspect == 0 ) {
+            pm_message("warning - illegal aspect ratio %d:%d, using 1:1", 
+                       bmhdP->xAspect, bmhdP->yAspect);
+            bmhdP->xAspect = bmhdP->yAspect = 1;
+        }
+
+        if( bmhdP->xAspect != bmhdP->yAspect ) {
+            pm_message("warning - non-square pixels; "
+                       "to fix do a 'pnmscale -%cscale %g'",
+                       bmhdP->xAspect > bmhdP->yAspect ? 'x' : 'y',
+                       bmhdP->xAspect > bmhdP->yAspect ? 
+                       (float)(bmhdP->xAspect)/bmhdP->yAspect : 
+                       (float)(bmhdP->yAspect)/bmhdP->xAspect);
+        }
+    }
+    return bmhdP;
+}
+
+
+/****************************************************************************
+ ILBM functions
+ ****************************************************************************/
+
+
+static void
+read_ilbm_plane(FILE *          const ifP,
+                unsigned long * const remainingChunksizeP,
+                int             const bytes,
+                int             const compression) {
+
+    unsigned char *ubp;
+    int j, byte;
+    int bytesRemaining;
+
+    bytesRemaining = bytes;  /* initial value */
+
+    switch(compression) {
+        case cmpNone:
+            read_bytes(ifP, bytesRemaining, ilbmrow,
+                       ID_BODY, remainingChunksizeP);
+            break;
+        case cmpByteRun1:
+            ubp = ilbmrow;
+            do {
+                byte = (int)get_byte(ifP, ID_BODY, remainingChunksizeP);
+                if( byte <= 127 ) {
+                    j = byte;
+                    bytesRemaining -= (j+1);
+                    if( bytesRemaining < 0 )
+                        pm_error("error doing ByteRun1 decompression");
+                    for( ; j >= 0; j-- )
+                        *ubp++ = get_byte(ifP, ID_BODY, remainingChunksizeP);
+                }
+                else
+                if ( byte != 128 ) {
+                    j = 256 - byte;
+                    bytesRemaining -= (j+1);
+                    if( bytesRemaining < 0 )
+                        pm_error("error doing ByteRun1 decompression");
+                    byte = (int)get_byte(ifP, ID_BODY, remainingChunksizeP);
+                    for( ; j >= 0; j-- )
+                        *ubp++ = (unsigned char)byte;
+                }
+                /* 128 is a NOP */
+            }
+            while( bytesRemaining > 0 );
+            break;
+        default:
+            pm_error("unknown compression type %d", compression);
+    }
+}
+
+
+static const unsigned char bit_mask[] = {1, 2, 4, 8, 16, 32, 64, 128};
+
+static void
+decode_row(FILE *          const ifP,
+           unsigned long * const remainingChunksizeP,
+           rawtype *       const chunkyrow,
+           int             const nPlanes,
+           BitMapHeader *  const bmhdP) {
+
+    int plane, col, cols, cbit, bytes;
+    unsigned char *ilp;
+    rawtype *chp;
+
+    cols = bmhdP->w;
+    bytes = RowBytes(cols);
+    for( plane = 0; plane < nPlanes; plane++ ) {
+        int mask;
+
+        mask = 1 << plane;
+        read_ilbm_plane(ifP, remainingChunksizeP, bytes, bmhdP->compression);
+
+        ilp = ilbmrow;
+        chp = chunkyrow;
+
+        cbit = 7;
+        for( col = 0; col < cols; col++, cbit--, chp++ ) {
+            if( cbit < 0 ) {
+                cbit = 7;
+                ilp++;
+            }
+            if( *ilp & bit_mask[cbit] )
+                *chp |= mask;
+            else
+                *chp &= ~mask;
+        }
+    }
+}
+
+
+static void
+decode_mask(FILE *          const ifP,
+            unsigned long * const remainingChunksizeP,
+            rawtype *       const chunkyrow,
+            BitMapHeader *  const bmhdP) {
+
+    int col, cols, cbit;
+    unsigned char *ilp;
+
+    cols = bmhdP->w;
+    switch( bmhdP->masking ) {
+    case mskNone:
+        break;
+    case mskHasMask:        /* mask plane */
+        read_ilbm_plane(ifP, remainingChunksizeP, RowBytes(cols), 
+                        bmhdP->compression);
+        if( maskfile ) {
+            ilp = ilbmrow;
+            cbit = 7;
+            for( col = 0; col < cols; col++, cbit-- ) {
+                if( cbit < 0 ) {
+                    cbit = 7;
+                    ilp++;
+                }
+                if( *ilp & bit_mask[cbit] )
+                    maskrow[col] = PBM_BLACK;
+                else
+                    maskrow[col] = PBM_WHITE;
+            }
+            pbm_writepbmrow(maskfile, maskrow, cols, 0);
+            wrotemask = 1;
+        }
+        break;
+    case mskHasTransparentColor:
+        if( !chunkyrow )
+            pm_error("decode_mask(): chunkyrow == NULL - can't happen");
+        
+        if( maskfile ) {
+            for( col = 0; col < cols; col++ ) {
+                if( chunkyrow[col] == bmhdP->transparentColor )
+                    maskrow[col] = PBM_WHITE;
+                else
+                    maskrow[col] = PBM_BLACK;
+            }
+            pbm_writepbmrow(maskfile, maskrow, cols, 0);
+                wrotemask = 1;
+        }
+        break;
+    case mskLasso:
+        pm_error("This program does not know how to process Lasso masking");
+        break;
+    default:
+        pm_error("decode_mask(): unknown masking type %d - can't happen", 
+                 bmhdP->masking);
+    }
+}
+
+
+/****************************************************************************
+ Multipalette handling
+ ****************************************************************************/
+
+
+static void
+multi_adjust(cmap, row, palchange)
+    ColorMap *cmap;
+    int row;
+    PaletteChange *palchange;
+{
+    int i, reg;
+
+    for( i = 0; palchange[i].reg != MP_REG_END; i++ ) {
+        reg = palchange[i].reg;
+        if( reg >= cmap->ncolors ) {
+            pm_message("warning - palette change register out of range");
+            pm_message("    row %d  change structure %d  reg=%d (max %d)", 
+                       row, i, reg, cmap->ncolors-1);
+            pm_message("    ignoring it...  "
+                       "colors might get messed up from here");
+        }
+        else
+        if( reg != MP_REG_IGNORE ) {
+            PPM_ASSIGN(cmap->color[reg], 
+                       palchange[i].r, palchange[i].g, palchange[i].b);
+        }
+    }
+}
+
+static void
+multi_init(cmap, viewportmodes)
+    ColorMap *cmap;
+    long viewportmodes;
+{
+    if( cmap->mp_init )
+        multi_adjust(cmap, -1, cmap->mp_init);
+    if( !(viewportmodes & vmLACE) )
+        cmap->mp_flags &= ~(MP_FLAGS_SKIPLACED);
+}
+
+static void
+multi_update(cmap, row)
+    ColorMap *cmap;
+    int row;
+{
+    if( cmap->mp_flags & MP_FLAGS_SKIPLACED ) {
+        if( ODD(row) )
+            return;
+        if( row/2 < cmap->mp_rows && cmap->mp_change[row/2] )
+            multi_adjust(cmap, row, cmap->mp_change[row/2]);
+    }
+    else {
+        if( row < cmap->mp_rows && cmap->mp_change[row] )
+            multi_adjust(cmap, row, cmap->mp_change[row]);
+    }
+}
+
+static void
+multi_free(cmap)
+    ColorMap *cmap;
+{
+    int i;
+
+    if( cmap->mp_init ) {
+        free(cmap->mp_init);
+        cmap->mp_init = NULL;
+    }
+    if( cmap->mp_change ) {
+        for( i = 0; i < cmap->mp_rows; i++ ) {
+            if( cmap->mp_change[i] )
+                free(cmap->mp_change[i]);
+        }
+        free(cmap->mp_change);
+        cmap->mp_change = NULL;
+    }
+    cmap->mp_rows = 0;
+    cmap->mp_type = 0;
+    cmap->mp_flags = 0;
+}
+
+
+/****************************************************************************
+ Colormap handling
+ ****************************************************************************/
+
+static void
+check_cmap(bmhd, cmap)
+    BitMapHeader *bmhd;
+    ColorMap *cmap;
+{
+    pixval colmaxval = 0;
+    int shifted = 1;
+    int i, r, g, b;
+
+    if( bmhd ) {
+        if( bmhd->masking == mskHasTransparentColor || 
+            bmhd->masking == mskLasso ) {
+            transpIndex = bmhd->transparentColor;
+            if( !transpName ) {
+                MALLOCVAR_NOFAIL(transpColor);
+                if( transpIndex >= cmap->ncolors ) {
+                    pm_message("using default transparent color (black)");
+                    PPM_ASSIGN(*transpColor, 0, 0, 0);
+                }
+                else
+                    *transpColor = cmap->color[transpIndex];
+            }
+        }
+
+        if( bmhd->flags & BMHD_FLAGS_CMAPOK )
+            return;
+    }
+
+    if( !HAS_COLORMAP(cmap) )
+        return;
+
+    for( i = 0; i < cmap->ncolors; i++ ) {
+        r = PPM_GETR(cmap->color[i]);
+        if( r > colmaxval ) colmaxval = r;
+        if( r & 0x0f ) shifted = 0;
+
+        g = PPM_GETG(cmap->color[i]);
+        if( g > colmaxval ) colmaxval = g;
+        if( g & 0x0f ) shifted = 0;
+
+        b = PPM_GETB(cmap->color[i]);
+        if( b > colmaxval ) colmaxval = b;
+        if( b & 0x0f ) shifted = 0;
+    }
+
+#ifdef DEBUG
+    pm_message("colormap maxval is %d", colmaxval);
+#endif
+    if( colmaxval == 0 )
+        pm_message("warning - black colormap");
+    else
+    if( shifted || colmaxval <= 15 ) {
+        if( !adjustcolors ) {
+            pm_message("warning - probably %s4-bit colormap",
+                        shifted ? "shifted " : "");
+            pm_message("    use '-adjustcolors' to scale colormap to 8 bits");
+        }
+        else {
+            pm_message("scaling colormap to 8 bits");
+            for( i = 0; i < cmap->ncolors; i++ ) {
+                r = PPM_GETR(cmap->color[i]);
+                g = PPM_GETG(cmap->color[i]);
+                b = PPM_GETB(cmap->color[i]);
+                if( shifted ) {
+                    r >>= 4;
+                    g >>= 4;
+                    b >>= 4;
+                }
+                r *= FACTOR_4BIT;
+                g *= FACTOR_4BIT;
+                b *= FACTOR_4BIT;
+                PPM_ASSIGN(cmap->color[i], r, g, b);
+            }
+        }
+    }
+}
+
+
+static pixval
+lookup_red(cmap, oldval)
+    ColorMap *cmap;
+    int oldval;
+{
+    if( cmap && cmap->redlut && oldval < 256 )
+        return cmap->redlut[oldval];
+    else
+        return oldval;
+}
+
+static pixval
+lookup_green(cmap, oldval)
+    ColorMap *cmap;
+    int oldval;
+{
+    if( cmap && cmap->greenlut && oldval < 256 )
+        return cmap->greenlut[oldval];
+    else
+        return oldval;
+}
+
+static pixval
+lookup_blue(cmap, oldval)
+    ColorMap *cmap;
+    int oldval;
+{
+    if( cmap && cmap->bluelut && oldval < 256 )
+        return cmap->bluelut[oldval];
+    else
+        return oldval;
+}
+
+static pixval
+lookup_mono(cmap, oldval)
+    ColorMap *cmap;
+    int oldval;
+{
+    if( cmap && cmap->monolut && oldval < 256 )
+        return cmap->monolut[oldval];
+    else
+        return oldval;
+}
+
+static ColorMap *
+ehbcmap(cmap)
+    ColorMap *cmap;
+{
+    pixel *tempcolor = NULL;
+    int i, col;
+
+    col = cmap->ncolors;
+
+    tempcolor = ppm_allocrow(col * 2);
+    for( i = 0; i < col; i++ ) {
+        tempcolor[i] = cmap->color[i];
+        PPM_ASSIGN(tempcolor[col + i],  PPM_GETR(cmap->color[i]) / 2,
+                                        PPM_GETG(cmap->color[i]) / 2,
+                                        PPM_GETB(cmap->color[i]) / 2 );
+    }
+    ppm_freerow(cmap->color);
+    cmap->color = tempcolor;
+    cmap->ncolors *= 2;
+
+    return cmap;
+}
+
+
+
+static pixval
+lut_maxval(ColorMap * const cmap, 
+           pixval     const maxval) {
+
+    pixval retval;
+    
+    if (maxval >= 255)
+        retval = maxval;
+    else {
+        if (!HAS_COLORLUT(cmap))
+            retval = maxval;
+        else {
+            unsigned int i;
+            unsigned char maxlut;
+            maxlut = maxval;
+            for( i = 0; i < maxval; i++ ) {
+                if( cmap->redlut   && cmap->redlut[i]   > maxlut ) 
+                    maxlut = cmap->redlut[i];
+                if( cmap->greenlut && cmap->greenlut[i] > maxlut ) 
+                    maxlut = cmap->greenlut[i];
+                if( cmap->bluelut  && cmap->bluelut[i]  > maxlut ) 
+                    maxlut = cmap->bluelut[i];
+            }
+            pm_message("warning - "
+                       "%d-bit index into 8-bit color lookup table, "
+                       "table maxval=%d", 
+                       pm_maxvaltobits(maxval), maxlut);
+            if( maxlut != maxval )
+                retval = 255;
+            else
+                retval = maxval;
+            pm_message("    assuming image maxval=%d", retval);
+        }
+    }
+    return retval;
+}
+
+
+
+static void
+get_color(cmap, idx, red, green, blue)
+    ColorMap *cmap;
+    int idx;
+    pixval *red, *green, *blue;
+{
+    if( HAS_COLORMAP(cmap) ) {
+        pixval r, g, b;
+
+        if( idx >= cmap->ncolors )
+            pm_error("color index out of range: %d (max %d)", 
+                     idx, cmap->ncolors);
+        r = PPM_GETR(cmap->color[idx]);
+        g = PPM_GETG(cmap->color[idx]);
+        b = PPM_GETB(cmap->color[idx]);
+
+        *red    = lookup_red(cmap, r);
+        *green  = lookup_green(cmap, g);
+        *blue   = lookup_blue(cmap, b);
+    }
+    else {
+        *red = *green = *blue = lookup_mono(cmap, idx);
+    }
+}
+
+
+/****************************************************************************
+ Conversion functions
+ ****************************************************************************/
+
+static void
+std_to_ppm(FILE *         const ifp, 
+           long           const chunksize, 
+           BitMapHeader * const bmhd, 
+           ColorMap *     const cmap, 
+           long           const viewportmodes);
+
+static void
+ham_to_ppm(FILE *         const ifp, 
+           long           const chunksize, 
+           BitMapHeader * const bmhd, 
+           ColorMap *     const cmap, 
+           long           const viewportmodes) {
+
+    int cols, rows, hambits, hammask, hamshift, hammask2, col, row;
+    pixval maxval;
+    rawtype *rawrow;
+    unsigned char hamlut[256];
+
+    cols = bmhd->w;
+    rows = bmhd->h;
+    hambits = bmhd->nPlanes - 2;
+    hammask = (1 << hambits) - 1;
+    hamshift = 8 - hambits;
+    hammask2 = (1 << hamshift) - 1;
+
+    if( hambits > 8 || hambits < 1 ) {
+        int const assumed_viewportmodes = viewportmodes & ~(vmHAM);
+
+        pm_message("%d-plane HAM?? - interpreting image as a normal ILBM", 
+                   bmhd->nPlanes);
+        std_to_ppm(ifp, chunksize, bmhd, cmap, assumed_viewportmodes);
+        return;
+    } else {
+        unsigned long remainingChunksize;
+
+        pm_message("input is a %sHAM%d file", 
+                   HAS_MULTIPALETTE(cmap) ? "multipalette " : "", 
+                   bmhd->nPlanes);
+
+        if( HAS_COLORLUT(cmap) || HAS_MONOLUT(cmap) ) {
+            pm_message("warning - color lookup tables ignored in HAM");
+            if( cmap->redlut )      free(cmap->redlut);
+            if( cmap->greenlut )    free(cmap->greenlut);
+            if( cmap->bluelut )     free(cmap->bluelut);
+            if( cmap->monolut )     free(cmap->monolut);
+            cmap->redlut = cmap->greenlut = cmap->bluelut = 
+                cmap->monolut = NULL;
+        }
+        if( !HAS_COLORMAP(cmap) ) {
+            pm_message("no colormap - interpreting values as grayscale");
+            maxval = pm_bitstomaxval(hambits);
+            for( col = 0; col <= maxval; col++ )
+                hamlut[col] = col * MAXCOLVAL / maxval;
+            cmap->monolut = hamlut;
+        }
+
+        if( transpName ) {
+            MALLOCVAR_NOFAIL(transpColor);
+            *transpColor = ppm_parsecolor(transpName, MAXCOLVAL);
+        }
+
+        if( HAS_MULTIPALETTE(cmap) )
+            multi_init(cmap, viewportmodes);
+
+        rawrow = alloc_rawrow(cols);
+
+        ppm_writeppminit(stdout, cols, rows, MAXCOLVAL, 0);
+
+        remainingChunksize = chunksize;  /* initial value */
+
+        for (row = 0; row < rows; ++row) {
+            pixval r, g, b;
+
+            if( HAS_MULTIPALETTE(cmap) )
+                multi_update(cmap, row);
+
+            decode_row(ifp, &remainingChunksize, rawrow, bmhd->nPlanes, bmhd);
+            decode_mask(ifp, &remainingChunksize, rawrow, bmhd);
+
+            r = g = b = 0;
+            for( col = 0; col < cols; col++ ) {
+                int idx = rawrow[col] & hammask;
+
+                if( transpColor && maskrow && maskrow[col] == PBM_WHITE )
+                    pixelrow[col] = *transpColor;
+                else {
+                    switch((rawrow[col] >> hambits) & 0x03) {
+                    case HAMCODE_CMAP:
+                        get_color(cmap, idx, &r, &g, &b);
+                        break;
+                    case HAMCODE_BLUE:
+                        b = ((b & hammask2) | (idx << hamshift));
+                        /*b = hamlut[idx];*/
+                        break;
+                    case HAMCODE_RED:
+                        r = ((r & hammask2) | (idx << hamshift));
+                        /*r = hamlut[idx];*/
+                        break;
+                    case HAMCODE_GREEN:
+                        g = ((g & hammask2) | (idx << hamshift));
+                        /*g = hamlut[idx];*/
+                        break;
+                    default:
+                        pm_error("ham_to_ppm(): "
+                                 "impossible HAM code - can't happen");
+                    }
+                    PPM_ASSIGN(pixelrow[col], r, g, b);
+                }
+            }
+            ppm_writeppmrow(stdout, pixelrow, cols, MAXCOLVAL, 0);
+        }
+        chunk_end(ifp, ID_BODY, remainingChunksize);
+    }
+}
+
+
+
+static void
+std_to_ppm(FILE *         const ifP, 
+           long           const chunksize, 
+           BitMapHeader * const bmhd, 
+           ColorMap *     const cmap, 
+           long           const viewportmodes) {
+
+    if (viewportmodes & vmHAM) {
+        ham_to_ppm(ifP, chunksize, bmhd, cmap, viewportmodes);
+    } else {
+        unsigned int const cols = bmhd->w;
+        unsigned int const rows = bmhd->h;
+
+        rawtype *rawrow;
+        unsigned int row, col;
+        pixval maxval;
+        unsigned long remainingChunksize;
+
+        pm_message("input is a %d-plane %s%sILBM", bmhd->nPlanes,
+                   HAS_MULTIPALETTE(cmap) ? "multipalette " : "",
+                   viewportmodes & vmEXTRA_HALFBRITE ? "EHB " : ""
+            );
+
+        if( bmhd->nPlanes > MAXPLANES )
+            pm_error("too many planes (max %d)", MAXPLANES);
+
+        if( HAS_COLORMAP(cmap) ) {
+            if( viewportmodes & vmEXTRA_HALFBRITE )
+                ehbcmap(cmap);  /* Modifies *cmap */
+            maxval = MAXCOLVAL;
+        }
+        else {
+            pm_message("no colormap - interpreting values as grayscale");
+            maxval = lut_maxval(cmap, pm_bitstomaxval(bmhd->nPlanes));
+            if( maxval > PPM_OVERALLMAXVAL )
+                pm_error("nPlanes is too large");
+        }
+
+        if( transpName ) {
+            MALLOCVAR_NOFAIL(transpColor);
+            *transpColor = ppm_parsecolor(transpName, maxval);
+        }
+
+        rawrow = alloc_rawrow(cols);
+
+        if( HAS_MULTIPALETTE(cmap) )
+            multi_init(cmap, viewportmodes);
+
+        ppm_writeppminit( stdout, cols, rows, maxval, 0 );
+
+        remainingChunksize = chunksize;  /* initial value */
+    
+        for (row = 0; row < rows; ++row) {
+
+            if( HAS_MULTIPALETTE(cmap) )
+                multi_update(cmap, row);
+
+            decode_row(ifP, &remainingChunksize, rawrow, bmhd->nPlanes, bmhd);
+            decode_mask(ifP, &remainingChunksize, rawrow, bmhd);
+
+            for( col = 0; col < cols; col++ ) {
+                pixval r, g, b;
+                if( transpColor && maskrow && maskrow[col] == PBM_WHITE )
+                    pixelrow[col] = *transpColor;
+                else {
+                    get_color(cmap, rawrow[col], &r, &g, &b);
+                    PPM_ASSIGN(pixelrow[col], r, g, b);
+                }
+            }
+            ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0);
+        }
+        chunk_end(ifP, ID_BODY, remainingChunksize);
+    }
+}
+
+
+
+static void
+deep_to_ppm(FILE *         const ifP, 
+            long           const chunksize, 
+            BitMapHeader * const bmhdP, 
+            ColorMap *     const cmap) {
+
+    unsigned int const cols = bmhdP->w;
+    unsigned int const rows = bmhdP->h;
+    unsigned int const planespercolor = bmhdP->nPlanes / 3;
+
+    unsigned int col, row;
+    rawtype *Rrow, *Grow, *Brow;
+    pixval maxval;
+    unsigned long remainingChunksize;
+
+    pm_message("input is a deep (%d-bit) ILBM", bmhdP->nPlanes);
+    if( planespercolor > MAXPLANES )
+        pm_error("too many planes (max %d)", MAXPLANES * 3);
+
+    if( bmhdP->masking == mskHasTransparentColor || 
+        bmhdP->masking == mskLasso ) {
+        pm_message("masking type '%s' in a deep ILBM?? - ignoring it", 
+                   mskNAME[bmhdP->masking]);
+        bmhdP->masking = mskNone;
+    }
+
+    maxval = lut_maxval(cmap, pm_bitstomaxval(planespercolor));
+    if( maxval > PPM_OVERALLMAXVAL )
+        pm_error("nPlanes is too large");
+
+    if( transpName ) {
+        MALLOCVAR_NOFAIL(transpColor);
+        *transpColor = ppm_parsecolor(transpName, maxval);
+    }
+
+    Rrow = alloc_rawrow(cols);
+    Grow = alloc_rawrow(cols);
+    Brow = alloc_rawrow(cols);
+
+    ppm_writeppminit(stdout, cols, rows, maxval, 0);
+
+    remainingChunksize = chunksize;  /* initial value */
+
+    for( row = 0; row < rows; row++ ) {
+        decode_row(ifP, &remainingChunksize, Rrow, planespercolor, bmhdP);
+        decode_row(ifP, &remainingChunksize, Grow, planespercolor, bmhdP);
+        decode_row(ifP, &remainingChunksize, Brow, planespercolor, bmhdP);
+        decode_mask(ifP, &remainingChunksize, NULL, bmhdP);
+
+        for( col = 0; col < cols; col++ ) {
+            if( transpColor && maskrow && maskrow[col] == PBM_WHITE )
+                pixelrow[col] = *transpColor;
+            else
+                PPM_ASSIGN(pixelrow[col],   lookup_red(cmap, Rrow[col]),
+                                            lookup_green(cmap, Grow[col]),
+                                            lookup_blue(cmap, Brow[col]) );
+        }
+        ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0);
+    }
+    chunk_end(ifP, ID_BODY, remainingChunksize);
+}
+
+
+
+static void
+dcol_to_ppm(FILE *         const ifP,
+            long           const chunksize,
+            BitMapHeader * const bmhdP,
+            ColorMap *     const cmap,
+            DirectColor *  const dcol) {
+
+    unsigned int const cols = bmhdP->w;
+    unsigned int const rows = bmhdP->h;
+    unsigned int const redplanes   = dcol->r;
+    unsigned int const greenplanes = dcol->g;
+    unsigned int const blueplanes  = dcol->b;
+    
+    int col, row, i;
+    rawtype *Rrow, *Grow, *Brow;
+    pixval maxval, redmaxval, greenmaxval, bluemaxval;
+    pixval *redtable, *greentable, *bluetable;
+    unsigned long remainingChunksize;
+
+    pm_message("input is a %d:%d:%d direct color ILBM",
+                redplanes, greenplanes, blueplanes);
+
+    if( redplanes > MAXPLANES || 
+        blueplanes > MAXPLANES || 
+        greenplanes > MAXPLANES )
+        pm_error("too many planes (max %d per color component)", MAXPLANES);
+
+    if( bmhdP->nPlanes != (redplanes + greenplanes + blueplanes) )
+        pm_error("%s/%s plane number mismatch", 
+                 ID2string(ID_BMHD), ID2string(ID_DCOL));
+
+    if( bmhdP->masking == mskHasTransparentColor || 
+        bmhdP->masking == mskLasso ) {
+        pm_message("masking type '%s' in a direct color ILBM?? - ignoring it",
+                   mskNAME[bmhdP->masking]);
+        bmhdP->masking = mskNone;
+    }
+
+    if( HAS_COLORLUT(cmap) ) {
+        pm_error("This program does not know how to process a %s chunk "
+                 "in direct color", ID2string(ID_CLUT));
+        cmap->redlut = cmap->greenlut = cmap->bluelut = NULL;
+    }
+
+    redmaxval   = pm_bitstomaxval(redplanes);
+    greenmaxval = pm_bitstomaxval(greenplanes);
+    bluemaxval  = pm_bitstomaxval(blueplanes);
+    maxval = MAX(redmaxval, MAX(greenmaxval, bluemaxval));
+
+    if( maxval > PPM_OVERALLMAXVAL )
+        pm_error("too many planes");
+
+    if( redmaxval != maxval || greenmaxval != maxval || bluemaxval != maxval )
+        pm_message("scaling colors to %d bits", pm_maxvaltobits(maxval));
+    
+    MALLOCARRAY_NOFAIL(redtable,   redmaxval   +1);
+    MALLOCARRAY_NOFAIL(greentable, greenmaxval +1);
+    MALLOCARRAY_NOFAIL(bluetable,  bluemaxval  +1);
+
+    for( i = 0; i <= redmaxval; i++ )
+        redtable[i] = (i * maxval + redmaxval/2)/redmaxval;
+    for( i = 0; i <= greenmaxval; i++ )
+        greentable[i] = (i * maxval + greenmaxval/2)/greenmaxval;
+    for( i = 0; i <= bluemaxval; i++ )
+        bluetable[i] = (i * maxval + bluemaxval/2)/bluemaxval;
+
+    if( transpName ) {
+        MALLOCVAR_NOFAIL(transpColor);
+        *transpColor = ppm_parsecolor(transpName, maxval);
+    }
+
+    Rrow = alloc_rawrow(cols);
+    Grow = alloc_rawrow(cols);
+    Brow = alloc_rawrow(cols);
+
+    ppm_writeppminit(stdout, cols, rows, maxval, 0);
+
+    remainingChunksize = chunksize;  /* initial value */
+
+    for( row = 0; row < rows; row++ ) {
+        decode_row(ifP, &remainingChunksize, Rrow, redplanes, bmhdP);
+        decode_row(ifP, &remainingChunksize, Grow, greenplanes, bmhdP);
+        decode_row(ifP, &remainingChunksize, Brow, blueplanes, bmhdP);
+        decode_mask(ifP, &remainingChunksize, NULL, bmhdP);
+
+        for( col = 0; col < cols; col++ ) {
+            if( transpColor && maskrow && maskrow[col] == PBM_WHITE )
+                pixelrow[col] = *transpColor;
+            else
+                PPM_ASSIGN( pixelrow[col],  redtable[Rrow[col]],
+                                            greentable[Grow[col]],
+                                            bluetable[Brow[col]]   );
+        }
+        ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0);
+    }
+    chunk_end(ifP, ID_BODY, remainingChunksize);
+}
+
+
+
+static void
+cmap_to_ppm(cmap)
+    ColorMap *cmap;
+{
+    ppm_colorrowtomapfile(stdout, cmap->color, cmap->ncolors, MAXCOLVAL);
+#if 0
+    int i;
+    ppm_writeppminit(stdout, cmap->ncolors, 1, MAXCOLVAL, 1);
+    for( i = 0; i < cmap->ncolors; i++ )
+        ppm_writeppmrow(stdout, &(cmap->color[i]), 1, MAXCOLVAL, 1);
+#endif
+}
+
+
+
+static void
+ipbm_to_ppm(FILE *         const ifP,
+            long           const chunksize,
+            BitMapHeader * const bmhdP,
+            ColorMap *     const cmap,
+            long           const viewportmodes) {
+
+    unsigned int const cols = bmhdP->w;
+    unsigned int const rows = bmhdP->h;
+
+    int col, row;
+    pixval maxval;
+    unsigned long remainingChunksize;
+
+    pm_message("input is a %sPBM ", 
+               HAS_MULTIPALETTE(cmap) ? "multipalette " : "");
+
+    if( bmhdP->nPlanes != 8 )
+        pm_error("invalid number of planes for IFF-PBM: %d (must be 8)", 
+                 bmhdP->nPlanes);
+
+    if( bmhdP->masking == mskHasMask )
+        pm_error("Image has a maskplane, which is invalid in IFF-PBM");
+
+    if( HAS_COLORMAP(cmap) )
+        maxval = MAXCOLVAL;
+    else {
+        pm_message("no colormap - interpreting values as grayscale");
+        maxval = lut_maxval(cmap, pm_bitstomaxval(bmhdP->nPlanes));
+    }
+
+    if( transpName ) {
+        MALLOCVAR_NOFAIL(transpColor);
+        *transpColor = ppm_parsecolor(transpName, maxval);
+    }
+
+    if( HAS_MULTIPALETTE(cmap) )
+        multi_init(cmap, viewportmodes);
+
+    MALLOCARRAY_NOFAIL(ilbmrow, cols);
+
+    ppm_writeppminit(stdout, cols, rows, maxval, 0);
+
+    remainingChunksize = chunksize;  /* initial value */
+
+    for( row = 0; row < rows; row++ ) {
+        if( HAS_MULTIPALETTE(cmap) )
+            multi_update(cmap, row);
+        
+        read_ilbm_plane(ifP, &remainingChunksize, cols, bmhdP->compression);
+
+        for( col = 0; col < cols; col++ ) {
+            pixval r, g, b;
+            if( transpColor && maskrow && maskrow[col] == PBM_WHITE )
+                pixelrow[col] = *transpColor;
+            else {
+                get_color(cmap, ilbmrow[col], &r, &g, &b);
+                PPM_ASSIGN(pixelrow[col], r, g, b);
+            }
+        }
+        ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0);
+    }
+    chunk_end(ifP, ID_BODY, remainingChunksize);
+}
+
+
+
+static void
+rgbn_to_ppm(FILE *         const ifP,
+            long           const chunksize,
+            BitMapHeader * const bmhdP,
+            ColorMap *     const cmap  /* for CLUTs */
+    ) {
+
+    unsigned int const rows = bmhdP->h;
+    unsigned int const cols = bmhdP->w;
+
+    int row, col, count, genlock, tries;
+    pixval r, g, b, maxval;
+    unsigned long remainingChunksize;
+
+    pm_message("input is a %d-bit RGB image", (typeid == ID_RGB8 ? 8 : 4));
+
+    if( bmhdP->compression != 4 )
+        pm_error("invalid compression mode for %s: %d (must be 4)", 
+                 ID2string(typeid), bmhdP->compression);
+
+    switch( typeid ) {
+        case ID_RGBN:
+            if( bmhdP->nPlanes != 13 )
+                pm_error("invalid number of planes for %s: %d (must be 13)", 
+                         ID2string(typeid), bmhdP->nPlanes);
+            maxval = lut_maxval(cmap, 15);
+            break;
+        case ID_RGB8:
+            if( bmhdP->nPlanes != 25 )
+                pm_error("invalid number of planes for %s: %d (must be 25)", 
+                         ID2string(typeid), bmhdP->nPlanes);
+            maxval = 255;
+            break;
+        default:
+            pm_error("rgbn_to_ppm(): invalid IFF ID %s - can't happen", 
+                     ID2string(typeid));
+    }
+
+    if( transpName ) {
+        MALLOCVAR_NOFAIL(transpColor);
+        *transpColor = ppm_parsecolor(transpName, maxval);
+    }
+
+    ppm_writeppminit(stdout, cols, rows, maxval, 0);
+
+    remainingChunksize = chunksize;  /* initial value */
+    count = 0;
+    for( row = 0; row < rows; row++ ) {
+        for( col = 0; col < cols; col++ ) {
+            tries = 0;
+            while( !count ) {
+                if( typeid == ID_RGB8 ) {
+                    r = lookup_red(cmap,   get_byte(ifP, ID_BODY, 
+                                                    &remainingChunksize));
+                    g = lookup_green(cmap, get_byte(ifP, ID_BODY,
+                                                    &remainingChunksize));
+                    b = lookup_blue(cmap,  get_byte(ifP, ID_BODY,
+                                                    &remainingChunksize));
+                    count = get_byte(ifP, ID_BODY, &remainingChunksize);
+                    genlock = count & 0x80;
+                    count &= 0x7f;
+                }
+                else {
+                    int word;
+                    word = get_big_short(ifP, ID_BODY, &remainingChunksize);
+                    r = lookup_red(cmap, (word & 0xf000) >> 12);
+                    g = lookup_green(cmap, (word & 0x0f00) >> 8);
+                    b = lookup_blue(cmap, (word & 0x00f0) >> 4);
+                    genlock = word & 0x0008;
+                    count = word & 0x0007;
+                }
+                if( !count ) {
+                    count = get_byte(ifP, ID_BODY, &remainingChunksize);
+                    if( !count )
+                        count =
+                            get_big_short(ifP, ID_BODY, &remainingChunksize);
+                        if( !count )
+                            ++tries;
+                }
+            }
+            if( tries ) {
+                pm_message("warning - repeat count 0 at col %d row %d: "
+                           "skipped %d RGB entr%s",
+                            col, row, tries, (tries == 1 ? "y" : "ies"));
+            }
+            if( maskfile ) {
+                /* genlock bit set -> transparent */
+                if( genlock )
+                    maskrow[col] = PBM_WHITE;
+                else
+                    maskrow[col] = PBM_BLACK;
+            }
+            if( transpColor && maskrow && maskrow[col] == PBM_WHITE )
+                pixelrow[col] = *transpColor;
+            else
+                PPM_ASSIGN(pixelrow[col], r, g, b);
+            --count;
+        }
+        ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0);
+        if( maskfile ) {
+            pbm_writepbmrow(maskfile, maskrow, cols, 0);
+            wrotemask = 1;
+        }
+    }
+    chunk_end(ifP, ID_BODY, remainingChunksize);
+}
+
+/****************************************************************************
+ Multipalette chunk reader
+
+    Currently there are three multipalette formats:
+        SHAM - sliced HAM (obselete)
+        CTBL - dynamic HAM/Hires (obselete)
+        PCHG - palette change
+    There is no official documentation available for SHAM and CTBL, so
+   this is mostly guesswork from other sources and hexdumps of pictures...
+
+    CTBL format (dynamic HAM/Hires):
+        16 bigendian words per row (16 bit: 0000rrrrggggbbbb)
+    This is simply an entire 4-bit colormap per row.
+    I have no idea what the DYCP chunk is for.
+
+    SHAM format (sliced HAM):
+        1 word  - version info (?) - always 0
+        16 bigendian words per row (16 bit: 0000rrrrggggbbbb)
+    If the picture is laced, then there are only rows/2 changes, don't change
+    palette on odd lines.
+
+    PCHG format: A detailed description of this format is available
+    from AmiNet as PCHGLib12.lha.
+
+ ****************************************************************************/
+
+/* Turn big-endian 4-byte long and 2-byte short stored at x (unsigned char *)
+ * into the native format of the CPU
+ */
+#define BIG_LONG(x) (   ((unsigned long)((x)[0]) << 24) + \
+                        ((unsigned long)((x)[1]) << 16) + \
+                        ((unsigned long)((x)[2]) <<  8) + \
+                        ((unsigned long)((x)[3]) <<  0) )
+#define BIG_WORD(x) (   ((unsigned short)((x)[0]) << 8) + \
+                        ((unsigned short)((x)[1]) << 0) )
+
+
+
+static void
+read_4bit_mp(FILE *     const ifP,
+             IFF_ID     const iffid,
+             long       const chunksize,
+             ColorMap * const cmap) {
+
+    int row, rows, i, type;
+    short data;
+    unsigned long remainingChunksize;
+
+    type = (iffid == ID_SHAM ? MP_TYPE_SHAM : MP_TYPE_CTBL);
+
+    if( cmap->mp_type >= type ) {
+        skip_chunk(ifP, iffid, chunksize);
+    } else {
+        if( cmap->mp_type )
+            multi_free(cmap);
+        cmap->mp_type = type;
+
+        remainingChunksize = chunksize;  /* initial value */
+
+        if( type == MP_TYPE_SHAM ) {
+            cmap->mp_flags = MP_FLAGS_SKIPLACED;
+            get_big_short(ifP, iffid, &remainingChunksize); /* skip first wd */
+        }
+
+        cmap->mp_rows = rows = remainingChunksize/32; /* sizeof(word) * 16 */
+        MALLOCARRAY_NOFAIL(cmap->mp_change, rows);
+
+        for( row = 0; row < rows; row++ ) {
+            MALLOCARRAY_NOFAIL(cmap->mp_change[row], 17);   /* 16 + sentinel */
+            for( i = 0; i < 16; i++ ) {
+                data = get_big_short(ifP, iffid, &remainingChunksize);
+                cmap->mp_change[row][i].reg = i;
+                cmap->mp_change[row][i].r =
+                    ((data & 0x0f00) >> 8) * FACTOR_4BIT;
+                cmap->mp_change[row][i].g =
+                    ((data & 0x00f0) >> 4) * FACTOR_4BIT;
+                cmap->mp_change[row][i].b =
+                    ((data & 0x000f) >> 0) * FACTOR_4BIT;
+            }
+            cmap->mp_change[row][16].reg = MP_REG_END;   /* sentinel */
+        }
+        chunk_end(ifP, iffid, remainingChunksize);
+    }
+}
+
+
+
+static void
+PCHG_DecompHuff(src, dest, tree, origsize)
+    unsigned char *src, *dest;
+    short *tree;
+    unsigned long origsize;
+{
+    unsigned long i = 0, bits = 0;
+    unsigned char thisbyte;
+    short *p;
+
+    p = tree;
+    while( i < origsize ) {
+        if( bits == 0 ) {
+            thisbyte = *src++;
+            bits = 8;
+        }
+        if( thisbyte & (1 << 7) ) {
+            if( *p >= 0 ) {
+                *dest++ = (unsigned char)*p;
+                i++;
+                p = tree;
+            }
+            else
+                p += (*p / 2);
+        }
+        else {
+            p--;
+            if( *p > 0 && (*p & 0x100) ) {
+                *dest++ = (unsigned char )*p;
+                i++;
+                p = tree;
+            }
+        }
+        thisbyte <<= 1;
+        bits--;
+    }
+}
+
+
+static void
+PCHG_Decompress(PCHG, CompHdr, compdata, compsize, comptree, data)
+    PCHGHeader *PCHG;
+    PCHGCompHeader *CompHdr;
+    unsigned char *compdata;
+    unsigned long compsize;
+    unsigned char *comptree;
+    unsigned char *data;
+{
+    short *hufftree;
+    unsigned long huffsize, i;
+    unsigned long treesize = CompHdr->CompInfoSize;
+
+    switch( PCHG->Compression ) {
+        case PCHG_COMP_HUFFMAN:
+
+#ifdef DEBUG
+            pm_message("PCHG Huffman compression");
+#endif
+            /* turn big-endian 2-byte shorts into native format */
+            huffsize = treesize/2;
+            MALLOCVAR_NOFAIL(hufftree);
+            for( i = 0; i < huffsize; i++ ) {
+                hufftree[i] = (short)BIG_WORD(comptree);
+                comptree += 2;
+            }
+
+            /* decompress the change structure data */
+            PCHG_DecompHuff(compdata, data, &hufftree[huffsize-1], 
+                            CompHdr->OriginalDataSize);
+
+            free(hufftree);
+            break;
+        default:
+            pm_error("unknown PCHG compression type %d", PCHG->Compression);
+    }
+}
+
+
+static void
+PCHG_ConvertSmall(PCHG, cmap, mask, datasize)
+    PCHGHeader *PCHG;
+    ColorMap *cmap;
+    unsigned char *mask;
+    unsigned long datasize;
+{
+    unsigned char *data;
+    unsigned char thismask;
+    int bits, row, i, changes, masklen, reg;
+    unsigned char ChangeCount16, ChangeCount32;
+    unsigned short SmallChange;
+    unsigned long totalchanges = 0;
+    int changedlines = PCHG->ChangedLines;
+
+    masklen = 4 * MaskLongWords(PCHG->LineCount);
+    data = mask + masklen; datasize -= masklen;
+
+    bits = 0;
+    for( row = PCHG->StartLine; changedlines && row < 0; row++ ) {
+        if( bits == 0 ) {
+            if( masklen == 0 ) goto fail2;
+            thismask = *mask++;
+            --masklen;
+            bits = 8;
+        }
+        if( thismask & (1<<7) ) {
+            if( datasize < 2 ) goto fail;
+            ChangeCount16 = *data++;
+            ChangeCount32 = *data++;
+            datasize -= 2;
+
+            changes = ChangeCount16 + ChangeCount32;
+            for( i = 0; i < changes; i++ ) {
+                if( totalchanges >= PCHG->TotalChanges ) goto fail;
+                if( datasize < 2 ) goto fail;
+                SmallChange = BIG_WORD(data); data += 2; datasize -= 2;
+                reg = ((SmallChange & 0xf000) >> 12) + 
+                    (i >= ChangeCount16 ? 16 : 0);
+                cmap->mp_init[reg - PCHG->MinReg].reg = reg;
+                cmap->mp_init[reg - PCHG->MinReg].r = 
+                    ((SmallChange & 0x0f00) >> 8) * FACTOR_4BIT;
+                cmap->mp_init[reg - PCHG->MinReg].g = 
+                    ((SmallChange & 0x00f0) >> 4) * FACTOR_4BIT;
+                cmap->mp_init[reg - PCHG->MinReg].b = 
+                    ((SmallChange & 0x000f) >> 0) * FACTOR_4BIT;
+                ++totalchanges;
+            }
+            --changedlines;
+        }
+        thismask <<= 1;
+        bits--;
+    }
+
+    for( row = PCHG->StartLine; changedlines && row < cmap->mp_rows; row++ ) {
+        if( bits == 0 ) {
+            if( masklen == 0 ) goto fail2;
+            thismask = *mask++;
+            --masklen;
+            bits = 8;
+        }
+        if( thismask & (1<<7) ) {
+            if( datasize < 2 ) goto fail;
+            ChangeCount16 = *data++;
+            ChangeCount32 = *data++;
+            datasize -= 2;
+
+            changes = ChangeCount16 + ChangeCount32;
+            MALLOCARRAY_NOFAIL(cmap->mp_change[row], changes + 1);
+            for( i = 0; i < changes; i++ ) {
+                if( totalchanges >= PCHG->TotalChanges ) goto fail;
+                if( datasize < 2 ) goto fail;
+                SmallChange = BIG_WORD(data); data += 2; datasize -= 2;
+                reg = ((SmallChange & 0xf000) >> 12) + 
+                    (i >= ChangeCount16 ? 16 : 0);
+                cmap->mp_change[row][i].reg = reg;
+                cmap->mp_change[row][i].r = 
+                    ((SmallChange & 0x0f00) >> 8) * FACTOR_4BIT;
+                cmap->mp_change[row][i].g = 
+                    ((SmallChange & 0x00f0) >> 4) * FACTOR_4BIT;
+                cmap->mp_change[row][i].b = 
+                    ((SmallChange & 0x000f) >> 0) * FACTOR_4BIT;
+                ++totalchanges;
+            }
+            cmap->mp_change[row][changes].reg = MP_REG_END;
+            --changedlines;
+        }
+        thismask <<= 1;
+        bits--;
+    }
+    if( totalchanges != PCHG->TotalChanges )
+        pm_message("warning - got %ld change structures, "
+                   "chunk header reports %ld", 
+                   totalchanges, PCHG->TotalChanges);
+    return;
+fail:
+    pm_error("insufficient data in SmallLineChanges structures");
+fail2:
+    pm_error("insufficient data in line mask");
+}
+
+
+static void
+PCHG_ConvertBig(PCHG, cmap, mask, datasize)
+    PCHGHeader *PCHG;
+    ColorMap *cmap;
+    unsigned char *mask;
+    unsigned long datasize;
+{
+    unsigned char *data;
+    unsigned char thismask;
+    int bits, row, i, changes, masklen, reg;
+    unsigned long totalchanges = 0;
+    int changedlines = PCHG->ChangedLines;
+
+    masklen = 4 * MaskLongWords(PCHG->LineCount);
+    data = mask + masklen; datasize -= masklen;
+
+    bits = 0;
+    for( row = PCHG->StartLine; changedlines && row < 0; row++ ) {
+        if( bits == 0 ) {
+            if( masklen == 0 ) goto fail2;
+            thismask = *mask++;
+            --masklen;
+            bits = 8;
+        }
+        if( thismask & (1<<7) ) {
+            if( datasize < 2 ) goto fail;
+            changes = BIG_WORD(data); data += 2; datasize -= 2;
+
+            for( i = 0; i < changes; i++ ) {
+                if( totalchanges >= PCHG->TotalChanges ) goto fail;
+                if( datasize < 6 ) goto fail;
+                reg = BIG_WORD(data); data += 2;
+                cmap->mp_init[reg - PCHG->MinReg].reg = reg;
+                ++data; /* skip alpha */
+                cmap->mp_init[reg - PCHG->MinReg].r = *data++;
+                cmap->mp_init[reg - PCHG->MinReg].b = *data++;  /* yes, RBG */
+                cmap->mp_init[reg - PCHG->MinReg].g = *data++;
+                datasize -= 6;
+                ++totalchanges;
+            }
+            --changedlines;
+        }
+        thismask <<= 1;
+        bits--;
+    }
+
+    for( row = PCHG->StartLine; changedlines && row < cmap->mp_rows; row++ ) {
+        if( bits == 0 ) {
+            if( masklen == 0 ) goto fail2;
+            thismask = *mask++;
+            --masklen;
+            bits = 8;
+        }
+        if( thismask & (1<<7) ) {
+            if( datasize < 2 ) goto fail;
+            changes = BIG_WORD(data); data += 2; datasize -= 2;
+
+            MALLOCARRAY_NOFAIL(cmap->mp_change[row], changes + 1);
+            for( i = 0; i < changes; i++ ) {
+                if( totalchanges >= PCHG->TotalChanges ) goto fail;
+                if( datasize < 6 ) goto fail;
+                reg = BIG_WORD(data); data += 2;
+                cmap->mp_change[row][i].reg = reg;
+                ++data; /* skip alpha */
+                cmap->mp_change[row][i].r = *data++;
+                cmap->mp_change[row][i].b = *data++;    /* yes, RBG */
+                cmap->mp_change[row][i].g = *data++;
+                datasize -= 6;
+                ++totalchanges;
+            }
+            cmap->mp_change[row][changes].reg = MP_REG_END;
+            --changedlines;
+        }
+        thismask <<= 1;
+        bits--;
+    }
+    if( totalchanges != PCHG->TotalChanges )
+        pm_message("warning - got %ld change structures, "
+                   "chunk header reports %ld", 
+                   totalchanges, PCHG->TotalChanges);
+    return;
+fail:
+    pm_error("insufficient data in BigLineChanges structures");
+fail2:
+    pm_error("insufficient data in line mask");
+}
+
+
+static void
+read_pchg(FILE *     const ifp,
+          IFF_ID     const iffid,
+          long       const chunksize,
+          ColorMap * const cmap) {
+
+    if( cmap->mp_type >= MP_TYPE_PCHG ) {
+        skip_chunk(ifp, iffid, chunksize);
+    } else {
+        PCHGHeader      PCHG;
+        unsigned char   *data;
+        unsigned long   datasize;
+        unsigned long   remainingChunksize;
+        int i;
+
+        if( cmap->mp_type )
+            multi_free(cmap);
+        cmap->mp_type = MP_TYPE_PCHG;
+
+        remainingChunksize = chunksize;  /* initial value */
+
+        PCHG.Compression = get_big_short(ifp, iffid, &remainingChunksize);
+        PCHG.Flags       = get_big_short(ifp, iffid, &remainingChunksize);
+        PCHG.StartLine   = get_big_short(ifp, iffid, &remainingChunksize);
+        PCHG.LineCount   = get_big_short(ifp, iffid, &remainingChunksize);
+        PCHG.ChangedLines= get_big_short(ifp, iffid, &remainingChunksize);
+        PCHG.MinReg      = get_big_short(ifp, iffid, &remainingChunksize);
+        PCHG.MaxReg      = get_big_short(ifp, iffid, &remainingChunksize);
+        PCHG.MaxChanges  = get_big_short(ifp, iffid, &remainingChunksize);
+        PCHG.TotalChanges= get_big_long(ifp, iffid, &remainingChunksize);
+
+#ifdef DEBUG
+        pm_message("PCHG StartLine   : %d", PCHG.StartLine);
+        pm_message("PCHG LineCount   : %d", PCHG.LineCount);
+        pm_message("PCHG ChangedLines: %d", PCHG.ChangedLines);
+        pm_message("PCHG TotalChanges: %d", PCHG.TotalChanges);
+#endif
+
+        if( PCHG.Compression != PCHG_COMP_NONE ) {
+            PCHGCompHeader  CompHdr;
+            unsigned char *compdata, *comptree;
+            long treesize, compsize;
+
+            CompHdr.CompInfoSize     =
+                get_big_long(ifp, iffid, &remainingChunksize);
+            CompHdr.OriginalDataSize =
+                get_big_long(ifp, iffid, &remainingChunksize);
+
+            treesize = CompHdr.CompInfoSize;
+            MALLOCVAR_NOFAIL(comptree);
+            read_bytes(ifp, treesize, comptree, iffid, &remainingChunksize);
+
+            compsize = remainingChunksize;
+            MALLOCARRAY_NOFAIL(compdata, remainingChunksize);
+            read_bytes(ifp, compsize, compdata, iffid, &remainingChunksize);
+
+            datasize = CompHdr.OriginalDataSize;
+            MALLOCARRAY_NOFAIL(data, datasize);
+            PCHG_Decompress(&PCHG, &CompHdr, compdata, 
+                            compsize, comptree, data);
+
+            free(comptree);
+            free(compdata);
+        } else {
+#ifdef DEBUG
+            pm_message("uncompressed PCHG");
+#endif
+            datasize = remainingChunksize;
+            MALLOCARRAY_NOFAIL(data, datasize);
+            read_bytes(ifp, datasize, data, iffid, &remainingChunksize);
+        }
+
+        if( PCHG.Flags & PCHGF_USE_ALPHA )
+            pm_message("warning - ignoring PCHG alpha channel because "
+                       "this program doesn't know what to do with it");
+
+        cmap->mp_rows = PCHG.StartLine + PCHG.LineCount;
+        MALLOCARRAY_NOFAIL(cmap->mp_change, cmap->mp_rows);
+        for( i = 0; i < cmap->mp_rows; i++ )
+            cmap->mp_change[i] = NULL;
+        if( PCHG.StartLine < 0 ) {
+            int nch;
+            nch = PCHG.MaxReg - PCHG.MinReg +1;
+            MALLOCARRAY_NOFAIL(cmap->mp_init, nch + 1);
+            for( i = 0; i < nch; i++ )
+                cmap->mp_init[i].reg = MP_REG_IGNORE;
+            cmap->mp_init[nch].reg = MP_REG_END;
+        }
+
+        if( PCHG.Flags & PCHGF_12BIT ) {
+#ifdef DEBUG
+            pm_message("SmallLineChanges");
+#endif
+            PCHG_ConvertSmall(&PCHG, cmap, data, datasize);
+        }
+        else {
+            if( PCHG.Flags & PCHGF_32BIT ) {
+#ifdef DEBUG
+                pm_message("BigLineChanges");
+#endif
+                PCHG_ConvertBig(&PCHG, cmap, data, datasize);
+            }
+            else
+                pm_error("unknown palette changes structure "
+                         "format in %s chunk", 
+                         ID2string(iffid));
+        }
+        free(data);
+        chunk_end(ifp, iffid, remainingChunksize);
+    }
+}
+
+
+
+static bool
+ignored_iffid(IFF_ID       const iffid,
+              IFF_ID       const ignorelist[],
+              unsigned int const ignorecount) {
+
+    bool ignore;
+
+    unsigned int i;
+    ignore = FALSE;  /* initial assumption */
+    for( i = 0; i < ignorecount && !ignore; i++ ) {
+        if( iffid == ignorelist[i] )
+            ignore = TRUE;
+    }
+    return ignore;
+}
+
+
+
+static void 
+process_body( FILE *          const ifp,
+              long            const chunksize,
+              BitMapHeader *  const bmhdP,
+              ColorMap *      const cmap,
+              FILE *          const maskfile,
+              int             const fakeviewport,
+              int             const isdeepopt,
+              DirectColor *   const dcol,
+              int *           const viewportmodesP) {
+    
+    if( bmhdP == NULL )
+        pm_error("%s chunk without %s chunk", 
+                 ID2string(ID_BODY), ID2string(ID_BMHD));
+
+    check_cmap(bmhdP, cmap);
+
+    pixelrow = ppm_allocrow(bmhdP->w);
+    if( maskfile ) {
+        maskrow = pbm_allocrow(bmhdP->w);
+        pbm_writepbminit(maskfile, bmhdP->w, bmhdP->h, 0);
+    }
+
+    if( typeid == ID_ILBM ) {
+        int isdeep;
+
+        MALLOCARRAY_NOFAIL(ilbmrow, RowBytes(bmhdP->w));
+        *viewportmodesP |= fakeviewport;      /* -isham/-isehb */
+
+        if( isdeepopt > 0 && (bmhdP->nPlanes % 3 != 0) ) {
+            pm_message("cannot interpret %d-plane image as 'deep' "
+                       "(# of planes must be divisible by 3)", 
+                       bmhdP->nPlanes);
+            isdeep = 0;
+        } else
+            isdeep = isdeepopt;
+        
+        if( isdeep > 0 )
+            deep_to_ppm(ifp, chunksize, bmhdP, cmap);
+        else if( dcol )
+            dcol_to_ppm(ifp, chunksize, bmhdP, cmap, dcol);
+        else if( bmhdP->nPlanes > 8 ) {
+            if( bmhdP->nPlanes <= 16 && HAS_COLORMAP(cmap) )
+                std_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP);
+            else if( isdeep >= 0 && (bmhdP->nPlanes % 3 == 0) )
+                deep_to_ppm(ifp, chunksize, bmhdP, cmap);
+            else if( bmhdP->nPlanes <= 16 )   
+                /* will be interpreted as grayscale */
+                std_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP);
+            else
+                pm_error("don't know how to interpret %d-plane image", 
+                         bmhdP->nPlanes);
+        } else
+            std_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP);
+    } else if( typeid == ID_PBM )
+        ipbm_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP);
+    else   /* RGBN or RGB8 */
+        rgbn_to_ppm(ifp, chunksize, bmhdP, cmap);
+}
+
+
+
+static void 
+process_chunk(FILE *          const ifp,
+              long            const formsize,
+              IFF_ID          const ignorelist[],
+              unsigned int    const ignorecount,
+              int             const fakeviewport,
+              int             const viewportmask,
+              int             const isdeepopt,
+              bool            const cmaponly,
+              bool *          const bodyChunkProcessedP,
+              bool *          const endchunkP,
+              BitMapHeader ** const bmhdP,
+              ColorMap *      const cmap,
+              DirectColor **  const dcolP,
+              int *           const viewportmodesP,
+              long *          const bytesReadP
+    ) {
+
+    IFF_ID iffid;
+    long chunksize;
+    long bytesread;
+
+    bytesread = 0;
+
+    iffid = get_big_long(ifp, ID_FORM, NULL);
+    chunksize = get_big_long(ifp, iffid, NULL);
+    bytesread += 8;
+
+    if (debug)
+        pm_message("reading %s chunk: %ld bytes", 
+                   ID2string(iffid), chunksize);
+
+    if( ignored_iffid(iffid, ignorelist, ignorecount) ) {
+        pm_message("ignoring %s chunk", ID2string(iffid));
+        skip_chunk(ifp, iffid, chunksize);
+    } else if( iffid == ID_END ) {
+        /* END chunks are not officially valid in IFF, but
+           suggested as a future expansion for stream-writing,
+           see Amiga RKM Devices, 3rd Ed, page 376 
+        */
+        if( chunksize != 0 ) {
+            pm_message("warning - non-0 %s chunk", ID2string(iffid));
+            skip_chunk(ifp, iffid, chunksize);
+        }
+        if( formsize != 0xffffffff )
+            pm_message("warning - %s chunk with FORM size 0x%08lx "
+                       "(should be 0x%08x)",
+                       ID2string(iffid), formsize, 0xffffffff);
+        *endchunkP = 1;
+    } else if( *bodyChunkProcessedP ) {
+        pm_message("%s chunk found after %s chunk - skipping", 
+                   ID2string(iffid), ID2string(ID_BODY));
+        skip_chunk(ifp, iffid, chunksize);
+    } else
+        switch( iffid ) {
+        case ID_BMHD:
+            *bmhdP = read_bmhd(ifp, iffid, chunksize);
+            break;
+        case ID_CMAP:
+            read_cmap(ifp, iffid, chunksize, cmap);
+            break;
+        case ID_CMYK:
+            read_cmyk(ifp, iffid, chunksize, cmap);
+            break;
+        case ID_CLUT:
+            read_clut(ifp, iffid, chunksize, cmap);
+            break;
+        case ID_CAMG:
+            if( chunksize != CAMGChunkSize )
+                pm_error("%s chunk size mismatch", ID2string(iffid));
+            *viewportmodesP = get_big_long(ifp, ID_CAMG, NULL);
+            *viewportmodesP &= viewportmask;      /* -isnotham/-isnotehb */
+            break;
+        case ID_PCHG:
+            read_pchg(ifp, iffid, chunksize, cmap);
+            break;
+        case ID_CTBL:
+        case ID_SHAM:
+            read_4bit_mp(ifp, iffid, chunksize, cmap);
+            break;
+        case ID_DCOL:
+            if( chunksize != DirectColorSize )
+                pm_error("%s chunk size mismatch", ID2string(iffid));
+            MALLOCVAR_NOFAIL(*dcolP);
+            (*dcolP)->r = get_byte(ifp, iffid, NULL);
+            (*dcolP)->g = get_byte(ifp, iffid, NULL);
+            (*dcolP)->b = get_byte(ifp, iffid, NULL);
+            (void)get_byte(ifp, iffid, NULL);       /* skip pad byte */
+            break;
+        case ID_BODY: 
+            if( cmaponly || (*bmhdP && (*bmhdP)->nPlanes == 0 )) {
+                skip_chunk(ifp, ID_BODY,  chunksize);
+                return;
+            }
+    
+            process_body(ifp, chunksize, *bmhdP, cmap, 
+                         maskfile, fakeviewport, isdeepopt, *dcolP,
+                         viewportmodesP);
+
+            *bodyChunkProcessedP = TRUE;
+
+            break;
+        case ID_GRAB:   case ID_DEST:   case ID_SPRT:   case ID_CRNG:
+        case ID_CCRT:   case ID_DYCP:   case ID_DPPV:   case ID_DRNG:
+        case ID_EPSF:   case ID_JUNK:   case ID_CNAM:   case ID_PRVW:
+        case ID_TINY:   case ID_DPPS:
+            skip_chunk(ifp, iffid, chunksize);
+            break;
+        case ID_copy:   case ID_AUTH:   case ID_NAME:   case ID_ANNO:
+        case ID_TEXT:   case ID_FVER:
+            if( verbose )
+                display_chunk(ifp, iffid, chunksize);
+            else
+                skip_chunk(ifp, iffid, chunksize);
+            break;
+        case ID_DPI:
+        {
+            int x, y;
+
+            x = get_big_short(ifp, ID_DPI, NULL);
+            y = get_big_short(ifp, ID_DPI, NULL);
+            if( verbose )
+                pm_message("%s chunk:  dpi_x = %d    dpi_y = %d", 
+                           ID2string(ID_DPI), x, y);
+        }
+        break;
+        default:
+            pm_message("unknown chunk type %s - skipping", 
+                       ID2string(iffid));
+            skip_chunk(ifp, iffid, chunksize);
+            break;
+        }
+
+    bytesread += chunksize;
+
+    if( ODD(chunksize) ) {
+        (void) get_byte(ifp, iffid, NULL);
+        bytesread += 1;
+    } 
+    *bytesReadP = bytesread;
+}
+
+
+int
+main(int argc, char *argv[]) {
+
+    FILE *ifp;
+    int argn;
+    short cmaponly = 0, isdeepopt = 0;
+    bool endchunk;
+    bool bodyChunkProcessed;
+    long formsize, bytesread;
+    int viewportmodes = 0, fakeviewport = 0, viewportmask;
+    IFF_ID firstIffid;
+    BitMapHeader *bmhdP = NULL;
+    ColorMap * cmap;
+    DirectColor *dcol = NULL;
+#define MAX_IGNORE  16
+    IFF_ID ignorelist[MAX_IGNORE];
+    int ignorecount = 0;
+    char *maskname;
+    const char * const usage =
+        "[-verbose] [-ignore <chunkID> [-ignore <chunkID>] ...] "
+        "[-isham|-isehb|-isdeep|-isnotham|-isnotehb|-isnotdeep] "
+        "[-cmaponly] [-adjustcolors] "
+        "[-transparent <color>] [-maskfile <filename>] [ilbmfile]";
+    ppm_init(&argc, argv);
+
+    viewportmask = 0xffffffff;
+
+    argn = 1;
+    while( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) {
+        if( pm_keymatch(argv[argn], "-verbose", 2) )
+            verbose = 1;
+        else if( pm_keymatch(argv[argn], "-noverbose", 4) )
+            verbose = 0;
+        else if( pm_keymatch(argv[argn], "-isham", 4) )
+            fakeviewport |= vmHAM;
+        else if( pm_keymatch(argv[argn], "-isehb", 4) )
+            fakeviewport |= vmEXTRA_HALFBRITE;
+        else if( pm_keymatch(argv[argn], "-isdeep", 4) )
+            isdeepopt = 1;
+        else if( pm_keymatch(argv[argn], "-isnotham", 7) )
+            viewportmask &= ~(vmHAM);
+        else if( pm_keymatch(argv[argn], "-isnotehb", 7) )
+            viewportmask &= ~(vmEXTRA_HALFBRITE);
+        else if( pm_keymatch(argv[argn], "-isnotdeep", 7) )
+            isdeepopt = -1;
+        else if( pm_keymatch(argv[argn], "-cmaponly", 2) )
+            cmaponly = 1;
+        else if( pm_keymatch(argv[argn], "-adjustcolors", 2) )
+            adjustcolors = 1;
+        else if( pm_keymatch(argv[argn], "-noadjustcolors", 4) )
+            adjustcolors = 0;
+        else  if( pm_keymatch(argv[argn], "-transparent", 2) ) {
+            if( ++argn >= argc )
+                pm_usage(usage);
+            transpName = argv[argn];
+        } else if( pm_keymatch(argv[argn], "-maskfile", 2) ) {
+            if( ++argn >= argc )
+                pm_usage(usage);
+            maskname = argv[argn];
+            maskfile = pm_openw(maskname);
+        } else if( pm_keymatch(argv[argn], "-ignore", 2) ) {
+            if( ++argn >= argc )
+                pm_usage(usage);
+            if( strlen(argv[argn]) != 4 )
+                pm_error("'-ignore' option needs a 4 byte chunk ID string "
+                         "as argument");
+            if( ignorecount >= MAX_IGNORE )
+                pm_error("max %d chunk IDs to ignore", MAX_IGNORE);
+            ignorelist[ignorecount++] = 
+                MAKE_ID(argv[argn][0], argv[argn][1], argv[argn][2], 
+                        argv[argn][3]);
+        } else
+            pm_usage(usage);
+        ++argn;
+    }    
+
+    if( argn < argc ) {
+        ifp = pm_openr( argv[argn] );
+        argn++;
+    } else
+        ifp = stdin;
+
+    if( argn != argc )
+        pm_usage(usage);
+
+    /* Read in the ILBM file. */
+
+    firstIffid = get_big_long(ifp, ID_FORM, NULL);
+    if( firstIffid != ID_FORM )
+        pm_error("input is not a FORM type IFF file");
+    formsize = get_big_long(ifp, ID_FORM, NULL);
+    typeid = get_big_long(ifp, ID_FORM, NULL);
+    if( typeid != ID_ILBM && typeid != ID_RGBN && typeid != ID_RGB8 && 
+        typeid != ID_PBM )
+        pm_error( "input is not an ILBM, RGBN, RGB8 or PBM "
+                  "type FORM IFF file" );
+    bytesread = 4;  /* FORM and formsize do not count */
+
+    cmap = alloc_cmap();
+
+    /* Main loop, parsing the IFF FORM. */
+    bodyChunkProcessed = FALSE;
+    endchunk = FALSE;
+    while( !endchunk && formsize-bytesread >= 8 ) {
+        long bytes_read_for_chunk;
+
+        process_chunk(ifp, formsize, ignorelist, ignorecount,
+                      fakeviewport, viewportmask,
+                      isdeepopt, cmaponly,
+                      &bodyChunkProcessed,
+                      &endchunk, &bmhdP, cmap, &dcol,
+                      &viewportmodes, &bytes_read_for_chunk);
+
+        bytesread += bytes_read_for_chunk;
+    }
+
+    if( maskfile ) {
+        pm_close(maskfile);
+        if( !wrotemask )
+            remove(maskname);
+        pbm_freerow(maskrow);
+    }
+
+    if( cmaponly || (bmhdP && bmhdP->nPlanes == 0 )) {
+        if( HAS_COLORMAP(cmap) ) {
+            check_cmap(bmhdP, cmap);
+            cmap_to_ppm(cmap);
+        } else
+            pm_error("no colormap");
+    } else if( bodyChunkProcessed ) {
+        if( HAS_COLORMAP(cmap) ) {
+            pm_message("input is a colormap file");
+            check_cmap(bmhdP, cmap);
+            cmap_to_ppm(cmap);
+        } else
+            pm_error("no %s or %s chunk found", 
+                     ID2string(ID_BODY), ID2string(ID_CMAP));
+    }
+
+    {
+        unsigned int skipped;
+        
+        for( skipped = 0; fgetc(ifp) != EOF; ++skipped )
+            bytesread++;
+
+        if( skipped > 0 )
+            pm_message("skipped %u extraneous byte%s after last chunk",
+                       skipped, (skipped == 1 ? "" : "s"));
+    }
+    pm_close(ifp);
+
+    if( !endchunk && bytesread != formsize ) {
+        pm_message("warning - file length/FORM size field mismatch "
+                   "(%ld != %ld+8)",
+                   bytesread+8 /* FORM+size */, formsize);
+    }
+
+    return 0;
+}
diff --git a/converter/ppm/imgtoppm.c b/converter/ppm/imgtoppm.c
new file mode 100644
index 00000000..7078b882
--- /dev/null
+++ b/converter/ppm/imgtoppm.c
@@ -0,0 +1,142 @@
+/* imgtoppm.c - read an Img-whatnot file and produce a portable pixmap
+**
+** Based on a simple conversion program posted to comp.graphics by Ed Falk.
+**
+** Copyright (C) 1989 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <string.h>
+#include "ppm.h"
+
+
+
+int
+main(int argc, char ** argv) {
+
+    FILE* ifp;
+    pixel* pixelrow;
+    pixel colormap[256];
+    register pixel* pP;
+    unsigned int cols;
+    unsigned int rows;
+    int argn, row, i;
+    int col;
+    pixval maxval;
+    unsigned int cmaplen;
+    int len, gotAT, gotCM, gotPD;
+    unsigned char buf[4096];
+    unsigned char* bP;
+
+
+    ppm_init( &argc, argv );
+
+    argn = 1;
+
+    if ( argn < argc )
+    {
+        ifp = pm_openr( argv[argn] );
+        argn++;
+    }
+    else
+        ifp = stdin;
+
+    if ( argn != argc )
+        pm_usage( "[imgfile]" );
+
+    /* Get signature. */
+    fread( buf, 8, 1, ifp );
+    buf[8] = '\0';
+
+    /* Get entries. */
+    gotAT = 0;
+    gotCM = 0;
+    gotPD = 0;
+    while ( fread( buf, 2, 1, ifp ) == 1 )
+    {
+        if ( strncmp( (char*) buf, "AT", 2 ) == 0 )
+        {
+            if ( fread( buf, 8, 1, ifp ) != 1 )
+                pm_error( "bad attributes header" );
+            buf[8] = '\0';
+            len = atoi( (char*) buf );
+            if ( fread( buf, len, 1, ifp ) != 1 )
+                pm_error( "bad attributes buf" );
+            buf[len] = '\0';
+            sscanf( (char*) buf, "%4u%4u%4u", &cols, &rows, &cmaplen );
+            maxval = 255;
+            gotAT = 1;
+        }
+
+        else if ( strncmp( (char*) buf, "CM", 2 ) == 0 )
+        {
+            if ( ! gotAT )
+                pm_error( "missing attributes header" );
+            if ( fread( buf, 8, 1, ifp ) != 1 )
+                pm_error( "bad colormap header" );
+            buf[8] = '\0';
+            len = atoi((char*) buf );
+            if ( fread( buf, len, 1, ifp ) != 1 )
+                pm_error( "bad colormap buf" );
+            if ( cmaplen * 3 != len )
+            {
+                pm_message(
+                    "cmaplen (%d) and colormap buf length (%d) do not match",
+                    cmaplen, len );
+                if ( cmaplen * 3 < len )
+                    len = cmaplen * 3;
+                else if ( cmaplen * 3 > len )
+                    cmaplen = len / 3;
+            }
+            for ( i = 0; i < len; i += 3 )
+                PPM_ASSIGN( colormap[i / 3], buf[i], buf[i + 1], buf[i + 2] );
+            gotCM = 1;
+        }
+
+        else if ( strncmp( (char*) buf, "PD", 2 ) == 0 )
+        {
+            if ( fread( buf, 8, 1, ifp ) != 1 )
+                pm_error( "bad pixel data header" );
+            buf[8] = '\0';
+            len = atoi((char*) buf );
+            if ( len != cols * rows )
+                pm_message(
+                    "pixel data length (%d) does not match image size (%d)",
+                    len, cols * rows );
+
+            ppm_writeppminit( stdout, cols, rows, maxval, 0 );
+            pixelrow = ppm_allocrow( cols );
+
+            for ( row = 0; row < rows; row++ )
+            {
+                if ( fread( buf, 1, cols, ifp ) != cols )
+                    pm_error( "EOF / read error" );
+                for ( col = 0, pP = pixelrow, bP = buf;
+                      col < cols; col++, pP++, bP++ )
+                {
+                    if ( gotCM )
+                        *pP = colormap[*bP];
+                    else
+                        PPM_ASSIGN( *pP, *bP, *bP, *bP );
+                }
+                ppm_writeppmrow( stdout, pixelrow, cols, maxval, 0 );
+            }
+            gotPD = 1;
+
+        }
+    }
+    if ( ! gotPD )
+        pm_error( "missing pixel data header" );
+
+    pm_close( ifp );
+    /* If the program failed, it previously aborted with nonzero completion
+       code, via various function calls.
+    */
+    return 0;
+}
diff --git a/converter/ppm/leaftoppm.c b/converter/ppm/leaftoppm.c
new file mode 100644
index 00000000..889400f6
--- /dev/null
+++ b/converter/ppm/leaftoppm.c
@@ -0,0 +1,216 @@
+/* leaftoppm.c - read an ileaf img file and write a PPM
+ *
+ * Copyright (C) 1994 by Bill O'Donnell.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  This software is provided "as is" without express or
+ * implied warranty.
+ *
+ * known problems: doesn't do compressed ileaf images.
+ * 
+ */
+
+#include <stdio.h>
+#include "ppm.h"
+
+#define MAXCOLORS 256
+#define LEAF_MAXVAL 255
+
+static void
+leaf_init(FILE *  const fp, 
+          int *   const colsP,
+          int *   const rowsP, 
+          int *   const depthP, 
+          int *   const ncolorsP, 
+          pixel * const colors) {
+
+    unsigned char buf[256];
+    unsigned char compressed;
+    short version;
+    short cols;
+    short rows;
+    short depth;
+    long magic;
+    
+    pm_readbiglong(fp, &magic);
+    if (magic != 0x894f5053)
+        pm_error("Bad magic number.  First 4 bytes should be "
+                 "0x894f5053 but are instead 0x%08x", (unsigned)magic);
+    
+    /* version =   2 bytes
+       hres =      2 
+       vres =      2
+       unique id = 4
+       offset x =  2
+       offset y =  2
+       TOTAL    =  14 bytes 
+    */
+    
+    pm_readbigshort(fp, &version);
+
+    if (fread(buf, 1, 12, fp) != 12)
+        pm_error("bad header, short file?");
+    
+    pm_readbigshort(fp, &cols);
+    *colsP = cols;
+    pm_readbigshort(fp, &rows);
+    *rowsP = rows;
+    pm_readbigshort(fp, &depth);
+    *depthP = depth;
+    
+    if ((compressed = fgetc(fp)) != 0)
+        pm_error("Can't do compressed images.");
+
+    if ((*depthP == 1) && (version < 4)) {
+        fgetc(fp); 
+        *ncolorsP = 0;
+    } else if ((*depthP == 8) && (version < 4)) {
+        fgetc(fp); 
+        *ncolorsP = 0;
+    } else {
+        long format;
+        pm_readbiglong(fp, &format);
+        if (format == 0x29000000) {
+            /* color image */
+            short ncolors;
+            pm_readbigshort(fp, &ncolors);
+
+            if (ncolors > 0) {
+                /* 8-bit image */
+                unsigned int i;
+
+                if (ncolors > 256)
+                    pm_error("Can't have > 256 colors in colormap.");
+                /* read colormap */
+                for (i=0; i < 256; ++i)
+                    PPM_PUTR(colors[i], fgetc(fp));
+                for (i=0; i < 256; ++i)
+                    PPM_PUTR(colors[i], fgetc(fp));
+                for (i=0; i < 256; ++i)
+                    PPM_PUTR(colors[i], fgetc(fp));
+                *ncolorsP = ncolors;
+            } else {
+                /* 24-bit image */
+                *ncolorsP = 0;
+            }
+        } else if (*depthP ==1) {
+            /* mono image */
+            short dummy;
+            pm_readbigshort(fp, &dummy);  /* must toss cmap size */
+            *ncolorsP = 0;
+        } else {
+            /* gray image */
+            short dummy;
+            pm_readbigshort(fp, &dummy);  /* must toss cmap size */
+            *ncolorsP = 0;
+        }
+    }
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    pixval const maxval = LEAF_MAXVAL;
+
+    FILE *ifd;
+    pixel colormap[MAXCOLORS];
+    int rows, cols, row, col, depth, ncolors;
+
+    
+    ppm_init(&argc, argv);
+    
+    if (argc-1 > 1)
+        pm_error("Too many arguments.  Only argument is ileaf file name");
+    
+    if (argc-1 == 1)
+        ifd = pm_openr(argv[1]);
+    else
+        ifd = stdin;
+    
+    leaf_init(ifd, &cols, &rows, &depth, &ncolors, colormap);
+    
+    if ((depth == 8) && (ncolors == 0)) {
+        /* gray image */
+        gray * grayrow;
+        unsigned int row;
+        pgm_writepgminit( stdout, cols, rows, maxval, 0 );
+        grayrow = pgm_allocrow(cols);
+        for (row = 0; row < rows; ++row) {
+            unsigned int col;
+            for ( col = 0; col < cols; ++col)
+                grayrow[col] = (gray)fgetc(ifd);
+            if (cols % 2)
+                fgetc (ifd); /* padding */
+            pgm_writepgmrow(stdout, grayrow, cols, maxval, 0);
+        }
+        pgm_freerow(grayrow);
+    } else if (depth == 24) {
+        pixel * pixrow;
+        unsigned int row;
+        ppm_writeppminit(stdout, cols, rows, maxval, 0);
+        pixrow = ppm_allocrow(cols);
+        /* true color */
+        for (row = 0; row < rows; ++row) {
+            for (col = 0; col < cols; ++col)
+                PPM_PUTR(pixrow[col], fgetc(ifd));
+            if (cols % 2)
+                fgetc (ifd); /* padding */
+            for (col = 0; col < cols; ++col)
+                PPM_PUTG(pixrow[col], fgetc(ifd));
+            if (cols % 2)
+                fgetc (ifd); /* padding */
+            for (col = 0; col < cols; ++col)
+                PPM_PUTB(pixrow[col], fgetc(ifd));
+            if (cols % 2)
+                fgetc (ifd); /* padding */
+            ppm_writeppmrow(stdout, pixrow, cols, maxval, 0);
+        }
+        ppm_freerow(pixrow);
+    } else if (depth == 8) {
+        /* 8-bit (color mapped) image */
+        pixel * pixrow;
+
+        ppm_writeppminit(stdout, cols, rows, (pixval) maxval, 0);
+        pixrow = ppm_allocrow( cols );
+    
+        for (row = 0; row < rows; ++row) {
+            for (col = 0; col < cols; ++col)
+                pixrow[col] = colormap[fgetc(ifd)];
+            if (cols %2)
+                fgetc(ifd); /* padding */
+            ppm_writeppmrow(stdout, pixrow, cols, maxval, 0);
+        }
+        ppm_freerow(pixrow);
+    } else if (depth == 1) {
+        /* mono image */
+        bit *bitrow;
+        unsigned int row;
+        
+        pbm_writepbminit(stdout, cols, rows, 0);
+        bitrow = pbm_allocrow(cols);
+    
+        for (row = 0; row < rows; ++row) {
+            unsigned char bits;
+            bits = 0x00;  /* initial value */
+            for (col = 0; col < cols; ++col) {
+                int const shift = col % 8;
+                if (shift == 0) 
+                    bits = (unsigned char) fgetc(ifd);
+                bitrow[col] = (bits & (unsigned char)(0x01 << (7 - shift))) ? 
+                    PBM_WHITE : PBM_BLACK;
+            }
+            if ((cols % 16) && (cols % 16) <= 8)
+                fgetc(ifd);  /* 16 bit pad */
+            pbm_writepbmrow(stdout, bitrow, cols, 0);
+        }
+        pbm_freerow(bitrow);
+    }
+    pm_close(ifd);
+    
+    return 0;
+}
diff --git a/converter/ppm/mitsu.h b/converter/ppm/mitsu.h
new file mode 100644
index 00000000..8676a39d
--- /dev/null
+++ b/converter/ppm/mitsu.h
@@ -0,0 +1,97 @@
+#ifndef MITSU_H_INCLUDED
+#define MITSU_H_INCLUDED
+
+/* static char SCCSid[] = "@(#)mitsu.h\t\t1.3\t(SPZ)\t3/11/92\n"; */
+#define MAXLUTCOL   255
+
+#define A4_MAXCOLS  1184
+#define A4_MAXROWS  1452
+#define A4S_MAXROWS 1754
+#define A_MAXCOLS   1216
+#define A_MAXROWS   1350
+#define AS_MAXROWS  1650
+
+#define ONLINE         cmd('\021')
+#define CLRMEM         cmd('\033'), cmd('Z')
+
+struct mediasize {
+        char size;
+        int  maxcols, maxrows;
+};
+
+const struct mediasize MSize_User={' ',1184,1350};
+const struct mediasize MSize_A4  ={'0',1184,1452};
+const struct mediasize MSize_A   ={'1',1216,1350};
+const struct mediasize MSize_A4S ={'2',1184,1754};
+const struct mediasize MSize_AS  ={'3',1216,1650};
+#define MEDIASIZE(chr) cmd('\033'), cmd('#'), cmd('P'), cmd((chr).size)
+
+#define HENLARGE(enl)  cmd('\033'), cmd('&'), cmd('P'), cmd(enl), cmd('\001')
+#define VENLARGE(enl)  cmd('\033'), cmd('&'), cmd('Q'), cmd(enl), cmd('\001')
+#define NOENLARGE '\001'
+#define ENLARGEx2 '\002'
+#define ENLARGEx3 '\003'
+
+#define COLREVERSION(arg) cmd('\033'), cmd('&'), cmd('W'), cmd(arg)
+#define DONTREVERTCOLOR '0'
+#define REVERTCOLOR   '2'
+
+#define NUMCOPY(num)   cmd('\033'), cmd('#'), cmd('C'), cmd((num) & 0xff)
+
+#define HOFFINCH(off)  cmd('\033'), cmd('&'), cmd('S'), cmd((off) & 0xff)
+#define VOFFINCH(off)  cmd('\033'), cmd('&'), cmd('T'), cmd((off) & 0xff)
+
+#define CENTERING(cen) cmd('\033'), cmd('&'), cmd('C'), cmd(cen)
+#define DONTCENTER '0'
+#define DOCENTER   '1'
+
+#define TRANSFERFORMAT(fmt) cmd('\033'), cmd('&'), cmd('A'), cmd(fmt)
+#define FRAMEORDER  '0'
+#define LINEORDER   '1'
+#define LOOKUPTABLE '3'
+
+#define COLORSYSTEM(cs) cmd('\033'), cmd('&'), cmd('I'), cmd(cs)
+#define RGB '0'
+#define YMC '1'
+
+#define SHARPNESS(spn) cmd('\033'), cmd('#'), cmd('E'), cmd(spn)
+#define SP_USER ' '
+#define SP_NONE '0'
+#define SP_LOW  '1'
+#define SP_MIDLOW '2'
+#define SP_MIDHIGH '3'
+#define SP_HIGH '4'
+
+#define COLORDES(col) cmd('\033'), cmd('C'), cmd(col)
+#define RED   '1'
+#define GREEN '2'
+#define BLUE  '3'
+#define YELLOW  '1'
+#define MAGENTA '2'
+#define CYAN    '3'
+
+#define HPIXELS(hpix) cmd('\033'), cmd('&'), cmd('H'),\
+                                                        cmd(((hpix) >> 8) & 0xff), cmd((hpix) & 0xff)
+#define VPIXELS(vpix) cmd('\033'), cmd('&'), cmd('V'),\
+                                                        cmd(((vpix) >> 8) & 0xff), cmd((vpix) & 0xff)
+#define HPIXELSOFF(hoff) cmd('\033'), cmd('&'), cmd('J'),\
+                                                        cmd(((hoff) >> 8) & 0xff), cmd((hoff) & 0xff)
+#define VPIXELSOFF(voff) cmd('\033'), cmd('&'), cmd('K'),\
+                                                        cmd(((voff) >> 8) & 0xff), cmd((voff) & 0xff)
+
+#define GRAYSCALELVL(lvl) cmd('\033'), cmd('#'), cmd('L'), cmd(lvl)
+#define BIT_6 '\006'
+#define BIT_8 '\010'
+
+#define LOADLOOKUPTABLE cmd('\033'), cmd('&'), cmd('L')
+#define DONELOOKUPTABLE cmd('\004')
+
+#define ROTATEIMG(rot)  cmd('\033'), cmd('#'), cmd('R'), cmd(rot)
+#define DONTROTATE '0'
+#define DOROTATE   '1'
+
+#define DATASTART cmd('\033'), cmd('O')
+#define PRINTIT cmd('\014')
+#define OFFLINE cmd('\023')
+
+#endif
diff --git a/converter/ppm/mtvtoppm.c b/converter/ppm/mtvtoppm.c
new file mode 100644
index 00000000..e8558632
--- /dev/null
+++ b/converter/ppm/mtvtoppm.c
@@ -0,0 +1,68 @@
+/* mtvtoppm.c - read an MTV ray-tracer output file and produce a portable pixmap
+**
+** Copyright (C) 1989 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "ppm.h"
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    pixel* pixelrow;
+    register pixel* pP;
+    int rows, cols, row, col;
+    pixval maxval;
+#define MAXLINE 500
+    char line[MAXLINE];
+    unsigned char buf[3];
+
+
+    ppm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[mtvfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    /* Read in the MTV file.  First the header. */
+    if ( fgets( line, MAXLINE, ifp ) == NULL )
+	pm_error( "unable to read MTV file header" );
+    if ( sscanf( line, "%d%d", &cols, &rows ) != 2 )
+	pm_error( "unable to parse MTV file header" );
+
+    if ( cols <= 0 || rows <= 0 )
+	pm_error( "invalid size: %d %d", cols, rows );
+    maxval = 255;
+
+    ppm_writeppminit( stdout, cols, rows, maxval, 0 );
+    pixelrow = ppm_allocrow( cols );
+
+    for ( row = 0; row < rows; row++ )
+	{
+	for ( col = 0, pP = pixelrow; col < cols; col++, pP++ )
+	    {
+	    if ( fread( buf, sizeof(buf), 1, ifp ) != 1 )
+		pm_error( "EOF / read error" );
+	    PPM_ASSIGN( *pP, buf[0], buf[1], buf[2] );
+	    }
+	ppm_writeppmrow( stdout, pixelrow, cols, maxval, 0 );
+	}
+
+    pm_close( ifp );
+    pm_close( stdout );
+
+    exit( 0 );
+    }
diff --git a/converter/ppm/neotoppm.c b/converter/ppm/neotoppm.c
new file mode 100644
index 00000000..d8cf2918
--- /dev/null
+++ b/converter/ppm/neotoppm.c
@@ -0,0 +1,104 @@
+/* neotoppm.c - read a Neochrome NEO file and produce a portable pixmap
+**
+** Copyright (C) 2001 by Teemu Hukkanen <tjhukkan@iki.fi>
+**  Based on pi1toppm by Steve Belczyk (seb3@gte.com) and Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "ppm.h"
+
+#define ROWS 200
+#define COLS 320
+#define MAXVAL 7
+
+static short screen[ROWS*COLS/4];        /* simulates the Atari's video RAM */
+
+int
+main( int argc, char * argv[] ) {
+    FILE* ifp;
+    pixel pal[16];                      /* Degas palette */
+    pixel* pixelrow;
+    int row;
+
+    ppm_init( &argc, argv );
+
+    /* Check args. */
+    if ( argc > 2 )
+        pm_usage( "[neofile]" );
+
+    if ( argc == 2 )
+        ifp = pm_openr( argv[1] );
+    else
+        ifp = stdin;
+
+    /* Check header */
+    {
+        /* Flag */
+        short j;
+        pm_readbigshort (ifp, &j);
+        if ( j != 0 )
+            pm_error( "not a NEO file" );
+    }
+    {
+        /* Check resolution word */
+        short j;
+        pm_readbigshort (ifp, &j);
+        if ( j != 0 )
+            pm_error( "not a NEO file" );
+    }
+    {
+        int i;
+        /* Read the palette. */
+        for ( i = 0; i < 16; ++i ) {
+            short j;
+            
+            pm_readbigshort (ifp, &j);
+            PPM_ASSIGN( pal[i],
+                        ( j & 0x700 ) >> 8,
+                        ( j & 0x070 ) >> 4,
+                        ( j & 0x007 ) );
+        }
+    }
+
+    /* Skip rest of header */
+    fseek(ifp, 128, SEEK_SET);
+
+    {
+        /* Read the screen data */
+        int i;
+        for ( i = 0; i < ROWS*COLS/4; ++i )
+            pm_readbigshort( ifp, &screen[i] );
+    }
+    pm_close( ifp );
+
+    /* Ok, get set for writing PPM. */
+    ppm_writeppminit( stdout, COLS, ROWS, MAXVAL, 0 );
+    pixelrow = ppm_allocrow( COLS );
+
+    /* Now do the conversion. */
+    for ( row = 0; row < ROWS; ++row ) {
+        int col;
+        for ( col = 0; col < COLS; ++col ) {
+            int c, ind, b, plane;
+
+            ind = 80 * row + ( ( col >> 4 ) << 2 );
+            b = 0x8000 >> ( col & 0xf );
+            c = 0;
+            for ( plane = 0; plane < 4; ++plane )
+                if ( b & screen[ind+plane] )
+                    c |= (1 << plane);
+            pixelrow[col] = pal[c];
+        }
+        ppm_writeppmrow( stdout, pixelrow, COLS, MAXVAL, 0 );
+    }
+
+    pm_close( stdout );
+
+    exit( 0 );
+}
diff --git a/converter/ppm/pc1toppm.c b/converter/ppm/pc1toppm.c
new file mode 100644
index 00000000..5ba247e9
--- /dev/null
+++ b/converter/ppm/pc1toppm.c
@@ -0,0 +1,233 @@
+/* pc1toppm.c - read a Degas Elite PC1 file and produce a PPM
+
+ Copyright (C) 1991 by Steve Belczyk (seb3@gte.com) and Jef Poskanzer.
+ Copyright (C) 2004 by Roine Gustafsson (roine@users.sourceforge.net)
+
+ Converted to modern Netpbm code style by Bryan Henderson April 2004.
+ That work is contributed to the public domain by Bryan.
+
+ Permission to use, copy, modify, and distribute this software and its
+ documentation for any purpose and without fee is hereby granted, provided
+ that the above copyright notice appear in all copies and that both that
+ copyright notice and this permission notice appear in supporting
+ documentation.  This software is provided "as is" without express or
+ implied warranty.
+
+ Algorithm for PC1 compression from "gimp-degas" GIMP plugin 
+ by Markus F.X.J. Oberhumer
+
+*/
+
+#include "ppm.h"
+
+/* The code has not been tested for other resolutions than 320x200 */
+static unsigned int const rows   = 200;
+static unsigned int const cols   = 320;
+static unsigned int const planes = 4;  /* 4 bits for 16 colors */
+static pixval       const maxval = 7;
+
+unsigned int const colsPerBlock = 16;
+
+#define BLOCKS (cols/colsPerBlock)
+#define ROWBYTES (cols * planes / 8)
+#define BLOCKBYTES (colsPerBlock / 8)
+#define PLANEBYTES (ROWBYTES / planes)
+#define ROWSHORTS (cols * planes / 16)
+#define BLOCKSHORTS (colsPerBlock * planes / 16)
+#define PLANESHORTS (ROWSHORTS / planes)
+
+
+
+static void
+readPalette(FILE *   const ifP, 
+            pixel (* const palP)[]) {
+
+    /* Read the palette. */
+    unsigned int i;
+    for (i = 0; i < 16; ++i) {
+        unsigned short j;
+        pm_readbigshortu(ifP, &j);
+        PPM_ASSIGN((*palP)[i],
+                   (j & 0x700) >> 8,
+                   (j & 0x070) >> 4,
+                   (j & 0x007) >> 0);
+    }
+}
+
+
+
+static void
+processStretch(unsigned int     const countbyte,
+               FILE *           const ifP,
+               unsigned int *   const colP,
+               unsigned char ** const lineposP) {
+
+    if (countbyte <= 127) {
+        /* countbyte+1 literal bytes follows */
+
+        unsigned int const count = countbyte + 1;
+        unsigned int i;
+
+        if (*colP + count > ROWBYTES)
+            pm_error("Error in PC1 file.  "
+                     "A literal stretch extends beyond the end of a row");
+
+        for (i = 0; i < count; ++i) {
+            *(*lineposP)++ = getc(ifP);
+            ++(*colP);
+        }
+    } else {
+        /* next byte repeated 257-countbyte times */
+                
+        unsigned char const duplicated_color = getc(ifP);
+        unsigned int  const count = 257 - countbyte;
+        unsigned int i;
+
+        if (*colP + count > ROWBYTES)
+            pm_error("Error in PC1 file.  "
+                     "A run extends beyond the end of a row.");
+        for (i = 0; i < count; ++i) {
+            *(*lineposP)++ = duplicated_color;
+            ++(*colP);
+        }
+    }
+}
+
+
+
+static void
+readPc1(FILE * const ifP,
+        pixel (*palP)[],
+        unsigned char (*bufferP)[]) {
+
+    unsigned short j;
+    unsigned int row;
+    unsigned char* bufferCursor;
+
+    /* Check resolution word */
+    pm_readbigshortu(ifP, &j);
+    if (j != 0x8000)
+        pm_error("This is not a PC1 file.  "
+                 "The first two bytes are 0x%04X instead of 0x8000", j);
+
+    readPalette(ifP, palP);
+
+    /* Read the screen data */
+    bufferCursor = &(*bufferP)[0];
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        for (col = 0; col < ROWBYTES;) {
+            unsigned int const countbyte = getc(ifP);
+
+            processStretch(countbyte, ifP, &col, &bufferCursor);
+	    }
+	}
+}
+
+
+
+static void
+reInterleave(unsigned char     const buffer[],
+             unsigned short (* const screenP)[]) {
+    
+    /* The buffer is in one plane for each line, to optimize packing */
+    /* Re-interleave to match the Atari screen layout                */
+
+    unsigned short * const screen = *screenP;
+    unsigned int row;
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int block;
+        for (block = 0; block < BLOCKS; ++block) {
+            unsigned int plane;
+            for (plane = 0; plane < planes; ++plane) {
+                unsigned int const blockIndex = 
+                    row*ROWBYTES + plane*PLANEBYTES + block*BLOCKBYTES;
+
+                screen[row*ROWSHORTS + block*BLOCKSHORTS + plane] = 
+                    (buffer[blockIndex+0] << 8) + (buffer[blockIndex+1]);
+            }
+        }
+    }
+}
+
+
+
+static void
+writePpm(FILE *         const ofP,
+         unsigned short const screen[],
+         pixel          const palette[]) {
+
+    pixel* pixelrow;
+    unsigned int row;
+
+    ppm_writeppminit(ofP, cols, rows, maxval, 0);
+    pixelrow = ppm_allocrow(cols);
+
+    for (row = 0; row < rows; ++row) {
+        /* Each row is arranged into blocks of 16 columns.  Each block
+           is represented by 4 shorts (16 bit integers).  The
+           first is for Plane 0, the second for Plane 1, etc.  Each short
+           contains the bits for that plane for each of the 16 columns,
+           arranged from most signficant bit to least in increasing column
+           number.
+        */
+        unsigned int col0ScreenIndex = cols/16*planes * row;
+
+        unsigned int col;
+        for (col = 0; col < cols; ++col) {
+            unsigned int const ind = col0ScreenIndex + col/16 * planes;
+            unsigned int const b = 0x8000 >> (col % 16);
+
+            unsigned int plane;
+            unsigned int colorIndex;
+
+            colorIndex = 0;
+            for (plane = 0; plane < planes; ++plane)
+                if (b & screen[ind+plane])
+                    colorIndex |= (1 << plane);
+            pixelrow[col] = palette[colorIndex];
+        }
+        ppm_writeppmrow(ofP, pixelrow, cols, maxval, 0);
+    }
+    ppm_freerow(pixelrow);
+}
+
+
+
+int
+main(int argc, char ** argv) {
+
+    const char * inputFilename;
+    FILE* ifP;
+    pixel palette[16];  /* Degas palette */
+    static unsigned short screen[32000/2];   
+        /* simulates the Atari's video RAM */
+    static unsigned char buffer[32000];   
+        /* simulates the Atari's video RAM */
+
+    ppm_init(&argc, argv);
+
+    if (argc-1 == 0)
+        inputFilename = "-";
+    else if (argc-1 == 1)
+        inputFilename = argv[1];
+    else
+        pm_error("There is at most one argument to this program:  "
+                 "the input file name.  You specified %d", argc-1);
+
+    ifP = pm_openr(inputFilename);
+
+    readPc1(ifP, &palette, &buffer);
+
+    pm_close(ifP);
+
+    reInterleave(buffer, &screen);
+
+    writePpm(stdout, screen, palette);
+
+    pm_close(stdout);
+
+    return 0;
+}
diff --git a/converter/ppm/pcxstd.ppm b/converter/ppm/pcxstd.ppm
new file mode 100644
index 00000000..513602e7
--- /dev/null
+++ b/converter/ppm/pcxstd.ppm
@@ -0,0 +1,19 @@
+P3
+16 1
+255
+  0   0   0
+  0   0 170
+  0 170   0
+  0 170 170
+170   0   0
+170   0 170
+170 170   0
+170 170 170
+ 85  85  85
+ 85  85 255
+ 85 255  85
+ 85 255 255
+255  85  85
+255  85 255
+255 255  85
+255 255 255
diff --git a/converter/ppm/pcxtoppm.c b/converter/ppm/pcxtoppm.c
new file mode 100644
index 00000000..9f403538
--- /dev/null
+++ b/converter/ppm/pcxtoppm.c
@@ -0,0 +1,710 @@
+/*
+ * pcxtoppm.c - Converts from a PC Paintbrush PCX file to a PPM file.
+ *
+ * Copyright (c) 1990 by Michael Davidson
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind.  The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof.  In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ *
+ * Modifications by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
+ * 20/Apr/94:
+ *  - checks if 16-color-palette is completely black -> use standard palette
+ *  - "-stdpalette" option to enforce this
+ *  - row-by-row operation (PPM output)
+ *  11/Dec/94:
+ *  - capability for 24bit and 32bit (24bit + 8bit intensity) images
+ *  - row-by-row operation (PCX input, for 16-color and truecolor images)
+ *  - some code restructuring
+ *  15/Feb/95:
+ *  - bugfix for 16 color-images: few bytes allocated for rawrow in some cases
+ *  - added sanity checks for cols<->BytesPerLine
+ *  17/Jul/95:
+ *  - moved check of 16-color-palette into pcx_16col_to_ppm(),
+ *    now checks if it contains only a single color
+ */
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "ppm.h"
+
+#define PCX_MAGIC       0x0a            /* PCX magic number             */
+#define PCX_HDR_SIZE    128             /* size of PCX header           */
+#define PCX_256_COLORS  0x0c            /* magic number for 256 colors  */
+
+#define PCX_MAXVAL      (pixval)255
+
+/* standard palette */
+static unsigned char const StdRed[]   = { 0, 255,   0,   0, 170, 170, 170, 170, 85,  85,  85,  85, 255, 255, 255, 255 };
+static unsigned char const StdGreen[] = { 0, 255, 170, 170,   0,   0, 170, 170, 85,  85, 255, 255,  85,  85, 255, 255 };
+static unsigned char const StdBlue[]  = { 0, 255,   0, 170,   0, 170,   0, 170, 85, 255,  85, 255,  85, 255,  85, 255 };
+
+static pixel stdPalette[16];
+
+static void
+generateStdPalette(void) {
+
+    int i;
+    for (i = 0; i < 16; i++)
+        PPM_ASSIGN(stdPalette[i], StdRed[i], StdGreen[i], StdBlue[i]);
+}
+
+
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *inputFilespec;  /* '-' if stdin */
+    unsigned int verbose;  
+    unsigned int stdpalette;
+};
+
+
+
+static void
+parseCommandLine ( int argc, char ** argv,
+                   struct cmdlineInfo *cmdlineP ) {
+/*----------------------------------------------------------------------------
+   parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def = malloc( 100*sizeof( optEntry ) );
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "stdpalette",     OPT_FLAG,   NULL,                  
+            &cmdlineP->stdpalette,    0 );
+    OPTENT3(0, "verbose",        OPT_FLAG,   NULL,                  
+            &cmdlineP->verbose,       0 );
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+        /* Uses and sets argc, argv, and some of *cmdline_p and others. */
+
+    if (argc-1 < 1)
+        cmdlineP->inputFilespec = "-";
+    else if (argc-1 == 1)
+        cmdlineP->inputFilespec = argv[1];
+    else
+        pm_error("Program takes at most one argument "
+                 "(input file specification).  You specified %d",
+                 argc-1);
+}
+
+
+
+struct pcxHeader {
+    int Version;
+    /* Xmin, Ymin, Xmax, and Ymax are positions in some field (in units of
+       pixels) of the edges of the image.  They may be negative.  You can
+       derive the image width and height from these.
+    */
+    short Xmin;            
+    short Ymin;
+    short Xmax;
+    short Ymax;
+    short Encoding;
+    short Planes;
+    short BitsPerPixel;
+    short BytesPerLine;
+        /* Number of decompressed bytes each plane of each row of the image 
+           takes.  Due to padding (this is always an even number), there may
+           be garbage on the right end that isn't part of the image.
+        */
+    short PaletteInfo;
+    short HorizontalResolution;
+    short VerticalResolution;
+    pixel cmap16[16];
+};
+
+
+
+static void
+readInt(FILE * const ifP, short * const retvalP) {
+/*----------------------------------------------------------------------------
+   Read a 2-byte little-endian word from the file.
+-----------------------------------------------------------------------------*/
+    int rc;
+
+    rc = pm_readlittleshort(ifP, retvalP);
+
+    if (rc != 0)
+        pm_error("EOF/error reading integer from input file.");
+}
+
+
+
+static int
+GetByte(FILE * const fp) {
+
+    int    c;
+
+    if ((c = fgetc(fp)) == EOF)
+        pm_error("unexpected end of file" );
+
+    return c;
+}
+
+
+
+static void
+readPcxHeader(FILE *             const ifP, 
+              struct pcxHeader * const pcxHeaderP) {
+/*----------------------------------------------------------------------------
+   Read the PCX header
+-----------------------------------------------------------------------------*/
+    if (GetByte(ifP) != PCX_MAGIC)
+        pm_error("bad magic number - not a PCX file");
+
+    pcxHeaderP->Version = GetByte(ifP);  /* get version # */
+
+    pcxHeaderP->Encoding = GetByte(ifP);
+    if (pcxHeaderP->Encoding != 1)    /* check for PCX run length encoding   */
+        pm_error("unknown encoding scheme: %d", pcxHeaderP->Encoding);
+
+    pcxHeaderP->BitsPerPixel= GetByte(ifP);
+    readInt(ifP, &pcxHeaderP->Xmin);
+    readInt(ifP, &pcxHeaderP->Ymin);
+    readInt(ifP, &pcxHeaderP->Xmax);
+    readInt(ifP, &pcxHeaderP->Ymax);
+
+    if (pcxHeaderP->Xmax < pcxHeaderP->Xmin)
+        pm_error("Invalid PCX input:  minimum X value (%d) is greater than "
+                 "maximum X value (%d).", 
+                 pcxHeaderP->Xmin, pcxHeaderP->Xmax);
+    if (pcxHeaderP->Ymax < pcxHeaderP->Ymin)
+        pm_error("Invalid PCX input:  minimum Y value (%d) is greater than "
+                 "maximum Y value (%d).", 
+                 pcxHeaderP->Ymin, pcxHeaderP->Ymax);
+
+    readInt(ifP, &pcxHeaderP->HorizontalResolution);  
+    readInt(ifP, &pcxHeaderP->VerticalResolution);  
+
+    {
+        int i;
+        /*
+         * get the 16-color color map
+         */
+        for (i = 0; i < 16; i++) {
+            unsigned int const r = GetByte(ifP);
+            unsigned int const g = GetByte(ifP);
+            unsigned int const b = GetByte(ifP);
+            PPM_ASSIGN(pcxHeaderP->cmap16[i], r, g, b);
+        }
+    }
+
+    GetByte(ifP);                /* skip reserved byte       */
+    pcxHeaderP->Planes      = GetByte(ifP);     /* # of color planes        */
+    readInt(ifP, &pcxHeaderP->BytesPerLine);
+    readInt(ifP, &pcxHeaderP->PaletteInfo);
+
+    /* Read past a bunch of reserved space in the header.  We have read
+       70 bytes of the header so far.  We would just seek here, except that
+       we want to work with unseekable (e.g. pipe input).
+    */
+    {
+        unsigned int pos;
+
+        for (pos = 70; pos < PCX_HDR_SIZE; ++pos)
+            GetByte(ifP);
+    }
+}
+
+
+
+static void 
+reportPcxHeader(struct pcxHeader const pcxHeader) {
+
+    pm_message("Version: %d", pcxHeader.Version);
+    pm_message("BitsPerPixel: %d", pcxHeader.BitsPerPixel);
+    pm_message("Xmin: %d   Ymin: %d   Xmax: %d   Ymax: %d", 
+               pcxHeader.Xmin, pcxHeader.Ymin, pcxHeader.Xmax, pcxHeader.Ymax);
+    pm_message("Planes: %d    BytesPerLine: %d    PaletteInfo: %d", 
+               pcxHeader.Planes, pcxHeader.BytesPerLine, 
+               pcxHeader.PaletteInfo);
+    pm_message("Color map in image:  (index: r/g/b)");
+    if (pcxHeader.BitsPerPixel < 8) {
+        unsigned int colorIndex;
+        for (colorIndex = 0; colorIndex < 16; ++colorIndex) {
+            pixel const p = pcxHeader.cmap16[colorIndex];
+            pm_message("  %u: %u/%u/%u", colorIndex, 
+                       PPM_GETR(p), PPM_GETG(p), PPM_GETB(p));
+        }
+    }
+}
+
+
+
+static bool
+allBlackPalette(pixel cmap16[]) {
+
+    unsigned int colorIndex;
+    bool allBlack;
+
+    allBlack = TRUE;  /* initial assumption */
+    for (colorIndex = 0; colorIndex < 16; ++colorIndex) {
+        pixel const p = cmap16[colorIndex];
+
+        if (PPM_GETR(p) != 0 ||
+            PPM_GETG(p) != 0 ||
+            PPM_GETB(p) != 0)
+
+            allBlack = FALSE;
+    }
+    return allBlack;
+}
+
+
+
+/*
+ *  read a single row encoded row, handles encoding across rows
+ */
+static void
+GetPCXRow(FILE * const ifP, unsigned char * const pcxrow, 
+          int const bytesperline) {
+/*----------------------------------------------------------------------------
+   Read one row from the PCX raster.
+   
+   The PCX raster is run length encoded as follows:  If the upper two
+   bits of a byte are 11, the lower 6 bits are a repetition count for the
+   raster byte that follows.  If the upper two bits are not 11, the byte 
+   _is_ a raster byte, with repetition count 1.
+
+   A run can't span rows, but it can span planes within a row.  That's
+   why 'repetitionsLeft' and 'c' are static variables in this
+   subroutine.
+-----------------------------------------------------------------------------*/
+    static int repetitionsLeft = 0;
+    static int c;
+    int bytesGenerated;
+
+    bytesGenerated = 0;
+    while( bytesGenerated < bytesperline ) {
+        if(repetitionsLeft > 0) {
+            pcxrow[bytesGenerated++] = c;
+            --repetitionsLeft;
+        } else {
+            c = GetByte(ifP);
+            if ((c & 0xc0) != 0xc0)
+                /* This is a 1-shot byte, not a repetition count */
+                pcxrow[bytesGenerated++] = c;
+            else {
+                /* This is a repetition count for the following byte */
+                repetitionsLeft = c & 0x3f;
+                c = GetByte(ifP);
+            }
+        }
+    }
+}
+
+
+
+/*
+ * convert packed pixel format into 1 pixel per byte
+ */
+static void
+pcx_unpack_pixels(pixels, bitplanes, bytesperline, planes, bitsperpixel)
+    unsigned char   *pixels;
+    unsigned char   *bitplanes;
+    int             bytesperline;
+    int             planes;
+    int             bitsperpixel;
+{
+    register int        bits;
+
+    if (planes != 1)
+        pm_error("can't handle packed pixels with more than 1 plane" );
+#if 0
+    if (bitsperpixel == 8)
+    {
+        while (--bytesperline >= 0)
+            *pixels++ = *bitplanes++;
+    }
+    else
+#endif
+        if (bitsperpixel == 4)
+        {
+            while (--bytesperline >= 0)
+            {
+                bits        = *bitplanes++;
+                *pixels++   = (bits >> 4) & 0x0f;
+                *pixels++   = (bits     ) & 0x0f;
+            }
+        }
+        else if (bitsperpixel == 2)
+        {
+            while (--bytesperline >= 0)
+            {
+                bits        = *bitplanes++;
+                *pixels++   = (bits >> 6) & 0x03;
+                *pixels++   = (bits >> 4) & 0x03;
+                *pixels++   = (bits >> 2) & 0x03;
+                *pixels++   = (bits     ) & 0x03;
+            }
+        }
+        else if (bitsperpixel == 1)
+        {
+            while (--bytesperline >= 0)
+            {
+                bits        = *bitplanes++;
+                *pixels++   = ((bits & 0x80) != 0);
+                *pixels++   = ((bits & 0x40) != 0);
+                *pixels++   = ((bits & 0x20) != 0);
+                *pixels++   = ((bits & 0x10) != 0);
+                *pixels++   = ((bits & 0x08) != 0);
+                *pixels++   = ((bits & 0x04) != 0);
+                *pixels++   = ((bits & 0x02) != 0);
+                *pixels++   = ((bits & 0x01) != 0);
+            }
+        }
+        else
+            pm_error("pcx_unpack_pixels - can't handle %d bits per pixel", 
+                     bitsperpixel);
+}
+
+
+
+/*
+ * convert multi-plane format into 1 pixel per byte
+ */
+static void
+pcx_planes_to_pixels(pixels, bitplanes, bytesperline, planes, bitsperpixel)
+    unsigned char   *pixels;
+    unsigned char   *bitplanes;
+    int             bytesperline;
+    int             planes;
+    int             bitsperpixel;
+{
+    int  i, j;
+    int  npixels;
+    unsigned char    *p;
+
+    if (planes > 4)
+        pm_error("can't handle more than 4 planes" );
+    if (bitsperpixel != 1)
+        pm_error("can't handle more than 1 bit per pixel" );
+
+    /*
+     * clear the pixel buffer
+     */
+    npixels = (bytesperline * 8) / bitsperpixel;
+    p    = pixels;
+    while (--npixels >= 0)
+        *p++ = 0;
+
+    /*
+     * do the format conversion
+     */
+    for (i = 0; i < planes; i++)
+    {
+        int pixbit, bits, mask;
+
+        p    = pixels;
+        pixbit    = (1 << i);
+        for (j = 0; j < bytesperline; j++)
+        {
+            bits = *bitplanes++;
+            for (mask = 0x80; mask != 0; mask >>= 1, p++)
+                if (bits & mask)
+                    *p |= pixbit;
+        }
+    }
+}
+
+
+
+static void
+pcx_16col_to_ppm(FILE *       const ifP,
+                 unsigned int const headerCols,
+                 unsigned int const rows,
+                 unsigned int const BytesPerLine,
+                 unsigned int const BitsPerPixel,
+                 unsigned int const Planes,
+                 pixel *      const cmap) {
+
+    unsigned int cols;
+    int row, col, rawcols, colors;
+    unsigned char * pcxrow;
+    unsigned char * rawrow;
+    pixel * ppmrow;
+    bool paletteOk;
+
+    paletteOk = FALSE;
+
+    /* check if palette is ok  */
+    colors = (1 << BitsPerPixel) * (1 << Planes);
+    for (col = 0; col < colors - 1; ++col) {
+        if (!PPM_EQUAL(cmap[col], cmap[col+1])) {
+            paletteOk = TRUE;
+            break;
+        }
+    }
+    if (!paletteOk) {
+        unsigned int col;
+        pm_message("warning - useless header palette, "
+                   "using builtin standard palette");
+        for (col = 0; col < colors; ++col)
+            PPM_ASSIGN(cmap[col], StdRed[col], StdGreen[col], StdBlue[col]);
+    }
+
+    /*  BytesPerLine should be >= BitsPerPixel * cols / 8  */
+    rawcols = BytesPerLine * 8 / BitsPerPixel;
+    if (headerCols > rawcols) {
+        pm_message("warning - BytesPerLine = %d, "
+                   "truncating image to %d pixels",
+                   BytesPerLine, rawcols);
+        cols = rawcols;
+    } else
+        cols = headerCols;
+
+    MALLOCARRAY(pcxrow, Planes * BytesPerLine);
+    if (pcxrow == NULL)
+        pm_error("Out of memory");
+    MALLOCARRAY(rawrow, rawcols);
+    if (rawrow == NULL)
+        pm_error("Out of memory");
+
+    ppmrow = ppm_allocrow(cols);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        GetPCXRow(ifP, pcxrow, Planes * BytesPerLine);
+
+        if (Planes == 1)
+            pcx_unpack_pixels(rawrow, pcxrow, BytesPerLine, 
+                              Planes, BitsPerPixel);
+        else
+            pcx_planes_to_pixels(rawrow, pcxrow, BytesPerLine, 
+                                 Planes, BitsPerPixel);
+
+        for (col = 0; col < cols; ++col)
+            ppmrow[col] = cmap[rawrow[col]];
+        ppm_writeppmrow(stdout, ppmrow, cols, PCX_MAXVAL, 0);
+    }
+    ppm_freerow(ppmrow);
+    free(rawrow);
+    free(pcxrow);
+}
+
+
+
+static void
+pcx_256col_to_ppm(FILE *       const ifP,
+                  unsigned int const headerCols,
+                  unsigned int const rows,
+                  unsigned int const BytesPerLine) {
+
+    unsigned int cols;
+    pixel colormap[256];
+    pixel * ppmrow;
+    unsigned char ** image;
+    unsigned char colormapSignature;
+    unsigned int row;
+
+    if (headerCols > BytesPerLine) {
+        pm_message("warning - BytesPerLine = %u, "
+                   "truncating image to %u pixels",
+                   BytesPerLine,  BytesPerLine);
+        cols = BytesPerLine;
+    } else
+        cols = headerCols;
+
+    image = (unsigned char **)pm_allocarray(BytesPerLine, rows, 
+                                            sizeof(unsigned char));
+    for (row = 0; row < rows; ++row)
+        GetPCXRow(ifP, image[row], BytesPerLine);
+
+    /*
+     * 256 color images have their color map at the end of the file
+     * preceeded by a magic byte
+     */
+    colormapSignature = GetByte(ifP);
+    if (colormapSignature != PCX_256_COLORS)
+        pm_error("bad color map signature.  In a 1-plane PCX image "
+                 "such as this, we expect a magic number of %u in the byte "
+                 "following the raster, to introduce the color map.  "
+                 "Instead, this image has %u.", 
+                 PCX_256_COLORS, colormapSignature);
+    else {
+        unsigned int colorIndex;
+        
+        for (colorIndex = 0; colorIndex < 256; ++colorIndex) {
+            pixval const r = GetByte(ifP);
+            pixval const g = GetByte(ifP);
+            pixval const b = GetByte(ifP);
+            PPM_ASSIGN(colormap[colorIndex], r, g, b);
+        }
+    }
+
+    ppmrow = ppm_allocrow(cols);
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        for (col = 0; col < cols; ++col)
+            ppmrow[col] = colormap[image[row][col]];
+        ppm_writeppmrow(stdout, ppmrow, cols, PCX_MAXVAL, 0);
+    }
+
+    ppm_freerow(ppmrow);
+    pm_freearray((void *)image, rows);
+}
+
+
+
+static void
+pcx_truecol_to_ppm(FILE *       const ifP,
+                   unsigned int const headerCols,
+                   unsigned int const rows,
+                   unsigned int const BytesPerLine,
+                   unsigned int const Planes) {
+
+    unsigned int cols;
+    unsigned char *redrow, *greenrow, *bluerow, *intensityrow;
+    pixel * ppmrow;
+    unsigned int row;
+    
+    if (headerCols > BytesPerLine) {
+        pm_message("warning - BytesPerLine = %u, "
+                   "truncating image to %u pixels",
+                   BytesPerLine,  BytesPerLine);
+        cols = BytesPerLine;
+    } else
+        cols = headerCols;
+
+    MALLOCARRAY(redrow, BytesPerLine);
+    MALLOCARRAY(greenrow, BytesPerLine);
+    MALLOCARRAY(bluerow, BytesPerLine);
+
+    if (redrow == NULL || greenrow == NULL || bluerow == NULL)
+        pm_error("out of memory");
+
+    if (Planes == 4) {
+        MALLOCARRAY(intensityrow, BytesPerLine);
+        if (intensityrow == NULL)
+            pm_error("out of memory");
+    } else
+        intensityrow = NULL;
+
+    ppmrow = ppm_allocrow(cols);
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        GetPCXRow(ifP, redrow, BytesPerLine);
+        GetPCXRow(ifP, greenrow, BytesPerLine);
+        GetPCXRow(ifP, bluerow, BytesPerLine);
+        if( intensityrow )
+            GetPCXRow(ifP, intensityrow, BytesPerLine);
+        for (col = 0; col < cols; ++col) {
+            unsigned int const r = redrow[col];
+            unsigned int const g = greenrow[col];
+            unsigned int const b = bluerow[col];
+            unsigned int const i = intensityrow ? intensityrow[col] : 256;
+
+            PPM_ASSIGN(ppmrow[col],
+                       r * i / 256, g * i / 256, b * i / 256);
+        }
+        ppm_writeppmrow(stdout, ppmrow, cols, PCX_MAXVAL, 0);
+    }
+
+    ppm_freerow(ppmrow);
+    if (intensityrow)
+        free(intensityrow);
+    free(bluerow);
+    free(greenrow);
+    free(redrow);
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    FILE * ifP;
+    struct cmdlineInfo cmdline;
+    struct pcxHeader pcxHeader;
+    unsigned int Width, Height;
+    pixel * cmap16;
+
+    ppm_init(&argc, argv);
+
+    generateStdPalette();
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    readPcxHeader(ifP, &pcxHeader);
+
+    if (cmdline.verbose)
+        reportPcxHeader(pcxHeader);
+
+    Width  = (pcxHeader.Xmax - pcxHeader.Xmin) + 1;
+    Height = (pcxHeader.Ymax - pcxHeader.Ymin) + 1;
+
+    if (cmdline.stdpalette || allBlackPalette(pcxHeader.cmap16))
+        cmap16 = stdPalette;
+    else
+        cmap16 = pcxHeader.cmap16;
+
+    ppm_writeppminit(stdout, Width, Height, PCX_MAXVAL, 0);
+    switch (pcxHeader.BitsPerPixel) {
+    case 1:
+        if(pcxHeader.Planes >= 1 && pcxHeader.Planes <= 4)
+            pcx_16col_to_ppm(ifP, Width, Height, pcxHeader.BytesPerLine, 
+                             pcxHeader.BitsPerPixel, pcxHeader.Planes, cmap16);
+        else
+            goto fail;
+        break;
+    case 2:
+    case 4:
+        if (pcxHeader.Planes == 1)
+            pcx_16col_to_ppm(ifP, Width, Height, pcxHeader.BytesPerLine, 
+                             pcxHeader.BitsPerPixel, pcxHeader.Planes, cmap16);
+        else
+            goto fail;
+        break;
+    case 8:
+        switch(pcxHeader.Planes) {
+        case 1:
+            pcx_256col_to_ppm(ifP, Width, Height, pcxHeader.BytesPerLine);
+            break;
+        case 3:
+        case 4:
+            pcx_truecol_to_ppm(ifP, Width, Height, 
+                               pcxHeader.BytesPerLine, pcxHeader.Planes);
+            break;
+        default:
+            goto fail;
+        }
+        break;
+    default:
+    fail:
+        pm_error("can't handle %d bits per pixel image with %d planes",
+                 pcxHeader.BitsPerPixel, pcxHeader.Planes);
+    }
+    pm_close(ifP);
+    
+    return 0;
+}
+
+
diff --git a/converter/ppm/pi1toppm.c b/converter/ppm/pi1toppm.c
new file mode 100644
index 00000000..69b99863
--- /dev/null
+++ b/converter/ppm/pi1toppm.c
@@ -0,0 +1,92 @@
+/* pi1toppm.c - read a Degas PI1 file and produce a portable pixmap
+**
+** Copyright (C) 1991 by Steve Belczyk (seb3@gte.com) and Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "ppm.h"
+
+#define ROWS 200
+#define COLS 320
+#define MAXVAL 7
+
+static short screen[ROWS*COLS/4];          /* simulates the Atari's video RAM */
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    pixel pal[16];                      /* Degas palette */
+    short i;
+    short j;
+    pixel* pixelrow;
+    register pixel* pP;
+    int row, col;
+
+
+    ppm_init( &argc, argv );
+
+    /* Check args. */
+    if ( argc > 2 )
+        pm_usage( "[pi1file]" );
+
+    if ( argc == 2 )
+        ifp = pm_openr( argv[1] );
+    else
+        ifp = stdin;
+
+    /* Check resolution word */
+    (void) pm_readbigshort (ifp, &j);
+    if ( j != 0 )
+        pm_error( "not a PI1 file" );
+
+    /* Read the palette. */
+    for ( i = 0; i < 16; ++i )
+        {
+        (void) pm_readbigshort (ifp, &j);
+        PPM_ASSIGN( pal[i],
+            ( j & 0x700 ) >> 8,
+            ( j & 0x070 ) >> 4,
+            ( j & 0x007 ) );
+        }
+
+    /* Read the screen data */
+    for ( i = 0; i < ROWS*COLS/4; ++i )
+        (void) pm_readbigshort( ifp, &screen[i] );
+
+    pm_close( ifp );
+
+    /* Ok, get set for writing PPM. */
+    ppm_writeppminit( stdout, COLS, ROWS, (pixval) MAXVAL, 0 );
+    pixelrow = ppm_allocrow( COLS );
+
+    /* Now do the conversion. */
+    for ( row = 0; row < ROWS; ++row )
+        {
+        for ( col = 0, pP = pixelrow; col < COLS; ++col, ++pP )
+            {
+            register int c, ind, b, plane;
+
+            ind = 80 * row + ( ( col >> 4 ) << 2 );
+            b = 0x8000 >> ( col & 0xf );
+            c = 0;
+            for ( plane = 0; plane < 4; ++plane )
+                if ( b & screen[ind+plane] )
+                    c |= (1 << plane);
+            *pP = pal[c];
+            }
+        ppm_writeppmrow( stdout, pixelrow, COLS, (pixval) MAXVAL, 0 );
+        }
+
+    pm_close( stdout );
+
+    exit( 0 );
+    }
diff --git a/converter/ppm/picttoppm.c b/converter/ppm/picttoppm.c
new file mode 100644
index 00000000..cfc5760e
--- /dev/null
+++ b/converter/ppm/picttoppm.c
@@ -0,0 +1,3788 @@
+/*
+ * picttoppm.c -- convert a MacIntosh PICT file to PPM format.
+ *
+ * Copyright 1989,1992,1993 George Phillips
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  This software is provided "as is" without express or
+ * implied warranty.
+ *
+ * George Phillips <phillips@cs.ubc.ca>
+ * Department of Computer Science
+ * University of British Columbia
+ *
+ *
+ * 2003-02:    Handling for DirectBitsRgn opcode (0x9b) added by 
+ *             kabe@sra-tohoku.co.jp.
+ *
+ * 2004-03-27: Several bugs fixed by Steve Summit, scs@eskimo.com.
+ *
+ */
+
+#define _XOPEN_SOURCE
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "pm_c_util.h"
+#include "ppm.h"
+#include "pbmfont.h"
+#include "mallocvar.h"
+#include "nstring.h"
+
+
+/*
+ * Typical byte, 2 byte and 4 byte integers.
+ */
+typedef unsigned char byte;
+typedef char signed_byte;
+typedef unsigned short word;
+typedef unsigned long longword;
+
+
+/*
+ * Data structures for QuickDraw (and hence PICT) stuff.
+ */
+
+struct Rect {
+    word top;
+    word left;
+    word bottom;
+    word right;
+};
+
+struct pixMap {
+    struct Rect Bounds;
+    word version;
+    word packType;
+    longword packSize;
+    longword hRes;
+    longword vRes;
+    word pixelType;
+    word pixelSize;
+    word cmpCount;
+    word cmpSize;
+    longword planeBytes;
+    longword pmTable;
+    longword pmReserved;
+};
+
+struct RGBColor {
+    word red;
+    word grn;
+    word blu;
+};
+
+struct Point {
+    word x;
+    word y;
+};
+
+struct Pattern {
+    byte pix[64];
+};
+
+struct rgbPlanes {
+    word * red;
+    word * grn;
+    word * blu;
+};
+
+typedef void (*transfer_func) (struct RGBColor* src, struct RGBColor* dst);
+
+static const char* stage;
+static struct Rect picFrame;
+static word* red;
+static word* green;
+static word* blue;
+static word rowlen;
+static word collen;
+static longword planelen;
+static int verbose;
+static int fullres;
+static int recognize_comment;
+
+static struct RGBColor black = { 0, 0, 0 };
+static struct RGBColor white = { 0xffff, 0xffff, 0xffff };
+
+/* various bits of drawing state */
+static struct RGBColor foreground = { 0, 0, 0 };
+static struct RGBColor background = { 0xffff, 0xffff, 0xffff };
+static struct RGBColor op_color;
+static struct Pattern bkpat;
+static struct Pattern fillpat;
+static struct Rect clip_rect;
+static struct Rect cur_rect;
+static struct Point current;
+static struct Pattern pen_pat;
+static word pen_width;
+static word pen_height;
+static word pen_mode;
+static transfer_func pen_trf;
+static word text_font;
+static byte text_face;
+static word text_mode;
+static transfer_func text_trf;
+static word text_size;
+static struct font* tfont;
+
+/* state for magic printer comments */
+static int ps_text;
+static byte ps_just;
+static byte ps_flip;
+static word ps_rotation;
+static byte ps_linespace;
+static int ps_cent_x;
+static int ps_cent_y;
+static int ps_cent_set;
+
+struct opdef {
+    const char* name;
+    int len;
+    void (*impl) (int);
+    const char* description;
+};
+
+struct raster {
+/*----------------------------------------------------------------------------
+   An image raster.  May be either truecolor or paletted.
+
+   This is an array of pixels in row-major order, with 'rowSize'
+   bytes per row, 'rowCount' high.
+
+   Within a row, pixels go left to right.  The rows go top to bottom.
+
+   Each pixel is either a palette index or an RGB triple, depending on
+   the format of the associated PICT.
+   
+   Each pixel is one byte if the associated PICT has 8 or fewer bits
+   per pixel.  If the associated PICT has 16 or 32 bits per pixel, an
+   element herein is 2 or 4 bytes, respectively.
+
+   For 16 bits per pixel, the two bytes for each pixel encode RGB values
+   as described in decode16().
+
+   For 32 bits per pixel, each row is divided into 4 planes.  Red,
+   green, blue, and something else, in that order.  The format of a
+   plane is one byte per pixel, left to right.
+-----------------------------------------------------------------------------*/
+    unsigned char * bytes;  /* malloc'ed */
+    unsigned int rowSize;
+    unsigned int rowCount;
+};
+
+
+static void
+allocateRaster(struct raster * const rasterP,
+               unsigned int    const width,
+               unsigned int    const height,
+               unsigned int    const bitsPerPixel) {
+
+    if (width > UINT_MAX/4)
+        pm_error("Width %u pixels too large for arithmetic", width);
+
+    rasterP->rowCount = height;
+
+    switch (bitsPerPixel) {
+    case 32:
+        /* TODO: I'm still trying to figure out this format.
+
+           My theory today:
+           The row data is in plane order (a row consists of red
+           plane, then, green, then blue, then some 4th plane).
+
+           The old hack code said 3 bytes per pixel here, and could get
+           away with it because it never got to decoding the 4th plane.
+
+           But the new clean code needs to tell it like it is and allocate
+           4 bytes per pixel.  If we say 3 bytes per pixel here, we get an
+           "invalid PICT" error because the image actually contains 4
+           bytes per pixel and as we decompress it, we run out of place
+           to put the data.
+
+           We have yet to see if we can properly interpret the data.
+        */
+
+        rasterP->rowSize = width * 4;
+        break;
+    case 16:
+        rasterP->rowSize = width * 2;
+        break;
+    case 8:
+    case 4:
+    case 2:
+    case 1:
+        rasterP->rowSize = width * 1;
+        break;
+    default:
+        pm_error("INTERNAL ERROR: impossible bitsPerPixel value in "
+                 "unpackbits(): %u", bitsPerPixel);
+    }    
+    if (UINT_MAX / rasterP->rowSize < rasterP->rowCount)
+        pm_error("Arithmetic overflow computing size of %u x %u pixel "
+                 "array.", rasterP->rowSize, rasterP->rowCount);
+
+    MALLOCARRAY(rasterP->bytes, rasterP->rowSize * rasterP->rowCount);
+    if (rasterP->bytes == NULL)
+        pm_error("unable to get memory for %u x %u pixel packbits rectangle",
+                 width, height);
+}
+
+
+static void
+freeRaster(struct raster const raster) {
+
+    free(raster.bytes);
+}
+
+
+struct blit_info {
+    struct Rect        srcRect;
+    struct Rect        srcBounds;
+    struct raster      srcplane;
+    int                pixSize;
+    struct Rect        dstRect;
+    struct RGBColor *  color_map;
+    int                mode;
+    struct blit_info * next;
+};
+
+static struct blit_info* blit_list = 0;
+static struct blit_info** last_bl = &blit_list;
+
+#define WORD_LEN (-1)
+
+/*
+ * a table of the first 194(?) opcodes.  The table is too empty.
+ *
+ * Probably could use an entry specifying if the opcode is valid in version
+ * 1, etc.
+ */
+
+/* for reserved opcodes of known length */
+#define res(length) \
+{ "reserved", (length), NULL, "reserved for Apple use" }
+
+/* for reserved opcodes of length determined by a function */
+#define resf(skipfunction) \
+{ "reserved", NA, (skipfunction), "reserved for Apple use" }
+
+/* seems like RGB colors are 6 bytes, but Apple says they're variable */
+/* I'll use 6 for now as I don't care that much. */
+#define RGB_LEN (6)
+
+
+static FILE* ifp;
+static int align = 0;
+
+
+
+static byte
+read_byte(void) {
+    int c;
+
+    if ((c = fgetc(ifp)) == EOF)
+        pm_error("EOF / read error while %s", stage);
+
+    ++align;
+    return c & 255;
+}
+
+
+
+static word
+read_word(void) {
+    byte b;
+
+    b = read_byte();
+
+    return (b << 8) | read_byte();
+}
+
+
+
+static void read_point(struct Point * const p) {
+    p->y = read_word();
+    p->x = read_word();
+}
+
+
+
+static longword
+read_long(void) {
+    word i;
+
+    i = read_word();
+    return (i << 16) | read_word();
+}
+
+
+
+static signed_byte
+read_signed_byte(void) {
+    return (signed_byte)read_byte();
+}
+
+
+
+static void 
+read_short_point(struct Point * const p) {
+    p->x = read_signed_byte();
+    p->y = read_signed_byte();
+}
+
+
+
+static void
+skip(int const byteCount) {
+    static byte buf[1024];
+    int n;
+
+    align += byteCount;
+
+    for (n = byteCount; n > 0; n -= 1024)
+        if (fread(buf, n > 1024 ? 1024 : n, 1, ifp) != 1)
+            pm_error("EOF / read error while %s", stage);
+}
+
+
+
+struct const_name {
+    int value;
+    const char * name;
+};
+
+struct const_name const transfer_name[] = {
+    { 0,    "srcCopy" },
+    { 1,    "srcOr" },
+    { 2,    "srcXor" },
+    { 3,    "srcBic" },
+    { 4,    "notSrcCopy" },
+    { 5,    "notSrcOr" },
+    { 6,    "notSrcXor" },
+    { 7,    "notSrcBic" },
+    { 32,   "blend" },
+    { 33,   "addPin" },
+    { 34,   "addOver" },
+    { 35,   "subPin" },
+    { 36,   "transparent" },
+    { 37,   "adMax" },
+    { 38,   "subOver" },
+    { 39,   "adMin" },
+    { -1,   0 }
+};
+
+struct const_name font_name[] = {
+    { 0,    "systemFont" },
+    { 1,    "applFont" },
+    { 2,    "newYork" },
+    { 3,    "geneva" },
+    { 4,    "monaco" },
+    { 5,    "venice" },
+    { 6,    "london" },
+    { 7,    "athens" },
+    { 8,    "sanFran" },
+    { 9,    "toronto" },
+    { 11,   "cairo" },
+    { 12,   "losAngeles" },
+    { 20,   "times" },
+    { 21,   "helvetica" },
+    { 22,   "courier" },
+    { 23,   "symbol" },
+    { 24,   "taliesin" },
+    { -1,   0 }
+};
+
+struct const_name ps_just_name[] = {
+    { 0,    "no" },
+    { 1,    "left" },
+    { 2,    "center" },
+    { 3,    "right" },
+    { 4,    "full" },
+    { -1,   0 }
+};
+
+struct const_name ps_flip_name[] = {
+    { 0,    "no" },
+    { 1,    "horizontal" },
+    { 2,    "vertical" },
+    { -1,   0 }
+};
+
+
+
+static const char*
+const_name(const struct const_name * const table,
+           unsigned int              const ct) {
+
+    static char numbuf[32];
+
+    unsigned int i;
+
+    for (i = 0; table[i].name; ++i)
+        if (table[i].value == ct)
+            return table[i].name;
+    
+    sprintf(numbuf, "? (%u)", ct);
+    return numbuf;
+}
+
+
+
+static void 
+picComment(word const type, 
+           int const length) {
+
+    unsigned int remainingLength;
+
+    switch (type) {
+    case 150:
+        if (verbose) pm_message("TextBegin");
+        if (length >= 6) {
+            ps_just = read_byte();
+            ps_flip = read_byte();
+            ps_rotation = read_word();
+            ps_linespace = read_byte();
+            remainingLength = length - 5;
+            if (recognize_comment)
+                ps_text = 1;
+            ps_cent_set = 0;
+            if (verbose) {
+                pm_message("%s justification, %s flip, %d degree rotation, "
+                           "%d/2 linespacing",
+                           const_name(ps_just_name, ps_just),
+                           const_name(ps_flip_name, ps_flip),
+                           ps_rotation, ps_linespace);
+            }
+        } else
+            remainingLength = length;
+        break;
+    case 151:
+        if (verbose) pm_message("TextEnd");
+        ps_text = 0;
+        remainingLength = length;
+        break;
+    case 152:
+        if (verbose) pm_message("StringBegin");
+        remainingLength = length;
+        break;
+    case 153:
+        if (verbose) pm_message("StringEnd");
+        remainingLength = length;
+        break;
+    case 154:
+        if (verbose) pm_message("TextCenter");
+        if (length < 8)
+            remainingLength = length;
+        else {
+            ps_cent_y = read_word();
+            if (ps_cent_y > 32767)
+                ps_cent_y -= 65536;
+            skip(2); /* ignore fractional part */
+            ps_cent_x = read_word();
+            if (ps_cent_x > 32767)
+                ps_cent_x -= 65536;
+            skip(2); /* ignore fractional part */
+            remainingLength = length - 8;
+            if (verbose)
+                pm_message("offset %d %d", ps_cent_x, ps_cent_y);
+        }
+        break;
+    case 155:
+        if (verbose) pm_message("LineLayoutOff");
+        remainingLength = length;
+        break;
+    case 156:
+        if (verbose) pm_message("LineLayoutOn");
+        remainingLength = length;
+        break;
+    case 160:
+        if (verbose) pm_message("PolyBegin");
+        remainingLength = length;
+        break;
+    case 161:
+        if (verbose) pm_message("PolyEnd");
+        remainingLength = length;
+        break;
+    case 163:
+        if (verbose) pm_message("PolyIgnore");
+        remainingLength = length;
+        break;
+    case 164:
+        if (verbose) pm_message("PolySmooth");
+        remainingLength = length;
+        break;
+    case 165:
+        if (verbose) pm_message("picPlyClo");
+        remainingLength = length;
+        break;
+    case 180:
+        if (verbose) pm_message("DashedLine");
+        remainingLength = length;
+        break;
+    case 181:
+        if (verbose) pm_message("DashedStop");
+        remainingLength = length;
+        break;
+    case 182:
+        if (verbose) pm_message("SetLineWidth");
+        remainingLength = length;
+        break;
+    case 190:
+        if (verbose) pm_message("PostScriptBegin");
+        remainingLength = length;
+        break;
+    case 191:
+        if (verbose) pm_message("PostScriptEnd");
+        remainingLength = length;
+        break;
+    case 192:
+        if (verbose) pm_message("PostScriptHandle");
+        remainingLength = length;
+        break;
+    case 193:
+        if (verbose) pm_message("PostScriptFile");
+        remainingLength = length;
+        break;
+    case 194:
+        if (verbose) pm_message("TextIsPostScript");
+        remainingLength = length;
+        break;
+    case 195:
+        if (verbose) pm_message("ResourcePS");
+        remainingLength = length;
+        break;
+    case 200:
+        if (verbose) pm_message("RotateBegin");
+        remainingLength = length;
+        break;
+    case 201:
+        if (verbose) pm_message("RotateEnd");
+        remainingLength = length;
+        break;
+    case 202:
+        if (verbose) pm_message("RotateCenter");
+        remainingLength = length;
+        break;
+    case 210:
+        if (verbose) pm_message("FormsPrinting");
+        remainingLength = length;
+        break;
+    case 211:
+        if (verbose) pm_message("EndFormsPrinting");
+        remainingLength = length;
+        break;
+    default:
+        if (verbose) pm_message("%d", type);
+        remainingLength = length;
+        break;
+    }
+    if (remainingLength > 0)
+        skip(remainingLength);
+}
+
+
+
+static void
+ShortComment(int const version) {
+    picComment(read_word(), 0);
+}
+
+
+
+static void
+LongComment(int const version) {
+    word type;
+
+    type = read_word();
+    picComment(type, read_word());
+}
+
+
+
+static void
+skip_poly_or_region(int const version) {
+    stage = "skipping polygon or region";
+    skip(read_word() - 2);
+}
+
+
+#define NA (0)
+
+#define FNT_BOLD    (1)
+#define FNT_ITALIC  (2)
+#define FNT_ULINE   (4)
+#define FNT_OUTLINE (8)
+#define FNT_SHADOW  (16)
+#define FNT_CONDENSE    (32)
+#define FNT_EXTEND  (64)
+
+/* Some font searching routines */
+
+struct fontinfo {
+    int font;
+    int size;
+    int style;
+    char* filename;
+    struct font* loaded;
+    struct fontinfo* next;
+};
+
+static struct fontinfo* fontlist = 0;
+static struct fontinfo** fontlist_ins = &fontlist;
+
+
+
+static int 
+load_fontdir(const char * const dirfile) {
+/*----------------------------------------------------------------------------
+   Load the font directory from file named 'dirfile'.  Add its contents
+   to the global list of fonts 'fontlist'.
+-----------------------------------------------------------------------------*/
+    FILE* fp;
+    int n, nfont;
+    char* arg[5], line[1024];
+    struct fontinfo* fontinfo;
+
+    if (!(fp = fopen(dirfile, "rb")))
+        return -1;
+    
+    nfont = 0;
+    while (fgets(line, 1024, fp)) {
+        if ((n = mk_argvn(line, arg, 5)) == 0 || arg[0][0] == '#')
+            continue;
+        if (n != 4)
+            continue;
+        MALLOCVAR(fontinfo);
+        if (fontinfo == NULL)
+            pm_error("out of memory for font information");
+        MALLOCARRAY(fontinfo->filename, strlen(arg[3] + 1));
+        if (fontinfo->filename == NULL)
+            pm_error("out of memory for font information file name");
+
+        fontinfo->font = atoi(arg[0]);
+        fontinfo->size = atoi(arg[1]);
+        fontinfo->style = atoi(arg[2]);
+        strcpy(fontinfo->filename, arg[3]);
+        fontinfo->loaded = 0;
+
+        fontinfo->next = 0;
+        *fontlist_ins = fontinfo;
+        fontlist_ins = &fontinfo->next;
+        nfont++;
+    }
+
+    return nfont;
+}
+
+
+
+static void
+read_rect(struct Rect * const r) {
+    r->top = read_word();
+    r->left = read_word();
+    r->bottom = read_word();
+    r->right = read_word();
+}
+
+
+
+static void
+dumpRect(const char * const label,
+         struct Rect  const rectangle) {
+
+    pm_message("%s (%u,%u) (%u,%u)",
+               label,
+               rectangle.left,  rectangle.top,
+               rectangle.right, rectangle.bottom);
+}
+
+
+
+static int
+rectwidth(const struct Rect * const r) {
+    return r->right - r->left;
+}
+
+
+
+static int
+rectheight(const struct Rect * const r) {
+    return r->bottom - r->top;
+}
+
+
+
+static bool
+rectsamesize(const struct Rect * const r1, 
+             const struct Rect * const r2) {
+    return r1->right - r1->left == r2->right - r2->left &&
+           r1->bottom - r1->top == r2->bottom - r2->top ;
+}
+
+
+
+static void
+rectinter(struct Rect   const r1, 
+          struct Rect   const r2, 
+          struct Rect * const intersectionP) {
+
+    intersectionP->left   = MAX(r1.left,   r2.left);
+    intersectionP->top    = MAX(r1.top,    r2.top);
+    intersectionP->right  = MIN(r1.right,  r2.right);
+    intersectionP->bottom = MIN(r1.bottom, r2.bottom);
+}
+
+
+
+static void
+rectscale(struct Rect * const r, 
+          double        const xscale, 
+          double        const yscale) {
+    r->left *= xscale;
+    r->right *= xscale;
+    r->top *= yscale;
+    r->bottom *= yscale;
+}
+
+
+
+static struct blit_info* 
+add_blit_list(void) {
+
+    struct blit_info * bi;
+    
+    MALLOCVAR(bi);
+    if (bi == NULL)
+        pm_error("out of memory for blit list");
+    
+    bi->next = 0;
+    *last_bl = bi;
+    last_bl = &bi->next;
+    
+    return bi;
+}
+
+
+
+/* Various transfer functions for blits.
+ *
+ * Note src[Not]{Or,Xor,Copy} only work if the source pixmap was originally
+ * a bitmap.
+ * There's also a small bug that the foreground and background colors
+ * are not used in a srcCopy; this wouldn't be hard to fix.
+ * It IS a problem since the foreground and background colors CAN be changed.
+ */
+
+static bool
+rgbAllSame(const struct RGBColor * const colorP,
+           unsigned int            const value) {
+
+    return (colorP->red == value &&
+            colorP->grn == value &&
+            colorP->blu == value);
+}
+
+
+static bool
+rgbIsWhite(const struct RGBColor * const colorP) {
+
+    return rgbAllSame(colorP, 0xffff);
+}
+
+static bool
+rgbIsBlack(const struct RGBColor * const colorP) {
+
+    return rgbAllSame(colorP, 0);
+}
+
+
+static void 
+srcCopy(struct RGBColor * const src, 
+        struct RGBColor * const dst) {
+
+    if (rgbIsBlack(src))
+        *dst = foreground;
+    else
+        *dst = background;
+}
+
+
+
+static void 
+srcOr(struct RGBColor * const src, 
+      struct RGBColor * const dst) {
+    if (rgbIsBlack(src))
+        *dst = foreground;
+}
+
+
+
+static void 
+srcXor(struct RGBColor * const src, 
+       struct RGBColor * const dst) {
+    dst->red ^= ~src->red;
+    dst->grn ^= ~src->grn;
+    dst->blu ^= ~src->blu;
+}
+
+
+
+static void 
+srcBic(struct RGBColor * const src, 
+       struct RGBColor * const dst) {
+    if (rgbIsBlack(src))
+        *dst = background;
+}
+
+
+
+static void 
+notSrcCopy(struct RGBColor * const src, 
+           struct RGBColor * const dst) {
+    if (rgbIsWhite(src))
+        *dst = foreground;
+    else if (rgbIsBlack(src))
+        *dst = background;
+}
+
+
+
+static void 
+notSrcOr(struct RGBColor * const src, 
+         struct RGBColor * const dst) {
+    if (rgbIsWhite(src))
+        *dst = foreground;
+}
+
+
+
+static void 
+notSrcBic(struct RGBColor * const src, 
+          struct RGBColor * const dst) {
+    if (rgbIsWhite(src))
+        *dst = background;
+}
+
+
+
+static void 
+notSrcXor(struct RGBColor * const src, 
+          struct RGBColor * const dst) {
+    dst->red ^= src->red;
+    dst->grn ^= src->grn;
+    dst->blu ^= src->blu;
+}
+
+
+
+static void 
+addOver(struct RGBColor * const src, 
+        struct RGBColor * const dst) {
+    dst->red += src->red;
+    dst->grn += src->grn;
+    dst->blu += src->blu;
+}
+
+
+
+static void 
+addPin(struct RGBColor * const src, 
+       struct RGBColor * const dst) {
+    if ((long)dst->red + (long)src->red > (long)op_color.red)
+        dst->red = op_color.red;
+    else
+        dst->red = dst->red + src->red;
+
+    if ((long)dst->grn + (long)src->grn > (long)op_color.grn)
+        dst->grn = op_color.grn;
+    else
+        dst->grn = dst->grn + src->grn;
+
+    if ((long)dst->blu + (long)src->blu > (long)op_color.blu)
+        dst->blu = op_color.blu;
+    else
+        dst->blu = dst->blu + src->blu;
+}
+
+
+
+static void 
+subOver(struct RGBColor * const src, 
+        struct RGBColor * const dst) {
+    dst->red -= src->red;
+    dst->grn -= src->grn;
+    dst->blu -= src->blu;
+}
+
+
+
+/* or maybe its src - dst; my copy of Inside Mac is unclear */
+
+
+static void 
+subPin(struct RGBColor * const src, 
+       struct RGBColor * const dst) {
+    if ((long)dst->red - (long)src->red < (long)op_color.red)
+        dst->red = op_color.red;
+    else
+        dst->red = dst->red - src->red;
+
+    if ((long)dst->grn - (long)src->grn < (long)op_color.grn)
+        dst->grn = op_color.grn;
+    else
+        dst->grn = dst->grn - src->grn;
+
+    if ((long)dst->blu - (long)src->blu < (long)op_color.blu)
+        dst->blu = op_color.blu;
+    else
+        dst->blu = dst->blu - src->blu;
+}
+
+
+
+static void 
+adMax(struct RGBColor * const src, 
+      struct RGBColor * const dst) {
+    if (src->red > dst->red) dst->red = src->red;
+    if (src->grn > dst->grn) dst->grn = src->grn;
+    if (src->blu > dst->blu) dst->blu = src->blu;
+}
+
+
+
+static void 
+adMin(struct RGBColor * const src, 
+      struct RGBColor * const dst) {
+    if (src->red < dst->red) dst->red = src->red;
+    if (src->grn < dst->grn) dst->grn = src->grn;
+    if (src->blu < dst->blu) dst->blu = src->blu;
+}
+
+
+
+static void 
+blend(struct RGBColor * const src, 
+      struct RGBColor * const dst) {
+#define blend_component(cmp)    \
+    ((long)src->cmp * (long)op_color.cmp) / 65536 +    \
+    ((long)dst->cmp * (long)(65536 - op_color.cmp) / 65536)
+
+    dst->red = blend_component(red);
+    dst->grn = blend_component(grn);
+    dst->blu = blend_component(blu);
+}
+
+
+
+static void 
+transparent(struct RGBColor * const src, 
+            struct RGBColor * const dst) {
+    if (src->red != background.red ||
+        src->grn != background.grn ||
+        src->blu != background.blu) {
+        *dst = *src;
+    }
+}
+
+
+
+static transfer_func 
+transfer(int const mode) {
+    switch (mode) {
+    case  0: return srcCopy;
+    case  1: return srcOr;
+    case  2: return srcXor;
+    case  3: return srcBic;
+    case  4: return notSrcCopy;
+    case  5: return notSrcOr;
+    case  6: return notSrcXor;
+    case  7: return notSrcBic;
+    case 32: return blend;
+    case 33: return addPin;
+    case 34: return addOver;
+    case 35: return subPin;
+    case 36: return transparent;
+    case 37: return adMax;
+    case 38: return subOver;
+    case 39: return adMin;
+    default:
+        if (mode != 0)
+            pm_message("no transfer function for code %s, using srcCopy",
+                const_name(transfer_name, mode));
+        return srcCopy;
+    }
+}
+
+
+
+static pixval
+redepth(pixval const c,
+        pixval const oldMaxval) {
+    
+    return (c * PPM_MAXMAXVAL + oldMaxval / 2) / oldMaxval;
+}
+
+
+
+static struct RGBColor
+decode16(unsigned char * const sixteen) {
+/*----------------------------------------------------------------------------
+   Decode a 16 bit PICT encoding of RGB:
+
+      Bit   0:    nothing
+      Bits  1- 5: red
+      Bits  6-10: green
+      Bits 11-15: blue
+
+   'sixteen' is a two byte array.
+-----------------------------------------------------------------------------*/
+    struct RGBColor retval;
+
+    retval.red = (sixteen[0] & 0x7c) >> 2;
+    retval.grn = (sixteen[0] & 0x03) << 3 | (sixteen[1] & 0xe0) >> 5;
+    retval.blu = (sixteen[1] & 0x1f) >> 0;
+                
+    return retval;
+}
+
+
+
+static void
+doDiffSize(struct Rect       const clipsrc,
+           struct Rect       const clipdst,
+           int               const pixSize,
+           int               const xsize,
+           int               const ysize,
+           transfer_func     const trf,
+           struct RGBColor * const color_map, 
+           unsigned char *   const src,
+           int               const srcwid, 
+           struct rgbPlanes  const dst,
+           unsigned int      const dstwid) {
+
+    unsigned int const dstadd = dstwid - xsize;
+
+    FILE * pnmscalePipeP;
+    const char * command;
+    FILE * scaled;
+    int cols, rows, format;
+    pixval maxval;
+    pixel * row;
+    pixel * rowp;
+    FILE * tempFileP;
+    const char * tempFilename;
+    word * reddst;
+    word * grndst;
+    word * bludst;
+
+    reddst = dst.red;  /* initial value */
+    grndst = dst.grn;  /* initial value */
+    bludst = dst.blu;  /* initial value */
+
+    pm_make_tmpfile(&tempFileP, &tempFilename);
+
+    pm_close(tempFileP);
+
+    asprintfN(&command, "pnmscale -xsize %d -ysize %d > %s",
+              rectwidth(&clipdst), rectheight(&clipdst), tempFilename);
+
+    pm_message("running command '%s'", command);
+
+    pnmscalePipeP = popen(command, "w");
+    if (pnmscalePipeP == NULL)
+        pm_error("cannot execute command '%s'  popen() errno = %s (%d)",
+                 command, strerror(errno), errno);
+
+    strfree(command);
+
+    fprintf(pnmscalePipeP, "P6\n%d %d\n%d\n",
+            rectwidth(&clipsrc), rectheight(&clipsrc), PPM_MAXMAXVAL);
+
+    switch (pixSize) {
+    case 8: {
+        unsigned int rowNumber;
+        for (rowNumber = 0; rowNumber < ysize; ++rowNumber) {
+            unsigned char * const row = &src[rowNumber * srcwid];
+            unsigned int colNumber;
+            for (colNumber = 0; colNumber < xsize; ++colNumber) {
+                unsigned int const colorIndex = row[colNumber];
+                struct RGBColor * const ct = &color_map[colorIndex];
+                fputc(redepth(ct->red, 65535L), pnmscalePipeP);
+                fputc(redepth(ct->grn, 65535L), pnmscalePipeP);
+                fputc(redepth(ct->blu, 65535L), pnmscalePipeP);
+            }
+        }
+    }
+    break;
+    case 16: {
+        unsigned int rowNumber;
+        for (rowNumber = 0; rowNumber < ysize; ++rowNumber) {
+            unsigned char * const row = &src[rowNumber * srcwid];
+            unsigned int colNumber;
+            for (colNumber = 0; colNumber < xsize; ++colNumber) {
+                struct RGBColor const color = decode16(&row[colNumber * 2]);
+                fputc(redepth(color.red, 32), pnmscalePipeP);
+                fputc(redepth(color.grn, 32), pnmscalePipeP);
+                fputc(redepth(color.blu, 32), pnmscalePipeP);
+            }
+        }
+    }
+    break;
+    case 32: {
+        unsigned int const planeSize = srcwid / 4;
+        unsigned int rowNumber;
+        
+        for (rowNumber = 0; rowNumber < ysize; ++rowNumber) {
+            unsigned char * const row = &src[rowNumber * srcwid];
+            unsigned char * const redPlane = &row[planeSize * 0];
+            unsigned char * const grnPlane = &row[planeSize * 1];
+            unsigned char * const bluPlane = &row[planeSize * 2];
+
+            unsigned int colNumber;
+            for (colNumber = 0; colNumber < xsize; ++colNumber) {
+                fputc(redepth(redPlane[colNumber], 256), pnmscalePipeP);
+                fputc(redepth(grnPlane[colNumber], 256), pnmscalePipeP);
+                fputc(redepth(bluPlane[colNumber], 256), pnmscalePipeP);
+            }
+        }
+    }
+    break;
+    }
+
+    if (pclose(pnmscalePipeP))
+        pm_error("pnmscale failed.  pclose() returned Errno %s (%d)",
+                 strerror(errno), errno);
+
+    ppm_readppminit(scaled = pm_openr(tempFilename), &cols, &rows,
+                    &maxval, &format);
+    row = ppm_allocrow(cols);
+    /* couldn't hurt to assert cols, rows and maxval... */  
+
+    if (trf == NULL) {
+        while (rows-- > 0) {
+            unsigned int i;
+            ppm_readppmrow(scaled, row, cols, maxval, format);
+            for (i = 0, rowp = row; i < cols; ++i, ++rowp) {
+                *reddst++ = PPM_GETR(*rowp) * 65536L / (maxval + 1); 
+                *grndst++ = PPM_GETG(*rowp) * 65536L / (maxval + 1); 
+                *bludst++ = PPM_GETB(*rowp) * 65536L / (maxval + 1); 
+            }
+            reddst += dstadd;
+            grndst += dstadd;
+            bludst += dstadd;
+        }
+    }
+    else {
+        while (rows-- > 0) {
+            unsigned int i;
+            ppm_readppmrow(scaled, row, cols, maxval, format);
+            for (i = 0, rowp = row; i < cols; i++, rowp++) {
+                struct RGBColor dst_c, src_c;
+                dst_c.red = *reddst;
+                dst_c.grn = *grndst;
+                dst_c.blu = *bludst;
+                src_c.red = PPM_GETR(*rowp) * 65536L / (maxval + 1); 
+                src_c.grn = PPM_GETG(*rowp) * 65536L / (maxval + 1); 
+                src_c.blu = PPM_GETB(*rowp) * 65536L / (maxval + 1); 
+                (*trf)(&src_c, &dst_c);
+                *reddst++ = dst_c.red;
+                *grndst++ = dst_c.grn;
+                *bludst++ = dst_c.blu;
+            }
+            reddst += dstadd;
+            grndst += dstadd;
+            bludst += dstadd;
+        }
+    }
+
+    pm_close(scaled);
+    ppm_freerow(row);
+    strfree(tempFilename);
+    unlink(tempFilename);
+}
+
+
+
+static void
+getRgb(struct rgbPlanes  const planes,
+       unsigned int      const index,
+       struct RGBColor * const rgbP) {
+
+    rgbP->red = planes.red[index];
+    rgbP->grn = planes.grn[index];
+    rgbP->blu = planes.blu[index];
+}
+
+
+
+static void
+putRgb(struct RGBColor  const rgb,
+       unsigned int     const index,
+       struct rgbPlanes const planes) {
+
+    planes.red[index] = rgb.red;
+    planes.grn[index] = rgb.grn;
+    planes.blu[index] = rgb.blu;
+}
+
+
+
+static void
+doSameSize(transfer_func           trf,
+           int               const pixSize,
+           int               const xsize,
+           int               const ysize,
+           unsigned char *   const src,
+           unsigned int      const srcwid,
+           struct RGBColor * const color_map,
+           struct rgbPlanes  const dst,
+           unsigned int      const dstwid) {
+/*----------------------------------------------------------------------------
+   Generalized (but slow) blit.
+
+   Transfer pixels from 'src' to 'dst', applying the transfer function
+   'trf'.
+
+   'src' has the same format as the 'bytes' member of struct raster.
+   'srcwid' is the size in bytes of each row, like raster.rowSize.
+
+   We use only the first 'ysize' rows and only the first 'xsize'
+   pixels of each row.
+
+   We really should clean this up so that we can take pixels out of
+   the middle of a row and rows out of the middle of the raster.  As
+   it stands, Caller achieves the same result by passing as 'src'
+   a pointer into the middle of a raster -- the upper left corner of
+   the rectangle he wants.  But that is messy and nonobvious.
+
+   Each plane of 'dst' is one word per pixel and contains actual
+   colors, never a palette index.  It is an array in row-major order
+   with 'dstwid' words per row.
+-----------------------------------------------------------------------------*/
+    switch (pixSize) {
+    case 8: {
+        unsigned int rowNumber;
+
+        for (rowNumber = 0; rowNumber < ysize; ++rowNumber) {
+            unsigned char * const srcrow = &src[rowNumber * srcwid];
+            unsigned int const dstRowCurs = rowNumber * dstwid;
+
+            unsigned int colNumber;
+            for (colNumber = 0; colNumber < xsize; ++colNumber) {
+                unsigned int const dstCursor = dstRowCurs + colNumber;
+                unsigned int const colorIndex = srcrow[colNumber];
+                struct RGBColor dstColor;
+                getRgb(dst, dstCursor, &dstColor);
+                (*trf)(&color_map[colorIndex], &dstColor);
+                putRgb(dstColor, dstCursor, dst);
+            }
+        }
+    } break;
+    case 16: {
+        unsigned int rowNumber;
+
+        for (rowNumber = 0; rowNumber < ysize; ++rowNumber) {
+            unsigned char * const row = &src[rowNumber * srcwid];
+            unsigned int const dstRowCurs = rowNumber * dstwid;
+
+            unsigned int colNumber;
+            for (colNumber = 0; colNumber < xsize; ++colNumber) {
+                unsigned int const dstCursor = dstRowCurs + colNumber;
+                struct RGBColor const srcColor = decode16(&row[colNumber*2]);
+                struct RGBColor dstColor;
+                struct RGBColor scaledSrcColor;
+                scaledSrcColor.red = srcColor.red << 11;
+                scaledSrcColor.grn = srcColor.grn << 11;
+                scaledSrcColor.blu = srcColor.blu << 11;
+                getRgb(dst, dstCursor, &dstColor);
+                (*trf)(&scaledSrcColor, &dstColor);
+                putRgb(dstColor, dstCursor, dst);
+            }
+        }
+    } break;
+    case 32: {
+        unsigned int const planeSize = srcwid / 4;
+        unsigned int rowNumber;
+
+        for (rowNumber = 0; rowNumber < ysize; ++rowNumber) {
+            unsigned char * const row = &src[rowNumber * srcwid];
+            unsigned char * const redPlane = &row[planeSize * 0];
+            unsigned char * const grnPlane = &row[planeSize * 1];
+            unsigned char * const bluPlane = &row[planeSize * 2];
+            unsigned int const dstRowCurs = rowNumber * dstwid;
+
+            unsigned int colNumber;
+
+            for (colNumber = 0; colNumber < xsize; ++colNumber) {
+                unsigned int const dstCursor = dstRowCurs + colNumber;
+                struct RGBColor srcColor, dstColor;
+                getRgb(dst, dstCursor, &dstColor);
+                srcColor.red = redPlane[colNumber] << 8;
+                srcColor.grn = grnPlane[colNumber] << 8;
+                srcColor.blu = bluPlane[colNumber] << 8;
+                (*trf)(&srcColor, &dstColor);
+                putRgb(dstColor, dstCursor, dst);
+            }
+        }
+    } break;
+    default:
+        pm_error("Impossible value of pixSize: %u", pixSize);
+    }
+}
+
+
+
+static void
+blitIdempotent(unsigned int          const pixSize,
+               unsigned int          const xsize,
+               unsigned int          const ysize,
+               unsigned char *       const src,
+               unsigned int          const srcwid,
+               struct RGBColor *     const colorMap, 
+               struct rgbPlanes      const dst,
+               unsigned int          const dstwid) {
+/*----------------------------------------------------------------------------
+  This is the same as doSameSize(), except optimized for the case that
+  the transfer function is idempotent (i.e. it's just a straight copy).
+  The original author's comments suggest that this optimization isn't
+  all that important -- that he just wrote this first and instead of
+  expanding it to handle arbitrary transfer functions, added functions
+  for that.
+-----------------------------------------------------------------------------*/
+    switch (pixSize) {
+    case 8: {
+        unsigned int rowNumber;
+        
+        for (rowNumber = 0; rowNumber < ysize; ++rowNumber) {
+            unsigned char * const srcrow = &src[rowNumber * srcwid];
+            unsigned int const dstRowCurs = rowNumber * dstwid;
+            unsigned int colNumber;
+            for (colNumber = 0; colNumber < xsize; ++colNumber) {
+                unsigned int const dstCursor = dstRowCurs + colNumber;
+                struct RGBColor * const ct = colorMap + srcrow[colNumber];
+                dst.red[dstCursor] = ct->red;
+                dst.grn[dstCursor] = ct->grn;
+                dst.blu[dstCursor] = ct->blu;
+            }
+        }
+    } break;
+    case 16: {
+        unsigned int rowNumber;
+
+        for (rowNumber = 0; rowNumber < ysize; ++rowNumber) {
+            unsigned char * const srcrow = &src[rowNumber * srcwid];
+            unsigned int const dstRowCurs = rowNumber * dstwid;
+            unsigned int colNumber;
+            for (colNumber = 0; colNumber < xsize; ++colNumber) {
+                unsigned int const dstCursor = dstRowCurs + colNumber;
+                struct RGBColor const srcColor =
+                    decode16(&srcrow[colNumber * 2]);
+                dst.red[dstCursor] = srcColor.red << 11;
+                dst.grn[dstCursor] = srcColor.grn << 11;
+                dst.blu[dstCursor] = srcColor.blu << 11;
+            }
+        }
+    } break;
+    case 32: {
+        unsigned int const planeSize = srcwid / 4;
+        unsigned int rowNumber;
+
+        for (rowNumber = 0; rowNumber < ysize; ++rowNumber) {
+            unsigned char * const srcrow = &src[rowNumber * srcwid];
+            unsigned char * const redPlane = &srcrow[planeSize * 0];
+            unsigned char * const grnPlane = &srcrow[planeSize * 1];
+            unsigned char * const bluPlane = &srcrow[planeSize * 2];
+            unsigned int const dstRowCurs = rowNumber * dstwid;
+
+            unsigned int colNumber;
+            for (colNumber = 0; colNumber < xsize; ++colNumber) {
+                unsigned int const dstCursor = dstRowCurs + colNumber;
+                dst.red[dstCursor] = redPlane[colNumber] << 8;
+                dst.grn[dstCursor] = grnPlane[colNumber] << 8;
+                dst.blu[dstCursor] = bluPlane[colNumber] << 8;
+            }
+        }
+    } break;
+    default:
+        pm_error("INTERNAL ERROR: invalid bits per pixel (%u) in "
+                 "blitIdempotent()", pixSize);
+    }
+}
+
+
+
+static void
+generalBlit(struct Rect       const srcRect, 
+            struct Rect       const srcBounds, 
+            struct raster     const srcplane,
+            int               const pixSize, 
+            struct Rect       const dstRect, 
+            struct Rect       const dstBounds, 
+            int               const dstwid, 
+            struct RGBColor * const color_map, 
+            int               const mode,
+            struct Rect       const clipsrc,
+            struct Rect       const clipdst) {
+    
+    unsigned char * src;
+    struct rgbPlanes dst;
+    int dstoff;
+    int xsize;
+    int ysize;
+    int srcadd;
+    transfer_func trf;
+
+    if (verbose) {
+        dumpRect("copying from:", clipsrc);
+        dumpRect("to:          ", clipdst);
+        pm_message("a %u x %u area to a %u x %u area",
+                   rectwidth(&clipsrc), rectheight(&clipsrc),
+                   rectwidth(&clipdst), rectheight(&clipdst));
+    }
+
+    {
+        unsigned int const pkpixsize = pixSize == 16 ? 2 : 1;
+        unsigned int const srcRowNumber = clipsrc.top - srcBounds.top;
+        unsigned int const srcRowOffset =
+            (clipsrc.left - srcBounds.left) * pkpixsize;
+        assert(srcRowNumber < srcplane.rowCount);
+        assert(srcRowOffset < srcplane.rowSize);
+        src = srcplane.bytes + srcRowNumber * srcplane.rowSize + srcRowOffset;
+        xsize = clipsrc.right - clipsrc.left;
+        ysize = clipsrc.bottom - clipsrc.top;
+        srcadd = srcplane.rowSize - xsize * pkpixsize;
+    }
+
+    dstoff = (clipdst.top - dstBounds.top) * dstwid +
+        (clipdst.left - dstBounds.left);
+    dst.red = red + dstoff;
+    dst.grn = green + dstoff;
+    dst.blu = blue + dstoff;
+
+    /* get rid of Text mask mode bit, if (erroneously) set */
+    if ((mode & ~64) == 0)
+        trf = NULL;    /* optimized srcCopy */
+    else
+        trf = transfer(mode & ~64);
+
+    if (!rectsamesize(&clipsrc, &clipdst))
+        doDiffSize(clipsrc, clipdst, pixSize, xsize, ysize,
+                   trf, color_map, src, srcplane.rowSize, dst, dstwid);
+    else {
+        if (trf == NULL)
+            blitIdempotent(pixSize, xsize, ysize, src, srcplane.rowSize,
+                           color_map, dst, dstwid);
+        else
+            doSameSize(trf, pixSize, xsize, ysize, src, srcplane.rowSize,
+                       color_map, dst, dstwid);
+    }
+}
+
+
+
+static int
+blit(struct Rect       const srcRect, 
+     struct Rect       const srcBounds, 
+     struct raster     const srcplane,
+     int               const pixSize, 
+     struct Rect       const dstRect, 
+     struct Rect       const dstBounds, 
+     int               const dstwid, 
+     struct RGBColor * const color_map, 
+     int               const mode) {
+
+    /* I can't tell what the result value of this function is supposed to mean,
+       but I found several return statements that did not set it to anything,
+       and several calls that examine it.  I'm guessing that "1" is the 
+       appropriate thing to return in those cases, so I made it so.
+       -Bryan 00.03.02
+    */
+
+    int retval;
+
+    if (ps_text)
+        retval = 1;
+    else {
+        /* Almost got it.  Clip source rect with source bounds.
+           clip dest rect with dest bounds.  If source and
+           destination are not the same size, use Pnmscale
+           to get a nicely sized rectangle.
+        */
+        struct Rect clipsrc;
+        struct Rect clipdst;
+
+        rectinter(srcBounds, srcRect, &clipsrc);
+        rectinter(dstBounds, dstRect, &clipdst);
+
+        if (fullres) {
+            struct blit_info * bi;
+            bi = add_blit_list();
+            bi->srcRect   = clipsrc;
+            bi->srcBounds = srcBounds;
+            bi->srcplane  = srcplane;
+            bi->pixSize   = pixSize;
+            bi->dstRect   = clipdst;
+            bi->color_map = color_map;
+            bi->mode      = mode;
+
+            retval = 0;
+        } else {
+            generalBlit(srcRect, srcBounds, srcplane, pixSize,
+                        dstRect, dstBounds, dstwid, color_map, mode,
+                        clipsrc, clipdst);
+
+            retval = 1;
+        }
+    }
+    return retval;
+}
+
+
+
+/* allocation is same for version 1 or version 2.  We are super-duper
+ * wasteful of memory for version 1 picts.  Someday, we'll separate
+ * things and only allocate a byte per pixel for version 1 (or heck,
+ * even only a bit, but that would require even more extra work).
+ */
+
+static void 
+allocPlanes(struct rgbPlanes * const planesP) {
+
+    struct rgbPlanes planes;
+
+    rowlen = picFrame.right - picFrame.left;
+    collen = picFrame.bottom - picFrame.top;
+
+    clip_rect = picFrame;
+
+    planelen = rowlen * collen;
+    MALLOCARRAY(planes.red,  planelen);
+    MALLOCARRAY(planes.grn, planelen);
+    MALLOCARRAY(planes.blu, planelen);
+    if (planes.red == NULL || planes.grn == NULL || planes.blu == NULL)
+        pm_error("not enough memory to hold picture");
+
+    /* initialize background to white */
+    memset(planes.red, 255, planelen * sizeof(word));
+    memset(planes.grn, 255, planelen * sizeof(word));
+    memset(planes.blu, 255, planelen * sizeof(word));
+
+    /* Until we wean this program off of global variables, we have to
+       set these:
+    */
+
+    red   = planes.red;
+    green = planes.grn;
+    blue  = planes.blu;
+}
+
+
+
+static void
+freePlanes(struct rgbPlanes const planes) {
+
+    free(planes.red);
+    free(planes.grn);
+    free(planes.blu);
+}
+
+
+
+static unsigned char
+compact(word const input) {
+
+    return (input >> 8) & 0xff;
+}
+
+
+
+static void
+do_blits(struct rgbPlanes * const planesP) {
+
+    struct blit_info* bi;
+    int srcwidth, dstwidth, srcheight, dstheight;
+    double  scale, scalelow, scalehigh;
+    double  xscale = 1.0;
+    double  yscale = 1.0;
+    double  lowxscale, highxscale, lowyscale, highyscale;
+    int     xscalecalc = 0, yscalecalc = 0;
+
+    if (!blit_list) return;
+
+    fullres = 0;
+
+    for (bi = blit_list; bi; bi = bi->next) {
+        srcwidth = rectwidth(&bi->srcRect);
+        dstwidth = rectwidth(&bi->dstRect);
+        srcheight = rectheight(&bi->srcRect);
+        dstheight = rectheight(&bi->dstRect);
+        if (srcwidth > dstwidth) {
+            scalelow  = (double)(srcwidth      ) / (double)dstwidth;
+            scalehigh = (double)(srcwidth + 1.0) / (double)dstwidth;
+            switch (xscalecalc) {
+            case 0:
+                lowxscale = scalelow;
+                highxscale = scalehigh;
+                xscalecalc = 1;
+                break;
+            case 1:
+                if (scalelow < highxscale && scalehigh > lowxscale) {
+                    if (scalelow > lowxscale) lowxscale = scalelow;
+                    if (scalehigh < highxscale) highxscale = scalehigh;
+                }
+                else {
+                    scale = (lowxscale + highxscale) / 2.0;
+                    xscale = (double)srcwidth / (double)dstwidth;
+                    if (scale > xscale) xscale = scale;
+                    xscalecalc = 2;
+                }
+                break;
+            case 2:
+                scale = (double)srcwidth / (double)dstwidth;
+                if (scale > xscale) xscale = scale;
+                break;
+            }
+        }
+
+        if (srcheight > dstheight) {
+            scalelow =  (double)(srcheight      ) / (double)dstheight;
+            scalehigh = (double)(srcheight + 1.0) / (double)dstheight;
+            switch (yscalecalc) {
+            case 0:
+                lowyscale = scalelow;
+                highyscale = scalehigh;
+                yscalecalc = 1;
+                break;
+            case 1:
+                if (scalelow < highyscale && scalehigh > lowyscale) {
+                    if (scalelow > lowyscale) lowyscale = scalelow;
+                    if (scalehigh < highyscale) highyscale = scalehigh;
+                }
+                else {
+                    scale = (lowyscale + highyscale) / 2.0;
+                    yscale = (double)srcheight / (double)dstheight;
+                    if (scale > yscale) yscale = scale;
+                    yscalecalc = 2;
+                }
+                break;
+            case 2:
+                scale = (double)srcheight / (double)dstheight;
+                if (scale > yscale) yscale = scale;
+                break;
+            }
+        }
+    }
+
+    if (xscalecalc == 1) {
+        if (1.0 >= lowxscale && 1.0 <= highxscale)
+            xscale = 1.0;
+        else
+            xscale = lowxscale;
+    }
+    if (yscalecalc == 1) {
+        if (1.0 >= lowyscale && 1.0 <= lowyscale)
+            yscale = 1.0;
+        else
+            yscale = lowyscale;
+    }
+
+    if (xscale != 1.0 || yscale != 1.0) {
+        for (bi = blit_list; bi; bi = bi->next)
+            rectscale(&bi->dstRect, xscale, yscale);
+
+        pm_message("Scaling output by %f in X and %f in Y",
+                   xscale, yscale);
+        rectscale(&picFrame, xscale, yscale);
+    }
+
+    allocPlanes(planesP);
+
+    for (bi = blit_list; bi; bi = bi->next) {
+        blit(bi->srcRect, bi->srcBounds, bi->srcplane,
+             bi->pixSize,
+             bi->dstRect, picFrame, rowlen,
+             bi->color_map,
+             bi->mode);
+    }
+}
+
+
+
+static void
+outputPpm(struct rgbPlanes const planes) {
+
+    unsigned int width;
+    unsigned int height;
+    pixel * pixelrow;
+    unsigned int row;
+    unsigned int srcCursor;
+
+    stage = "writing PPM";
+
+    assert(picFrame.right  > picFrame.left);
+    assert(picFrame.bottom > picFrame.top);
+
+    width  = picFrame.right  - picFrame.left;
+    height = picFrame.bottom - picFrame.top;
+
+    ppm_writeppminit(stdout, width, height, PPM_MAXMAXVAL, 0);
+    pixelrow = ppm_allocrow(width);
+    srcCursor = 0;
+    for (row = 0; row < height; ++row) {
+        unsigned int col;
+        for (col = 0; col < width; ++col) {
+            PPM_ASSIGN(pixelrow[col],
+                       compact(planes.red[srcCursor]),
+                       compact(planes.grn[srcCursor]),
+                       compact(planes.blu[srcCursor])
+                );
+            ++srcCursor;
+        }
+        ppm_writeppmrow(stdout, pixelrow, width, PPM_MAXMAXVAL, 0);
+    }
+    pm_close(stdout);
+}
+
+
+
+/*
+ * All data in version 2 is 2-byte word aligned.  Odd size data
+ * is padded with a null.
+ */
+static word
+get_op(int const version) {
+    if ((align & 1) && version == 2) {
+        stage = "aligning for opcode";
+        read_byte();
+    }
+
+    stage = "reading opcode";
+
+    if (version == 1)
+        return read_byte();
+    else
+        return read_word();
+}
+
+
+
+static void
+Clip(int const version) {
+    word len;
+
+    len = read_word();
+
+    if (len == 0x000a) {    /* null rgn */
+        read_rect(&clip_rect);
+        /* XXX should clip this by picFrame */
+        if (verbose)
+            dumpRect("clipping to", clip_rect);
+    }
+    else
+        skip(len - 2);
+}
+
+
+
+static void
+OpColor(int const version) {
+    op_color.red = read_word();
+    op_color.grn = read_word();
+    op_color.blu = read_word();
+}
+
+
+
+static void
+read_pixmap(struct pixMap * const p) {
+
+    stage = "getting pixMap header";
+
+    read_rect(&p->Bounds);
+    p->version = read_word();
+    p->packType = read_word();
+    p->packSize = read_long();
+    p->hRes = read_long();
+    p->vRes = read_long();
+    p->pixelType = read_word();
+    p->pixelSize = read_word();
+    p->cmpCount = read_word();
+    p->cmpSize = read_word();
+    p->planeBytes = read_long();
+    p->pmTable = read_long();
+    p->pmReserved = read_long();
+
+    if (verbose) {
+        pm_message("pixelType: %d", p->pixelType);
+        pm_message("pixelSize: %d", p->pixelSize);
+        pm_message("cmpCount:  %d", p->cmpCount);
+        pm_message("cmpSize:   %d", p->cmpSize);
+    }
+
+    if (p->pixelType != 0)
+        pm_error("sorry, I do only chunky format.  "
+                 "This image has pixel type %hu", p->pixelType);
+    if (p->cmpCount != 1)
+        pm_error("sorry, cmpCount != 1");
+    if (p->pixelSize != p->cmpSize)
+        pm_error("oops, pixelSize != cmpSize");
+}
+
+
+
+static struct RGBColor*
+read_color_table(void) {
+    longword ctSeed;
+    word ctFlags;
+    word ctSize;
+    word val;
+    int i;
+    struct RGBColor* color_table;
+
+    stage = "getting color table info";
+
+    ctSeed = read_long();
+    ctFlags = read_word();
+    ctSize = read_word();
+
+    if (verbose) {
+        pm_message("ctSeed:  %ld", ctSeed);
+        pm_message("ctFlags: %d", ctFlags);
+        pm_message("ctSize:  %d", ctSize);
+    }
+
+    stage = "reading color table";
+
+    MALLOCARRAY(color_table, ctSize + 1);
+    if (color_table == NULL)
+        pm_error("no memory for color table");
+
+    for (i = 0; i <= ctSize; i++) {
+        val = read_word();
+        /* The indices in a device color table are bogus and usually == 0.
+         * so I assume we allocate up the list of colors in order.
+         */
+        if (ctFlags & 0x8000)
+            val = i;
+        if (val > ctSize)
+            pm_error("pixel value greater than color table size");
+        color_table[val].red = read_word();
+        color_table[val].grn = read_word();
+        color_table[val].blu = read_word();
+
+        if (verbose > 1)
+            pm_message("%d: [%d,%d,%d]", val,
+                color_table[val].red,
+                color_table[val].grn,
+                color_table[val].blu);
+    }
+
+    return color_table;
+}
+
+
+
+static void
+readBytes(FILE *          const ifP,
+          unsigned int    const n, 
+          unsigned char * const buf) {
+
+    align += n;
+
+    if (fread(buf, n, 1, ifP) != 1)
+        pm_error("EOF / read error while %s", stage);
+}
+
+
+
+static void
+copyFullBytes(unsigned char * const packed,
+              unsigned char * const expanded,
+              unsigned int    const packedLen) {
+
+    unsigned int i;
+
+    for (i = 0; i < packedLen; ++i)
+        expanded[i] = packed[i];
+}
+
+
+
+static void
+expand4Bits(unsigned char * const packed,
+            unsigned char * const expanded,
+            unsigned int    const packedLen) {
+
+    unsigned int i;
+    unsigned char * dst;
+
+    dst = &expanded[0];
+    
+    for (i = 0; i < packedLen; ++i) {
+        *dst++ = (packed[i] >> 4) & 0x0f;
+        *dst++ = (packed[i] >> 0) & 0x0f;
+    }
+}
+
+
+
+static void
+expand2Bits(unsigned char * const packed,
+            unsigned char * const expanded,
+            unsigned int    const packedLen) {
+        
+    unsigned int i;
+    unsigned char * dst;
+
+    dst = &expanded[0];
+    
+    for (i = 0; i < packedLen; ++i) {
+        *dst++ = (packed[i] >> 6) & 0x03;
+        *dst++ = (packed[i] >> 4) & 0x03;
+        *dst++ = (packed[i] >> 2) & 0x03;
+        *dst++ = (packed[i] >> 0) & 0x03;
+    }
+}
+
+
+
+static void
+expand1Bit(unsigned char * const packed,
+           unsigned char * const expanded,
+           unsigned int    const packedLen) {
+
+    unsigned int i;
+    unsigned char * dst;
+
+    dst = &expanded[0];
+    
+    for (i = 0; i < packedLen; ++i) {
+        *dst++ = (packed[i] >> 7) & 0x01;
+        *dst++ = (packed[i] >> 6) & 0x01;
+        *dst++ = (packed[i] >> 5) & 0x01;
+        *dst++ = (packed[i] >> 4) & 0x01;
+        *dst++ = (packed[i] >> 3) & 0x01;
+        *dst++ = (packed[i] >> 2) & 0x01;
+        *dst++ = (packed[i] >> 1) & 0x01;
+        *dst++ = (packed[i] >> 0) & 0x01;
+    }
+}
+
+
+
+static void
+unpackBuf(unsigned char *  const packed, 
+          unsigned int     const packedLen,
+          int              const bitsPerPixel,
+          unsigned char ** const expandedP,
+          unsigned int *   const expandedLenP) {
+/*----------------------------------------------------------------------------
+   Expand the bit string 'packed', which is 'packedLen' bytes long
+   into an array of bytes, with one byte per pixel.  Each 'bitsPerPixel'
+   of 'packed' is a pixel.
+
+   So e.g. if it's 4 bits per pixel and 'packed' is 0xabcdef01, we
+   return 0x0a0b0c0d0e0f0001 as *expandedP.
+
+   As a special case, if there are multiple bytes per pixel, we just
+   return the exact same bit string.
+
+   *expandedP is static storage.
+
+   'packedLen' must not be greater than 256.
+-----------------------------------------------------------------------------*/
+    static unsigned char expanded[256 * 8];
+    unsigned char * src;
+    unsigned char * dst;
+
+    assert(packedLen <= 256);
+
+    src = &packed[0];
+    dst = &expanded[0];
+
+    switch (bitsPerPixel) {
+    case 8:
+    case 16:
+    case 32:
+        assert(packedLen <= sizeof(expanded));
+        copyFullBytes(packed, expanded, packedLen);
+        *expandedLenP = packedLen;
+        break;
+    case 4:
+        assert(packedLen * 2 <= sizeof(expanded));
+        expand4Bits(packed, expanded, packedLen);
+        *expandedLenP = packedLen * 2;
+        break;
+    case 2:
+        assert(packedLen * 4 <= sizeof(expanded));
+        expand2Bits(packed, expanded, packedLen);
+        *expandedLenP = packedLen * 4;
+        break;
+    case 1:
+        assert(packedLen * 8 <= sizeof(expanded));
+        expand1Bit(packed, expanded, packedLen);
+        *expandedLenP = packedLen * 8;
+        break;
+    default:
+        pm_error("INTERNAL ERROR: bitsPerPixel = %u in unpackBuf",
+                 bitsPerPixel);
+    }
+    *expandedP = expanded;
+}
+
+
+
+static void
+unpackUncompressedBits(FILE *          const ifP,
+                       struct raster   const raster,
+                       unsigned int    const rowBytes,
+                       unsigned int    const bitsPerPixel) {
+/*----------------------------------------------------------------------------
+   Read the raster from the file into 'raster'.  The data in the file is not
+   compressed (but may still be packed multiple pixels per byte).
+
+   In PICT terminology, it appears that compression is called
+   "packing" and I don't know what packing is called.  But we don't
+   use that confusing terminology in this program, except when talking
+   to the user.
+-----------------------------------------------------------------------------*/
+    unsigned int rowOfRect;
+    unsigned char * linebuf;
+
+    MALLOCARRAY(linebuf, rowBytes + 100);
+    if (linebuf == NULL)
+        pm_error("can't allocate memory for line buffer");
+
+    for (rowOfRect = 0; rowOfRect < raster.rowCount; ++rowOfRect) { 
+        unsigned char * bytePixels;
+        unsigned int expandedByteCount;
+        unsigned char * rasterRow;
+        unsigned int i;
+
+        rasterRow = raster.bytes + rowOfRect * raster.rowSize;
+
+        readBytes(ifP, rowBytes, linebuf);
+
+        unpackBuf(linebuf, rowBytes, bitsPerPixel,
+                  &bytePixels, &expandedByteCount);
+
+        assert(expandedByteCount <= raster.rowSize);
+
+        for (i = 0; i < expandedByteCount; ++i)
+            rasterRow[i] = bytePixels[i];
+    }
+    free(linebuf);
+}
+
+
+
+static void
+expandRun(unsigned char * const block,
+          unsigned int    const blockLimit,
+          unsigned int    const bitsPerPixel,
+          unsigned char * const expanded,
+          unsigned int    const expandedSize,
+          unsigned int *  const blockLengthP,
+          unsigned int *  const expandedByteCountP) {
+/*----------------------------------------------------------------------------
+   Expand a run (the data says, "repeat the next pixel N times").
+
+   Return the expanded run as expanded[], which has room for 'expandedSize'
+   elements.  Return as *expandedByteCountP the number of elements actually
+   returned.
+-----------------------------------------------------------------------------*/
+    unsigned int const pkpixsize = bitsPerPixel == 16 ? 2 : 1;
+
+    if (1 + pkpixsize > blockLimit)
+        pm_error("PICT run block runs off the end of its line.  "
+                 "Invalid PICT file.");
+    else {
+        unsigned int const runLength = (block[0] ^ 0xff) + 2;
+        
+        unsigned int i;
+        unsigned char * bytePixels;  /* Points to static storage */
+        unsigned int expandedByteCount;
+        unsigned int outputCursor;
+        
+        assert(block[0] & 0x80);  /* It's a run */
+
+        if (verbose > 1)
+            pm_message("Block: run of %u pixels or plane samples", runLength);
+        
+        unpackBuf(&block[1], pkpixsize, bitsPerPixel,
+                  &bytePixels, &expandedByteCount);
+        
+        if (expandedByteCount > expandedSize)
+            pm_error("Invalid PICT image.  It contains a row with more pixels "
+                     "than the width of the image");
+        
+        outputCursor = 0;
+        for (i = 0; i < runLength; ++i) {
+            unsigned int j;
+            for (j = 0; j < expandedByteCount; ++j)
+                expanded[outputCursor++] = bytePixels[j];
+        }
+        *blockLengthP = 1 + pkpixsize;
+        *expandedByteCountP = expandedByteCount * runLength;
+    }
+}
+
+
+
+static void
+copyPixelGroup(unsigned char * const block,
+               unsigned int    const blockLimit,
+               unsigned int    const bitsPerPixel,
+               unsigned char * const dest,
+               unsigned int    const destSize,
+               unsigned int *  const blockLengthP,
+               unsigned int *  const rasterBytesGeneratedP) {
+/*----------------------------------------------------------------------------
+   Copy a group of pixels (the data says, "take the following N pixels").
+-----------------------------------------------------------------------------*/
+    unsigned int const pkpixsize = bitsPerPixel == 16 ? 2 : 1;
+    unsigned int const groupLen = block[0] + 1;
+    unsigned int const blockLength = 1 + groupLen * pkpixsize;
+
+    if (blockLength > blockLimit)
+        pm_error("PICT non-run block (length %u) "
+                 "runs off the end of its line.  "
+                 "Invalid PICT file.", blockLength);
+    else {
+        unsigned int i;
+        unsigned char * bytePixels;  /* Points to static storage */
+        unsigned int bytePixelLen;
+        
+        assert(blockLimit >= 1);  /* block[0] exists */
+        assert((block[0] & 0x80) == 0);  /* It's not a run */
+        
+        if (verbose > 1)
+            pm_message("Block: %u individual pixels or plane samples",
+                       groupLen);
+        
+        unpackBuf(&block[1], groupLen * pkpixsize, bitsPerPixel,
+                  &bytePixels, &bytePixelLen);
+        
+        if (bytePixelLen > destSize)
+            pm_error("Invalid PICT image.  It contains a row with more pixels "
+                     "than the width of the image");
+        
+        for (i = 0; i < bytePixelLen; ++i)
+            dest[i] = bytePixels[i];
+        
+        *blockLengthP = blockLength;
+        *rasterBytesGeneratedP = bytePixelLen;
+    }
+}
+
+
+
+static void
+interpretOneRasterBlock(unsigned char * const block,
+                        unsigned int    const blockLimit,
+                        unsigned int    const bitsPerPixel,
+                        unsigned char * const raster,
+                        unsigned int    const rasterSize,
+                        unsigned int *  const blockLengthP,
+                        unsigned int *  const rasterBytesGeneratedP) {
+/*----------------------------------------------------------------------------
+   Return the pixels described by the PICT block block[], assuming
+   the PICT format is 'bitsPerPixel' bits per pixel.
+
+   Return them as raster[], which has 'rasterSize' elements of space.
+   Return as *rasterBytesGeneratedP the number of elements actually
+   returned.
+
+   block[] self-describes its length, and we return that as
+   *blockLengthP.  But there are at most 'blockLimit' bytes there, so
+   if block[] claims to be longer than that, some of the block is
+   missing (i.e. corrupt PICT).
+-----------------------------------------------------------------------------*/
+    if (block[0] & 0x80)
+        expandRun(block, blockLimit, bitsPerPixel, raster, rasterSize,
+                  blockLengthP, rasterBytesGeneratedP);
+    else
+        copyPixelGroup(block, blockLimit, bitsPerPixel, raster, rasterSize,
+                       blockLengthP, rasterBytesGeneratedP);
+
+    assert(*rasterBytesGeneratedP <= rasterSize);
+}
+
+
+
+static unsigned int const maxPixelBytesPerBlock = 1024;
+
+
+
+static void
+unpackCompressedBits(FILE *          const ifP,
+                     struct raster   const raster,
+                     unsigned int    const rowBytes,
+                     unsigned int    const bitsPerPixel) {
+/*----------------------------------------------------------------------------
+   Read the raster of on file *ifP and place it in 'raster'.
+
+   The data in the file is compressed with run length encoding and
+   possibly packed multiple pixels per byte as well.
+
+   In PICT terminology, it appears that compression is called
+   "packing" and I don't know what packing is called.  But we don't
+   use that confusing terminology in this program, except when talking
+   to the user.
+
+   *boundsP describes the rectangle.
+-----------------------------------------------------------------------------*/
+    unsigned int const llsize = rowBytes > 200 ? 2 : 1;
+    unsigned int rowOfRect;
+    unsigned char * linebuf;
+    unsigned int linebufSize;
+
+    linebufSize = rowBytes;
+    MALLOCARRAY(linebuf, linebufSize);
+    if (linebuf == NULL)
+        pm_error("can't allocate memory for line buffer");
+
+    for (rowOfRect = 0; rowOfRect < raster.rowCount; ++rowOfRect) {
+        unsigned char * const rowRaster =
+            &raster.bytes[rowOfRect * raster.rowSize];
+        unsigned int linelen;
+        unsigned int lineCursor;
+        unsigned int rasterCursor;
+
+        if (llsize == 2)
+            linelen = read_word();
+        else
+            linelen = read_byte();
+
+        if (verbose > 1)
+            pm_message("linelen: %u", linelen);
+
+        if (linelen > linebufSize) {
+            linebufSize = linelen;
+            REALLOCARRAY(linebuf, linebufSize);
+            if (linebuf == NULL)
+                pm_error("can't allocate memory for line buffer");
+        }
+        readBytes(ifP, linelen, linebuf);
+
+        for (lineCursor = 0, rasterCursor = 0; lineCursor < linelen; ) {
+            unsigned int blockLength, rasterBytesGenerated;
+
+            assert(lineCursor <= linelen);
+            
+            interpretOneRasterBlock(
+                &linebuf[lineCursor], linelen - lineCursor,
+                bitsPerPixel,
+                &rowRaster[rasterCursor], raster.rowSize - rasterCursor,
+                &blockLength, &rasterBytesGenerated);
+
+            lineCursor += blockLength;
+            rasterCursor += rasterBytesGenerated;
+            assert(rasterCursor <= raster.rowSize);
+        }
+        if (verbose > 1)
+            pm_message("row %u: got %u", rowOfRect, rasterCursor);
+    }
+    free(linebuf);
+}
+
+
+
+static void
+unpackbits(FILE *           const ifP,
+           struct Rect *    const boundsP,
+           word             const rowBytesArg, 
+           int              const bitsPerPixel,
+           struct raster *  const rasterP) {
+
+    unsigned int const rectHeight = boundsP->bottom - boundsP->top;
+    unsigned int const rectWidth  = boundsP->right  - boundsP->left;
+    
+    struct raster raster;
+    unsigned int rowBytes;
+
+    stage = "unpacking packbits";
+
+    if (verbose)
+        pm_message("rowBytes = %u, bitsPerPixel = %d",
+                   rowBytesArg, bitsPerPixel);
+        
+    allocateRaster(&raster, rectWidth, rectHeight, bitsPerPixel);
+
+    rowBytes = rowBytesArg ? rowBytesArg : raster.rowSize;
+
+    if (verbose)
+        pm_message("raster.rowSize: %u bytes; file row = %u bytes",
+                   raster.rowSize, rowBytes);
+
+    if (rowBytes < 8)
+        unpackUncompressedBits(ifP, raster, rowBytes, bitsPerPixel);
+    else
+        unpackCompressedBits(ifP, raster, rowBytes, bitsPerPixel);
+
+    *rasterP = raster;
+}
+
+
+
+static void
+interpretRowBytesWord(word           const rowBytesWord,
+                      bool *         const pixMapP,
+                      unsigned int * const rowBytesP) {
+
+    *pixMapP   = !!(rowBytesWord & 0x8000);
+    *rowBytesP = rowBytesWord & 0x7fff;
+
+    if (verbose)
+        pm_message("PCT says %s, %u bytes per row",
+                   *pixMapP ? "pixmap" : "bitmap", *rowBytesP);
+}
+
+
+
+/* this just skips over a version 2 pattern.  Probably will return
+ * a pattern in the fabled complete version.
+ */
+static void
+read_pattern(void) {
+
+    word PatType;
+
+    stage = "Reading a pattern";
+
+    PatType = read_word();
+
+    switch (PatType) {
+    case 2:
+        skip(8); /* old pattern data */
+        skip(5); /* RGB for pattern */
+        break;
+    case 1: {
+        word rowBytesWord;
+        bool pixMap;
+        unsigned int rowBytes;
+        struct pixMap p;
+        struct raster raster;
+        struct RGBColor * ct;
+
+        skip(8); /* old pattern data */
+        rowBytesWord = read_word();
+        interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes);
+        read_pixmap(&p);
+        ct = read_color_table();
+        unpackbits(ifp, &p.Bounds, rowBytes, p.pixelSize, &raster);
+        freeRaster(raster);
+        free(ct);
+    } break;
+    default:
+        pm_error("unknown pattern type in read_pattern");
+    }
+}
+
+
+
+/* these 3 do nothing but skip over their data! */
+
+static void
+BkPixPat(int const version) {
+    read_pattern();
+}
+
+static void
+PnPixPat(int const version) {
+    read_pattern();
+}
+
+static void
+FillPixPat(int const version) {
+    read_pattern();
+}
+
+static void
+read_8x8_pattern(struct Pattern * const pat) {
+    unsigned char buf[8];
+    unsigned char * exp;
+    unsigned int len;
+    unsigned int expandedLen;
+    unsigned int i;
+
+    len = 8;  /* initial value */
+    readBytes(ifp, len, buf);
+    if (verbose) {
+        pm_message("pattern: %02x%02x%02x%02x",
+                   buf[0], buf[1], buf[2], buf[3]);
+        pm_message("pattern: %02x%02x%02x%02x",
+            buf[4], buf[5], buf[6], buf[7]);
+    }
+    unpackBuf(buf, len, 1, &exp, &expandedLen);
+    for (i = 0; i < 64; ++i)
+        pat->pix[i] = exp[i];
+}
+
+
+
+static void 
+BkPat(int const version) {
+    read_8x8_pattern(&bkpat);
+}
+
+
+
+static void 
+PnPat(int const version) {
+    read_8x8_pattern(&pen_pat);
+}
+
+
+
+static void 
+FillPat(int const version) {
+    read_8x8_pattern(&fillpat);
+}
+
+
+
+static void 
+PnSize(int const version) {
+    pen_height = read_word();
+    pen_width = read_word();
+    if (verbose)
+        pm_message("pen size %d x %d", pen_width, pen_height);
+}
+
+
+
+static void 
+PnMode(int const version) {
+
+    pen_mode = read_word();
+
+    if (pen_mode >= 8 && pen_mode < 15)
+        pen_mode -= 8;
+    if (verbose)
+        pm_message("pen transfer mode = %s",
+            const_name(transfer_name, pen_mode));
+    
+    pen_trf = transfer(pen_mode);
+}
+
+
+
+static void 
+read_rgb(struct RGBColor * const rgb) {
+    rgb->red = read_word();
+    rgb->grn = read_word();
+    rgb->blu = read_word();
+}
+
+
+
+static void 
+RGBFgCol(int const v) {
+    read_rgb(&foreground);
+    if (verbose)
+        pm_message("foreground now [%d,%d,%d]", 
+            foreground.red, foreground.grn, foreground.blu);
+}
+
+
+
+static void 
+RGBBkCol(int const v) {
+    read_rgb(&background);
+    if (verbose)
+        pm_message("background now [%d,%d,%d]", 
+            background.red, background.grn, background.blu);
+}
+
+
+
+#define PIXEL_INDEX(x,y) ((y) - picFrame.top) * rowlen + (x) - picFrame.left
+
+static void 
+draw_pixel(int                const x, 
+           int                const y, 
+           struct RGBColor * const clr, 
+           transfer_func            trf) {
+
+    int i;
+    struct RGBColor dst;
+
+    if (x < clip_rect.left || x >= clip_rect.right ||
+        y < clip_rect.top || y >= clip_rect.bottom)
+    {
+        return;
+    }
+
+    i = PIXEL_INDEX(x, y);
+    dst.red = red[i];
+    dst.grn = green[i];
+    dst.blu = blue[i];
+    (*trf)(clr, &dst);
+    red[i] = dst.red;
+    green[i] = dst.grn;
+    blue[i] = dst.blu;
+}
+
+
+
+static void 
+draw_pen_rect(struct Rect * const r) {
+    int const rowadd = rowlen - (r->right - r->left);
+
+    int i;
+    int x, y;
+    struct RGBColor dst;
+
+    i = PIXEL_INDEX(r->left, r->top);  /* initial value */
+    
+    for (y = r->top; y < r->bottom; y++) {
+        for (x = r->left; x < r->right; x++) {
+            dst.red = red[i];
+            dst.grn = green[i];
+            dst.blu = blue[i];
+            if (pen_pat.pix[(x & 7) + (y & 7) * 8])
+                (*pen_trf)(&black, &dst);
+            else
+                (*pen_trf)(&white, &dst);
+            red[i] = dst.red;
+            green[i] = dst.grn;
+            blue[i] = dst.blu;
+
+            i++;
+        }
+        i += rowadd;
+    }
+}
+
+
+
+static void 
+draw_pen(int const x, 
+         int const y) {
+    struct Rect penrect;
+
+    penrect.left = x;
+    penrect.right = x + pen_width;
+    penrect.top = y;
+    penrect.bottom = y + pen_height;
+
+    rectinter(penrect, clip_rect, &penrect);
+
+    draw_pen_rect(&penrect);
+}
+
+/*
+ * Digital Line Drawing
+ * by Paul Heckbert
+ * from "Graphics Gems", Academic Press, 1990
+ */
+
+/*
+ * digline: draw digital line from (x1,y1) to (x2,y2),
+ * calling a user-supplied procedure at each pixel.
+ * Does no clipping.  Uses Bresenham's algorithm.
+ *
+ * Paul Heckbert    3 Sep 85
+ */
+static void 
+scan_line(short const x1, 
+          short const y1, 
+          short const x2, 
+          short const y2) {
+    int d, x, y, ax, ay, sx, sy, dx, dy;
+
+    if (!(pen_width == 0 && pen_height == 0)) {
+
+        dx = x2-x1;  ax = ABS(dx)<<1;  sx = SGN(dx);
+        dy = y2-y1;  ay = ABS(dy)<<1;  sy = SGN(dy);
+
+        x = x1;
+        y = y1;
+        if (ax>ay) {        /* x dominant */
+            d = ay-(ax>>1);
+            for (;;) {
+                draw_pen(x, y);
+                if (x==x2) return;
+                if ((x > rowlen) && (sx > 0)) return;
+                if (d>=0) {
+                    y += sy;
+                    d -= ax;
+                }
+                x += sx;
+                d += ay;
+            }
+        }
+        else {          /* y dominant */
+            d = ax-(ay>>1);
+            for (;;) {
+                draw_pen(x, y);
+                if (y==y2) return;
+                if ((y > collen) && (sy > 0)) return;
+                if (d>=0) {
+                    x += sx;
+                    d -= ay;
+                }
+                y += sy;
+                d += ax;
+            }
+        }
+    }
+}
+
+
+
+static void 
+Line(int const v) {
+  struct Point p1;
+  read_point(&p1);
+  read_point(&current);
+  if (verbose)
+    pm_message("(%d,%d) to (%d, %d)",
+           p1.x,p1.y,current.x,current.y);
+  scan_line(p1.x,p1.y,current.x,current.y);
+}
+
+
+
+static void 
+LineFrom(int const v) {
+  struct Point p1;
+  read_point(&p1);
+  if (verbose)
+    pm_message("(%d,%d) to (%d, %d)",
+           current.x,current.y,p1.x,p1.y);
+
+  if (!fullres)
+      scan_line(current.x,current.y,p1.x,p1.y);
+
+  current.x = p1.x;
+  current.y = p1.y;
+}
+
+
+
+static void 
+ShortLine(int const v) {
+  struct Point p1;
+  read_point(&p1);
+  read_short_point(&current);
+  if (verbose)
+    pm_message("(%d,%d) delta (%d, %d)",
+           p1.x,p1.y,current.x,current.y);
+  current.x += p1.x;
+  current.y += p1.y;
+
+  if (!fullres)
+      scan_line(p1.x,p1.y,current.x,current.y);
+}
+
+
+
+static void 
+ShortLineFrom(int const v) {
+  struct Point p1;
+  read_short_point(&p1);
+  if (verbose)
+    pm_message("(%d,%d) delta (%d, %d)",
+               current.x,current.y,p1.x,p1.y);
+  p1.x += current.x;
+  p1.y += current.y;
+  if (!fullres)
+      scan_line(current.x,current.y,p1.x,p1.y);
+  current.x = p1.x;
+  current.y = p1.y;
+}
+
+static void 
+do_paintRect(struct Rect const prect) {
+    struct Rect rect;
+  
+    if (fullres)
+        return;
+
+    if (verbose)
+        dumpRect("painting", prect);
+
+    rectinter(clip_rect, prect, &rect);
+
+    draw_pen_rect(&rect);
+}
+
+
+
+static void 
+paintRect(int const v) {
+    read_rect(&cur_rect);
+    do_paintRect(cur_rect);
+}
+
+
+
+static void 
+paintSameRect(int const v) {
+    do_paintRect(cur_rect);
+}
+
+
+
+static void 
+do_frameRect(struct Rect const rect) {
+    int x, y;
+
+    if (fullres)
+        return;
+  
+    if (verbose)
+        dumpRect("framing", rect);
+
+    if (pen_width == 0 || pen_height == 0)
+        return;
+
+    for (x = rect.left; x <= rect.right - pen_width; x += pen_width) {
+        draw_pen(x, rect.top);
+        draw_pen(x, rect.bottom - pen_height);
+    }
+
+    for (y = rect.top; y <= rect.bottom - pen_height ; y += pen_height) {
+        draw_pen(rect.left, y);
+        draw_pen(rect.right - pen_width, y);
+    }
+}
+
+
+
+static void 
+frameRect(int const v) {
+    read_rect(&cur_rect);
+    do_frameRect(cur_rect);
+}
+
+
+
+static void 
+frameSameRect(int const v) {
+    do_frameRect(cur_rect);
+}
+
+
+
+/* a stupid shell sort - I'm so embarassed  */
+
+static void 
+poly_sort(int const sort_index, struct Point points[]) {
+  int d, i, j, temp;
+
+  /* initialize and set up sort interval */
+  d = 4;
+  while (d<=sort_index) d <<= 1;
+  d -= 1;
+
+  while (d > 1) {
+    d >>= 1;
+    for (j = 0; j <= (sort_index-d); j++) {
+      for(i = j; i >= 0; i -= d) {
+    if ((points[i+d].y < points[i].y) ||
+        ((points[i+d].y == points[i].y) &&
+         (points[i+d].x <= points[i].x))) {
+      /* swap x1,y1 with x2,y2 */
+      temp = points[i].y;
+      points[i].y = points[i+d].y;
+      points[i+d].y = temp;
+      temp = points[i].x;
+      points[i].x = points[i+d].x;
+      points[i+d].x = temp;
+    }
+      }
+    }
+  }
+}
+
+
+
+/* Watch out for the lack of error checking in the next two functions ... */
+
+static void 
+scan_poly(int          const np, 
+          struct Point       pts[]) {
+  int dx,dy,dxabs,dyabs,i,scan_index,j,k,px,py;
+  int sdx,sdy,x,y,toggle,old_sdy,sy0;
+
+  /* This array needs to be at least as large as the largest dimension of
+     the bounding box of the poly (but I don't check for overflows ...) */
+  struct Point coord[5000];
+
+  scan_index = 0;
+
+  /* close polygon */
+  px = pts[np].x = pts[0].x;
+  py = pts[np].y = pts[0].y;
+
+  /*  This section draws the polygon and stores all the line points
+   *  in an array. This doesn't work for concave or non-simple polys.
+   */
+  /* are y levels same for first and second points? */
+  if (pts[1].y == pts[0].y) {
+    coord[scan_index].x = px;
+    coord[scan_index].y = py;
+    scan_index++;
+  }
+
+#define sign(x) ((x) > 0 ? 1 : ((x)==0 ? 0:(-1)) )   
+
+  old_sdy = sy0 = sign(pts[1].y - pts[0].y);
+  for (j=0; j<np; j++) {
+    /* x,y difference between consecutive points and their signs  */
+    dx = pts[j+1].x - pts[j].x;
+    dy = pts[j+1].y - pts[j].y;
+    sdx = SGN(dx);
+    sdy = SGN(dy);
+    dxabs = abs(dx);
+    dyabs = abs(dy);
+    x = y = 0;
+
+    if (dxabs >= dyabs)
+      {
+    for (k=0; k < dxabs; k++) {
+      y += dyabs;
+      if (y >= dxabs) {
+        y -= dxabs;
+        py += sdy;
+        if (old_sdy != sdy) {
+          old_sdy = sdy;
+          scan_index--;
+        }
+        coord[scan_index].x = px+sdx;
+        coord[scan_index].y = py;
+        scan_index++;
+      }
+      px += sdx;
+      draw_pen(px, py);
+    }
+      }
+    else
+      {
+    for (k=0; k < dyabs; k++) {
+      x += dxabs;
+      if (x >= dyabs) {
+        x -= dyabs;
+        px += sdx;
+      }
+      py += sdy;
+      if (old_sdy != sdy) {
+        old_sdy = sdy;
+        if (sdy != 0) scan_index--;
+      }
+      draw_pen(px,py);
+      coord[scan_index].x = px;
+      coord[scan_index].y = py;
+      scan_index++;
+    }
+      }
+  }
+
+  /* after polygon has been drawn now fill it */
+
+  scan_index--;
+  if (sy0 + sdy == 0) scan_index--;
+
+  poly_sort(scan_index, coord);
+  
+  toggle = 0;
+  for (i = 0; i < scan_index; i++) {
+    if ((coord[i].y == coord[i+1].y) && (toggle == 0))
+      {
+    for (j = coord[i].x; j <= coord[i+1].x; j++)
+      draw_pen(j, coord[i].y);
+    toggle = 1;
+      }
+    else
+      toggle = 0;
+  }
+}
+  
+
+  
+static void 
+paintPoly(int const v) {
+  struct Rect bb;
+  struct Point pts[100];
+  int i, np = (read_word() - 10) >> 2;
+
+  read_rect(&bb);
+  for (i=0; i<np; ++i)
+    read_point(&pts[i]);
+
+  /* scan convert poly ... */
+  if (!fullres)
+      scan_poly(np, pts);
+}
+
+
+
+static void 
+PnLocHFrac(int const version) {
+    word frac = read_word();
+
+    if (verbose)
+        pm_message("PnLocHFrac = %d", frac);
+}
+
+
+
+static void 
+TxMode(int const version) {
+    text_mode = read_word();
+
+    if (text_mode >= 8 && text_mode < 15)
+        text_mode -= 8;
+    if (verbose)
+        pm_message("text transfer mode = %s",
+            const_name(transfer_name, text_mode));
+    
+    /* ignore the text mask bit 'cause we don't handle it yet */
+    text_trf = transfer(text_mode & ~64);
+}
+
+
+
+static void 
+TxFont(int const version) {
+    text_font = read_word();
+    if (verbose)
+        pm_message("text font %s", const_name(font_name, text_font));
+}
+
+
+
+static void 
+TxFace(int const version) {
+    text_face = read_byte();
+    if (verbose)
+        pm_message("text face %d", text_face);
+}
+
+
+
+static void 
+TxSize(int const version) {
+    text_size = read_word();
+    if (verbose)
+        pm_message("text size %d", text_size);
+}
+
+
+
+static void
+skip_text(void) {
+    skip(read_byte());
+}
+
+
+
+static int 
+abs_value(int const x) {
+    if (x < 0)
+        return -x;
+    else
+        return x;
+}
+
+
+
+static struct font* 
+get_font(int const font, 
+         int const size, 
+         int const style) {
+    int closeness, bestcloseness;
+    struct fontinfo* fi, *best;
+
+    best = 0;
+    for (fi = fontlist; fi; fi = fi->next) {
+        closeness = abs_value(fi->font - font) * 10000 +
+            abs_value(fi->size - size) * 100 +
+            abs_value(fi->style - style);
+        if (!best || closeness < bestcloseness) {
+            best = fi;
+            bestcloseness = closeness;
+        }
+    }
+
+    if (best) {
+        if (best->loaded)
+            return best->loaded;
+
+        if ((best->loaded = pbm_loadbdffont(best->filename)))
+            return best->loaded;
+    }
+
+    /* It would be better to go looking for the nth best font, really */
+    return 0;
+}
+
+
+
+/* This only does 0, 90, 180 and 270 degree rotations */
+
+static void 
+rotate(int * const x, 
+       int * const y) {
+    int tmp;
+
+    if (ps_rotation >= 315 || ps_rotation <= 45)
+        return;
+
+    *x -= ps_cent_x;
+    *y -= ps_cent_y;
+
+    if (ps_rotation > 45 && ps_rotation < 135) {
+        tmp = *x;
+        *x = *y;
+        *y = tmp;
+    }
+    else if (ps_rotation >= 135 && ps_rotation < 225) {
+        *x = -*x;
+    }
+    else if (ps_rotation >= 225 && ps_rotation < 315) {
+        tmp = *x;
+        *x = *y;
+        *y = -tmp;
+    }
+    *x += ps_cent_x;
+    *y += ps_cent_y;
+}
+
+
+
+static void
+do_ps_text(word const tx, 
+           word const ty) {
+    int len, width, i, w, h, x, y, rx, ry, o;
+    byte str[256], ch;
+    struct glyph* glyph;
+
+    current.x = tx;
+    current.y = ty;
+
+    if (!ps_cent_set) {
+        ps_cent_x += tx;
+        ps_cent_y += ty;
+        ps_cent_set = 1;
+    }
+
+    len = read_byte();
+
+    /* XXX this width calculation is not completely correct */
+    width = 0;
+    for (i = 0; i < len; i++) {
+        ch = str[i] = read_byte();
+        if (tfont->glyph[ch])
+            width += tfont->glyph[ch]->xadd;
+    }
+
+    if (verbose) {
+        str[len] = '\0';
+        pm_message("ps text: %s", str);
+    }
+
+    /* XXX The width is calculated in order to do different justifications.
+     * However, I need the width of original text to finish the job.
+     * In other words, font metrics for Quickdraw fonts
+     */
+
+    x = tx;
+
+    for (i = 0; i < len; i++) {
+        if (!(glyph = tfont->glyph[str[i]]))
+            continue;
+        
+        y = ty - glyph->height - glyph->y;
+        for (h = 0; h < glyph->height; h++) {
+            for (w = 0; w < glyph->width; w++) {
+                rx = x + glyph->x + w;
+                ry = y;
+                rotate(&rx, &ry);
+                if ((rx >= picFrame.left) && (rx < picFrame.right) &&
+                    (ry >= picFrame.top) && (ry < picFrame.bottom))
+                {
+                    o = PIXEL_INDEX(rx, ry);
+                    if (glyph->bmap[h * glyph->width + w]) {
+                        red[o] = foreground.red;
+                        green[o] = foreground.grn;
+                        blue[o] = foreground.blu;
+                    }
+                }
+            }
+            y++;
+        }
+        x += glyph->xadd;
+    }
+}
+
+
+
+static void
+do_text(word const startx, 
+        word const starty) {
+    if (fullres)
+        skip_text();
+    else {
+        if (!(tfont = get_font(text_font, text_size, text_face)))
+            tfont = pbm_defaultfont("bdf");
+
+        if (ps_text)
+            do_ps_text(startx, starty);
+        else {
+            int len;
+            word x, y;
+
+            x = startx;
+            y = starty;
+            for (len = read_byte(); len > 0; --len) {
+                struct glyph* const glyph = tfont->glyph[read_byte()];
+                if (glyph) {
+                    int dy;
+                    int h;
+                    for (h = 0, dy = y - glyph->height - glyph->y;
+                         h < glyph->height; 
+                         ++h, ++dy) {
+                        int w;
+                        for (w = 0; w < glyph->width; ++w) {
+                            struct RGBColor * const colorP = 
+                                glyph->bmap[h * glyph->width + w] ?
+                                &black : &white;
+                            draw_pixel(x + w + glyph->x, dy, colorP, text_trf);
+                        }
+                    }
+                    x += glyph->xadd;
+                }
+            }
+            current.x = x;
+            current.y = y;
+        }
+    }
+}
+
+
+
+static void
+LongText(int const version) {
+    struct Point p;
+
+    read_point(&p);
+    do_text(p.x, p.y);
+}
+
+
+
+static void
+DHText(int const version) {
+    current.x += read_byte();
+    do_text(current.x, current.y);
+}
+
+
+
+static void
+DVText(int const version) {
+    current.y += read_byte();
+    do_text(current.x, current.y);
+}
+
+
+
+static void
+DHDVText(int const version) {
+    byte dh, dv;
+
+    dh = read_byte();
+    dv = read_byte();
+
+    if (verbose)
+        pm_message("dh, dv = %d, %d", dh, dv);
+
+    current.x += dh;
+    current.y += dv;
+    do_text(current.x, current.y);
+}
+
+
+
+/*
+ * This could use read_pixmap, but I'm too lazy to hack read_pixmap.
+ */
+
+static void
+directBits(unsigned int const pictVersion, 
+           bool         const skipRegion) {
+
+    struct pixMap   p;
+    struct Rect     srcRect;
+    struct Rect     dstRect;
+    struct raster   raster;
+    word            mode;
+    unsigned int    rectWidth;
+
+    /* skip fake len, and fake EOF */
+    skip(4);    /* Ptr baseAddr == 0x000000ff */
+    read_word();    /* version */
+    read_rect(&p.Bounds);
+    rectWidth = p.Bounds.right - p.Bounds.left;
+    p.packType = read_word();
+    p.packSize = read_long();
+    p.hRes = read_long();
+    p.vRes = read_long();
+    p.pixelType = read_word();
+    p.pixelSize = read_word();
+    p.pixelSize = read_word();    /* XXX twice??? */
+    p.cmpCount = read_word();
+    p.cmpSize = read_word();
+    p.planeBytes = read_long();
+    p.pmTable = read_long();
+    p.pmReserved = read_long();
+
+    read_rect(&srcRect);
+    if (verbose)
+        dumpRect("source rectangle:", srcRect);
+
+    read_rect(&dstRect);
+    if (verbose)
+        dumpRect("destination rectangle:", dstRect);
+
+    mode = read_word();
+    if (verbose)
+        pm_message("transfer mode = %s", const_name(transfer_name, mode));
+
+    if (skipRegion) 
+        skip_poly_or_region(pictVersion);
+
+    unpackbits(ifp, &p.Bounds, 0, p.pixelSize, &raster);
+
+    blit(srcRect, p.Bounds, raster, p.pixelSize,
+         dstRect, picFrame, rowlen, NULL, mode);
+
+    freeRaster(raster);
+}
+
+
+
+#define SKIP_REGION_TRUE TRUE
+#define SKIP_REGION_FALSE FALSE
+
+static void
+DirectBitsRect(int const version) {
+
+    directBits(version, SKIP_REGION_FALSE);
+}
+
+
+
+static void
+DirectBitsRgn(int const version) {
+
+    directBits(version, SKIP_REGION_TRUE);
+}
+
+
+
+static void
+do_pixmap(int  const version, 
+          word const rowBytes, 
+          int  const is_region) {
+/*----------------------------------------------------------------------------
+   Do a paletted image.
+-----------------------------------------------------------------------------*/
+    word mode;
+    struct pixMap p;
+    struct raster raster;
+    struct RGBColor * color_table;
+    struct Rect srcRect;
+    struct Rect dstRect;
+
+    read_pixmap(&p);
+
+    if (verbose)
+        pm_message("%u x %u paletted image",
+                   p.Bounds.right - p.Bounds.left,
+                   p.Bounds.bottom - p.Bounds.top);
+
+    color_table = read_color_table();
+
+    read_rect(&srcRect);
+
+    if (verbose)
+        dumpRect("source rectangle:", srcRect);
+
+    read_rect(&dstRect);
+
+    if (verbose)
+        dumpRect("destination rectangle:", dstRect);
+
+    mode = read_word();
+
+    if (verbose)
+        pm_message("transfer mode = %s", const_name(transfer_name, mode));
+
+    if (is_region)
+        skip_poly_or_region(version);
+
+    stage = "unpacking rectangle";
+
+    unpackbits(ifp, &p.Bounds, rowBytes, p.pixelSize, &raster);
+
+    blit(srcRect, p.Bounds, raster, 8,
+         dstRect, picFrame, rowlen, color_table, mode);
+
+    free(color_table);
+    freeRaster(raster);
+}
+
+
+
+static void
+do_bitmap(int const version, 
+          int const rowBytes, 
+          int const is_region) {
+/*----------------------------------------------------------------------------
+   Do a bitmap.  That's one bit per pixel, 0 is white, 1 is black.
+-----------------------------------------------------------------------------*/
+    struct Rect Bounds;
+    struct Rect srcRect;
+    struct Rect dstRect;
+    word mode;
+    struct raster raster;
+    static struct RGBColor color_table[] = { 
+        {65535L, 65535L, 65535L}, {0, 0, 0} };
+
+    read_rect(&Bounds);
+    read_rect(&srcRect);
+    read_rect(&dstRect);
+    mode = read_word();
+    if (verbose)
+        pm_message("transfer mode = %s", const_name(transfer_name, mode));
+
+    if (is_region)
+        skip_poly_or_region(version);
+
+    stage = "unpacking rectangle";
+
+    unpackbits(ifp, &Bounds, rowBytes, 1, &raster);
+
+    blit(srcRect, Bounds, raster, 8,
+         dstRect, picFrame, rowlen, color_table, mode);
+
+    freeRaster(raster);
+}
+
+
+
+static void
+BitsRect(int const version) {
+
+    word rowBytesWord;
+    bool pixMap;
+    unsigned int rowBytes;
+
+    stage = "Reading rowBytes word for bitsrect";
+    rowBytesWord = read_word();
+
+    interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes);
+
+    if (pixMap)
+        do_pixmap(version, rowBytes, 0);
+    else
+        do_bitmap(version, rowBytes, 0);
+}
+
+
+
+static void
+BitsRegion(int const version) {
+
+    word rowBytesWord;
+    bool pixMap;
+    unsigned int rowBytes;
+
+    stage = "Reading rowBytes for bitsregion";
+    rowBytesWord = read_word();
+
+    interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes);
+
+    if (pixMap)
+        do_pixmap(version, rowBytes, 1);
+    else
+        do_bitmap(version, rowBytes, 1);
+}
+
+
+
+ /*
+  * See http://developer.apple.com/techpubs/mac/QuickDraw/QuickDraw-461.html
+  * for opcode description
+  */
+static struct opdef const optable[] = {
+/* 0x00 */  { "NOP", 0, NULL, "nop" },
+/* 0x01 */  { "Clip", NA, Clip, "clip" },
+/* 0x02 */  { "BkPat", 8, BkPat, "background pattern" },
+/* 0x03 */  { "TxFont", 2, TxFont, "text font (word)" },
+/* 0x04 */  { "TxFace", 1, TxFace, "text face (byte)" },
+/* 0x05 */  { "TxMode", 2, TxMode, "text mode (word)" },
+/* 0x06 */  { "SpExtra", 4, NULL, "space extra (fixed point)" },
+/* 0x07 */  { "PnSize", 4, PnSize, "pen size (point)" },
+/* 0x08 */  { "PnMode", 2, PnMode, "pen mode (word)" },
+/* 0x09 */  { "PnPat", 8, PnPat, "pen pattern" },
+/* 0x0a */  { "FillPat", 8, FillPat, "fill pattern" },
+/* 0x0b */  { "OvSize", 4, NULL, "oval size (point)" },
+/* 0x0c */  { "Origin", 4, NULL, "dh, dv (word)" },
+/* 0x0d */  { "TxSize", 2, TxSize, "text size (word)" },
+/* 0x0e */  { "FgColor", 4, NULL, "foreground color (longword)" },
+/* 0x0f */  { "BkColor", 4, NULL, "background color (longword)" },
+/* 0x10 */  { "TxRatio", 8, NULL, "numerator (point), denominator (point)" },
+/* 0x11 */  { "Version", 1, NULL, "version (byte)" },
+/* 0x12 */  { "BkPixPat", NA, BkPixPat, "color background pattern" },
+/* 0x13 */  { "PnPixPat", NA, PnPixPat, "color pen pattern" },
+/* 0x14 */  { "FillPixPat", NA, FillPixPat, "color fill pattern" },
+/* 0x15 */  { "PnLocHFrac", 2, PnLocHFrac, "fractional pen position" },
+/* 0x16 */  { "ChExtra", 2, NULL, "extra for each character" },
+/* 0x17 */  res(0),
+/* 0x18 */  res(0),
+/* 0x19 */  res(0),
+/* 0x1a */  { "RGBFgCol", RGB_LEN, RGBFgCol, "RGB foreColor" },
+/* 0x1b */  { "RGBBkCol", RGB_LEN, RGBBkCol, "RGB backColor" },
+/* 0x1c */  { "HiliteMode", 0, NULL, "hilite mode flag" },
+/* 0x1d */  { "HiliteColor", RGB_LEN, NULL, "RGB hilite color" },
+/* 0x1e */  { "DefHilite", 0, NULL, "Use default hilite color" },
+/* 0x1f */  { "OpColor", NA, OpColor, "RGB OpColor for arithmetic modes" },
+/* 0x20 */  { "Line", 8, Line, "pnLoc (point), newPt (point)" },
+/* 0x21 */  { "LineFrom", 4, LineFrom, "newPt (point)" },
+/* 0x22 */  { "ShortLine", 6, ShortLine, 
+              "pnLoc (point, dh, dv (-128 .. 127))" },
+/* 0x23 */  { "ShortLineFrom", 2, ShortLineFrom, "dh, dv (-128 .. 127)" },
+/* 0x24 */  res(WORD_LEN),
+/* 0x25 */  res(WORD_LEN),
+/* 0x26 */  res(WORD_LEN),
+/* 0x27 */  res(WORD_LEN),
+/* 0x28 */  { "LongText", NA, LongText, 
+              "txLoc (point), count (0..255), text" },
+/* 0x29 */  { "DHText", NA, DHText, "dh (0..255), count (0..255), text" },
+/* 0x2a */  { "DVText", NA, DVText, "dv (0..255), count (0..255), text" },
+/* 0x2b */  { "DHDVText", NA, DHDVText, 
+              "dh, dv (0..255), count (0..255), text" },
+/* 0x2c */  res(WORD_LEN),
+/* 0x2d */  res(WORD_LEN),
+/* 0x2e */  res(WORD_LEN),
+/* 0x2f */  res(WORD_LEN),
+/* 0x30 */  { "frameRect", 8, frameRect, "rect" },
+/* 0x31 */  { "paintRect", 8, paintRect, "rect" },
+/* 0x32 */  { "eraseRect", 8, NULL, "rect" },
+/* 0x33 */  { "invertRect", 8, NULL, "rect" },
+/* 0x34 */  { "fillRect", 8, NULL, "rect" },
+/* 0x35 */  res(8),
+/* 0x36 */  res(8),
+/* 0x37 */  res(8),
+/* 0x38 */  { "frameSameRect", 0, frameSameRect, "rect" },
+/* 0x39 */  { "paintSameRect", 0, paintSameRect, "rect" },
+/* 0x3a */  { "eraseSameRect", 0, NULL, "rect" },
+/* 0x3b */  { "invertSameRect", 0, NULL, "rect" },
+/* 0x3c */  { "fillSameRect", 0, NULL, "rect" },
+/* 0x3d */  res(0),
+/* 0x3e */  res(0),
+/* 0x3f */  res(0),
+/* 0x40 */  { "frameRRect", 8, NULL, "rect" },
+/* 0x41 */  { "paintRRect", 8, NULL, "rect" },
+/* 0x42 */  { "eraseRRect", 8, NULL, "rect" },
+/* 0x43 */  { "invertRRect", 8, NULL, "rect" },
+/* 0x44 */  { "fillRRrect", 8, NULL, "rect" },
+/* 0x45 */  res(8),
+/* 0x46 */  res(8),
+/* 0x47 */  res(8),
+/* 0x48 */  { "frameSameRRect", 0, NULL, "rect" },
+/* 0x49 */  { "paintSameRRect", 0, NULL, "rect" },
+/* 0x4a */  { "eraseSameRRect", 0, NULL, "rect" },
+/* 0x4b */  { "invertSameRRect", 0, NULL, "rect" },
+/* 0x4c */  { "fillSameRRect", 0, NULL, "rect" },
+/* 0x4d */  res(0),
+/* 0x4e */  res(0),
+/* 0x4f */  res(0),
+/* 0x50 */  { "frameOval", 8, NULL, "rect" },
+/* 0x51 */  { "paintOval", 8, NULL, "rect" },
+/* 0x52 */  { "eraseOval", 8, NULL, "rect" },
+/* 0x53 */  { "invertOval", 8, NULL, "rect" },
+/* 0x54 */  { "fillOval", 8, NULL, "rect" },
+/* 0x55 */  res(8),
+/* 0x56 */  res(8),
+/* 0x57 */  res(8),
+/* 0x58 */  { "frameSameOval", 0, NULL, "rect" },
+/* 0x59 */  { "paintSameOval", 0, NULL, "rect" },
+/* 0x5a */  { "eraseSameOval", 0, NULL, "rect" },
+/* 0x5b */  { "invertSameOval", 0, NULL, "rect" },
+/* 0x5c */  { "fillSameOval", 0, NULL, "rect" },
+/* 0x5d */  res(0),
+/* 0x5e */  res(0),
+/* 0x5f */  res(0),
+/* 0x60 */  { "frameArc", 12, NULL, "rect, startAngle, arcAngle" },
+/* 0x61 */  { "paintArc", 12, NULL, "rect, startAngle, arcAngle" },
+/* 0x62 */  { "eraseArc", 12, NULL, "rect, startAngle, arcAngle" },
+/* 0x63 */  { "invertArc", 12, NULL, "rect, startAngle, arcAngle" },
+/* 0x64 */  { "fillArc", 12, NULL, "rect, startAngle, arcAngle" },
+/* 0x65 */  res(12),
+/* 0x66 */  res(12),
+/* 0x67 */  res(12),
+/* 0x68 */  { "frameSameArc", 4, NULL, "rect, startAngle, arcAngle" },
+/* 0x69 */  { "paintSameArc", 4, NULL, "rect, startAngle, arcAngle" },
+/* 0x6a */  { "eraseSameArc", 4, NULL, "rect, startAngle, arcAngle" },
+/* 0x6b */  { "invertSameArc", 4, NULL, "rect, startAngle, arcAngle" },
+/* 0x6c */  { "fillSameArc", 4, NULL, "rect, startAngle, arcAngle" },
+/* 0x6d */  res(4),
+/* 0x6e */  res(4),
+/* 0x6f */  res(4),
+/* 0x70 */  { "framePoly", NA, skip_poly_or_region, "poly" },
+/* 0x71 */  { "paintPoly", NA, paintPoly, "poly" },
+/* 0x72 */  { "erasePoly", NA, skip_poly_or_region, "poly" },
+/* 0x73 */  { "invertPoly", NA, skip_poly_or_region, "poly" },
+/* 0x74 */  { "fillPoly", NA, skip_poly_or_region, "poly" },
+/* 0x75 */  resf(skip_poly_or_region),
+/* 0x76 */  resf(skip_poly_or_region),
+/* 0x77 */  resf(skip_poly_or_region),
+/* 0x78 */  { "frameSamePoly", 0, NULL, "poly (NYI)" },
+/* 0x79 */  { "paintSamePoly", 0, NULL, "poly (NYI)" },
+/* 0x7a */  { "eraseSamePoly", 0, NULL, "poly (NYI)" },
+/* 0x7b */  { "invertSamePoly", 0, NULL, "poly (NYI)" },
+/* 0x7c */  { "fillSamePoly", 0, NULL, "poly (NYI)" },
+/* 0x7d */  res(0),
+/* 0x7e */  res(0),
+/* 0x7f */  res(0),
+/* 0x80 */  { "frameRgn", NA, skip_poly_or_region, "region" },
+/* 0x81 */  { "paintRgn", NA, skip_poly_or_region, "region" },
+/* 0x82 */  { "eraseRgn", NA, skip_poly_or_region, "region" },
+/* 0x83 */  { "invertRgn", NA, skip_poly_or_region, "region" },
+/* 0x84 */  { "fillRgn", NA, skip_poly_or_region, "region" },
+/* 0x85 */  resf(skip_poly_or_region),
+/* 0x86 */  resf(skip_poly_or_region),
+/* 0x87 */  resf(skip_poly_or_region),
+/* 0x88 */  { "frameSameRgn", 0, NULL, "region (NYI)" },
+/* 0x89 */  { "paintSameRgn", 0, NULL, "region (NYI)" },
+/* 0x8a */  { "eraseSameRgn", 0, NULL, "region (NYI)" },
+/* 0x8b */  { "invertSameRgn", 0, NULL, "region (NYI)" },
+/* 0x8c */  { "fillSameRgn", 0, NULL, "region (NYI)" },
+/* 0x8d */  res(0),
+/* 0x8e */  res(0),
+/* 0x8f */  res(0),
+/* 0x90 */  { "BitsRect", NA, BitsRect, "copybits, rect clipped" },
+/* 0x91 */  { "BitsRgn", NA, BitsRegion, "copybits, rgn clipped" },
+/* 0x92 */  res(WORD_LEN),
+/* 0x93 */  res(WORD_LEN),
+/* 0x94 */  res(WORD_LEN),
+/* 0x95 */  res(WORD_LEN),
+/* 0x96 */  res(WORD_LEN),
+/* 0x97 */  res(WORD_LEN),
+/* 0x98 */  { "PackBitsRect", NA, BitsRect, "packed copybits, rect clipped" },
+/* 0x99 */  { "PackBitsRgn", NA, BitsRegion, "packed copybits, rgn clipped" },
+/* 0x9a */  { "DirectBitsRect", NA, DirectBitsRect, 
+              "PixMap, srcRect, dstRect, int copymode, PixData" },
+/* 0x9b */  { "DirectBitsRgn", NA, DirectBitsRgn, 
+              "PixMap, srcRect, dstRect, int copymode, maskRgn, PixData" },
+/* 0x9c */  res(WORD_LEN),
+/* 0x9d */  res(WORD_LEN),
+/* 0x9e */  res(WORD_LEN),
+/* 0x9f */  res(WORD_LEN),
+/* 0xa0 */  { "ShortComment", 2, ShortComment, "kind (word)" },
+/* 0xa1 */  { "LongComment", NA, LongComment, 
+              "kind (word), size (word), data" }
+};
+
+
+
+static void
+interpret_pict(void) {
+    byte ch;
+    word picSize;
+    word opcode;
+    word len;
+    unsigned int version;
+    int i;
+    struct rgbPlanes planes;
+
+    for (i = 0; i < 64; i++)
+        pen_pat.pix[i] = bkpat.pix[i] = fillpat.pix[i] = 1;
+    pen_width = pen_height = 1;
+    pen_mode = 0; /* srcCopy */
+    pen_trf = transfer(pen_mode);
+    text_mode = 0; /* srcCopy */
+    text_trf = transfer(text_mode);
+
+    stage = "Reading picture size";
+    picSize = read_word();
+
+    if (verbose)
+        pm_message("picture size = %d (0x%x)", picSize, picSize);
+
+    stage = "reading picture frame";
+    read_rect(&picFrame);
+
+    if (verbose) {
+        dumpRect("Picture frame:", picFrame);
+        pm_message("Picture size is %d x %d",
+            picFrame.right - picFrame.left,
+            picFrame.bottom - picFrame.top);
+    }
+
+    if (!fullres)
+        allocPlanes(&planes);
+
+    while ((ch = read_byte()) == 0)
+        ;
+    if (ch != 0x11)
+        pm_error("No version number");
+
+    version = read_byte();
+
+    switch (version) {
+    case 1:
+        break;
+    case 2: {
+        unsigned char const subcode = read_byte();
+        if (subcode != 0xff)
+            pm_error("The only Version 2 PICT images this program "
+                     "undertands are subcode 0xff.  This image has "
+                     "subcode 0x%02x", subcode);
+    } break;
+    default:
+        pm_error("Unrecognized PICT version %u", version);
+    }
+
+    if (verbose)
+        pm_message("PICT version %u", version);
+
+    while((opcode = get_op(version)) != 0xff) {
+        if (opcode < 0xa2) {
+            stage = optable[opcode].name;
+            if (verbose) {
+                if (STREQ(stage, "reserved"))
+                    pm_message("reserved opcode=0x%x", opcode);
+                else
+                    pm_message("Opcode: %s", optable[opcode].name);
+            }
+
+            if (optable[opcode].impl != NULL)
+                (*optable[opcode].impl)(version);
+            else if (optable[opcode].len >= 0)
+                skip(optable[opcode].len);
+            else switch (optable[opcode].len) {
+            case WORD_LEN:
+                len = read_word();
+                skip(len);
+                break;
+            default:
+                pm_error("can't do length %u", optable[opcode].len);
+            }
+        } else if (opcode == 0xc00) {
+            if (verbose)
+                pm_message("HeaderOp");
+            stage = "HeaderOp";
+            skip(24);
+        } else if (opcode >= 0xa2 && opcode <= 0xaf) {
+            stage = "skipping reserved";
+            if (verbose)
+                pm_message("%s 0x%x", stage, opcode);
+            skip(read_word());
+        } else if (opcode >= 0xb0 && opcode <= 0xcf) {
+            /* just a reserved opcode, no data */
+            if (verbose)
+                pm_message("reserved 0x%x", opcode);
+        } else if (opcode >= 0xd0 && opcode <= 0xfe) {
+            stage = "skipping reserved";
+            if (verbose)
+                pm_message("%s 0x%x", stage, opcode);
+            skip(read_long());
+        } else if (opcode >= 0x100 && opcode <= 0x7fff) {
+            stage = "skipping reserved";
+            if (verbose)
+                pm_message("%s 0x%x", stage, opcode);
+            skip((opcode >> 7) & 255);
+        } else if (opcode >= 0x8000 && opcode <= 0x80ff) {
+            /* just a reserved opcode */
+            if (verbose)
+                pm_message("reserved 0x%x", opcode);
+        } else if (opcode >= 0x8100) {
+            stage = "skipping reserved";
+            if (verbose)
+                pm_message("%s 0x%x", stage, opcode);
+            skip(read_long());
+        } else
+            pm_error("This program does not understand opcode 0x%04x", opcode);
+    }
+    
+    if (fullres)
+        do_blits(&planes);
+
+    outputPpm(planes);
+
+    freePlanes(planes);
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+    int argn;
+    int header;
+    const char* const usage =
+"[-verbose] [-fullres] [-noheader] [-quickdraw] [-fontdir file] [pictfile]";
+
+    ppm_init( &argc, argv );
+
+    argn = 1;
+    verbose = 0;
+    fullres = 0;
+    header = 1;
+    recognize_comment = 1;
+
+    while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') {
+        if (pm_keymatch(argv[argn], "-verbose", 2))
+            verbose++;
+        else if (pm_keymatch(argv[argn], "-fullres", 3))
+            fullres = 1;
+        else if (pm_keymatch(argv[argn], "-noheader", 2))
+            header = 0;
+        else if (pm_keymatch(argv[argn], "-quickdraw", 2))
+            recognize_comment = 0;
+        else if (pm_keymatch(argv[argn], "-fontdir", 3)) {
+            argn++;
+            if (!argv[argn])
+                pm_usage(usage);
+            else
+                load_fontdir(argv[argn]);
+        }
+        else
+            pm_usage(usage);
+        ++argn;
+    }
+
+    load_fontdir("fontdir");
+
+    if (argn < argc) {
+        ifp = pm_openr(argv[argn]);
+        ++argn;
+    } else
+        ifp = stdin;
+
+    if (argn != argc)
+        pm_usage(usage);
+
+    if (header) {
+        stage = "Reading 512 byte header";
+        skip(512);
+    }
+
+    interpret_pict();
+
+    return 0;
+}
diff --git a/converter/ppm/pjtoppm.c b/converter/ppm/pjtoppm.c
new file mode 100644
index 00000000..7b694fb3
--- /dev/null
+++ b/converter/ppm/pjtoppm.c
@@ -0,0 +1,253 @@
+/* pjtoppm.c - convert an HP PainJetXL image to a portable pixmap file
+**
+** Copyright (C) 1990 by Christos Zoulas (christos@ee.cornell.edu)
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "ppm.h"
+#include "mallocvar.h"
+
+static char usage[] =  "[paintjetfile]";
+
+static int egetc ARGS((FILE *fp));
+static int
+egetc(fp)
+    FILE *fp;
+{
+    int c;
+    if ((c = fgetc(fp)) == -1)
+        pm_error("unexpected end of file");
+    return(c);
+}
+
+int
+main(argc, argv)
+    int argc;
+    char *argv[];
+{
+    int cmd, val;
+    char buffer[BUFSIZ];
+    int planes = 3, rows = -1, cols = -1;
+    int r = 0, c = 0, p = 0, i;
+    unsigned char **image = NULL;
+    int *imlen;
+    FILE *fp = stdin;
+    int mode;
+    int argn;
+    unsigned char bf[3];
+    pixel *pixrow;
+
+
+    ppm_init(&argc, argv);
+    argn = 1;
+    if (argn != argc)
+        fp = pm_openr(argv[argn++]);
+    else
+        fp = stdin;
+
+    if (argn != argc)
+        pm_usage(usage);
+
+    while ((c = fgetc(fp)) != -1) {
+        if (c != '\033')
+            continue;
+        switch (c = egetc(fp)) {
+        case 'E':   /* reset */
+            break;
+        case '*':
+            cmd = egetc(fp);
+            for (i = 0; i < BUFSIZ; i++) {
+                if (!isdigit(c = egetc(fp)) && c != '+' && c != '-')
+                    break;
+                buffer[i] = c;
+            }
+            if (i != 0) {
+                buffer[i] = '\0';
+                if (sscanf(buffer, "%d", &val) != 1) 
+                    pm_error("bad value `%s' at <ESC>*%c%c", buffer, cmd, c);
+            }
+            else
+                val = -1;
+            switch (cmd) {
+            case 't':
+                switch (c) {
+                case 'J':   /* render */
+                    break;
+                case 'K':   /* back scale */
+                    break;
+                case 'I':   /* gamma */
+                    break;
+                case 'R':
+                    break;  /* set resolution */
+                default:
+                    pm_message("uninmplemented <ESC>*%c%d%c", cmd, val, c);
+                    break;
+                }
+                break;
+            case 'r':
+                switch (c) {
+                case 'S':   /* width */
+                    cols = val;
+                    break;
+                case 'T':   /* height */
+                    rows = val;
+                    break;
+                case 'U':   /* planes */
+                    planes = val;
+                    if (planes != 3) 
+                        pm_error("can handle only 3 plane files");
+                    break;
+                case 'A':   /* begin raster */
+                    break;
+                case 'B':
+                case 'C':   /* end raster */
+                    break;
+                case 'V':
+                    break;  /* set deci height */
+                case 'H':
+                    break;  /* set deci width */
+                default:
+                    pm_message("uninmplemented <ESC>*%c%d%c", cmd, val, c);
+                    break;
+                }
+                break;
+            case 'b':
+                switch (c) {
+                case 'M':   /* transmission mode */
+                    if (val != 0 && val != 1)
+                        pm_error("unimplemented trasmission mode %d", val);
+                    mode = val;
+                    break;
+                case 'V':   /* send plane */
+                case 'W':   /* send last plane */
+                    if (rows == -1 || r >= rows || image == NULL) {
+                        if (rows == -1 || r >= rows)
+                            rows += 100;
+                        if (image == NULL) {
+                            MALLOCARRAY(image, rows * planes);
+                            MALLOCARRAY(imlen, rows * planes);
+                        }
+                        else {
+                            image = (unsigned char **) 
+                                realloc(image, 
+                                        rows * planes * 
+                                        sizeof(unsigned char *));
+                            imlen = (int *) 
+                                realloc(imlen, rows * planes * sizeof(int));
+                        }
+                    }
+                    if (image == NULL || imlen == NULL)
+                        pm_error("out of memory");
+                    if (p == planes) 
+                        pm_error("too many planes");
+                    cols = cols > val ? cols : val;
+                    imlen[r * planes + p] = val;
+                    MALLOCARRAY(image[r * planes + p], val);
+                    if (image[r * planes + p] == NULL) 
+                        pm_error("out of memory");
+                    if (fread(image[r * planes + p], 1, val, fp) != val) 
+                        pm_error("short data");
+                    if (c == 'V')
+                        p++;
+                    else {
+                        p = 0;
+                        r++;
+                    }
+                    break;
+                default:
+                    pm_message("uninmplemented <ESC>*%c%d%c", cmd, val, c);
+                    break;
+                }
+                break;
+            case 'p': /* Position */
+                if (p != 0) 
+                    pm_error("changed position in the middle of "
+                             "transferring planes");
+                switch (c) {
+                case 'X':
+                    pm_message("can only position in y");
+                    break;
+                case 'Y':
+                    if (buffer[0] == '+')
+                        val = r + val;
+                    if (buffer[0] == '-')
+                        val = r - val;
+                    for (; val > r; r++) 
+                        for (p = 0; p < 3; p++) {
+                            imlen[r * planes + p] = 0;
+                            image[r * planes + p] = NULL;
+                        }
+                    r = val;
+                    break;
+                default:
+                    pm_message("uninmplemented <ESC>*%c%d%c", cmd, val, c);
+                    break;
+                }
+            default:
+                pm_message("uninmplemented <ESC>*%c%d%c", cmd, val, c);
+                break;
+            }
+        }
+    }
+    pm_close(fp);
+    rows = r;
+    if (mode == 1) {
+        unsigned char *buf;
+        int newcols = 0;
+        newcols = 10240; /* It could not be larger that that! */
+        cols = 0;
+        for (r = 0; r < rows; r++) {
+            if (image[r * planes] == NULL)
+                continue;
+            for (p = 0; p < planes; p++) {
+                MALLOCARRAY(buf, newcols);
+                if (buf == NULL) 
+                    pm_error("out of memory");
+                for (i = 0, c = 0; c < imlen[p + r * planes]; c += 2)
+                    for (cmd = image[p + r * planes][c],
+                             val = image[p + r * planes][c+1]; 
+                         cmd >= 0 && i < newcols; cmd--, i++) 
+                        buf[i] = val;
+                cols = cols > i ? cols : i;
+                free(image[p + r * planes]);
+                /* 
+                 * This is less than what we have so it realloc should 
+                 * not return null. Even if it does, tough! We will
+                 * lose a line, and probably die on the next line anyway
+                 */
+                image[p + r * planes] = (unsigned char *) realloc(buf, i);
+            }
+        }
+        cols *= 8;
+    }
+            
+       
+    ppm_writeppminit(stdout, cols, rows, (pixval) 255, 0);
+    pixrow = ppm_allocrow(cols);
+    for (r = 0; r < rows; r++) {
+        if (image[r * planes] == NULL) {
+            for (c = 0; c < cols; c++)
+                PPM_ASSIGN(pixrow[c], 0, 0, 0);
+            continue;
+        }
+        for (cmd = 0, c = 0; c < cols; c += 8, cmd++) 
+            for (i = 0; i < 8 && c + i < cols; i++) {
+                for (p = 0; p < planes; p++) 
+                    if (mode == 0 && cmd >= imlen[r * planes + p])
+                        bf[p] = 0;
+                    else
+                        bf[p] = (image[r * planes + p][cmd] & 
+                                 (1 << (7 - i))) ? 255 : 0;
+                PPM_ASSIGN(pixrow[c + i], bf[0], bf[1], bf[2]);
+            }
+        ppm_writeppmrow(stdout, pixrow, cols, (pixval) 255, 0);
+    }
+    pm_close(stdout);
+    exit(0);
+}
diff --git a/converter/ppm/ppmtoacad.c b/converter/ppm/ppmtoacad.c
new file mode 100644
index 00000000..4f927f02
--- /dev/null
+++ b/converter/ppm/ppmtoacad.c
@@ -0,0 +1,394 @@
+/*
+
+      Convert a portable pixmap to an AutoCAD slide or DXB file
+
+    Author:
+            John Walker
+            Autodesk SA
+            Avenue des Champs-Montants 14b
+            CH-2074 MARIN
+            Switzerland
+            Usenet: kelvin@Autodesk.com
+            Fax:    038/33 88 15
+            Voice:  038/33 76 33
+
+    Permission  to  use, copy, modify, and distribute this software and
+    its documentation  for  any  purpose  and  without  fee  is  hereby
+    granted,  without any conditions or restrictions.  This software is
+    provided "as is" without express or implied warranty.
+
+*/
+
+#include <stdio.h>
+#include "ppm.h"
+
+#define TRUE     1
+#define FALSE    0
+
+#define EOS     '\0'
+
+#define MAXHIST         32767         /* Color histogram maximum size */
+
+static pixel **pixels;                /* Input pixel map */
+static colorhash_table cht;           /* Color hash table */
+static int curcol = -1;               /* Current slide output color */
+static int polymode = FALSE;          /* Output filled polygons ? */
+static int dxbmode = FALSE;           /* Output .dxb format ? */
+static int bgcol = -1;                /* Screen background color */
+static double aspect = 1.0;           /* Pixel aspect ratio correction */
+static int gamut = 256;               /* Output color gamut */
+
+#include "autocad.h"                  /* AutoCAD standard color assignments */
+
+/* prototypes */
+static void outrun ARGS((int color, int ysize, int y, int xstart, int xend));
+static void slideout ARGS((int xdots, int ydots, int ncolors,
+        unsigned char *red, unsigned char *green, unsigned char *blue));
+
+
+/*  OUTRUN  --  Output a run of pixels. */
+
+static void outrun(color, ysize, y, xstart, xend)
+  int color, ysize, y, xstart, xend;
+{
+    if (color == 0) {
+        return;                       /* Let screen background handle this */
+    }
+
+    if (curcol != color) {
+        if (dxbmode) {
+            putchar((char)136);
+            (void) pm_writelittleshort(stdout, color);
+        } else {
+            (void) pm_writelittleshort(stdout, (short) 0xFF00 | color);
+        }
+        curcol = color;
+    }
+    if (polymode) {
+        int v, yb = (ysize - y) + 1, yspan = 1;
+
+        /* Since  we're emitting filled polygons,  let's scan downward
+           in the pixmap and see if we can extend the run on this line
+           vertically  as  well.   If  so, emit a polygon that handles
+           both the horizontal and vertical run and clear  the  pixels
+           in the subsequent lines to the background color. */
+
+        for (v = y + 1; v <= ysize; v++) {
+            int j, mismatch = FALSE;
+
+            for (j = xstart; j <= xend; j++) {
+                if (PPM_GETR(pixels[y][j]) != PPM_GETR(pixels[v][j])) {
+                    mismatch = TRUE;
+                    break;
+                }
+            }
+            if (mismatch) {
+                break;
+            }
+            for (j = xstart; j <= xend; j++) {
+                PPM_ASSIGN(pixels[v][j], 0, 0, 0);
+            }
+        }
+        yspan = v - y;
+
+        if (dxbmode) {
+            putchar(11);              /* Solid */
+            (void) pm_writelittleshort(
+                stdout, (int) (xstart * aspect + 0.4999));
+            (void) pm_writelittleshort(stdout, yb);
+
+            (void) pm_writelittleshort(
+                stdout, (int) ((xend + 1) * aspect + 0.4999));
+            (void) pm_writelittleshort(stdout, yb);
+
+            (void) pm_writelittleshort(
+                stdout, (int) (xstart * aspect + 0.4999));
+            (void) pm_writelittleshort(stdout, yb - yspan);
+
+            (void) pm_writelittleshort(
+                stdout, (int) ((xend + 1) * aspect + 0.4999));
+            (void) pm_writelittleshort(stdout, yb - yspan);
+        } else {
+            (void) pm_writelittleshort(stdout, (short) 0xFD00); 
+              /* Solid fill header */
+            (void) pm_writelittleshort(stdout, 4);      
+              /* Vertices to follow */
+            (void) pm_writelittleshort(stdout, -2);     /* Fill type */
+
+            (void) pm_writelittleshort(stdout, (short)0xFD00); 
+              /* Solid fill vertex */
+            (void) pm_writelittleshort(stdout, xstart);
+            (void) pm_writelittleshort(stdout, yb);
+
+            (void) pm_writelittleshort(stdout, (short) 0xFD00); 
+              /* Solid fill vertex */
+            (void) pm_writelittleshort(stdout, xend + 1);
+            (void) pm_writelittleshort(stdout, yb);
+
+            (void) pm_writelittleshort(stdout, (short) 0xFD00); 
+              /* Solid fill vertex */
+            (void) pm_writelittleshort(stdout, xend + 1);
+            (void) pm_writelittleshort(stdout, yb - yspan);
+
+            (void) pm_writelittleshort(stdout, (short) 0xFD00); 
+              /* Solid fill vertex */
+            (void) pm_writelittleshort(stdout, xstart);
+            (void) pm_writelittleshort(stdout, yb - yspan);
+
+            (void) pm_writelittleshort(stdout, (short) 0xFD00); 
+              /* Solid fill trailer */
+            (void) pm_writelittleshort(stdout, 4); /* Vertices that precede */
+            (void) pm_writelittleshort(stdout, -2);     /* Fill type */
+        }
+    } else {
+        (void) pm_writelittleshort(stdout, xstart);     /* Vector:  From X */
+        (void) pm_writelittleshort(stdout, ysize - y);  /*          From Y */
+        (void) pm_writelittleshort(stdout, xend);       /*          To   X */
+        (void) pm_writelittleshort(stdout, ysize - y);  /*          To   Y */
+    }
+}
+
+/*  SLIDEOUT  --  Write an AutoCAD slide.  */
+
+static void slideout(xdots, ydots, ncolors, red, green, blue)
+  int xdots, ydots, ncolors;
+  unsigned char *red, *green, *blue;
+{
+    static char sldhead[18] = "AutoCAD Slide\r\n\32";
+    static char dxbhead[20] = "AutoCAD DXB 1.0\r\n\32";
+    unsigned char *acadmap;
+    int i, xsize, ysize;
+
+    /* If the user has specified a non-black screen background color,
+       swap the screen background color into color  index  zero  and
+       move  black into the slot previously occupied by the background
+       color. */
+
+    if (bgcol > 0) {
+        acadcol[0][0] = acadcol[bgcol][0];
+        acadcol[0][1] = acadcol[bgcol][1];
+        acadcol[0][2] = acadcol[bgcol][2];
+        acadcol[bgcol][0] = acadcol[bgcol][1] = acadcol[bgcol][2] = 0;
+    }
+
+    acadmap = (unsigned char *) pm_allocrow(ncolors, sizeof(unsigned char));
+    xsize = polymode ? xdots : (xdots - 1);
+    ysize = polymode ? ydots : (ydots - 1);
+    if (dxbmode) {
+        fwrite(dxbhead, 19, 1, stdout); /* DXB file header */
+        putchar((char)135);                 /* Number mode */
+        (void) pm_writelittleshort(stdout, 0);        /* ...short integers */
+    } else {
+        fwrite(sldhead, 17, 1, stdout); /* Slide file header */
+        putchar(86);                  /* Number format indicator */
+        putchar(2);                   /* File level number */
+        (void) pm_writelittleshort(stdout, xsize); /* Max X co-ordinate value */
+        (void) pm_writelittleshort(stdout, ysize); /* Max Y co-ordinate value */
+                                      /* Aspect ratio indicator */
+        (void) pm_writelittlelong(
+            stdout, (long) ((((double) xsize) / ysize) * aspect * 1E7));
+        (void) pm_writelittleshort(stdout, 2);        /* Polygon fill type */
+        (void) pm_writelittleshort(stdout, 0x1234);   /* Byte order indicator */
+    }
+
+    /* Now  build  a  mapping  from  the  image's computed color map
+       indices to the closest representation  of  each  color  within
+       AutoCAD's color repertoire. */
+
+    for (i = 0; i < ncolors; i++) {
+        int best, j;
+        long dist = 3 * 256 * 256;
+
+        for (j = 0; j < gamut; j++) {
+            long dr = red[i] - acadcol[j][0],
+                 dg = green[i] - acadcol[j][1],
+                 db = blue[i] - acadcol[j][2];
+            long tdist = dr * dr + dg * dg + db * db;
+
+            if (tdist < dist) {
+                dist = tdist;
+                best = j;
+            }
+        }
+        acadmap[i] = best;
+    }
+
+    /* Swoop  over the entire map and replace each  RGB pixel with its
+       closest  AutoCAD  color  representation.   We  do  this  in  a
+       separate  pass  since it avoids repetitive mapping of pixels as
+       we examine them for compression. */
+
+    for (i = 0; i < ydots; i++) {
+        int x;
+
+        for (x = 0; x < xdots; x++) {
+            PPM_ASSIGN(pixels[i][x],
+                acadmap[ppm_lookupcolor(cht, &pixels[i][x])], 0 ,0);
+        }
+    }
+
+    /* Output a run-length encoded expression of the pixmap as AutoCAD
+       vectors. */
+
+    for (i = 0; i < ydots; i++) {
+        int x, rx, rpix = -1, nrun = 0;
+
+        for (x = 0; x < xdots; x++) {
+            int pix = PPM_GETR(pixels[i][x]);
+
+            if (pix != rpix) {
+                if (nrun > 0) {
+                    if (rpix > 0) {
+                        outrun(rpix, ydots - 1, i, rx, x - 1);
+                    }
+                }
+                rpix = pix;
+                rx = x;
+                nrun = 1;
+            }
+        }
+        if ((nrun > 0) && (rpix > 0)) {
+            outrun(rpix, ydots - 1, i, rx, xdots - 1);
+        }
+    }
+    if (dxbmode) {
+        putchar(0);                   /* DXB end sentinel */
+    } else {
+        (void) pm_writelittleshort(stdout, (short) 0xFC00); 
+          /* End of file marker */
+    }
+    pm_freerow((char *) acadmap);
+}
+
+/*  Main program.  */
+
+int main(argc, argv)
+  int argc;
+  char* argv[];
+{
+    FILE *ifp;
+    int argn, rows, cols, ncolors, i;
+    int aspectspec = FALSE;
+    pixval maxval;
+    colorhist_vector chv;
+    unsigned char *Red, *Green, *Blue;
+    const char * const usage =
+        "[-poly] [-dxb] [-white] [-background <col>]\n\
+                  [-aspect <f>] [-8] [ppmfile]";
+
+
+    ppm_init(&argc, argv);
+
+    argn = 1;
+    while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != EOS) {
+        if (pm_keymatch(argv[argn], "-dxb", 2)) {
+            dxbmode = polymode = TRUE;
+        } else if (pm_keymatch(argv[argn], "-poly", 2)) {
+            polymode = TRUE;
+        } else if (pm_keymatch(argv[argn], "-white", 2)) {
+            if (bgcol >= 0) {
+                pm_error("already specified a background color");
+            }
+            bgcol = 7;
+        } else if (pm_keymatch(argv[argn], "-background", 2)) {
+            if (bgcol >= 0) {
+                pm_error("already specified a background color");
+            }
+            argn++;
+            if ((argn == argc) || (sscanf(argv[argn], "%d", &bgcol) != 1))
+                pm_usage(usage);
+            if (bgcol < 0) {
+                pm_error("background color must be >= 0 and <= 255");
+            }
+        } else if (pm_keymatch(argv[argn], "-aspect", 2)) {
+            if (aspectspec) {
+                pm_error("already specified an aspect ratio");
+            }
+            argn++;
+            if ((argn == argc) || (sscanf(argv[argn], "%lf", &aspect) != 1))
+                pm_usage(usage);
+            if (aspect <= 0.0) {
+                pm_error("aspect ratio must be greater than 0");
+            }
+            aspectspec = TRUE;
+        } else if (pm_keymatch(argv[argn], "-8", 2)) {
+            gamut = 8;
+        } else {
+            pm_usage(usage);
+        }
+        argn++;
+    }
+
+    if (argn < argc) {
+        ifp = pm_openr(argv[argn]);
+        argn++;
+    } else {
+        ifp = stdin;
+    }
+
+    if (argn != argc) {
+        pm_usage(usage);
+    }
+
+    pixels = ppm_readppm(ifp, &cols, &rows, &maxval);
+
+    pm_close(ifp);
+
+    /* Figure out the colormap.  Logic for squeezing depth to limit the
+       number of colors in the image was swiped from ppmquant.c. */
+
+    while (TRUE) {
+        int row, col;
+        pixval newmaxval;
+        pixel *pP;
+
+        pm_message("computing colormap...");
+        chv = ppm_computecolorhist(pixels, cols, rows, MAXHIST, &ncolors);
+        if (chv != (colorhist_vector) 0)
+            break;
+        newmaxval = maxval / 2;
+        pm_message(
+        "scaling colors from maxval=%d to maxval=%d to improve clustering...",
+                   maxval, newmaxval );
+        for (row = 0; row < rows; ++row) {
+            for (col = 0, pP = pixels[row]; col < cols; ++col, ++pP) {
+                PPM_DEPTH(*pP, *pP, maxval, newmaxval);
+            }
+        }
+        maxval = newmaxval;
+    }
+    pm_message("%d colors found", ncolors);
+
+    /* Scale the color map derived for the PPM file into one compatible
+       with AutoCAD's convention of 8 bit intensities. */
+
+    if (maxval != 255) {
+        pm_message("maxval is not 255 - automatically rescaling colors");
+    }
+    Red = (unsigned char *) pm_allocrow(ncolors, sizeof(unsigned char));
+    Green = (unsigned char *) pm_allocrow(ncolors, sizeof(unsigned char));
+    Blue = (unsigned char *) pm_allocrow(ncolors, sizeof(unsigned char));
+
+    for (i = 0; i < ncolors; ++i) {
+        if ( maxval == 255 ) {
+            Red[i] = PPM_GETR(chv[i].color);
+            Green[i] = PPM_GETG(chv[i].color);
+            Blue[i] = PPM_GETB( chv[i].color );
+        } else {
+            Red[i] = ((int) PPM_GETR(chv[i].color) * 255) / maxval;
+            Green[i] = ((int) PPM_GETG(chv[i].color) * 255) / maxval;
+            Blue[i] = ((int) PPM_GETB(chv[i].color) * 255) / maxval;
+        }
+    }
+
+    /* And make a hash table for fast lookup. */
+
+    cht = ppm_colorhisttocolorhash(chv, ncolors);
+    ppm_freecolorhist(chv);
+
+    slideout(cols, rows, ncolors, Red, Green, Blue);
+    pm_freerow((char *) Red);
+    pm_freerow((char *) Green);
+    pm_freerow((char *) Blue);
+    exit(0);
+}
diff --git a/converter/ppm/ppmtoarbtxt.c b/converter/ppm/ppmtoarbtxt.c
new file mode 100644
index 00000000..774a47c4
--- /dev/null
+++ b/converter/ppm/ppmtoarbtxt.c
@@ -0,0 +1,505 @@
+/* ppmtoarbtxt.c - convert portable pixmap to cleartext
+**
+** Renamed from ppmtotxt.c by Bryan Henderson in January 2003.
+**
+** Copyright (C) 1995 by Peter Kirchgessner
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <string.h>
+
+#include "ppm.h"
+#include "mallocvar.h"
+#include "nstring.h"
+
+typedef enum {
+/* The types of object we handle */
+    BDATA, IRED, IGREEN, IBLUE, ILUM, FRED, FGREEN, FBLUE, FLUM,
+    WIDTH, HEIGHT, POSX, POSY
+} SKL_OBJ_TYP;
+
+/* Maximum size for a format string ("%d" etc.) */
+#define MAXFORMAT 16
+
+/* The data we keep for each object */
+typedef union
+ {
+  struct BNDAT { char *bdat;   /* Binary data (text with newlines etc.) */
+                 int ndat;
+               } bin_data;
+
+  struct ICDAT { char icformat[MAXFORMAT];  /* Integer colors */
+                 int icolmin, icolmax;
+               } icol_data;
+
+  struct FCDAT { char fcformat[MAXFORMAT];  /* Float colors */
+                 double fcolmin, fcolmax;
+               } fcol_data;
+
+  struct IDAT  { char iformat[MAXFORMAT];   /* Integer data */
+               } i_data;
+ } SKL_OBJ_DATA;
+
+
+/* Each object has a type and some data */
+typedef struct
+ { 
+   SKL_OBJ_TYP otyp;
+   SKL_OBJ_DATA odata;
+ } SKL_OBJ;
+
+
+#define MAX_SKL_HEAD_OBJ 64
+#define MAX_SKL_BODY_OBJ 256
+#define MAX_SKL_TAIL_OBJ 64
+#define MAX_LINE_BUF 1024
+#define MAX_OBJ_BUF 80
+
+
+static void write_txt (fout, nobj, obj, width, height, x, y, red, green, blue)
+FILE *fout;
+int nobj;
+SKL_OBJ *obj[];
+int width, height, x, y;
+double red, green, blue;
+
+{register int count;
+
+#define WRITE_BNDAT(fd,theobj) \
+ {struct BNDAT *bdata = &((theobj)->odata.bin_data); \
+       fwrite (bdata->bdat,bdata->ndat,1,fd); }
+
+#define WRITE_ICOL(fd,theobj,thecol) \
+ {struct ICDAT *icdata = &((theobj)->odata.icol_data); \
+  fprintf (fd,icdata->icformat,(int)(icdata->icolmin \
+                + (icdata->icolmax - icdata->icolmin)*(thecol))); }
+
+#define WRITE_FCOL(fd,theobj,thecol) \
+ {struct FCDAT *fcdata = &((theobj)->odata.fcol_data); \
+  fprintf (fd,fcdata->fcformat,(double)(fcdata->fcolmin \
+                + (fcdata->fcolmax - fcdata->fcolmin)*(thecol))); }
+
+#define WRITE_IDAT(fd,theobj,thedat) \
+ {struct IDAT *idata = &((theobj)->odata.i_data); \
+  fprintf (fd,idata->iformat,thedat); }
+
+ for (count = 0; count < nobj; count++)
+ {
+   switch (obj[count]->otyp)
+   {
+     case BDATA:
+       WRITE_BNDAT (fout,obj[count]);
+       break;
+     case IRED:
+       WRITE_ICOL (fout,obj[count],red);
+       break;
+     case IGREEN:
+       WRITE_ICOL (fout,obj[count],green);
+       break;
+     case IBLUE:
+       WRITE_ICOL (fout,obj[count],blue);
+       break;
+     case ILUM:
+       WRITE_ICOL (fout,obj[count],0.299*red+0.587*green+0.114*blue);
+       break;
+     case FRED:
+       WRITE_FCOL (fout,obj[count],red);
+       break;
+     case FGREEN:
+       WRITE_FCOL (fout,obj[count],green);
+       break;
+     case FBLUE:
+       WRITE_FCOL (fout,obj[count],blue);
+       break;
+     case FLUM:
+       WRITE_FCOL (fout,obj[count],0.299*red+0.587*green+0.114*blue);
+       break;
+     case WIDTH:
+       WRITE_IDAT (fout,obj[count],width);
+       break;
+     case HEIGHT:
+       WRITE_IDAT (fout,obj[count],height);
+       break;
+     case POSX:
+       WRITE_IDAT (fout,obj[count],x);
+       break;
+     case POSY:
+       WRITE_IDAT (fout,obj[count],y);
+       break;
+   }
+ }
+}
+
+
+static SKL_OBJ *
+save_bin_data(int    const ndat, 
+              char * const bdat) {
+
+    /* Save binary data in Object */
+
+    SKL_OBJ *obj;
+
+    obj = (SKL_OBJ *)malloc (sizeof (SKL_OBJ) + ndat);
+    if (obj != NULL)
+    {
+        obj->otyp = BDATA;
+        obj->odata.bin_data.ndat = ndat;
+        obj->odata.bin_data.bdat = ((char *)(obj))+sizeof (SKL_OBJ);
+        memcpy (obj->odata.bin_data.bdat,bdat,ndat);
+    }
+    return (obj);
+}
+
+
+
+/* Save integer color data in Object */
+static SKL_OBJ *save_icol_data (ctyp,format,icolmin,icolmax)
+SKL_OBJ_TYP ctyp;
+char *format;
+int icolmin, icolmax;
+
+{SKL_OBJ *obj;
+
+ obj = (SKL_OBJ *)malloc (sizeof (SKL_OBJ));
+ if (obj != NULL)
+ {
+   obj->otyp = ctyp;
+   strcpy (obj->odata.icol_data.icformat,format);
+   obj->odata.icol_data.icolmin = icolmin;
+   obj->odata.icol_data.icolmax = icolmax;
+ }
+ return (obj);
+}
+
+
+/* Save float color data in Object */
+static SKL_OBJ *save_fcol_data (ctyp,format,fcolmin,fcolmax)
+SKL_OBJ_TYP ctyp;
+char *format;
+double fcolmin, fcolmax;
+
+{SKL_OBJ *obj;
+
+ obj = (SKL_OBJ *)malloc (sizeof (SKL_OBJ));
+ if (obj != NULL)
+ {
+   obj->otyp = ctyp;
+   strcpy (obj->odata.fcol_data.fcformat,format);
+   obj->odata.fcol_data.fcolmin = fcolmin;
+   obj->odata.fcol_data.fcolmax = fcolmax;
+ }
+ return (obj);
+}
+
+
+/* Save universal data in Object */
+static SKL_OBJ *save_i_data (ctyp,format)
+SKL_OBJ_TYP ctyp;
+char *format;
+
+{SKL_OBJ *obj;
+
+ obj = (SKL_OBJ *)malloc (sizeof (SKL_OBJ));
+ if (obj != NULL)
+ {
+   obj->otyp = ctyp;
+   strcpy (obj->odata.i_data.iformat,format);
+ }
+ return (obj);
+}
+
+
+/* Read skeleton file */
+static int read_skeleton (filename,maxskl,nskl,skl)
+char *filename;
+int maxskl,*nskl;
+SKL_OBJ **skl;
+
+{FILE *sklfile;
+ int slen, objlen, chr, n_odata;
+ int icolmin,icolmax;
+ double fcolmin,fcolmax;
+ SKL_OBJ_TYP otyp;
+ char line[MAX_LINE_BUF+MAX_OBJ_BUF+16];
+ char objstr[MAX_OBJ_BUF],typstr[MAX_OBJ_BUF];
+ char formstr[MAX_OBJ_BUF];
+ char meta1 = '#', meta2 = '(', meta3 = ')';
+
+#define SAVE_BIN(slen,line) \
+ { if ((skl[*nskl] = save_bin_data (slen,line)) != NULL) (*nskl)++; \
+   slen = 0; }
+
+#define ADD_STR(slen,line,addlen,addstr) \
+ {int count=0; line[slen++] = meta1; line[slen++] = meta2; \
+  while (count++ < addlen) line[slen++] = addstr[count]; }
+
+ if ((sklfile = fopen (filename,"r")) == NULL)
+   return (-1);
+
+ /* Parse skeleton file */
+ *nskl = 0;
+
+ slen = 0;
+ while ((chr = getc (sklfile)) != EOF)   /* Up to end of skeleton file */
+ {
+   if (*nskl >= maxskl) return (-1);
+
+   if (slen+1 >= MAX_LINE_BUF)   /* Buffer finished ? Save as binary object */
+   {
+     SAVE_BIN (slen,line);
+   }
+
+   if (chr != meta1)      /* Look for start of replacement string */
+   {
+     line[slen++] = chr;
+     continue;
+   }
+
+   if ((chr = getc (sklfile)) == EOF)
+   {
+     line[slen++] = meta1;
+     break;
+   }
+   if (chr != meta2) /* '(' ? Otherwise no replacement */
+   {
+     line[slen++] = meta1;
+     line[slen++] = chr;
+     continue;
+   }
+
+   objlen = 0;
+   for (;;)   /* Read replacement string up to ')' */
+   {
+     if (objlen == sizeof (objstr)) break; /* ')' not found */
+     if ((chr = getc (sklfile)) == EOF) break;
+     if (chr == meta3) break;
+     objstr[objlen++] = chr;
+   }
+   objstr[objlen] = '\0'; /* Now objstr keeps data without metas */
+
+   if (chr != meta3)    /* Object not found ? */
+   {
+     ADD_STR (slen,line,objlen,objstr);   /* Save what we already read */
+     if (chr == EOF) break;
+     continue;
+   }
+
+   typstr[0] = '\0';           /* Get typ of data */
+   sscanf (objstr,"%s",typstr);
+
+                   /* Check for integer colors */
+   if      (STREQ(typstr,"ired")  ) otyp = IRED;
+   else if (STREQ(typstr,"igreen")) otyp = IGREEN;
+   else if (STREQ(typstr,"iblue") ) otyp = IBLUE;
+   else if (STREQ(typstr,"ilum")  ) otyp = ILUM;
+                   /* Check for real colors */
+   else if (STREQ(typstr,"fred")  ) otyp = FRED;
+   else if (STREQ(typstr,"fgreen")) otyp = FGREEN;
+   else if (STREQ(typstr,"fblue") ) otyp = FBLUE;
+   else if (STREQ(typstr,"flum")  ) otyp = FLUM;
+                   /* Check for integer data */
+   else if (STREQ(typstr,"width") ) otyp = WIDTH;
+   else if (STREQ(typstr,"height")) otyp = HEIGHT;
+   else if (STREQ(typstr,"posx")  ) otyp = POSX;
+   else if (STREQ(typstr,"posy")  ) otyp = POSY;
+   else                                    otyp = BDATA;
+
+   if ((otyp == IRED) || (otyp == IGREEN) || (otyp == IBLUE) || (otyp == ILUM))
+   {
+     n_odata = sscanf (objstr,"%*s%s%d%d",formstr,&icolmin,&icolmax);
+
+     if (n_odata == EOF)  /* No arguments specified ? Use defaults */
+     {
+       strcpy (formstr,"%d"); icolmin = 0; icolmax = 255;
+     }
+     else if (n_odata != 3)  /* Wrong specification */
+     {
+       otyp = BDATA;
+     }
+   }
+
+   if ((otyp == FRED) || (otyp == FGREEN) || (otyp == FBLUE) || (otyp == FLUM))
+   {
+     n_odata = sscanf (objstr,"%*s%s%lf%lf",formstr,&fcolmin,&fcolmax);
+
+     if (n_odata == EOF)  /* No arguments specified ? Use defaults */
+     {
+       strcpy (formstr,"%f"); fcolmin = 0.0; fcolmax = 1.0;
+     }
+     else if (n_odata != 3)  /* Wrong specification */
+     {
+       otyp = BDATA;
+     }
+   }
+
+   if (   (otyp == WIDTH) || (otyp == HEIGHT)
+       || (otyp == POSX) || (otyp == POSY))
+   {
+     n_odata = sscanf (objstr,"%*s%s",formstr);
+
+     if (n_odata == EOF)  /* No arguments specified ? Use defaults */
+     {
+       strcpy (formstr,"%d");
+     }
+     else if (n_odata != 1)  /* Wrong specification */
+     {
+       otyp = BDATA;
+     }
+   }
+
+   if (otyp != BDATA)   /* Got an object definition ? */
+   {
+     if (slen > 0)      /* Save what we already found */
+     {
+       SAVE_BIN (slen,line);
+     }
+   }
+
+   /* Now process the object in objstr */
+   switch (otyp)
+   {
+     case BDATA:   /* Bad object definition ? Save as text */
+       ADD_STR (slen,line,objlen,objstr);
+       break;
+
+     case IRED:
+     case IGREEN:
+     case IBLUE:
+     case ILUM:
+       skl[*nskl] = save_icol_data (otyp,formstr,icolmin,icolmax);
+       if (skl[*nskl] != NULL) (*nskl)++;
+       break;
+
+     case FRED:
+     case FGREEN:
+     case FBLUE:
+     case FLUM:
+       skl[*nskl] = save_fcol_data (otyp,formstr,fcolmin,fcolmax);
+       if (skl[*nskl] != NULL) (*nskl)++;
+       break;
+
+     case WIDTH:
+     case HEIGHT:
+     case POSX:
+     case POSY:
+       skl[*nskl] = save_i_data (otyp,formstr);
+       if (skl[*nskl] != NULL) (*nskl)++;
+       break;
+   }
+ } /* EOF of skeleton file */
+
+ if (slen > 0)      /* Drop finishing newline character */
+ {
+   if (line[slen-1] == '\n') slen--;
+ }
+
+ if (slen > 0)      /* Something left ? */
+ {
+   SAVE_BIN (slen,line);   /* Save it */
+ }
+
+ fclose (sklfile);
+ return (0);
+}
+
+
+int main( argc, argv )
+int argc;
+char* argv[];
+
+{register int col;
+ register pixel* xP;
+ pixel* pixelrow;
+ pixval maxval,red,green,blue;
+ double dmaxval;
+ int argn, rows, cols, format, row;
+ int head_nskl,body_nskl,tail_nskl;
+ SKL_OBJ *head_skl[MAX_SKL_HEAD_OBJ];
+ SKL_OBJ *body_skl[MAX_SKL_BODY_OBJ];
+ SKL_OBJ *tail_skl[MAX_SKL_TAIL_OBJ];
+ FILE *ifp;
+ const char *usage = "bodyskl [ -hd headskl ] [ -tl tailskl ] [pnmfile]";
+
+ ppm_init( &argc, argv );
+
+ argn = 1;
+ if (argn == argc)
+   pm_usage( usage );
+                          /* Read body skeleton file */
+ if (read_skeleton (argv[argn],sizeof (body_skl)/sizeof (SKL_OBJ *),
+                    &body_nskl,body_skl) < 0)
+   pm_usage ( usage );
+ ++argn;
+
+ head_nskl = tail_nskl = 0;
+
+ while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0')
+ {
+   if ( pm_keymatch ( argv[argn], "-hd", 1) && argn+1 < argc )
+   {
+     argn++;           /* Read header skeleton */
+     if (read_skeleton (argv[argn],sizeof (head_skl)/sizeof (SKL_OBJ *),
+                        &head_nskl,head_skl) < 0)
+       pm_usage ( usage );
+   }
+   else if ( pm_keymatch ( argv[argn], "-tl", 1) && argn+1 < argc )
+   {
+     argn++;           /* Read tail skeleton */
+     if (read_skeleton (argv[argn],sizeof (tail_skl)/sizeof (SKL_OBJ *),
+                        &tail_nskl,tail_skl) < 0)
+       pm_usage ( usage );
+   }
+   else
+   {
+     pm_usage ( usage );
+   }
+   argn++;
+ }
+
+ if ( argn != argc )
+ {
+   ifp = pm_openr( argv[argn] );
+   ++argn;
+ }
+ else 
+ {
+   ifp = stdin;
+ }
+
+ if ( argn != argc )
+   pm_usage( usage );
+
+ ppm_readppminit( ifp, &cols, &rows, &maxval, &format );
+ pixelrow = ppm_allocrow( cols );
+ dmaxval = (double)maxval;
+
+ if (head_nskl > 0)    /* Write header */
+   write_txt (stdout,head_nskl,head_skl,cols,rows,0,0,0.0,0.0,0.0);
+
+ for ( row = 0; row < rows; ++row )
+ {
+   ppm_readppmrow( ifp, pixelrow, cols, maxval, format );
+
+   for ( col = 0, xP = pixelrow; col < cols; ++col, ++xP )
+   {
+     red = PPM_GETR( *xP );
+     green = PPM_GETG( *xP );
+     blue = PPM_GETB( *xP );
+     write_txt (stdout,body_nskl,body_skl,cols,rows,col,row,
+                red/dmaxval,green/dmaxval,blue/dmaxval);
+   }
+ }
+
+ if (tail_nskl > 0)    /* Write trailer */
+   write_txt (stdout,tail_nskl,tail_skl,cols,rows,0,0,0.0,0.0,0.0);
+
+ pm_close( ifp );
+
+ exit( 0 );
+}
diff --git a/converter/ppm/ppmtobmp.c b/converter/ppm/ppmtobmp.c
new file mode 100644
index 00000000..071f3b12
--- /dev/null
+++ b/converter/ppm/ppmtobmp.c
@@ -0,0 +1,795 @@
+/*
+ * ppmtobmp.c - Converts from a PPM file to a Microsoft Windows or OS/2
+ * .BMP file.
+ *
+ * Copyright (C) 1992 by David W. Sanderson.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation.  This software is provided "as is"
+ * without express or implied warranty.
+ *
+ */
+
+#define _BSD_SOURCE 1    /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include <assert.h>
+#include <string.h>
+#include "bmp.h"
+#include "ppm.h"
+#include "shhopt.h"
+#include "bitio.h"
+
+#define MAXCOLORS 256
+
+enum colortype {TRUECOLOR, PALETTE};
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   A color map for a BMP file.
+-----------------------------------------------------------------------------*/
+    unsigned int count;
+        /* Number of colors in the map.  The first 'count' elements of these
+           arrays are defined; all others are not.
+        */
+    colorhash_table cht;
+
+    /* Indices in the following arrays are the same as in 'cht', above. */
+    unsigned char red[MAXCOLORS];
+    unsigned char grn[MAXCOLORS];
+    unsigned char blu[MAXCOLORS];
+} colorMap;
+
+
+
+static void
+freeColorMap(const colorMap * const colorMapP) {
+
+    if (colorMapP->cht)
+        ppm_freecolorhash(colorMapP->cht);
+}
+
+
+
+static struct cmdline_info {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    char *input_filename;
+    int class;  /* C_WIN or C_OS2 */
+    unsigned int bppSpec;
+    unsigned int bpp;
+} cmdline;
+
+
+static void
+parse_command_line(int argc, char ** argv,
+                   struct cmdline_info *cmdline_p) {
+/*----------------------------------------------------------------------------
+   Note that many of the strings that this function returns in the
+   *cmdline_p structure are actually in the supplied argv array.  And
+   sometimes, one of these strings is actually just a suffix of an entry
+   in argv!
+-----------------------------------------------------------------------------*/
+    optEntry *option_def = malloc(100*sizeof(optEntry));
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int windowsSpec, os2Spec;
+
+    unsigned int option_def_index;
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3('w', "windows",   OPT_FLAG, NULL, &windowsSpec,            0);
+    OPTENT3('o', "os2",       OPT_FLAG, NULL, &os2Spec,                0);
+    OPTENT3(0,   "bpp",       OPT_UINT, &cmdline_p->bpp, 
+            &cmdline_p->bppSpec,      0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+
+    if (windowsSpec && os2Spec) 
+        pm_error("Can't specify both -windows and -os2 options.");
+    else if (windowsSpec) 
+        cmdline_p->class = C_WIN;
+    else if (os2Spec)
+        cmdline_p->class = C_OS2;
+    else 
+        cmdline_p->class = C_WIN;
+
+
+    if (cmdline_p->bppSpec) {
+        if (cmdline_p->bpp != 1 && cmdline_p->bpp != 4 && 
+            cmdline_p->bpp != 8 && cmdline_p->bpp != 24)
+        pm_error("Invalid -bpp value specified: %u.  The only values valid\n"
+                 "in the BMP format are 1, 4, 8, and 24 bits per pixel",
+                 cmdline_p->bpp);
+    }
+
+    if (argc - 1 == 0)
+        cmdline_p->input_filename = strdup("-");  /* he wants stdin */
+    else if (argc - 1 == 1)
+        cmdline_p->input_filename = strdup(argv[1]);
+    else 
+        pm_error("Too many arguments.  The only argument accepted\n"
+                 "is the input file specificaton");
+
+}
+
+
+
+static void
+PutByte(FILE * const fp, unsigned char const v) {
+    if (putc(v, fp) == EOF) 
+        pm_error("Write of a byte to a file failed.");
+
+    /* Note:  a Solaris/SPARC user reported on 2003.09.29 that the above
+       putc() returned EOF when a former version of this code declared
+       v as "char" instead of "unsigned char".  This was apparently due
+       to a bug in his C library that caused 255 to look like -1 at some
+       critical internal point.
+    */
+}
+
+
+
+static void
+PutShort(FILE * const fp, short const v) {
+    if (pm_writelittleshort(fp, v) == -1) 
+        pm_error("Write of a halfword to a file failed.");
+}
+
+
+
+static void
+PutLong(FILE * const fp, long const v) {
+    if (pm_writelittlelong(fp, v) == -1)
+        pm_error("Write of a word to a file failed.");
+}
+
+
+
+/*
+ * BMP writing
+ */
+
+static int
+BMPwritefileheader(FILE *        const fp, 
+                   int           const class, 
+                   unsigned long const bitcount, 
+                   unsigned long const x, 
+                   unsigned long const y) {
+/*----------------------------------------------------------------------------
+  Return the number of bytes written, or -1 on error.
+-----------------------------------------------------------------------------*/
+    PutByte(fp, 'B');
+    PutByte(fp, 'M');
+
+    /* cbSize */
+    PutLong(fp, BMPlenfile(class, bitcount, -1, x, y));
+    
+    /* xHotSpot */
+    PutShort(fp, 0);
+    
+    /* yHotSpot */
+    PutShort(fp, 0);
+    
+    /* offBits */
+    PutLong(fp, BMPoffbits(class, bitcount, -1));
+    
+    return 14;
+}
+
+
+
+static int
+BMPwriteinfoheader(FILE *        const fp, 
+                   int           const class, 
+                   unsigned long const bitcount, 
+                   unsigned long const x, 
+                   unsigned long const y) {
+/*----------------------------------------------------------------------------
+  Return the number of bytes written, or -1 on error.
+----------------------------------------------------------------------------*/
+    long cbFix;
+
+    switch (class) {
+    case C_WIN: {
+        cbFix = 40;
+        PutLong(fp, cbFix);
+
+        PutLong(fp, x);         /* cx */
+        PutLong(fp, y);         /* cy */
+        PutShort(fp, 1);        /* cPlanes */
+        PutShort(fp, bitcount); /* cBitCount */
+
+        /*
+         * We've written 16 bytes so far, need to write 24 more
+         * for the required total of 40.
+         */
+
+        PutLong(fp, 0);   /* Compression */
+        PutLong(fp, 0);   /* ImageSize */
+        PutLong(fp, 0);   /* XpixelsPerMeter */
+        PutLong(fp, 0);   /* YpixelsPerMeter */
+        PutLong(fp, 0);   /* ColorsUsed */
+        PutLong(fp, 0);   /* ColorsImportant */
+    }
+    break;
+    case C_OS2: {
+        cbFix = 12;
+        PutLong(fp, cbFix);
+
+        PutShort(fp, x);        /* cx */
+        PutShort(fp, y);        /* cy */
+        PutShort(fp, 1);        /* cPlanes */
+        PutShort(fp, bitcount); /* cBitCount */
+    }
+    break;
+    default:
+        pm_error(er_internal, "BMPwriteinfoheader");
+    }
+
+    return cbFix;
+}
+
+
+
+static int
+BMPwritergb(FILE * const fp, 
+            int    const class, 
+            pixval const R, 
+            pixval const G, 
+            pixval const B) {
+/*----------------------------------------------------------------------------
+  Return the number of bytes written, or -1 on error.
+-----------------------------------------------------------------------------*/
+    switch (class) {
+    case C_WIN:
+        PutByte(fp, B);
+        PutByte(fp, G);
+        PutByte(fp, R);
+        PutByte(fp, 0);
+        return 4;
+    case C_OS2:
+        PutByte(fp, B);
+        PutByte(fp, G);
+        PutByte(fp, R);
+        return 3;
+    default:
+        pm_error(er_internal, "BMPwritergb");
+    }
+    return -1;
+}
+
+
+
+static int
+BMPwritecolormap(FILE *           const ifP, 
+                 int              const class, 
+                 int              const bpp,
+                 const colorMap * const colorMapP) {
+/*----------------------------------------------------------------------------
+  Return the number of bytes written, or -1 on error.
+-----------------------------------------------------------------------------*/
+    long const ncolors = (1 << bpp);
+
+    unsigned int  nbyte;
+    unsigned int  i;
+
+    nbyte = 0;
+    for (i = 0; i < colorMapP->count; ++i)
+        nbyte += BMPwritergb(ifP, class,
+                             colorMapP->red[i],
+                             colorMapP->grn[i],
+                             colorMapP->blu[i]);
+
+    for (; i < ncolors; ++i)
+        nbyte += BMPwritergb(ifP, class, 0, 0, 0);
+
+    return nbyte;
+}
+
+
+
+static int
+BMPwriterow_palette(FILE *          const fp, 
+                    const pixel *   const row, 
+                    unsigned long   const cx, 
+                    unsigned short  const bpp, 
+                    colorhash_table const cht) {
+/*----------------------------------------------------------------------------
+  Return the number of bytes written, or -1 on error.
+-----------------------------------------------------------------------------*/
+    BITSTREAM    b;
+    int retval;
+    
+    b = pm_bitinit(fp, "w");
+    if (b == NULL)
+        retval = -1;
+    else {
+        unsigned int nbyte;
+        unsigned int x;
+        bool         error;
+        
+        nbyte = 0;      /* initial value */
+        error = FALSE;  /* initial value */
+        
+        for (x = 0; x < cx && !error; ++x) {
+            int rc;
+            rc = pm_bitwrite(b, bpp, ppm_lookupcolor(cht, &row[x]));
+            if (rc == -1)
+                error = TRUE;
+            else
+                nbyte += rc;
+        }
+        if (error)
+            retval = -1;
+        else {
+            int rc;
+
+            rc = pm_bitfini(b);
+            if (rc == -1)
+                retval = -1;
+            else {
+                nbyte += rc;
+                
+                /* Make sure we write a multiple of 4 bytes.  */
+                while (nbyte % 4 != 0) {
+                    PutByte(fp, 0);
+                    ++nbyte;
+                }
+                retval = nbyte;
+            }
+        }
+    }
+    return retval;
+}
+
+
+
+static int
+BMPwriterow_truecolor(FILE *        const fp, 
+                      const pixel * const row, 
+                      unsigned long const cols,
+                      pixval        const maxval) {
+/*----------------------------------------------------------------------------
+  Write a row of a truecolor BMP image to the file 'fp'.  The row is 
+  'row', which is 'cols' columns long.
+
+  Return the number of bytes written.
+
+  On error, issue error message and exit program.
+-----------------------------------------------------------------------------*/
+    /* This works only for 24 bits per pixel.  To implement this for the
+       general case (which is only hypothetical -- this program doesn't
+       write any truecolor images except 24 bit and apparently no one
+       else does either), you would move this function into 
+       BMPwriterow_palette, which writes arbitrary bit strings.  But
+       that would be a lot slower and less robust.
+    */
+
+    int nbyte;  /* Number of bytes we have written to file so far */
+    int col;  
+        
+    nbyte = 0;  /* initial value */
+    for (col = 0; col < cols; col++) {
+        /* We scale to the BMP maxval, which is always 255. */
+        PutByte(fp, PPM_GETB(row[col]) * 255 / maxval);
+        PutByte(fp, PPM_GETG(row[col]) * 255 / maxval);
+        PutByte(fp, PPM_GETR(row[col]) * 255 / maxval);
+        nbyte += 3;
+    }
+
+    /*
+     * Make sure we write a multiple of 4 bytes.
+     */
+    while (nbyte % 4) {
+        PutByte(fp, 0);
+        nbyte++;
+    }
+    
+    return nbyte;
+}
+
+
+
+static int
+BMPwritebits(FILE *          const fp, 
+             unsigned long   const cx, 
+             unsigned long   const cy, 
+             enum colortype  const colortype,
+             unsigned short  const cBitCount, 
+             const pixel **  const pixels, 
+             pixval          const maxval,
+             colorhash_table const cht) {
+/*----------------------------------------------------------------------------
+  Return the number of bytes written, or -1 on error.
+-----------------------------------------------------------------------------*/
+    int  nbyte;
+    long y;
+
+    if (cBitCount > 24)
+        pm_error("cannot handle cBitCount: %d", cBitCount);
+
+    nbyte = 0;  /* initial value */
+
+    /* The picture is stored bottom line first, top line last */
+
+    for (y = cy - 1; y >= 0; --y) {
+        int rc;
+        if (colortype == PALETTE)
+            rc = BMPwriterow_palette(fp, pixels[y], cx, 
+                                     cBitCount, cht);
+        else 
+            rc = BMPwriterow_truecolor(fp, pixels[y], cx, maxval);
+
+        if (rc == -1)
+            pm_error("couldn't write row %ld", y);
+        if (rc % 4 != 0)
+            pm_error("row had bad number of bytes: %d", rc);
+        nbyte += rc;
+    }
+
+    return nbyte;
+}
+
+
+
+static void
+BMPEncode(FILE *           const ifP, 
+          int              const class, 
+          enum colortype   const colortype,
+          int              const bpp,
+          int              const x, 
+          int              const y, 
+          const pixel **   const pixels, 
+          pixval           const maxval,
+          const colorMap * const colorMapP) {
+/*----------------------------------------------------------------------------
+  Write a BMP file of the given class.
+-----------------------------------------------------------------------------*/
+    unsigned long nbyte;
+
+    if (colortype == PALETTE)
+        pm_message("Writing %d bits per pixel with a color palette", bpp);
+    else
+        pm_message("Writing %d bits per pixel truecolor (no palette)", bpp);
+
+    nbyte = 0;  /* initial value */
+    nbyte += BMPwritefileheader(ifP, class, bpp, x, y);
+    nbyte += BMPwriteinfoheader(ifP, class, bpp, x, y);
+    if (colortype == PALETTE)
+        nbyte += BMPwritecolormap(ifP, class, bpp, colorMapP);
+
+    if (nbyte != (BMPlenfileheader(class)
+                  + BMPleninfoheader(class)
+                  + BMPlencolormap(class, bpp, -1)))
+        pm_error(er_internal, "BMPEncode 1");
+
+    nbyte += BMPwritebits(ifP, x, y, colortype, bpp, pixels, maxval,
+                          colorMapP->cht);
+    if (nbyte != BMPlenfile(class, bpp, -1, x, y))
+        pm_error(er_internal, "BMPEncode 2");
+}
+
+
+
+static void
+makeBilevelColorMap(colorMap * const colorMapP) {
+
+    colorMapP->count  = 2;
+    colorMapP->cht    = NULL;
+    colorMapP->red[0] = 0;
+    colorMapP->grn[0] = 0;
+    colorMapP->blu[0] = 0;
+    colorMapP->red[1] = 255;
+    colorMapP->grn[1] = 255;
+    colorMapP->blu[1] = 255;
+}
+
+
+
+static void
+BMPEncodePBM(FILE *           const ifP, 
+             int              const class, 
+             int              const cols, 
+             int              const rows, 
+             unsigned char ** const bitrow) {
+/*----------------------------------------------------------------------------
+  Write a bi-level BMP file of the given class.
+-----------------------------------------------------------------------------*/
+    /* Note:
+       Only PBM input uses this routine.  Color images represented by 1 bpp via
+       color palette use the general BMPEncode().
+    */
+    unsigned int const adjustedCols = (cols + 31) / 32 * 32;
+    unsigned int const packedBytes  = adjustedCols / 8;
+
+    unsigned long nbyte;
+    colorMap bilevelColorMap;
+    unsigned int row;
+    
+    /* colortype == PALETTE */
+    pm_message("Writing 1 bit per pixel with a black-white palette");
+
+    nbyte = 0;  /* initial value */
+    nbyte += BMPwritefileheader(ifP, class, 1, cols, rows);
+    nbyte += BMPwriteinfoheader(ifP, class, 1, cols, rows);
+
+    makeBilevelColorMap(&bilevelColorMap);
+
+    nbyte += BMPwritecolormap(ifP, class, 1, &bilevelColorMap);
+
+    if (nbyte != (BMPlenfileheader(class)
+                  + BMPleninfoheader(class)
+                  + BMPlencolormap(class, 1, -1)))
+        pm_error(er_internal, "BMPEncodePBM 1");
+   
+    for (row = 0; row < rows; ++row){
+        size_t bytesWritten;
+
+        bytesWritten = fwrite(bitrow[row], 1, packedBytes, ifP);
+        if (bytesWritten != packedBytes){
+            if (feof(ifP))
+                pm_error("End of file writing row %u of BMP raster.", row);
+            else 
+                pm_error("Error writing BMP raster.  Errno=%d (%s)",
+                         errno, strerror(errno));
+        }  else
+            nbyte += bytesWritten;
+    }
+
+    if (nbyte != BMPlenfile(class, 1, -1, cols, rows))
+        pm_error(er_internal, "BMPEncodePBM 2");
+}
+
+
+
+static void
+analyze_colors(const pixel **    const pixels, 
+               int               const cols, 
+               int               const rows, 
+               pixval            const maxval, 
+               int *             const minimum_bpp_p,
+               colorMap *        const colorMapP) {
+/*----------------------------------------------------------------------------
+  Look at the colors in the image 'pixels' and compute values to use in
+  representing those colors in a BMP image.  
+
+  First of all, count the distinct colors.  Return as *minimum_bpp_p
+  the minimum number of bits per pixel it will take to represent all
+  the colors in BMP format.
+
+  If there are few enough colors to represent with a palette, also
+  return the number of colors as *colors_p and a suitable palette
+  (colormap) and a hash table in which to look up indexes into that
+  palette as *colorMapP.  Use only the first *colors_p entries of the
+  map.
+
+  If there are too many colors for a palette, return colorMapP->cht
+  == NULL.
+
+  Issue informational messages.
+-----------------------------------------------------------------------------*/
+    /* Figure out the colormap. */
+    colorhist_vector chv;
+    int colorCount;
+
+    pm_message("analyzing colors...");
+    chv = ppm_computecolorhist((pixel**)pixels, cols, rows, MAXCOLORS, 
+                               &colorCount);
+    colorMapP->count = colorCount;
+    if (chv == NULL) {
+        pm_message("More than %u colors found", MAXCOLORS);
+        *minimum_bpp_p = 24;
+        colorMapP->cht = NULL;
+    } else {
+        unsigned int const minbits = pm_maxvaltobits(colorMapP->count - 1);
+
+        unsigned int i;
+
+        pm_message("%u colors found", colorMapP->count);
+
+        /* Only 1, 4, 8, and 24 are defined in the BMP spec we
+           implement and other bpp's have in fact been seen to confuse
+           viewers.  There is an extended BMP format that has 16 bpp
+           too, but this program doesn't know how to generate that
+           (see Bmptopnm.c, though).  
+        */
+        if (minbits == 1)
+            *minimum_bpp_p = 1;
+        else if (minbits <= 4)
+            *minimum_bpp_p = 4;
+        else if (minbits <= 8)
+            *minimum_bpp_p = 8;
+        else
+            *minimum_bpp_p = 24;
+
+        /*
+         * Now scale the maxval to 255 as required by BMP format.
+         */
+        for (i = 0; i < colorMapP->count; ++i) {
+            colorMapP->red[i] = (pixval) PPM_GETR(chv[i].color) * 255 / maxval;
+            colorMapP->grn[i] = (pixval) PPM_GETG(chv[i].color) * 255 / maxval;
+            colorMapP->blu[i] = (pixval) PPM_GETB(chv[i].color) * 255 / maxval;
+        }
+    
+        /* And make a hash table for fast lookup. */
+        colorMapP->cht = ppm_colorhisttocolorhash(chv, colorMapP->count);
+        ppm_freecolorhist(chv);
+    }
+}
+
+
+
+static void
+choose_colortype_bpp(struct cmdline_info const cmdline,
+                     unsigned int        const colors, 
+                     unsigned int        const minimum_bpp,
+                     enum colortype *    const colortype_p, 
+                     unsigned int *      const bits_per_pixel_p) {
+
+    if (!cmdline.bppSpec) {
+        /* User has no preference as to bits per pixel.  Choose the
+           smallest number possible for this image.
+        */
+        *bits_per_pixel_p = minimum_bpp;
+    } else {
+        if (cmdline.bpp < minimum_bpp)
+            pm_error("There are too many colors in the image to "
+                     "represent in the\n"
+                     "number of bits per pixel you requested: %d.\n"
+                     "You may use Ppmquant to reduce the number of "
+                     "colors in the image.",
+                     cmdline.bpp);
+        else
+            *bits_per_pixel_p = cmdline.bpp;
+    }
+
+    assert(*bits_per_pixel_p == 1 || 
+           *bits_per_pixel_p == 4 || 
+           *bits_per_pixel_p == 8 || 
+           *bits_per_pixel_p == 24);
+
+    if (*bits_per_pixel_p > 8) 
+        *colortype_p = TRUECOLOR;
+    else {
+        *colortype_p = PALETTE;
+    }
+}
+
+
+
+static void
+doPbm(FILE *       const ifP,
+      unsigned int const cols,
+      unsigned int const rows,
+      int          const format,
+      int          const class,
+      FILE *       const ofP) {
+    
+    /*  In the PBM case the raster is read directly from the input by 
+        pbm_readpbmrow_packed.  The raster format is almost identical,
+        except that BMP specifies rows to be zero-filled to 32 bit borders 
+        and that in BMP the bottom row comes first in order.
+    */
+
+    int const CHARBITS = (sizeof(unsigned char)*8); 
+    int const colChars = pbm_packed_bytes(cols);
+    int const adjustedCols = (cols+31) /32 * 32;
+    int const packedBytes  =  adjustedCols /8;
+
+    unsigned char ** bitrow;    
+    unsigned int row;
+
+    bitrow = pbm_allocarray_packed(adjustedCols, rows);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned char * const thisRow = bitrow[rows - row - 1];
+
+        /* Clear end of each row */
+        thisRow[packedBytes-1] = 0x00;
+        thisRow[packedBytes-2] = 0x00;
+        thisRow[packedBytes-3] = 0x00;
+        thisRow[packedBytes-4] = 0x00;
+        
+        pbm_readpbmrow_packed(ifP, thisRow, cols, format);
+
+        {
+            unsigned int i;
+            for (i = 0; i < colChars; ++i) 
+                thisRow[i] = ~thisRow[i]; /* flip all pixels */
+        }
+        /* This may seem unnecessary, because the color palette 
+           (RGB[] in BMPEncodePBM) can be inverted for the same effect.
+           However we take this precaution, for there is indication that
+           some BMP viewers may get confused with that.
+        */
+
+        if (cols % 8 >0) {
+            /* adjust final partial byte */
+            thisRow[colChars-1] >>= CHARBITS - cols % CHARBITS;
+            thisRow[colChars-1] <<= CHARBITS - cols % CHARBITS;
+        }
+    }
+
+    BMPEncodePBM(ofP, class, cols, rows, bitrow);
+}            
+
+
+
+static void
+doPgmPpm(FILE * const ifP,
+         unsigned int const cols,
+         unsigned int const rows,
+         pixval       const maxval,
+         int          const ppmFormat,
+         int          const class,
+         FILE *       const ofP) {
+
+    /* PGM and PPM.  The input image is read into a PPM array, scanned
+       for color analysis and converted to a BMP raster.
+       Logic works for PBM.
+    */
+    int minimumBpp;
+    unsigned int bitsPerPixel;
+    enum colortype colortype;
+    unsigned int row;
+    
+    pixel ** pixels;
+    colorMap colorMap;
+    
+    pixels = ppm_allocarray(cols, rows);
+    
+    for (row = 0; row < rows; ++row)
+        ppm_readppmrow(ifP, pixels[row], cols, maxval, ppmFormat);
+    
+    analyze_colors((const pixel**)pixels, cols, rows, maxval, 
+                   &minimumBpp, &colorMap);
+    
+    choose_colortype_bpp(cmdline, colorMap.count, minimumBpp, &colortype, 
+                         &bitsPerPixel);
+    
+    BMPEncode(stdout, class, colortype, bitsPerPixel,
+              cols, rows, (const pixel**)pixels, maxval, &colorMap);
+    
+    freeColorMap(&colorMap);
+}
+
+
+
+int
+main(int argc, char **argv) {
+
+    FILE * ifP;
+    int rows;
+    int cols;
+    pixval maxval;
+    int ppmFormat;
+
+    ppm_init(&argc, argv);
+
+    parse_command_line(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.input_filename);
+    
+    ppm_readppminit(ifP, &cols, &rows, &maxval, &ppmFormat);
+    
+    if (PPM_FORMAT_TYPE(ppmFormat) == PBM_TYPE)
+        doPbm(ifP, cols, rows, ppmFormat, cmdline.class, stdout);
+    else
+        doPgmPpm(ifP, cols, rows, maxval, ppmFormat, cmdline.class, stdout);
+
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
+}
diff --git a/converter/ppm/ppmtoeyuv.c b/converter/ppm/ppmtoeyuv.c
new file mode 100644
index 00000000..f5ce1156
--- /dev/null
+++ b/converter/ppm/ppmtoeyuv.c
@@ -0,0 +1,396 @@
+/* Bryan got this from mm.ftp-cs.berkeley.edu from the package
+   mpeg-encode-1.5b-src under the name ppmtoeyuv.c on March 30, 2000.  
+   The file was dated January 19, 1995.  
+
+   Bryan changed the program to take an argument as the input filename
+   and fixed a crash when the input image has an odd number of rows or 
+   columns.
+
+   Then Bryan updated the program on March 15, 2001 to use the Netpbm
+   libraries to read the PPM input and handle multi-image PPM files
+   and arbitrary maxvals.
+
+   There was no attached documentation except for this:  Encoder/Berkeley
+   YUV format is merely the concatenation of Y, U, and V data in order.
+   Compare with Abekas YUV, which interlaces Y, U, and V data. 
+
+   Future enhancement: It may be useful to have an option to do the
+   calculations without multiplication tables to save memory at the
+   expense of execution speed for large maxvals.  Actually, a large
+   maxval without a lot of colors might actually make the tables
+   slower.
+
+*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /n/picasso/users/keving/encode/src/RCS/readframe.c,v 1.1 1993/07/22 22:23:43 keving Exp keving $
+ *  $Log: readframe.c,v $
+ * Revision 1.1  1993/07/22  22:23:43  keving
+ * nothing
+ *
+ */
+
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "pm_c_util.h"
+#include "ppm.h"
+#include "mallocvar.h"
+
+typedef	unsigned char uint8;
+
+/* Multiplication tables */
+
+#define YUVMAXVAL 255
+#define HALFYUVMAXVAL 128
+/* multXXX are multiplication tables used in RGB-YCC calculations for 
+   speed.  mult299[x] is x * .299, scaled to a maxval of 255.  These
+   are malloc'ed and essentially constant.
+
+   We use these tables because it is much faster to do a
+   multiplication once for each possible sample value than once for
+   each pixel in the image.  
+*/
+static float *mult299, *mult587, *mult114;
+static float *mult16874, *mult33126, *mult5;
+static float *mult41869, *mult08131;
+
+static __inline__ float
+luminance(const pixel p) {
+    return mult299[PPM_GETR(p)]
+        + mult587[PPM_GETG(p)] 
+        + mult114[PPM_GETB(p)]
+        ;
+}
+
+static __inline__ float
+chrominance_red(const pixel p) {
+    return mult5[PPM_GETR(p)] 
+        + mult41869[PPM_GETG(p)]
+        + mult08131[PPM_GETB(p)]
+        ;
+}
+
+static __inline__ float
+chrominance_blue(const pixel p) {
+    return mult16874[PPM_GETR(p)] 
+        + mult33126[PPM_GETG(p)]
+        + mult5[PPM_GETB(p)]
+        ;
+}
+
+
+
+static void
+create_multiplication_tables(const pixval maxval) {
+
+    int index;
+
+    MALLOCARRAY_NOFAIL(mult299   , maxval+1);
+    MALLOCARRAY_NOFAIL(mult587   , maxval+1);
+    MALLOCARRAY_NOFAIL(mult114   , maxval+1);
+    MALLOCARRAY_NOFAIL(mult16874 , maxval+1);
+    MALLOCARRAY_NOFAIL(mult33126 , maxval+1);
+    MALLOCARRAY_NOFAIL(mult5     , maxval+1);
+    MALLOCARRAY_NOFAIL(mult41869 , maxval+1);
+    MALLOCARRAY_NOFAIL(mult08131 , maxval+1);
+
+    if (maxval == YUVMAXVAL) {
+        /* fast path */
+        for ( index = 0; index <= maxval; index++ ) {
+            mult299[index]   =  0.29900*index;
+            mult587[index]   =  0.58700*index;
+            mult114[index]   =  0.11400*index;
+            mult5[index]     =  0.50000*index;
+            mult41869[index] = -0.41869*index;
+            mult08131[index] = -0.08131*index;
+            mult16874[index] = -0.16874*index;
+            mult33126[index] = -0.33126*index;
+        }
+    } else {
+        for ( index = 0; index <= maxval; index++ ) {
+            mult299[index]   =  0.29900*index*(maxval/YUVMAXVAL);
+            mult587[index]   =  0.58700*index*(maxval/YUVMAXVAL);
+            mult114[index]   =  0.11400*index*(maxval/YUVMAXVAL);
+            mult5[index]     =  0.50000*index*(maxval/YUVMAXVAL);
+            mult41869[index] = -0.41869*index*(maxval/YUVMAXVAL);
+            mult08131[index] = -0.08131*index*(maxval/YUVMAXVAL);
+            mult16874[index] = -0.16874*index*(maxval/YUVMAXVAL);
+            mult33126[index] = -0.33126*index*(maxval/YUVMAXVAL);
+        }
+        
+    }
+}
+
+
+
+static void
+free_multiplication_tables(void) {
+    free(mult299   );
+    free(mult587   );
+    free(mult114   );
+    free(mult16874 );
+    free(mult33126 );
+    free(mult5     );
+    free(mult41869 );
+    free(mult08131 );
+}
+
+
+
+/*===========================================================================*
+ *
+ * PPMtoYUV
+ *
+ *	convert PPM data into YUV data
+ *	assumes that ydivisor = 1
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ * This function processes the input file in 4 pixel squares.  If the
+ * Image does not have an even number of rows and columns, the rightmost
+ * column or the bottom row gets ignored and output has one fewer row
+ * or column than the input. 
+ *
+ *===========================================================================*/
+static void 
+PPMtoYUV(pixel ** const ppm_image, const int width, const int height,
+         uint8 *** const orig_yP, 
+         uint8 *** const orig_crP, 
+         uint8 *** const orig_cbP) {
+
+    int y;
+    uint8 ** orig_y;
+    uint8 ** orig_cr;
+    uint8 ** orig_cb;
+
+    orig_y = *orig_yP;
+    orig_cr = *orig_crP;
+    orig_cb = *orig_cbP;
+
+    for (y = 0; y + 1 < height; y += 2) {
+        uint8 *dy0, *dy1;
+        uint8 *dcr, *dcb;
+        const pixel *src0, *src1;
+          /* Pair of contiguous rows of the ppm input image we are
+             converting */
+        int x;
+
+        src0 = ppm_image[y];
+        src1 = ppm_image[y + 1];
+
+        dy0 = orig_y[y];
+        dy1 = orig_y[y + 1];
+        dcr = orig_cr[y / 2];
+        dcb = orig_cb[y / 2];
+
+        for ( x = 0; x + 1 < width; x += 2) {
+            dy0[x] = luminance(src0[x]);
+            dy1[x] = luminance(src1[x]);
+
+            dy0[x+1] = luminance(src0[x+1]);
+            dy1[x+1] = luminance(src1[x+1]);
+
+            dcr[x/2] = ((
+                chrominance_red(src0[x]) +
+                chrominance_red(src1[x]) +
+                chrominance_red(src0[x+1]) +
+                chrominance_red(src1[x+1]) 
+                ) / 4) + HALFYUVMAXVAL;
+
+            dcb[x/2] = ((
+                chrominance_blue(src0[x]) +
+                chrominance_blue(src1[x]) +
+                chrominance_blue(src0[x+1]) +
+                chrominance_blue(src1[x+1]) 
+                ) / 4) + HALFYUVMAXVAL;
+        }
+    }
+}
+
+
+
+static void 
+WriteYUV(FILE *fpointer, const int width, const int height,
+         uint8 ** const orig_y, uint8 ** const orig_cr, uint8 ** const orig_cb)
+{
+    register int y;
+
+    for (y = 0; y < height; y++)                        /* Y */
+        fwrite(orig_y[y], 1, width, fpointer);
+
+    for (y = 0; y < height / 2; y++)                    /* U */
+        fwrite(orig_cb[y], 1, width / 2, fpointer);
+
+    for (y = 0; y < height / 2; y++)                    /* V */
+        fwrite(orig_cr[y], 1, width / 2, fpointer);
+}
+
+
+
+static void
+AllocYUV(int       const width, 
+         int       const height,
+         uint8 *** const orig_yP, 
+         uint8 *** const orig_crP,
+         uint8 *** const orig_cbP) {
+
+    int y;
+    uint8 ** orig_y;
+    uint8 ** orig_cr;
+    uint8 ** orig_cb;
+
+    MALLOCARRAY_NOFAIL(*orig_yP, height);
+    orig_y = *orig_yP;
+    for (y = 0; y < height; y++) 
+        MALLOCARRAY_NOFAIL(orig_y[y], width);
+
+    MALLOCARRAY_NOFAIL(*orig_crP, height / 2);
+    orig_cr = *orig_crP;
+    for (y = 0; y < height / 2; y++) 
+        MALLOCARRAY_NOFAIL(orig_cr[y], width / 2);
+
+    MALLOCARRAY_NOFAIL(*orig_cbP, height / 2);
+    orig_cb = *orig_cbP;
+    for (y = 0; y < height / 2; y++) 
+        MALLOCARRAY_NOFAIL(orig_cb[y], width / 2);
+}
+
+
+
+static void
+FreeYUV(const int width, const int height,
+        uint8 ** const orig_y, uint8 ** const orig_cr, uint8 ** const orig_cb){
+
+    int y;
+
+    if (orig_y) {
+       for (y = 0; y < height; y++)
+           free(orig_y[y]);
+       free(orig_y);
+    }
+
+    if (orig_cr) {
+       for (y = 0; y < height / 2; y++)
+           free(orig_cr[y]);
+       free(orig_cr);
+    }
+
+    if (orig_cb) {
+       for (y = 0; y < height / 2; y++)
+           free(orig_cb[y]);
+       free(orig_cb);
+    }
+}
+
+
+
+int
+main(int argc, char **argv) {
+    const char *input_filename;  /* NULL for stdin */
+    FILE * ifp;
+    int width, height;
+    pixval maxval;
+    pixel **ppm_image;   /* malloc'ed */
+    uint8 **orig_y, **orig_cr, **orig_cb;
+        /* orig_y is the height x width array of individual pixel luminances 
+           orig_cr and orig_cb are the height/2 x width/2 arrays of average
+           red and blue chrominance values over each 4 pixel square.
+        */
+    int eof;
+
+    /* The following are width, height, and maxval of the image we 
+       processed before this one.  Zero if there was no image before 
+       this one.
+    */
+    int last_width, last_height;
+    pixval last_maxval;
+
+    ppm_init(&argc, argv);
+
+    if (argc > 2) {
+        pm_error("Program takes either one argument -- "
+                "the input filename -- or no arguments (input is stdin)");
+        exit(1);
+    } else if (argc == 2)
+        input_filename = argv[1];
+    else input_filename = NULL;
+
+    if (input_filename == NULL) ifp = stdin;
+    else ifp = pm_openr(input_filename);
+
+    eof = FALSE;
+    last_maxval = 0;  /* No previous maxval */
+    last_width = 0;	/* No previous width */
+    last_height = 0;	/* No previous height */
+    orig_y = orig_cr = orig_cb = 0;
+
+    while (!eof) {
+        ppm_image = ppm_readppm(ifp, &width, &height, &maxval);
+
+        if (width % 2 != 0) 
+            pm_message("Input image has odd number of columns.  The rightmost "
+                       "column will be omitted from the output.");
+        if (height % 2 != 0) 
+            pm_message("Input image has odd number of rows.  The bottom "
+                       "row will be omitted from the output.");
+
+        if (maxval != last_maxval) {
+            /* We're going to need all new multiplication tables. */
+            free_multiplication_tables();
+            create_multiplication_tables(maxval);
+        }
+        last_maxval = maxval;
+        
+        if (height != last_height || width != last_width) {
+            FreeYUV(width, height, orig_y, orig_cr, orig_cb);
+            /* Need new YUV buffers for different size */
+            AllocYUV(width, height, &orig_y, &orig_cr, &orig_cb);
+        }
+        last_height = height;
+        last_width = width;
+
+        PPMtoYUV(ppm_image, width, height, &orig_y, &orig_cr, &orig_cb);
+
+        WriteYUV(stdout, (width/2)*2, (height/2)*2, orig_y, orig_cr, orig_cb);
+
+        ppm_freearray(ppm_image, height);
+        ppm_nextimage(ifp, &eof);
+    }
+    FreeYUV(width, height, orig_y, orig_cr, orig_cb);
+    free_multiplication_tables();
+    pm_close(ifp);
+        
+    return 0;
+}
+
diff --git a/converter/ppm/ppmtogif.c b/converter/ppm/ppmtogif.c
new file mode 100644
index 00000000..9521237b
--- /dev/null
+++ b/converter/ppm/ppmtogif.c
@@ -0,0 +1,1681 @@
+/* ppmtogif.c - read a portable pixmap and produce a GIF file
+**
+** Based on GIFENCOD by David Rowley <mgardi@watdscu.waterloo.edu>.A
+** Lempel-Zim compression based on "compress".
+**
+** Modified by Marcel Wijkstra <wijkstra@fwi.uva.nl>
+**
+** The non-LZW GIF generation stuff was adapted from the Independent
+** JPEG Group's djpeg on 2001.09.29.  The uncompressed output subroutines
+** are derived directly from the corresponding subroutines in djpeg's
+** wrgif.c source file.  Its copyright notice say:
+
+ * Copyright (C) 1991-1997, Thomas G. Lane.
+ * This file is part of the Independent JPEG Group's software.
+ * For conditions of distribution and use, see the accompanying README file.
+ *
+   The reference README file is README.JPEG in the Netpbm package.
+**
+** Copyright (C) 1989 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+**
+** The Graphics Interchange Format(c) is the Copyright property of
+** CompuServe Incorporated.  GIF(sm) is a Service Mark property of
+** CompuServe Incorporated.
+*/
+
+/* TODO: merge the LZW and uncompressed subroutines.  They are separate
+   only because they had two different lineages and the code is too
+   complicated for me quickly to rewrite it.
+*/
+#include <assert.h>
+#include <string.h>
+
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "ppm.h"
+
+#define MAXCMAPSIZE 256
+
+static unsigned int const gifMaxval = 255;
+
+static bool verbose;
+/*
+ * a code_int must be able to hold 2**BITS values of type int, and also -1
+ */
+typedef int code_int;
+
+typedef long int          count_int;
+
+
+struct cmap {
+    /* This is the information for the GIF colormap (aka palette). */
+
+    int red[MAXCMAPSIZE], green[MAXCMAPSIZE], blue[MAXCMAPSIZE];
+        /* These arrays arrays map a color index, as is found in
+           the raster part of the GIF, to an intensity value for the indicated
+           RGB component.
+        */
+    int perm[MAXCMAPSIZE], permi[MAXCMAPSIZE];
+        /* perm[i] is the position in the sorted colormap of the color which
+           is at position i in the unsorted colormap.  permi[] is the inverse
+           function of perm[].
+        */
+    unsigned int cmapsize;
+        /* Number of entries in the GIF colormap.  I.e. number of colors
+           in the image, plus possibly one fake transparency color.
+        */
+    int transparent;
+        /* color index number in GIF palette of the color that is to be
+           transparent.  -1 if no color is transparent.
+        */
+    colorhash_table cht;
+        /* A hash table that relates a PPM pixel value to to a pre-sort
+           GIF colormap index.
+        */
+    pixval maxval;
+        /* The maxval for the colors in 'cht'. */
+};
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *input_filespec; /* Filespec of input file */
+    const char *alpha_filespec; /* Filespec of alpha file; NULL if none */
+    const char *alphacolor;     /* -alphacolor option value or default */
+    unsigned int interlace;     /* -interlace option value */
+    unsigned int sort;          /* -sort option value */
+    const char *mapfile;        /* -mapfile option value.  NULL if none. */
+    const char *transparent;    /* -transparent option value.  NULL if none. */
+    const char *comment;        /* -comment option value; NULL if none */
+    unsigned int nolzw;         /* -nolzw option */
+    unsigned int verbose;
+};
+
+
+static void
+handleLatex2htmlHack(void) {
+/*----------------------------------------------------------------------------
+  This program used to put out a "usage" message when it saw an option
+  it didn't understand.  Latex2html's configure program does a
+  ppmtogif -h (-h was never a valid option) to elicit that message and
+  then parses the message to see if it included the strings
+  "-interlace" and "-transparent".  That way it knows if the
+  'ppmtogif' program it found has those options or not.  I don't think
+  any 'ppmtogif' you're likely to find today lacks those options, but
+  latex2html checks anyway, and we don't want it to conclude that we
+  don't have them.
+
+  So we issue a special error message just to trick latex2html into
+  deciding that we have -interlace and -transparent options.  The function
+  is not documented in the man page.  We would like to see Latex2html 
+  either stop checking or check like configure programs usually do -- 
+  try the option and see if you get success or failure.
+
+  -Bryan 2001.11.14
+-----------------------------------------------------------------------------*/
+     pm_error("latex2html, you should just try the -interlace and "
+              "-transparent options to see if they work instead of "
+              "expecting a 'usage' message from -h");
+}
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Parse the program arguments (given by argc and argv) into a form
+   the program can deal with more easily -- a cmdline_info structure.
+   If the syntax is invalid, issue a message and exit the program via
+   pm_error().
+
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;  /* malloc'ed */
+    optStruct3 opt;  /* set by OPTENT3 */
+    unsigned int option_def_index;
+
+    unsigned int latex2htmlhack;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "interlace",   OPT_FLAG,   
+            NULL,                       &cmdlineP->interlace, 0);
+    OPTENT3(0,   "sort",        OPT_FLAG,   
+            NULL,                       &cmdlineP->sort, 0);
+    OPTENT3(0,   "nolzw",       OPT_FLAG,   
+            NULL,                       &cmdlineP->nolzw, 0);
+    OPTENT3(0,   "mapfile",     OPT_STRING, 
+            &cmdlineP->mapfile,        NULL, 0);
+    OPTENT3(0,   "transparent", OPT_STRING, 
+            &cmdlineP->transparent,    NULL, 0);
+    OPTENT3(0,   "comment",     OPT_STRING, 
+            &cmdlineP->comment,        NULL, 0);
+    OPTENT3(0,   "alpha",       OPT_STRING, 
+            &cmdlineP->alpha_filespec, NULL, 0);
+    OPTENT3(0,   "alphacolor",  OPT_STRING, 
+            &cmdlineP->alphacolor,     NULL, 0);
+    OPTENT3(0,   "h",           OPT_FLAG, 
+            NULL,                       &latex2htmlhack, 0);
+    OPTENT3(0,   "verbose",     OPT_FLAG, 
+            NULL,                       &cmdlineP->verbose, 0);
+    
+    /* Set the defaults */
+    cmdlineP->mapfile = NULL;
+    cmdlineP->transparent = NULL;  /* no transparency */
+    cmdlineP->comment = NULL;      /* no comment */
+    cmdlineP->alpha_filespec = NULL;      /* no alpha file */
+    cmdlineP->alphacolor = "rgb:0/0/0";      
+        /* We could say "black" here, but then we depend on the color names
+           database existing.
+        */
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (latex2htmlhack) 
+        handleLatex2htmlHack();
+
+    if (argc-1 == 0) 
+        cmdlineP->input_filespec = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
+    else
+        cmdlineP->input_filespec = argv[1];
+
+    if (cmdlineP->alpha_filespec && cmdlineP->transparent)
+        pm_error("You cannot specify both -alpha and -transparent.");
+}
+
+
+
+/*
+ * Write out a word to the GIF file
+ */
+static void
+Putword(int const w, FILE * const fp) {
+
+    fputc( w & 0xff, fp );
+    fputc( (w / 256) & 0xff, fp );
+}
+
+
+static int
+closestcolor(pixel         const color,
+             pixval        const maxval,
+             struct cmap * const cmapP) {
+/*----------------------------------------------------------------------------
+   Return the pre-sort colormap index of the color in the colormap *cmapP
+   that is closest to the color 'color', whose maxval is 'maxval'.
+
+   Also add 'color' to the colormap hash, with the colormap index we
+   are returning.  Caller must ensure that the color is not already in
+   there.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    unsigned int imin, dmin;
+
+    pixval const r = PPM_GETR(color) * gifMaxval / maxval;
+    pixval const g = PPM_GETG(color) * gifMaxval / maxval;
+    pixval const b = PPM_GETB(color) * gifMaxval / maxval;
+
+    dmin = SQR(255) * 3;
+    imin = 0;
+    for (i=0;i < cmapP->cmapsize; i++) {
+        int const d = SQR(r-cmapP->red[i]) + 
+            SQR(g-cmapP->green[i]) + 
+            SQR(b-cmapP->blue[i]);
+        if (d < dmin) {
+            dmin = d;
+            imin = i; 
+        } 
+    }
+    ppm_addtocolorhash(cmapP->cht, &color, cmapP->permi[imin]);
+
+    return cmapP->permi[imin];
+}
+
+
+
+enum pass {MULT8PLUS0, MULT8PLUS4, MULT4PLUS2, MULT2PLUS1};
+
+
+typedef struct {
+    FILE * fileP;
+        /* The PPM file stream from which pixels come.  The position
+           of this file is also part of the state of this pixelReader.
+        */
+    unsigned int width;
+        /* Width of the image, in columns */
+    unsigned int height;
+        /* Height of the image, in rows */
+    pixval maxval;
+    int format;
+    pm_filepos rasterPos;
+        /* Position in file fileP of the start of the raster */
+    bool interlace;
+        /* We're accessing the image in interlace fashion */
+    unsigned int nPixelsLeft;
+        /* Number of pixels we have left to read in the image */
+    pm_pixelcoord next;
+        /* Location of next pixel to read */
+    enum pass pass;
+        /* The interlace pass.  Undefined if !interlace */
+    pixel * curPixelRow;
+        /* The pixels of the current row (the one numbered in with pixel
+           'next' resides).
+           Dynamically allocated.
+        */
+} pixelReader;
+
+
+
+static void
+pixelReaderReadCurrentRow(pixelReader * const rdrP) {
+
+    ppm_readppmrow(rdrP->fileP, rdrP->curPixelRow,
+                   rdrP->width, rdrP->maxval, rdrP->format);
+}
+
+
+
+static void
+pixelReaderCreate(FILE *         const ifP,
+                  unsigned int   const width,
+                  unsigned int   const height,
+                  pixval         const maxval,
+                  int            const format,
+                  pm_filepos     const rasterPos,
+                  bool           const interlace,
+                  pixelReader ** const pixelReaderPP) {
+
+    pixelReader * rdrP;
+
+    MALLOCVAR_NOFAIL(rdrP);
+
+    rdrP->fileP       = ifP;
+    rdrP->width       = width;
+    rdrP->height      = height;
+    rdrP->maxval      = maxval;
+    rdrP->format      = format;
+    rdrP->rasterPos   = rasterPos;
+    rdrP->interlace   = interlace;
+    rdrP->pass        = MULT8PLUS0;
+    rdrP->next.col    = 0;
+    rdrP->next.row    = 0;
+    rdrP->nPixelsLeft = width * height;
+
+    rdrP->curPixelRow = ppm_allocrow(width);
+
+    pm_seek2(rdrP->fileP, &rasterPos, sizeof(rasterPos));
+
+    pixelReaderReadCurrentRow(rdrP);
+
+    *pixelReaderPP = rdrP;
+}
+
+
+
+static void
+pixelReaderDestroy(pixelReader * const pixelReaderP) {
+
+    ppm_freerow(pixelReaderP->curPixelRow);
+
+    free(pixelReaderP);
+}
+
+
+
+static size_t
+bytesPerSample(pixval const maxval) {
+
+    return maxval < (1 << 16) ? 1 : 2;
+}
+
+
+
+/* TODO - move this to libnetpbm */
+
+
+
+void
+ppm_seek(FILE *             const fileP,
+         const pm_filepos * const rasterPosP,
+         size_t             const rasterPosSize,
+         unsigned int       const cols,
+         pixval             const maxval,
+         unsigned int       const col,
+         unsigned int       const row);
+
+void
+ppm_seek(FILE *             const fileP,
+         const pm_filepos * const rasterPosP,
+         size_t             const rasterPosSize,
+         unsigned int       const cols,
+         pixval             const maxval,
+         unsigned int       const col,
+         unsigned int       const row) {
+
+    pm_filepos rasterPos;
+    pm_filepos pixelPos;
+
+    if (rasterPosSize == sizeof(pm_filepos)) 
+        rasterPos = *rasterPosP;
+    else if (rasterPosSize == sizeof(long))
+        rasterPos = *(long*)rasterPosP;
+    else
+        pm_error("File position size passed to ppm_seek() is invalid: %u.  "
+                 "Valid sizes are %u and %u", 
+                 rasterPosSize, sizeof(pm_filepos), sizeof(long));
+
+    pixelPos = rasterPos + row * cols * bytesPerSample(maxval) + col;
+
+    pm_seek2(fileP, &pixelPos, sizeof(pixelPos));
+}
+
+
+
+
+static void
+pixelReaderGotoNextInterlaceRow(pixelReader * const rdrP) {
+/*----------------------------------------------------------------------------
+  Position reader to the next row in the interlace pattern.
+
+  Assume there is at least one more row to read.
+-----------------------------------------------------------------------------*/
+    assert(rdrP->nPixelsLeft >= rdrP->width);
+
+    /* There are 4 passes:
+       MULT8PLUS0: Rows 0, 8, 16, 24, 32, etc.
+       MULT8PLUS4: Rows 4, 12, 20, 28, etc.
+       MULT4PLUS2: Rows 2, 6, 10, 14, etc.
+       MULT2PLUS1: Rows 1, 3, 5, 7, 9, etc.
+    */
+    
+    switch (rdrP->pass) {
+    case MULT8PLUS0:
+        rdrP->next.row += 8;
+        break;
+    case MULT8PLUS4:
+        rdrP->next.row += 8;
+        break;
+    case MULT4PLUS2:
+        rdrP->next.row += 4;
+        break;
+    case MULT2PLUS1:
+        rdrP->next.row += 2;
+        break;
+    }
+
+    /* If we've finished a pass, next.row is now beyond the end of
+       the image.  In that case, we switch to the next pass now.
+
+       Note that if there are more than 4 rows, the sequence of passes
+       is sequential, but when there are fewer than 4, we may skip
+       e.g. from MULT8PLUS0 to MULT4PLUS2.
+    */
+    while (rdrP->next.row >= rdrP->height) {
+        switch (rdrP->pass) {
+        case MULT8PLUS0:
+            rdrP->pass = MULT8PLUS4;
+            rdrP->next.row = 4;
+            break;
+        case MULT8PLUS4:
+            rdrP->pass = MULT4PLUS2;
+            rdrP->next.row = 2;
+            break;
+        case MULT4PLUS2:
+            rdrP->pass = MULT2PLUS1;
+            rdrP->next.row = 1;
+            break;
+        case MULT2PLUS1:
+            /* An entry condition is that there be a row left to read,
+               but we have finished the last pass.  That can't be:
+            */
+            assert(false);
+            break;
+        }
+    }
+    /* Now that we know which row should be current, get its
+       pixels into the buffer.
+    */
+    ppm_seek(rdrP->fileP, &rdrP->rasterPos, sizeof(rdrP->rasterPos),
+             rdrP->width, rdrP->maxval, 0, rdrP->next.row);
+}
+
+
+
+static void
+pixelReaderRead(pixelReader * const rdrP,
+                pixel *       const pixelP,
+                bool *        const eofP) {
+
+    if (rdrP->nPixelsLeft == 0)
+        *eofP = TRUE;
+    else {
+        *eofP = FALSE;
+
+        *pixelP = rdrP->curPixelRow[rdrP->next.col];
+
+        --rdrP->nPixelsLeft;
+
+        /* Move one column to the right */
+        ++rdrP->next.col;
+
+        if (rdrP->next.col >= rdrP->width) {
+            /* That pushed us past the end of a row. */
+            if (rdrP->nPixelsLeft > 0) {
+                /* Reset to the left edge ... */
+                rdrP->next.col = 0;
+                
+                /* ... of the next row */
+                if (!rdrP->interlace)
+                    ++rdrP->next.row;
+                else
+                    pixelReaderGotoNextInterlaceRow(rdrP);
+                
+                pixelReaderReadCurrentRow(rdrP);
+            }
+        }
+    }
+}
+
+
+
+static pm_pixelcoord
+pixelReaderNextCoord(pixelReader * const pixelReaderP) {
+
+    return pixelReaderP->next;
+}
+
+
+
+static void
+gifNextPixel(pixelReader *      const pixelReaderP,
+             pixval             const inputMaxval,
+             gray **            const alpha,
+             gray               const alphaThreshold, 
+             struct cmap *      const cmapP,
+             unsigned int *     const colorIndexP,
+             bool *             const eofP) {
+/*----------------------------------------------------------------------------
+   Return as *colorIndexP the colormap index of the next pixel supplied by
+   pixel reader 'pixelReaderP', using colormap *cmapP.
+
+   Iff the reader is at the end of the image, return *eofP == TRUE
+   and nothing as *colorIndexP.
+
+   'alphaThreshold' is the gray level such that a pixel in the alpha
+   map whose value is less that that represents a transparent pixel
+   in the output.
+-----------------------------------------------------------------------------*/
+    pm_pixelcoord const coord = pixelReaderNextCoord(pixelReaderP);
+
+    pixel pixel;
+
+    pixelReaderRead(pixelReaderP, &pixel, eofP);
+    if (!*eofP) {
+        int colorindex;
+
+        if (alpha && alpha[coord.row][coord.col] < alphaThreshold)
+            colorindex = cmapP->transparent;
+        else {
+            int presortColorindex;
+            
+            presortColorindex = ppm_lookupcolor(cmapP->cht, &pixel);
+            if (presortColorindex == -1)
+                presortColorindex = closestcolor(pixel, inputMaxval, cmapP);
+            colorindex = cmapP->perm[presortColorindex];
+        }
+        *colorIndexP = colorindex;
+    }
+}
+
+
+
+static void
+write_transparent_color_index_extension(FILE *fp, const int Transparent) {
+/*----------------------------------------------------------------------------
+   Write out extension for transparent color index.
+-----------------------------------------------------------------------------*/
+
+    fputc( '!', fp );
+    fputc( 0xf9, fp );
+    fputc( 4, fp );
+    fputc( 1, fp );
+    fputc( 0, fp );
+    fputc( 0, fp );
+    fputc( Transparent, fp );
+    fputc( 0, fp );
+}
+
+
+
+static void
+write_comment_extension(FILE *fp, const char comment[]) {
+/*----------------------------------------------------------------------------
+   Write out extension for a comment
+-----------------------------------------------------------------------------*/
+    char *segment;
+    
+    fputc('!', fp);   /* Identifies an extension */
+    fputc(0xfe, fp);  /* Identifies a comment */
+
+    /* Write it out in segments no longer than 255 characters */
+    for (segment = (char *) comment; 
+         segment < comment+strlen(comment); 
+         segment += 255) {
+
+        const int length_this_segment = MIN(255, strlen(segment));
+
+        fputc(length_this_segment, fp);
+
+        fwrite(segment, 1, length_this_segment, fp);
+    }
+
+    fputc(0, fp);   /* No more comment blocks in this extension */
+}
+
+
+
+/***************************************************************************
+ *
+ *  GIFCOMPR.C       - GIF Image compression routines
+ *
+ *  Lempel-Ziv compression based on 'compress'.  GIF modifications by
+ *  David Rowley (mgardi@watdcsu.waterloo.edu)
+ *
+ ***************************************************************************/
+
+/*
+ * General DEFINEs
+ */
+
+#define BITS    12
+
+#define HSIZE  5003            /* 80% occupancy */
+
+#ifdef NO_UCHAR
+ typedef char   char_type;
+#else /*NO_UCHAR*/
+ typedef        unsigned char   char_type;
+#endif /*NO_UCHAR*/
+
+/*
+ *
+ * GIF Image compression - modified 'compress'
+ *
+ * Based on: compress.c - File compression ala IEEE Computer, June 1984.
+ *
+ * By Authors:  Spencer W. Thomas       (decvax!harpo!utah-cs!utah-gr!thomas)
+ *              Jim McKie               (decvax!mcvax!jim)
+ *              Steve Davies            (decvax!vax135!petsd!peora!srd)
+ *              Ken Turkowski           (decvax!decwrl!turtlevax!ken)
+ *              James A. Woods          (decvax!ihnp4!ames!jaw)
+ *              Joe Orost               (decvax!vax135!petsd!joe)
+ *
+ */
+#include <ctype.h>
+
+#define ARGVAL() (*++(*argv) || (--argc && *++argv))
+
+static code_int const maxmaxcode = (code_int)1 << BITS;
+    /* should NEVER generate this code */
+#define MAXCODE(n_bits)        (((code_int) 1 << (n_bits)) - 1)
+
+static long htab [HSIZE];
+static unsigned short codetab [HSIZE];
+#define HashTabOf(i)       htab[i]
+#define CodeTabOf(i)    codetab[i]
+
+/*
+ * To save much memory, we overlay the table used by compress() with those
+ * used by decompress().  The tab_prefix table is the same size and type
+ * as the codetab.  The tab_suffix table needs 2**BITS characters.  We
+ * get this from the beginning of htab.  The output stack uses the rest
+ * of htab, and contains characters.  There is plenty of room for any
+ * possible stack (stack used to be 8000 characters).
+ */
+
+#define tab_prefixof(i) CodeTabOf(i)
+#define tab_suffixof(i)        ((char_type*)(htab))[i]
+#define de_stack               ((char_type*)&tab_suffixof((code_int)1<<BITS))
+
+static code_int free_ent = 0;                  /* first unused entry */
+
+/*
+ * block compression parameters -- after all codes are used up,
+ * and compression rate changes, start over.
+ */
+static int clear_flg = 0;
+
+static int offset;
+static long int in_count = 1;            /* length of input */
+static long int out_count = 0;           /* # of codes output (for debugging) */
+
+/*
+ * compress stdin to stdout
+ *
+ * Algorithm:  use open addressing double hashing (no chaining) on the
+ * prefix code / next character combination.  We do a variant of Knuth's
+ * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
+ * secondary probe.  Here, the modular division first probe is gives way
+ * to a faster exclusive-or manipulation.  Also do block compression with
+ * an adaptive reset, whereby the code table is cleared when the compression
+ * ratio decreases, but after the table fills.  The variable-length output
+ * codes are re-sized at this point, and a special CLEAR code is generated
+ * for the decompressor.  Late addition:  construct the table according to
+ * file size for noticeable speed improvement on small files.  Please direct
+ * questions about this implementation to ames!jaw.
+ */
+
+static int ClearCode;
+static int EOFCode;
+
+/***************************************************************************
+*                          BYTE OUTPUTTER                 
+***************************************************************************/
+
+typedef struct {
+    FILE * fileP;  /* The file to which to output */
+    unsigned int count;
+        /* Number of bytes so far in the current data block */
+    unsigned char buffer[256];
+        /* The current data block, under construction */
+} byteBuffer;
+
+
+
+static byteBuffer *
+byteBuffer_create(FILE * const fileP) {
+
+    byteBuffer * byteBufferP;
+
+    MALLOCVAR_NOFAIL(byteBufferP);
+
+    byteBufferP->fileP = fileP;
+    byteBufferP->count = 0;
+
+    return byteBufferP;
+}
+
+
+
+static void
+byteBuffer_destroy(byteBuffer * const byteBufferP) {
+
+    free(byteBufferP);
+}
+
+
+
+static void
+byteBuffer_flush(byteBuffer * const byteBufferP) {
+/*----------------------------------------------------------------------------
+   Write the current data block to the output file, then reset the current 
+   data block to empty.
+-----------------------------------------------------------------------------*/
+    if (byteBufferP->count > 0 ) {
+        if (verbose)
+            pm_message("Writing %u byte block", byteBufferP->count);
+        fputc(byteBufferP->count, byteBufferP->fileP);
+        fwrite(byteBufferP->buffer, 1, byteBufferP->count, byteBufferP->fileP);
+        byteBufferP->count = 0;
+    }
+}
+
+
+
+static void
+byteBuffer_flushFile(byteBuffer * const byteBufferP) {
+    
+    fflush(byteBufferP->fileP);
+    
+    if (ferror(byteBufferP->fileP))
+        pm_error("error writing output file");
+}
+
+
+
+static void
+byteBuffer_out(byteBuffer *  const byteBufferP,
+               unsigned char const c) {
+/*----------------------------------------------------------------------------
+  Add a byte to the end of the current data block, and if it is now 254
+  characters, flush the data block to the output file.
+-----------------------------------------------------------------------------*/
+    byteBufferP->buffer[byteBufferP->count++] = c;
+    if (byteBufferP->count >= 254)
+        byteBuffer_flush(byteBufferP);
+}
+
+
+
+struct gif_dest {
+    /* This structure controls output of uncompressed GIF raster */
+
+    byteBuffer * byteBufferP;  /* Where the full bytes go */
+
+    /* State for packing variable-width codes into a bitstream */
+    int n_bits;         /* current number of bits/code */
+    int maxcode;        /* maximum code, given n_bits */
+    int cur_accum;      /* holds bits not yet output */
+    int cur_bits;       /* # of bits in cur_accum */
+
+    /* State for GIF code assignment */
+    int ClearCode;      /* clear code (doesn't change) */
+    int EOFCode;        /* EOF code (ditto) */
+    int code_counter;   /* counts output symbols */
+};
+
+
+
+static unsigned long const masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
+                                       0x001F, 0x003F, 0x007F, 0x00FF,
+                                       0x01FF, 0x03FF, 0x07FF, 0x0FFF,
+                                       0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
+
+typedef struct {
+    byteBuffer * byteBufferP;
+    unsigned int initBits;
+    unsigned int n_bits;                        /* number of bits/code */
+    code_int maxcode;                  /* maximum code, given n_bits */
+    unsigned long curAccum;
+    int curBits;
+} codeBuffer;
+
+
+
+static codeBuffer *
+codeBuffer_create(FILE *       const ofP,
+                  unsigned int const initBits) {
+
+    codeBuffer * codeBufferP;
+
+    MALLOCVAR_NOFAIL(codeBufferP);
+
+    codeBufferP->initBits    = initBits;
+    codeBufferP->n_bits      = codeBufferP->initBits;
+    codeBufferP->maxcode     = MAXCODE(codeBufferP->n_bits);
+    codeBufferP->byteBufferP = byteBuffer_create(ofP);
+    codeBufferP->curAccum    = 0;
+    codeBufferP->curBits     = 0;
+
+    return codeBufferP;
+}
+
+
+
+static void
+codeBuffer_destroy(codeBuffer * const codeBufferP) {
+
+    byteBuffer_destroy(codeBufferP->byteBufferP);
+
+    free(codeBufferP);
+}
+
+
+
+static void
+codeBuffer_output(codeBuffer * const codeBufferP,
+                  code_int     const code) {
+/*----------------------------------------------------------------------------
+   Output one GIF code to the file, through the code buffer.
+
+   The code is represented as n_bits bits in the file -- the lower
+   n_bits bits of 'code'.
+
+   If the code is EOF, flush the code buffer to the file.
+
+   In some cases, change n_bits and recalculate maxcode to go with it.
+-----------------------------------------------------------------------------*/
+    /*
+      Algorithm:
+      Maintain a BITS character long buffer (so that 8 codes will
+      fit in it exactly).  Use the VAX insv instruction to insert each
+      code in turn.  When the buffer fills up empty it and start over.
+    */
+    
+    codeBufferP->curAccum &= masks[codeBufferP->curBits];
+
+    if (codeBufferP->curBits > 0)
+        codeBufferP->curAccum |= ((long)code << codeBufferP->curBits);
+    else
+        codeBufferP->curAccum = code;
+
+    codeBufferP->curBits += codeBufferP->n_bits;
+
+    while (codeBufferP->curBits >= 8) {
+        byteBuffer_out(codeBufferP->byteBufferP,
+                       codeBufferP->curAccum & 0xff);
+        codeBufferP->curAccum >>= 8;
+        codeBufferP->curBits -= 8;
+    }
+
+    if (clear_flg) {
+        codeBufferP->n_bits = codeBufferP->initBits;
+        codeBufferP->maxcode = MAXCODE(codeBufferP->n_bits);
+        clear_flg = 0;
+    } else if (free_ent > codeBufferP->maxcode) {
+        /* The next entry is going to be too big for the code size, so
+           increase it, if possible.
+        */
+        ++codeBufferP->n_bits;
+        if (codeBufferP->n_bits == BITS)
+            codeBufferP->maxcode = maxmaxcode;
+        else
+            codeBufferP->maxcode = MAXCODE(codeBufferP->n_bits);
+    }
+    
+    if (code == EOFCode) {
+        /* We're at EOF.  Output the possible partial byte in the buffer */
+        if (codeBufferP->curBits > 0) {
+            byteBuffer_out(codeBufferP->byteBufferP,
+                           codeBufferP->curAccum & 0xff);
+            codeBufferP->curBits = 0;
+        }
+        byteBuffer_flush(codeBufferP->byteBufferP);
+        
+        byteBuffer_flushFile(codeBufferP->byteBufferP);
+    }
+}
+
+
+
+static void
+cl_hash(long const hsize) {
+    /* reset code table */
+
+    long const m1 = -1;
+
+    long * htab_p;
+    long i;
+
+    htab_p = htab + hsize;  /* initial value */
+
+    i = hsize - 16;
+    do {                            /* might use Sys V memset(3) here */
+        *(htab_p-16) = m1;
+        *(htab_p-15) = m1;
+        *(htab_p-14) = m1;
+        *(htab_p-13) = m1;
+        *(htab_p-12) = m1;
+        *(htab_p-11) = m1;
+        *(htab_p-10) = m1;
+        *(htab_p-9) = m1;
+        *(htab_p-8) = m1;
+        *(htab_p-7) = m1;
+        *(htab_p-6) = m1;
+        *(htab_p-5) = m1;
+        *(htab_p-4) = m1;
+        *(htab_p-3) = m1;
+        *(htab_p-2) = m1;
+        *(htab_p-1) = m1;
+        htab_p -= 16;
+    } while ((i -= 16) >= 0);
+
+    for (i += 16; i > 0; --i)
+        *--htab_p = m1;
+}
+
+
+
+static void
+cl_block(codeBuffer * const codeBufferP) {
+/*----------------------------------------------------------------------------
+  Clear out the hash table
+-----------------------------------------------------------------------------*/
+    cl_hash(HSIZE);
+    free_ent = ClearCode + 2;
+    clear_flg = 1;
+    
+    codeBuffer_output(codeBufferP, (code_int)ClearCode);
+}
+
+
+
+static void
+writeRasterLzw(pixelReader * const pixelReaderP,
+               pixval        const inputMaxval,
+               gray **       const alpha,
+               gray          const alphaMaxval, 
+               struct cmap * const cmapP, 
+               int           const initBits,
+               FILE *        const ofP) {
+/*----------------------------------------------------------------------------
+   Write the raster to file 'ofP'.
+
+   The raster to write is 'pixels', which has maxval 'inputMaxval',
+   modified by alpha mask 'alpha', which has maxval 'alphaMaxval'.
+
+   Use the colormap 'cmapP' to generate the raster ('pixels' is 
+   composed of RGB samples; the GIF raster is colormap indices).
+
+   Write the raster using LZW compression.
+-----------------------------------------------------------------------------*/
+    gray const alpha_threshold = (alphaMaxval + 1) / 2;
+        /* gray levels below this in the alpha mask indicate transparent
+           pixels in the output image.
+        */
+    code_int ent;
+    code_int disp;
+    int hshift;
+    bool eof;
+    codeBuffer * codeBufferP;
+    unsigned int colorIndex;
+    
+    codeBufferP = codeBuffer_create(ofP, initBits);
+    
+    /*
+     * Set up the necessary values
+     */
+    offset = 0;
+    out_count = 0;
+    clear_flg = 0;
+    in_count = 1;
+
+    ClearCode = (1 << (initBits - 1));
+    EOFCode = ClearCode + 1;
+    free_ent = ClearCode + 2;
+
+    gifNextPixel(pixelReaderP, inputMaxval, alpha, alpha_threshold, cmapP,
+                 &colorIndex, &eof);
+    ent = colorIndex;
+
+    {
+        long fcode;
+        hshift = 0;
+        for (fcode = HSIZE; fcode < 65536L; fcode *= 2L)
+            ++hshift;
+        hshift = 8 - hshift;                /* set hash code range bound */
+    }
+    cl_hash(HSIZE);            /* clear hash table */
+
+    codeBuffer_output(codeBufferP, (code_int)ClearCode);
+
+    while (!eof) {
+        unsigned int gifpixel;
+            /* The value for the pixel in the GIF image.  I.e. the colormap
+               index.
+            */
+        gifNextPixel(pixelReaderP, inputMaxval, alpha, alpha_threshold, cmapP,
+                     &gifpixel, &eof);
+        if (!eof) {
+            long const fcode = (long) (((long) gifpixel << BITS) + ent);
+            code_int i;
+                /* xor hashing */
+
+            ++in_count;
+
+            i = (((code_int)gifpixel << hshift) ^ ent);    
+
+            if (HashTabOf (i) == fcode) {
+                ent = CodeTabOf (i);
+                continue;
+            } else if ((long)HashTabOf(i) < 0)      /* empty slot */
+                goto nomatch;
+            disp = HSIZE - i;        /* secondary hash (after G. Knott) */
+            if (i == 0)
+                disp = 1;
+        probe:
+            if ((i -= disp) < 0)
+                i += HSIZE;
+
+            if (HashTabOf(i) == fcode) {
+                ent = CodeTabOf(i);
+                continue;
+            }
+            if ((long)HashTabOf(i) > 0)
+                goto probe;
+        nomatch:
+            codeBuffer_output(codeBufferP, (code_int)ent);
+            ++out_count;
+            ent = gifpixel;
+            if (free_ent < maxmaxcode) {
+                CodeTabOf(i) = free_ent++; /* code -> hashtable */
+                HashTabOf(i) = fcode;
+            } else
+                cl_block(codeBufferP);
+        }
+    }
+    /* Put out the final code. */
+    codeBuffer_output(codeBufferP, (code_int)ent);
+    ++out_count;
+    codeBuffer_output(codeBufferP, (code_int) EOFCode);
+
+    codeBuffer_destroy(codeBufferP);
+}
+
+
+
+/* Routine to convert variable-width codes into a byte stream */
+
+static void
+outputUncompressed(struct gif_dest * const dinfoP,
+                   int               const code) {
+
+    /* Emit a code of n_bits bits */
+    /* Uses cur_accum and cur_bits to reblock into 8-bit bytes */
+    dinfoP->cur_accum |= ((int) code) << dinfoP->cur_bits;
+    dinfoP->cur_bits += dinfoP->n_bits;
+
+    while (dinfoP->cur_bits >= 8) {
+        byteBuffer_out(dinfoP->byteBufferP, dinfoP->cur_accum & 0xFF);
+        dinfoP->cur_accum >>= 8;
+        dinfoP->cur_bits -= 8;
+    }
+}
+
+
+static void
+writeRasterUncompressedInit(FILE *            const ofP,
+                            struct gif_dest * const dinfoP, 
+                            int               const i_bits) {
+/*----------------------------------------------------------------------------
+   Initialize pseudo-compressor
+-----------------------------------------------------------------------------*/
+
+    /* init all the state variables */
+    dinfoP->n_bits = i_bits;
+    dinfoP->maxcode = MAXCODE(dinfoP->n_bits);
+    dinfoP->ClearCode = (1 << (i_bits - 1));
+    dinfoP->EOFCode = dinfoP->ClearCode + 1;
+    dinfoP->code_counter = dinfoP->ClearCode + 2;
+    /* init output buffering vars */
+    dinfoP->byteBufferP = byteBuffer_create(ofP);
+    dinfoP->cur_accum = 0;
+    dinfoP->cur_bits = 0;
+    /* GIF specifies an initial Clear code */
+    outputUncompressed(dinfoP, dinfoP->ClearCode);
+}
+
+
+
+static void
+writeRasterUncompressedPixel(struct gif_dest * const dinfoP, 
+                             unsigned int      const colormapIndex) {
+/*----------------------------------------------------------------------------
+   "Compress" one pixel value and output it as a symbol.
+
+   'colormapIndex' must be less than dinfoP->n_bits wide.
+-----------------------------------------------------------------------------*/
+    assert(colormapIndex >> dinfoP->n_bits == 0);
+
+    outputUncompressed(dinfoP, colormapIndex);
+    /* Issue Clear codes often enough to keep the reader from ratcheting up
+     * its symbol size.
+     */
+    if (dinfoP->code_counter < dinfoP->maxcode) {
+        ++dinfoP->code_counter;
+    } else {
+        outputUncompressed(dinfoP, dinfoP->ClearCode);
+        dinfoP->code_counter = dinfoP->ClearCode + 2;	/* reset the counter */
+    }
+}
+
+
+
+static void
+writeRasterUncompressedTerm(struct gif_dest * const dinfoP) {
+
+    outputUncompressed(dinfoP, dinfoP->EOFCode);
+
+    if (dinfoP->cur_bits > 0)
+        byteBuffer_out(dinfoP->byteBufferP, dinfoP->cur_accum & 0xFF);
+
+    byteBuffer_flush(dinfoP->byteBufferP);
+
+    byteBuffer_destroy(dinfoP->byteBufferP);
+}
+
+
+
+static void
+writeRasterUncompressed(pixelReader *  const pixelReaderP,
+                        pixval         const inputMaxval,
+                        gray **        const alpha,
+                        gray           const alphaMaxval, 
+                        struct cmap *  const cmapP, 
+                        int            const initBits,
+                        FILE *         const ofP) {
+/*----------------------------------------------------------------------------
+   Write the raster to file 'ofP'.
+   
+   Same as writeRasterLzw(), except written out one code per
+   pixel (plus some clear codes), so no compression.  And no use
+   of the LZW patent.
+-----------------------------------------------------------------------------*/
+    gray const alphaThreshold = (alphaMaxval + 1) / 2;
+        /* gray levels below this in the alpha mask indicate transparent
+           pixels in the output image.
+        */
+    bool eof;
+    struct gif_dest gifDest;
+
+    writeRasterUncompressedInit(ofP, &gifDest, initBits);
+
+    eof = FALSE;
+    while (!eof) {
+        unsigned int gifpixel;
+            /* The value for the pixel in the GIF image.  I.e. the colormap
+               index.
+            */
+        gifNextPixel(pixelReaderP, inputMaxval, alpha, alphaThreshold, cmapP,
+                     &gifpixel, &eof);
+        if (!eof)
+            writeRasterUncompressedPixel(&gifDest, gifpixel);
+    }
+    writeRasterUncompressedTerm(&gifDest);
+}
+
+
+
+/******************************************************************************
+ *
+ * GIF Specific routines
+ *
+ *****************************************************************************/
+
+static void
+writeGifHeader(FILE * const fp,
+               int const Width, int const Height, 
+               int const GInterlace, int const Background, 
+               int const BitsPerPixel, struct cmap * const cmapP,
+               const char comment[]) {
+
+    int B;
+    int const Resolution = BitsPerPixel;
+    int const ColorMapSize = 1 << BitsPerPixel;
+
+    /* Write the Magic header */
+    if (cmapP->transparent != -1 || comment)
+        fwrite("GIF89a", 1, 6, fp);
+    else
+        fwrite("GIF87a", 1, 6, fp);
+
+    /* Write out the screen width and height */
+    Putword( Width, fp );
+    Putword( Height, fp );
+
+    /* Indicate that there is a global color map */
+    B = 0x80;       /* Yes, there is a color map */
+
+    /* OR in the resolution */
+    B |= (Resolution - 1) << 4;
+
+    /* OR in the Bits per Pixel */
+    B |= (BitsPerPixel - 1);
+
+    /* Write it out */
+    fputc( B, fp );
+
+    /* Write out the Background color */
+    fputc( Background, fp );
+
+    /* Byte of 0's (future expansion) */
+    fputc( 0, fp );
+
+    {
+        /* Write out the Global Color Map */
+        /* Note that the Global Color Map is always a power of two colors
+           in size, but *cmapP could be smaller than that.  So we pad with
+           black.
+        */
+        int i;
+        for ( i=0; i < ColorMapSize; ++i ) {
+            if ( i < cmapP->cmapsize ) {
+                fputc( cmapP->red[i], fp );
+                fputc( cmapP->green[i], fp );
+                fputc( cmapP->blue[i], fp );
+            } else {
+                fputc( 0, fp );
+                fputc( 0, fp );
+                fputc( 0, fp );
+            }
+        }
+    }
+        
+    if ( cmapP->transparent >= 0 ) 
+        write_transparent_color_index_extension(fp, cmapP->transparent);
+
+    if ( comment )
+        write_comment_extension(fp, comment);
+}
+
+
+
+static void
+writeImageHeader(FILE *       const ofP,
+                 unsigned int const leftOffset,
+                 unsigned int const topOffset,
+                 unsigned int const gWidth,
+                 unsigned int const gHeight,
+                 unsigned int const gInterlace,
+                 unsigned int const initCodeSize) {
+
+    Putword(leftOffset, ofP);
+    Putword(topOffset,  ofP);
+    Putword(gWidth,     ofP);
+    Putword(gHeight,    ofP);
+
+    /* Write out whether or not the image is interlaced */
+    if (gInterlace)
+        fputc(0x40, ofP);
+    else
+        fputc(0x00, ofP);
+
+    /* Write out the initial code size */
+    fputc(initCodeSize, ofP);
+}
+
+
+
+static void
+gifEncode(FILE *        const ofP, 
+          FILE *        const ifP,
+          int           const gWidth,
+          int           const gHeight, 
+          pixval        const inputMaxval,
+          int           const inputFormat,
+          pm_filepos    const rasterPos,
+          gray **       const alpha,
+          gray          const alphaMaxval,
+          int           const gInterlace,
+          int           const background, 
+          int           const bitsPerPixel,
+          struct cmap * const cmapP,
+          char          const comment[],
+          bool          const nolzw) {
+
+    unsigned int const leftOffset = 0;
+    unsigned int const topOffset  = 0;
+
+    unsigned int const initCodeSize = bitsPerPixel <= 1 ? 2 : bitsPerPixel;
+        /* The initial code size */
+
+    pixelReader * pixelReaderP;
+
+    writeGifHeader(ofP, gWidth, gHeight, gInterlace, background,
+                   bitsPerPixel, cmapP, comment);
+
+    /* Write an Image separator */
+    fputc(',', ofP);
+
+    writeImageHeader(ofP, leftOffset, topOffset, gWidth, gHeight, gInterlace,
+                     initCodeSize);
+
+    pixelReaderCreate(ifP, gWidth, gHeight, inputMaxval, inputFormat,
+                      rasterPos, gInterlace, &pixelReaderP);
+
+    /* Write the actual raster */
+    if (nolzw)
+        writeRasterUncompressed(pixelReaderP,
+                                inputMaxval, alpha, alphaMaxval, cmapP, 
+                                initCodeSize + 1, ofP);
+    else
+        writeRasterLzw(pixelReaderP, 
+                       inputMaxval, alpha, alphaMaxval, cmapP, 
+                       initCodeSize + 1, ofP);
+
+    pixelReaderDestroy(pixelReaderP);
+
+    /* Write out a zero length data block (to end the series) */
+    fputc(0, ofP);
+
+    /* Write the GIF file terminator */
+    fputc(';', ofP);
+}
+
+
+
+static int
+compute_transparent(const char colorarg[], 
+                    struct cmap * const cmapP) {
+/*----------------------------------------------------------------------------
+   Figure out the color index (index into the colormap) of the color
+   that is to be transparent in the GIF.
+
+   colorarg[] is the string that specifies the color the user wants to
+   be transparent (e.g. "red", "#fefefe").  Its maxval is the maxval
+   of the colormap.  'cmap' is the full colormap except that its
+   'transparent' component isn't valid.
+
+   colorarg[] is a standard Netpbm color specification, except that
+   may have a "=" prefix, which means it specifies a particular exact
+   color, as opposed to without the "=", which means "the color that
+   is closest to this and actually in the image."
+
+   Return -1 if colorarg[] specifies an exact color and that color is not
+   in the image.  Also issue an informational message.
+-----------------------------------------------------------------------------*/
+    int retval;
+
+    const char *colorspec;
+    bool exact;
+    int presort_colorindex;
+    pixel transcolor;
+
+    if (colorarg[0] == '=') {
+        colorspec = &colorarg[1];
+        exact = TRUE;
+    } else {
+        colorspec = colorarg;
+        exact = FALSE;
+    }
+        
+    transcolor = ppm_parsecolor((char*)colorspec, cmapP->maxval);
+    presort_colorindex = ppm_lookupcolor(cmapP->cht, &transcolor);
+    
+    if (presort_colorindex != -1)
+        retval = cmapP->perm[presort_colorindex];
+    else if (!exact)
+        retval = cmapP->perm[closestcolor(transcolor, cmapP->maxval, cmapP)];
+    else {
+        retval = -1;
+        pm_message(
+            "Warning: specified transparent color does not occur in image.");
+    }
+    return retval;
+}
+
+
+
+static void
+sort_colormap(int const sort, struct cmap * const cmapP) {
+/*----------------------------------------------------------------------------
+   Sort (in place) the colormap *cmapP.
+
+   Create the perm[] and permi[] mappings for the colormap.
+
+   'sort' is logical:  true means to sort the colormap by red intensity,
+   then by green intensity, then by blue intensity.  False means a null
+   sort -- leave it in the same order in which we found it.
+-----------------------------------------------------------------------------*/
+    int * const Red = cmapP->red;
+    int * const Blue = cmapP->blue;
+    int * const Green = cmapP->green;
+    int * const perm = cmapP->perm;
+    int * const permi = cmapP->permi;
+    unsigned int const cmapsize = cmapP->cmapsize;
+    
+    int i;
+
+    for (i=0; i < cmapsize; i++)
+        permi[i] = i;
+
+    if (sort) {
+        pm_message("sorting colormap");
+        for (i=0; i < cmapsize; i++) {
+            int j;
+            for (j=i+1; j < cmapsize; j++)
+                if (((Red[i]*MAXCMAPSIZE)+Green[i])*MAXCMAPSIZE+Blue[i] >
+                    ((Red[j]*MAXCMAPSIZE)+Green[j])*MAXCMAPSIZE+Blue[j]) {
+                    int tmp;
+                    
+                    tmp=permi[i]; permi[i]=permi[j]; permi[j]=tmp;
+                    tmp=Red[i]; Red[i]=Red[j]; Red[j]=tmp;
+                    tmp=Green[i]; Green[i]=Green[j]; Green[j]=tmp;
+                    tmp=Blue[i]; Blue[i]=Blue[j]; Blue[j]=tmp; } }
+    }
+
+    for (i=0; i < cmapsize; i++)
+        perm[permi[i]] = i;
+}
+
+
+
+static void
+normalize_to_255(colorhist_vector const chv, struct cmap * const cmapP) {
+/*----------------------------------------------------------------------------
+   With a PPM color histogram vector 'chv' as input, produce a colormap
+   of integers 0-255 as output in *cmapP.
+-----------------------------------------------------------------------------*/
+    int i;
+    pixval const maxval = cmapP->maxval;
+
+    if ( maxval != 255 )
+        pm_message(
+            "maxval is not 255 - automatically rescaling colors" );
+
+    for ( i = 0; i < cmapP->cmapsize; ++i ) {
+        if ( maxval == 255 ) {
+            cmapP->red[i] = (int) PPM_GETR( chv[i].color );
+            cmapP->green[i] = (int) PPM_GETG( chv[i].color );
+            cmapP->blue[i] = (int) PPM_GETB( chv[i].color );
+        } else {
+            cmapP->red[i] = (int) PPM_GETR( chv[i].color ) * 255 / maxval;
+            cmapP->green[i] = (int) PPM_GETG( chv[i].color ) * 255 / maxval;
+            cmapP->blue[i] = (int) PPM_GETB( chv[i].color ) * 255 / maxval;
+        }
+    }
+}
+
+
+
+static void add_to_colormap(struct cmap * const cmapP, 
+                            const char *  const colorspec, 
+                            int *         const new_indexP) {
+/*----------------------------------------------------------------------------
+  Add a new entry to the colormap.  Make the color that specified by
+  'colorspec', and return the index of the new entry as *new_indexP.
+
+  'colorspec' is a color specification given by the user, e.g.
+  "red" or "rgb:ff/03.0d".  The maxval for this color specification is
+  that for the colormap *cmapP.
+-----------------------------------------------------------------------------*/
+    pixel const transcolor = ppm_parsecolor((char*)colorspec, cmapP->maxval);
+    
+    *new_indexP = cmapP->cmapsize++; 
+
+    cmapP->red[*new_indexP] = PPM_GETR(transcolor);
+    cmapP->green[*new_indexP] = PPM_GETG(transcolor); 
+    cmapP->blue[*new_indexP] = PPM_GETB(transcolor); 
+}
+
+
+
+static void
+colormapFromFile(char               const filespec[],
+                 unsigned int       const maxcolors,
+                 colorhist_vector * const chvP, 
+                 pixval *           const maxvalP,
+                 unsigned int *     const colorsP) {
+/*----------------------------------------------------------------------------
+   Read a colormap from the PPM file filespec[].  Return the color histogram
+   vector (which is practically a colormap) of the input image as *cvhP
+   and the maxval for that histogram as *maxvalP.
+-----------------------------------------------------------------------------*/
+    FILE * mapfileP;
+    int cols, rows;
+    pixel ** colormapPpm;
+    int colors;
+
+    mapfileP = pm_openr(filespec);
+    colormapPpm = ppm_readppm(mapfileP, &cols, &rows, maxvalP);
+    pm_close(mapfileP);
+    
+    pm_message("computing other colormap ...");
+    *chvP = ppm_computecolorhist(colormapPpm, cols, rows, maxcolors, &colors);
+
+    *colorsP = colors;
+
+    ppm_freearray(colormapPpm, rows); 
+}
+
+
+
+static void
+get_alpha(const char * const alpha_filespec, int const cols, int const rows,
+          gray *** const alphaP, gray * const maxvalP) {
+
+    if (alpha_filespec) {
+        int alpha_cols, alpha_rows;
+        *alphaP = pgm_readpgm(pm_openr(alpha_filespec),
+                              &alpha_cols, &alpha_rows, maxvalP);
+        if (alpha_cols != cols || alpha_rows != rows)
+            pm_error("alpha mask is not the same dimensions as the "
+                     "input file (alpha is %dW x %dH; image is %dW x %dH)",
+                     alpha_cols, alpha_rows, cols, rows);
+    } else 
+        *alphaP = NULL;
+}
+
+
+
+static void
+computePpmColormap(FILE *             const ifP,
+                   unsigned int       const cols,
+                   unsigned int       const rows,
+                   pixval             const maxval,
+                   int                const format,
+                   bool               const haveAlpha, 
+                   const char *       const mapfile,
+                   colorhist_vector * const chvP,
+                   colorhash_table *  const chtP,
+                   pixval *           const colormapMaxvalP, 
+                   unsigned int *     const colorsP) {
+/*----------------------------------------------------------------------------
+   Compute a colormap, PPM style, for the image on file 'ifP', which
+   is positioned to the raster and is 'cols' by 'rows' with maxval
+   'maxval' and format 'format'.  If 'mapfile' is non-null, Use the
+   colors in that (PPM) file for the color map instead of the colors
+   in 'ifP'.
+
+   Return the colormap as *chvP and *chtP.  Return the maxval for that
+   colormap as *colormapMaxvalP.
+
+   While we're at it, count the colors and validate that there aren't
+   too many.  Return the count as *colorsP.  In determining if there are
+   too many, allow one slot for a fake transparency color if 'have_alpha'
+   is true.  If there are too many, issue an error message and abort the
+   program.
+-----------------------------------------------------------------------------*/
+    unsigned int maxcolors;
+        /* The most colors we can tolerate in the image.  If we have
+           our own made-up entry in the colormap for transparency, it
+           isn't included in this count.
+        */
+
+    if (haveAlpha)
+        maxcolors = MAXCMAPSIZE - 1;
+    else
+        maxcolors = MAXCMAPSIZE;
+
+    if (mapfile) {
+        /* Read the colormap from a separate colormap file. */
+        colormapFromFile(mapfile, maxcolors, chvP, colormapMaxvalP, 
+                         colorsP);
+    } else {
+        /* Figure out the color map from the input file */
+        int colors;
+        pm_message("computing colormap...");
+        *chvP = ppm_computecolorhist2(ifP, cols, rows, maxval, format,
+                                      maxcolors, &colors); 
+        *colorsP = colors;
+        *colormapMaxvalP = maxval;
+    }
+
+    if (*chvP == NULL)
+        pm_error("too many colors - try doing a 'pnmquant %d'", maxcolors);
+    pm_message("%d colors found", *colorsP);
+
+    /* And make a hash table for fast lookup. */
+    *chtP = ppm_colorhisttocolorhash(*chvP, *colorsP);
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    int rows, cols;
+    pixval inputMaxval;
+    int inputFormat;   
+    int BitsPerPixel;
+    gray ** alpha;     /* The supplied alpha mask; NULL if none */
+    gray alpha_maxval; /* Maxval for 'alpha' */
+    pm_filepos rasterPos;
+
+    struct cmap cmap;
+        /* The colormap, with all its accessories */
+    colorhist_vector chv;
+    int fake_transparent;
+        /* colormap index of the fake transparency color we're using to
+           implement the alpha mask.  Undefined if we're not doing an alpha
+           mask.
+        */
+
+    ppm_init( &argc, argv );
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    verbose = cmdline.verbose;
+
+    ifP = pm_openr_seekable(cmdline.input_filespec);
+
+    ppm_readppminit(ifP, &cols, &rows, &inputMaxval, &inputFormat);
+
+    pm_tell2(ifP, &rasterPos, sizeof(rasterPos));
+
+    get_alpha(cmdline.alpha_filespec, cols, rows, &alpha, &alpha_maxval);
+
+    computePpmColormap(ifP, cols, rows, inputMaxval, inputFormat,
+                       (alpha != NULL), cmdline.mapfile, 
+                       &chv, &cmap.cht, &cmap.maxval, &cmap.cmapsize);
+
+    /* Now turn the ppm colormap into the appropriate GIF colormap. */
+
+    normalize_to_255(chv, &cmap);
+
+    ppm_freecolorhist(chv);
+
+    if (alpha) {
+        /* Add a fake entry to the end of the colormap for transparency.  
+           Make its color black. 
+        */
+        add_to_colormap(&cmap, cmdline.alphacolor, &fake_transparent);
+    }
+    sort_colormap(cmdline.sort, &cmap);
+
+    BitsPerPixel = pm_maxvaltobits(cmap.cmapsize-1);
+
+    if (alpha) {
+        cmap.transparent = cmap.perm[fake_transparent];
+    } else {
+        if (cmdline.transparent)
+            cmap.transparent = 
+                compute_transparent(cmdline.transparent, &cmap);
+        else 
+            cmap.transparent = -1;
+    }
+
+    /* All set, let's do it. */
+    gifEncode(stdout, ifP, cols, rows, inputMaxval, inputFormat, rasterPos,
+              alpha, alpha_maxval, 
+              cmdline.interlace, 0, BitsPerPixel, &cmap, cmdline.comment,
+              cmdline.nolzw);
+
+    if (alpha)
+        pgm_freearray(alpha, rows);
+
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
+}
diff --git a/converter/ppm/ppmtoicr.c b/converter/ppm/ppmtoicr.c
new file mode 100644
index 00000000..feca0c18
--- /dev/null
+++ b/converter/ppm/ppmtoicr.c
@@ -0,0 +1,320 @@
+/* ppmtoicr.c - convert a portable pixmap to NCSA ICR protocol
+**
+** Copyright (C) 1990 by Kanthan Pillay (svpillay@Princeton.EDU)
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "ppm.h"
+
+#define MAXCOLORS 256
+#define CLUTCOLORS 768
+
+static int colorstobpp ARGS(( int colors ));
+static int GetPixel ARGS(( int x, int y ));
+static int rleit ARGS(( char* buf, char* bufto, int len ));
+
+static pixel** pixels;
+static colorhash_table cht;
+static char* testimage;
+
+int
+main(argc, argv)
+int argc;
+char* argv[];
+{
+	FILE* ifp;
+	int argn, rows, cols, colors, i, j, BitsPerPixel, newxsize;
+	pixval maxval;
+	colorhist_vector chv;
+	char rgb[CLUTCOLORS];
+	const char* windowname;
+	char* thischar;
+	char* thisline;
+	char* space;
+	register unsigned char c;
+	register char* p;
+	int display, expand;
+	int rleflag, winflag;
+	const char* const usage = "[-windowname windowname] [-expand expand] [-display display] [-rle] [ppmfile]";
+
+
+	ppm_init( &argc, argv );
+
+	argn = 1;
+	windowname = "untitled";
+	winflag = 0;
+	expand = 1;
+	display = 0;
+	rleflag = 0;
+
+	while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
+	    {
+	    if ( pm_keymatch(argv[argn],"-windowname",2) && argn + 1 < argc )
+		{
+		++argn;
+		windowname = argv[argn];
+		winflag = 1;
+		}
+	    else if ( pm_keymatch(argv[argn],"-expand",2) && argn + 1 < argc )
+		{
+		++argn;
+		if ( sscanf( argv[argn], "%d",&expand ) != 1 )
+		    pm_usage( usage );
+		}
+	    else if ( pm_keymatch(argv[argn],"-display",2) && argn + 1 < argc )
+		{
+		++argn;
+		if ( sscanf( argv[argn], "%d",&display ) != 1 )
+		    pm_usage( usage );
+		}
+	    else if ( pm_keymatch(argv[argn],"-rle",2) )
+		rleflag = 1;
+	    else if ( pm_keymatch(argv[argn],"-norle",2) )
+		rleflag = 0;
+	    else
+		pm_usage( usage );
+	    }
+
+	if ( argn < argc )
+	    {
+	    ifp = pm_openr( argv[argn] );
+	    if ( ! winflag )
+		windowname = argv[argn];
+	    ++argn;
+	    }
+	else
+	    ifp = stdin;
+
+	if ( argn != argc )
+	    pm_usage( usage );
+
+	pixels = ppm_readppm( ifp, &cols, &rows, &maxval );
+
+	pm_close( ifp );
+
+	for (i = 0; i < CLUTCOLORS; i++)
+	    rgb[i] = 0;
+
+	/* Figure out the colormap. */
+	pm_message("computing colormap..." );
+	chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors);
+	if (chv == (colorhist_vector) 0)
+	pm_error( "too many colors - try doing a 'pnmquant %d'", MAXCOLORS );
+	pm_message("%d colors found", colors );
+
+	/* Turn the ppm colormap into an ICR colormap. */
+	if (maxval > 255)
+	pm_message(
+		"maxval is not 255 - automatically rescaling colors" );
+	for (i = 0; i < colors; i++)
+	{
+	j = (3 * i);
+	if (maxval == 255)
+		{
+		rgb[j] = PPM_GETR(chv[i].color) ;
+		j++;
+		rgb[j] = PPM_GETG(chv[i].color) ;
+		j++;
+		rgb[j] = PPM_GETB(chv[i].color) ;
+		}
+	else
+		{
+		rgb[j] = (int) PPM_GETR(chv[i].color) * 255 / maxval;
+		j++;
+		rgb[j] = (int) PPM_GETG(chv[i].color) * 255 / maxval;
+		j++;
+		rgb[j] = (int) PPM_GETB(chv[i].color) * 255 / maxval;
+		}
+	}
+	BitsPerPixel = colorstobpp(colors);
+
+	/* And make a hash table for fast lookup. */
+	cht = ppm_colorhisttocolorhash(chv, colors);
+	ppm_freecolorhist(chv);
+
+
+	/************** Create a new window using ICR protocol *********/
+	/* Format is "ESC^W;left;top;width;height;display;windowname"  */
+
+	pm_message("creating window %s ...", windowname );
+	(void)printf("\033^W;%d;%d;%d;%d;%d;%s^",0,0,cols*expand,rows*expand,display,windowname);
+	fflush(stdout);
+
+
+	/****************** Download the colormap.  ********************/
+	pm_message("downloading colormap for %s ...", windowname );
+
+	(void)printf("\033^M;%d;%d;%d;%s^",0,MAXCOLORS,CLUTCOLORS,windowname);
+	thischar = rgb;
+	for (j=0; j<CLUTCOLORS; j++) {
+	c = *thischar++;
+		if (c > 31 && c < 123 ) {	 /* printable ASCII */
+		putchar(c);
+		}
+		else {
+		putchar((c>>6)+123);	 /* non-printable, so encode it */
+		putchar((c & 0x3f) + 32);
+		}
+	}
+	fflush(stdout);
+
+	/**************** send out picture *************************/
+	/* Protocol's RLE scheme is quicker but buggy              */
+
+	if (rleflag) {	
+		pm_message("sending run-length encoded picture data ..." );
+		testimage = (char*) malloc(rows*cols);
+		p = testimage;
+		for (i=0; i<rows; i++)
+			for (j=0; j<cols; j++) 
+			*p++ = GetPixel(j,i);
+		space = (char*) malloc(rows*3);
+		thisline = testimage;
+		for (i = 0; i < rows; i++) {
+			newxsize = rleit(thisline,space,cols);
+			thisline += cols;	/* increment to next line */
+		(void)printf("\033^R;%d;%d;%d;%d;%s^",0,i*expand,expand,newxsize,windowname);
+		thischar = space;
+		for (j=0; j< newxsize; j++) {
+			c= *thischar++;  /*get byte to send */
+			if (c>31 && c <123) {
+				putchar(c);
+				}
+			else {
+				putchar((c>>6) + 123);
+				putchar((c & 0x3f) + 32);
+				}
+			}
+			fflush(stdout);
+		}
+		free(space);
+		exit(0);
+		}
+
+	/* Otherwise, send out uncompressed pixel data via the slow method */
+
+		else {
+		pm_message("sending picture data ..." );
+		for (i = 0; i < rows; i++) {
+			(void)printf("\033^P;%d;%d;%d;%d;%s^",0,i*expand,expand,cols,windowname);
+			for (j = 0; j < cols; j++) {
+				c  = GetPixel(j,i);
+				if (c > 31 && c < 123) {
+						putchar(c);
+						}
+				else		{
+						putchar((c>>6)+123);
+						putchar((c & 0x3f) + 32);
+						}
+				}
+			}
+		fflush(stdout);
+		exit(0);
+		}
+	}
+
+static int
+colorstobpp(colors)
+int colors;
+	{
+	int bpp;
+
+	if (colors <= 2)
+	bpp = 1;
+	else if (colors <= 4)
+	bpp = 2;
+	else if (colors <= 8)
+	bpp = 3;
+	else if (colors <= 16)
+	bpp = 4;
+	else if (colors <= 32)
+	bpp = 5;
+	else if (colors <= 64)
+	bpp = 6;
+	else if (colors <= 128)
+	bpp = 7;
+	else if (colors <= 256)
+	bpp = 8;
+	else
+	pm_error("can't happen" );
+	return bpp;
+	}
+
+static int
+GetPixel(x, y)
+int x, y;
+	{
+	int color;
+
+	color = ppm_lookupcolor(cht, &pixels[y][x]);
+	return color;
+	}
+
+
+/* rleit   compress with run length encoding as per NCSA's documentation */
+
+static int
+rleit(buf,bufto,len)
+	char* buf;
+	char* bufto;
+	int len;
+	{
+	register char* p;
+	register char* q;
+	register char* cfoll;
+	register char* clead;
+	char* begp;
+	int i;
+
+	p = buf;
+	cfoll = bufto;
+	clead = cfoll + 1;
+
+	begp = p;
+	while (len > 0 ) {		/* encode until gone */
+		
+		q = p + 1;
+		i = len-1;
+	while (*p == *q && i+120 > len && i) {
+		q++;
+		i--;
+	}
+
+	if (q > p +2) {			/* three in a row */
+		if (p > begp) {
+			*cfoll = p - begp;
+			cfoll = clead;
+		}
+		*cfoll++ = 128 | (q-p);		/*len of seq*/
+		*cfoll++ = *p;			/* char of seq */
+		len -= q-p;		/* subtract len of seq */
+		p = q;
+		clead = cfoll+1;
+		begp = p;
+	}
+	else {
+		*clead++ = *p++;	/* copy one char */
+		len--;
+		if (p>begp + 120) {
+			*cfoll = p - begp;
+			cfoll = clead++;
+			begp = p;
+		}
+	}
+	}
+
+/* fillin last bytecount */
+
+	if (p>begp)
+		*cfoll = (p - begp);
+	else
+		clead--;
+
+	return((int) (clead-bufto));	/*how many stored as encoded */
+}
diff --git a/converter/ppm/ppmtoilbm.c b/converter/ppm/ppmtoilbm.c
new file mode 100644
index 00000000..6c04c9be
--- /dev/null
+++ b/converter/ppm/ppmtoilbm.c
@@ -0,0 +1,2362 @@
+/* ppmtoilbm.c - read a portable pixmap and produce an IFF ILBM file
+**
+** Copyright (C) 1989 by Jef Poskanzer.
+** Modified by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
+**  20/Jun/93:
+**  - 24-bit capability (new options -24if, -24force)
+**  - HAM8 capability (well, anything from HAM3 to HAM(MAXPLANES))
+**  - now writes up to 8 (16) planes (new options -maxplanes, -fixplanes)
+**  - colormap file (new option -map)
+**  - write colormap only (new option -cmaponly)
+**  - only writes CAMG chunk if it is a HAM-picture
+**  29/Aug/93:
+**  - operates row-by-row whenever possible
+**  - faster colorscaling with lookup-table (~20% faster on HAM pictures)
+**  - options -ham8 and -ham6 now imply -hamforce
+**  27/Nov/93:
+**  - byterun1 compression (this is now default) with new options:
+**    -compress, -nocompress, -cmethod, -savemem
+**  - floyd-steinberg error diffusion (for std+mapfile and HAM)
+**  - new options: -lace and -hires --> write CAMG chunk
+**  - LUT for luminance calculation (used by ppm_to_ham)
+**  23/Oct/94:
+**  - rework of mapfile handling
+**  - added RGB8 & RGBN image types
+**  - added maskplane and transparent color capability
+**  - 24-bit & direct color modified to n-bit deep ILBM
+**  - removed "-savemem" option
+**  22/Feb/95:
+**  - minor bugfixes
+**  - fixed "-camg 0" behaviour: now writes a CAMG chunk with value 0
+**  - "-24if" is now default
+**  - "-mmethod" and "-cmethod" options accept numeric args and keywords
+**  - direct color (DCOL) reimplemented
+**  - mapfile useable for HAM
+**  - added HAM colormap "fixed"
+**  29/Mar/95:
+**  - added HAM colormap "rgb4" and "rgb5" (compute with 4/5-bit table)
+**  - added IFF text chunks
+**
+**  TODO:
+**  - multipalette capability (PCHG chunk) for std and HAM
+**
+**
+**           std   HAM  deep  cmap  RGB8  RGBN
+**  -------+-----+-----+-----+-----+-----+-----
+**  BMHD     yes   yes   yes   yes   yes   yes
+**  CMAP     yes   (1)   no    yes   no    no
+**  BODY     yes   yes   yes   no    yes   yes
+**  CAMG     (2)   yes   (2)   no    yes   yes
+**  nPlanes  1-16  3-16  3-48  0     25    13
+**
+**  (1): grayscale colormap
+**  (2): only if "-lace", "-hires" or "-camg" option used
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "ppm.h"
+#include "ppmfloyd.h"
+#include "pbm.h"
+#include "ilbm.h"
+#include "lum.h"
+
+/*#define DEBUG*/
+
+#define MODE_RGB8       6   /* RGB8: 8-bit RGB */
+#define MODE_RGBN       5   /* RGBN: 4-bit RGB */
+#define MODE_CMAP       4   /* ILBM: colormap only */
+#define MODE_DCOL       3   /* ILBM: direct color */
+#define MODE_DEEP       2   /* ILBM: deep (24-bit) */
+#define MODE_HAM        1   /* ILBM: hold-and-modify (HAM) */
+#define MODE_NONE       0   /* ILBM: colormapped */
+
+#define HAMMODE_GRAY    0   /* HAM colormap: grayscale */
+#define HAMMODE_FIXED   1   /* HAM colormap: 7 "rays" in RGB cube */
+#define HAMMODE_MAPFILE 2   /* HAM colormap: loaded from mapfile */
+#define HAMMODE_RGB4    3   /* HAM colormap: compute, 4bit RGB */
+#define HAMMODE_RGB5    4   /* HAM colormap: compute, 5bit RGB */
+
+#define ECS_MAXPLANES   5
+#define ECS_HAMPLANES   6
+#define AGA_MAXPLANES   8
+#define AGA_HAMPLANES   8
+
+#define HAMMAXPLANES    10  /* maximum planes for HAM */
+
+#define DEF_MAXPLANES   ECS_MAXPLANES
+#define DEF_HAMPLANES   ECS_HAMPLANES
+#define DEF_COMPRESSION cmpByteRun1
+#define DEF_DEEPPLANES  8
+#define DEF_DCOLPLANES  5
+#define DEF_IFMODE      MODE_DEEP
+
+static void put_big_short ARGS((short s));
+static void put_big_long ARGS((long l));
+#define put_byte(b)     (void)(putc((unsigned char)(b), stdout))
+static void write_bytes ARGS((unsigned char *buffer, int bytes));
+static void ppm_to_ham ARGS((FILE *fp, int cols, int rows, int maxval, pixel *colormap, int colors, int cmapmaxval, int hamplanes));
+static void ppm_to_deep ARGS((FILE *fp, int cols, int rows, int maxval, int bitspercolor));
+static void ppm_to_dcol ARGS((FILE *fp, int cols, int rows, int maxval, DirectColor *dcol));
+static void ppm_to_rgb8 ARGS((FILE *fp, int cols, int rows, int maxval));
+static void ppm_to_rgbn ARGS((FILE *fp, int cols, int rows, int maxval));
+static void ppm_to_std ARGS((FILE *fp, int cols, int rows, int maxval, pixel *colormap, int colors, int cmapmaxval, int maxcolors, int nPlanes));
+static void ppm_to_cmap ARGS((pixel *colormap, int colors, int maxval));
+static void write_bmhd ARGS((int cols, int rows, int nPlanes));
+static void write_cmap ARGS((pixel *colormap, int colors, int maxval));
+static long encode_row ARGS((FILE *outfile, rawtype *rawrow, int cols, int nPlanes));
+static long encode_maskrow ARGS((FILE *outfile, rawtype *rawrow, int cols));
+static int compress_row ARGS((int bytes));
+static void store_bodyrow ARGS((unsigned char *row, int len));
+static int runbyte1 ARGS((int bytes));
+static pixel * next_pixrow ARGS((FILE *fp, int row));
+static int * make_val_table ARGS((int oldmaxval, int newmaxval));
+static void init_read ARGS((FILE *fp, int *colsP, int *rowsP, pixval *maxvalP, int *formatP, int readall));
+static void write_body_rows ARGS((void));
+static void write_camg ARGS((void));
+static int  length_of_text_chunks ARGS((void));
+static void write_text_chunks ARGS((void));
+#define PAD(n)      (ODD(n) ? 1 : 0)    /* pad to a word */
+
+
+/* global data */
+static unsigned char *coded_rowbuf; /* buffer for uncompressed scanline */
+static unsigned char *compr_rowbuf; /* buffer for compressed scanline */
+static pixel **pixels;  /* PPM image (NULL for row-by-row operation) */
+static pixel *pixrow;   
+    /* current row in PPM image (pointer into pixels array, or buffer
+       for row-by-row operation) 
+    */
+
+static long viewportmodes = 0;
+static int slicesize = 1; 
+    /* rows per slice for multipalette images - NOT USED */
+
+static unsigned char compmethod = DEF_COMPRESSION;   /* default compression */
+static unsigned char maskmethod = mskNone;
+
+static pixel *transpColor = NULL;   /* transparent color */
+static short  transpIndex = -1;     /* index of transparent color */
+
+static short hammapmode = HAMMODE_GRAY;
+static short sortcmap = 0;     /* sort colormap */
+
+static FILE *maskfile = NULL;
+static bit *maskrow = NULL;
+static int maskcols, maskformat;
+#define TOTALPLANES(nplanes) ((nplanes) + ((maskmethod == mskHasMask) ? 1 : 0))
+
+
+#define ROWS_PER_BLOCK  1024
+typedef struct bodyblock {
+    int used;
+    unsigned char *row[ROWS_PER_BLOCK];
+    int            len[ROWS_PER_BLOCK];
+    struct bodyblock *next;
+} bodyblock;
+static bodyblock firstblock = { 0 };
+static bodyblock *cur_block = &firstblock;
+
+static char *anno_chunk, *auth_chunk, *name_chunk, *text_chunk, *copyr_chunk;
+
+/* flags */
+static short compr_force = 0;   
+    /* force compressed output, even if the image got larger  - NOT USED */
+static short floyd = 0;         /* apply floyd-steinberg error diffusion */
+static short gen_camg = 0;      /* write CAMG chunk */
+
+#define WORSTCOMPR(bytes)       ((bytes) + (bytes)/128 + 1)
+#define DO_COMPRESS             (compmethod != cmpNone)
+
+
+/***** parse options and figure out what kind of ILBM to write *****/
+
+static int get_int_val ARGS((char *string, char *option, int bot, int top));
+static int get_compr_method ARGS((char *string));
+static int get_mask_type ARGS((char *string));
+static int get_hammap_mode ARGS((char *string));
+
+
+
+#define NEWDEPTH(pix, table) PPM_ASSIGN((pix), (table)[PPM_GETR(pix)], (table)[PPM_GETG(pix)], (table)[PPM_GETB(pix)])
+
+
+static void
+report_too_many_colors(int         const ifmode,
+                       int         const maxplanes,
+                       int         const hamplanes,
+                       DirectColor const dcol,
+                       int         const deepbits) {
+    
+    int const maxcolors = 1 << maxplanes;
+
+    switch( ifmode ) {
+    case MODE_HAM:
+        pm_message("too many colors for %d planes - "
+                   "proceeding to write a HAM%d file", 
+                   maxplanes, hamplanes);
+        pm_message("if you want a non-HAM file, try doing a 'pnmquant %d'", 
+                   maxcolors);
+        break;
+    case MODE_DCOL:
+        pm_message("too many colors for %d planes - "
+                   "proceeding to write a %d:%d:%d direct color ILBM", 
+                   maxplanes, dcol.r, dcol.g, dcol.b);
+        pm_message("if you want a non-direct color file, "
+                   "try doing a 'pnmquant %d'", maxcolors);
+        break;
+    case MODE_DEEP:
+        pm_message("too many colors for %d planes - "
+                   "proceeding to write a %d-bit \'deep\' ILBM", 
+                   maxplanes, deepbits*3);
+        pm_message("if you want a non-deep file, "
+                   "try doing a 'pnmquant %d'", 
+                   maxcolors);
+        break;
+    default:
+        pm_error("too many colors for %d planes - "
+                 "try doing a 'pnmquant %d'", 
+                 maxplanes, maxcolors);
+        break;
+    }
+}
+
+
+static int
+get_int_val(string, option, bot, top)
+    char *string, *option;
+    int bot, top;
+{
+    int val;
+
+    if( sscanf(string, "%d", &val) != 1 )
+        pm_error("option \"%s\" needs integer argument", option);
+
+    if( val < bot || val > top )
+        pm_error("option \"%s\" argument value out of range (%d..%d)", 
+                 option, bot, top);
+
+    return val;
+}
+
+
+static int
+get_compr_method(string)
+    char *string;
+{
+    int retval;
+    if( pm_keymatch(string, "none", 1) || pm_keymatch(string, "0", 1) )
+        retval = cmpNone;
+    else if( pm_keymatch(string, "byterun1", 1) || 
+             pm_keymatch(string, "1", 1) )
+        retval = cmpByteRun1;
+    else 
+        pm_error("unknown compression method: %s", string);
+    return retval;
+}
+
+
+static int
+get_mask_type(string)
+    char *string;
+{
+    int retval;
+
+    if( pm_keymatch(string, "none", 1) || pm_keymatch(string, "0", 1) )
+        retval = mskNone;
+    else
+    if( pm_keymatch(string, "plane", 1) || 
+        pm_keymatch(string, "maskplane", 1) ||
+        pm_keymatch(string, "1", 1) )
+        retval = mskHasMask;
+    else
+    if( pm_keymatch(string, "transparentcolor", 1) || 
+        pm_keymatch(string, "2", 1) )
+        retval = mskHasTransparentColor;
+    else
+    if( pm_keymatch(string, "lasso", 1) || pm_keymatch(string, "3", 1) )
+        retval = mskLasso;
+    else
+        pm_error("unknown masking method: %s", string);
+    return retval;
+}
+
+
+static int
+get_hammap_mode(string)
+    char *string;
+{
+    int retval;
+
+    if( pm_keymatch(string, "grey", 1) || pm_keymatch(string, "gray", 1) )
+        retval =  HAMMODE_GRAY;
+    else
+    if( pm_keymatch(string, "fixed", 1) )
+        retval =  HAMMODE_FIXED;
+    else
+    if( pm_keymatch(string, "rgb4", 4) )
+        retval = HAMMODE_RGB4;
+    else
+    if( pm_keymatch(string, "rgb5", 4) )
+        retval = HAMMODE_RGB5;
+    else 
+        pm_error("unknown HAM colormap selection mode: %s", string);
+    return retval;
+}
+
+
+/************ colormap file ************/
+
+static void
+ppm_to_cmap(colorrow, colors, maxval)
+    pixel *colorrow;
+    int colors;
+    int maxval;
+{
+    int formsize, cmapsize;
+
+    cmapsize = colors * 3;
+
+    formsize =
+        4 +                                 /* ILBM */
+        4 + 4 + BitMapHeaderSize +          /* BMHD size header */
+        4 + 4 + cmapsize + PAD(cmapsize) +  /* CMAP size colormap */
+        length_of_text_chunks();
+
+    put_big_long(ID_FORM);
+    put_big_long(formsize);
+    put_big_long(ID_ILBM);
+
+    write_bmhd(0, 0, 0);
+    write_text_chunks();
+    write_cmap(colorrow, colors, maxval);
+}
+
+/************ HAM ************/
+
+static long 
+do_ham_body     ARGS((FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval,
+                pixval hammaxval, int nPlanes, pixel *cmap, int colors));
+
+
+static int hcmp (const void *va, const void *vb);
+static pixel *compute_ham_cmap ARGS((int cols, int rows, int maxval, 
+                                     int maxcolors, int *colorsP, int hbits));
+
+
+typedef struct {
+    long count;
+    pixval r, g, b;
+} hentry;
+
+
+static int
+hcmp(const void *va, const void *vb)
+{
+    return(((hentry *)vb)->count - ((hentry *)va)->count);  
+        /* reverse sort, highest count first */
+}
+
+
+static pixel *
+compute_ham_cmap(cols, rows, maxval, maxcolors, colorsP, hbits)
+    int cols, rows, maxval, maxcolors;
+    int *colorsP;
+    int hbits;
+{
+    int colors;
+    hentry *hmap;
+    pixel *cmap;
+    pixval hmaxval;
+    int i, r, g, b, col, row, *htable;
+    unsigned long dist, maxdist;
+
+    pm_message("initializing HAM colormap...");
+
+    colors = 1<<(3*hbits);
+    MALLOCARRAY(hmap, colors);
+    if (hmap == NULL)
+        pm_error("Unable to allocate memory for HAM colormap.");
+    hmaxval = pm_bitstomaxval(hbits);
+
+    i = 0;
+    for( r = 0; r <= hmaxval; r++ ) {
+        for( g = 0; g <= hmaxval; g++ ) {
+            for( b = 0; b <= hmaxval; b++ ) {
+                hmap[i].r = r; hmap[i].g = g; hmap[i].b = b;
+                hmap[i].count = 0;
+                i++;
+            }
+        }
+    }
+
+    htable = make_val_table(maxval, hmaxval);
+    for( row = 0; row < rows; row++ ) {
+        unsigned int col;
+        for( col = 0; col < cols; ++col) {
+            pixel const p = pixels[row][col];
+            pixval const r = PPM_GETR(p);
+            pixval const g = PPM_GETG(p);
+            pixval const b = PPM_GETB(p);
+            i = (htable[r]<<(2*hbits)) + (htable[g]<<hbits) + htable[b];
+            hmap[i].count++;
+        }
+    }
+    free(htable);
+
+    qsort((void *)hmap, colors, sizeof(hentry), hcmp);
+    for( i = colors-1; i >= 0; i-- ) {
+        if( hmap[i].count )
+            break;
+    }
+    colors = i+1;
+
+    if( colors > maxcolors ) {
+        pm_message("selecting HAM colormap from %d colors...", colors);
+        for( maxdist = 1; ; maxdist++ ) {
+            for( col = colors-1; col > 0; col-- ) {
+                r = hmap[col].r; g = hmap[col].g; b = hmap[col].b;
+                for( i = 0; i < col; i++ ) {
+                    register int tmp;
+
+                    tmp = hmap[i].r - r; dist = tmp * tmp;
+                    tmp = hmap[i].g - g; dist += tmp * tmp;
+                    tmp = hmap[i].b - b; dist += tmp * tmp;
+
+                    if( dist <= maxdist ) {
+                        int sum = hmap[i].count + hmap[col].count;
+
+                        hmap[i].r = (hmap[i].r * hmap[i].count + 
+                                     r * hmap[col].count + sum/2)/sum;
+                        hmap[i].g = (hmap[i].g * hmap[i].count + 
+                                     g * hmap[col].count + sum/2)/sum;
+                        hmap[i].b = (hmap[i].b * hmap[i].count + 
+                                     b * hmap[col].count + sum/2)/sum;
+                        hmap[i].count = sum;
+
+                        hmap[col] = hmap[i];    /* temp store */
+                        for( tmp = i-1; 
+                             tmp >= 0 && hmap[tmp].count < hmap[col].count; 
+                             tmp-- )
+                            hmap[tmp+1] = hmap[tmp];
+                        hmap[tmp+1] = hmap[col];
+
+                        for( tmp = col; tmp < colors-1; tmp++ )
+                            hmap[tmp] = hmap[tmp+1];
+                        if( --colors <= maxcolors )
+                            goto out;
+                        break;
+                    }
+                }
+            }
+#ifdef DEBUG
+            pm_message("\tmaxdist=%ld: %d colors left", maxdist, colors);
+#endif
+        }
+    }
+out:
+    pm_message("%d colors in HAM colormap", colors);
+
+    cmap = ppm_allocrow(colors);
+    *colorsP = colors;
+
+    for( i = 0; i < colors; i++ ) {
+        r = hmap[i].r; g = hmap[i].g; b = hmap[i].b;
+        PPM_ASSIGN(cmap[i], r, g, b);
+    }
+
+    ppm_freerow(hmap);
+    return cmap;
+}
+
+
+static void
+ppm_to_ham(fp, cols, rows, maxval, colormap, colors, cmapmaxval, hamplanes)
+    FILE *fp;
+    int cols, rows, maxval;
+    pixel *colormap;
+    int colors, cmapmaxval, hamplanes;
+{
+    int hamcolors, nPlanes, i, hammaxval;
+    long oldsize, bodysize, formsize, cmapsize;
+    int *table = NULL;
+
+    if( maskmethod == mskHasTransparentColor ) {
+        pm_message("masking method '%s' not usable with HAM - "
+                   "using '%s' instead",
+                   mskNAME[mskHasTransparentColor], mskNAME[mskHasMask]);
+        maskmethod = mskHasMask;
+    }
+
+    hamcolors = 1 << (hamplanes-2);
+    hammaxval = pm_bitstomaxval(hamplanes-2);
+
+    if( colors == 0 ) {
+        /* no colormap, make our own */
+        switch( hammapmode ) {
+            case HAMMODE_GRAY:
+                colors = hamcolors;
+                MALLOCARRAY_NOFAIL(colormap, colors);
+#ifdef DEBUG
+                pm_message("generating grayscale colormap");
+#endif
+                table = make_val_table(hammaxval, MAXCOLVAL);
+                for( i = 0; i < colors; i++ )
+                    PPM_ASSIGN(colormap[i], table[i], table[i], table[i]);
+                free(table);
+                cmapmaxval = MAXCOLVAL;
+                break;
+            case HAMMODE_FIXED: {
+                int entries, val;
+                double step;
+
+#ifdef DEBUG
+                pm_message("generating rgb colormap");
+#endif
+                /* generate a colormap of 7 "rays" in an RGB color cube:
+                        r, g, b, r+g, r+b, g+b, r+g+b
+                   we need one colormap entry for black, so the number of
+                   entries per ray is (maxcolors-1)/7 */
+
+                entries = (hamcolors-1)/7;
+                colors = 7*entries+1;
+                MALLOCARRAY_NOFAIL(colormap, colors);
+                step = (double)MAXCOLVAL / (double)entries;
+
+                PPM_ASSIGN(colormap[0], 0, 0, 0);
+                for( i = 1; i <= entries; i++ ) {
+                    val = (int)((double)i * step);
+                    PPM_ASSIGN(colormap[          i], val,   0,   0); /* r */
+                    PPM_ASSIGN(colormap[  entries+i],   0, val,   0); /* g */
+                    PPM_ASSIGN(colormap[2*entries+i],   0,   0, val); /* b */
+                    PPM_ASSIGN(colormap[3*entries+i], val, val,   0); /* r+g */
+                    PPM_ASSIGN(colormap[4*entries+i], val,   0, val); /* r+b */
+                    PPM_ASSIGN(colormap[5*entries+i],   0, val, val); /* g+b */
+                    PPM_ASSIGN(colormap[6*entries+i], val, val, val); /*r+g+b*/
+                }
+                cmapmaxval = MAXCOLVAL;
+            }
+            break;
+            case HAMMODE_RGB4:
+                colormap = compute_ham_cmap(cols, rows, maxval, hamcolors, 
+                                            &colors, 4);
+                cmapmaxval = 15;
+                break;
+            case HAMMODE_RGB5:
+                colormap = compute_ham_cmap(cols, rows, maxval, 
+                                            hamcolors, &colors, 5);
+                cmapmaxval = 31;
+                break;
+            default:
+                pm_error("ppm_to_ham(): unknown hammapmode - can't happen");
+        }
+    }
+    else {
+        hammapmode = HAMMODE_MAPFILE;
+        if( colors > hamcolors ) {
+            pm_message("colormap too large - using first %d colors", 
+                       hamcolors);
+            colors = hamcolors;
+        }
+    }
+
+    if( cmapmaxval != maxval ) {
+        int i, *table;
+        pixel *newcmap;
+
+        newcmap = ppm_allocrow(colors);
+        table = make_val_table(cmapmaxval, maxval);
+        for( i = 0; i < colors; i++ )
+            PPM_ASSIGN(newcmap[i], 
+                       table[PPM_GETR(colormap[i])], 
+                       table[PPM_GETG(colormap[i])], 
+                       table[PPM_GETB(colormap[i])]);
+        free(table);
+        ppm_freerow(colormap);
+        colormap = newcmap;
+    }
+    if( sortcmap )
+        ppm_sortcolorrow(colormap, colors, PPM_STDSORT);
+
+    nPlanes = hamplanes;
+    cmapsize = colors * 3;
+
+    bodysize = oldsize = rows * TOTALPLANES(nPlanes) * RowBytes(cols);
+    if( DO_COMPRESS ) {
+        bodysize = do_ham_body(fp, NULL, cols, rows, maxval, 
+                               hammaxval, nPlanes, colormap, colors);
+        /*bodysize = do_ham_body(fp, NULL, cols, 
+          rows, maxval, hammaxval, nPlanes, colbits, nocolor);*/
+        if( bodysize > oldsize )
+            pm_message("warning - %s compression increases BODY size "
+                       "by %ld%%", 
+                       cmpNAME[compmethod], 100*(bodysize-oldsize)/oldsize);
+        else
+            pm_message("BODY compression (%s): %ld%%", 
+                       cmpNAME[compmethod], 100*(oldsize-bodysize)/oldsize);
+    }
+
+
+    formsize =
+        4 +                                 /* ILBM */
+        4 + 4 + BitMapHeaderSize +          /* BMHD size header */
+        4 + 4 + CAMGChunkSize +             /* CAMG size viewportmodes */
+        4 + 4 + cmapsize + PAD(cmapsize) +  /* CMAP size colormap */
+        4 + 4 + bodysize + PAD(bodysize) +  /* BODY size data */
+        length_of_text_chunks();
+
+    put_big_long(ID_FORM);
+    put_big_long(formsize);
+    put_big_long(ID_ILBM);
+
+    write_bmhd(cols, rows, nPlanes);
+    write_text_chunks();
+    write_camg();       /* HAM requires CAMG chunk */
+    write_cmap(colormap, colors, maxval);
+
+    /* write body */
+    put_big_long(ID_BODY);
+    put_big_long(bodysize);
+    if( DO_COMPRESS )
+        write_body_rows();
+    else
+        do_ham_body(fp, NULL, cols, rows, maxval, hammaxval, 
+                    nPlanes, colormap, colors);
+}
+
+
+static long
+#ifdef __STDC__
+do_ham_body(FILE *ifP, FILE *ofp, int cols, int rows,
+            pixval maxval, pixval hammaxval, int nPlanes,
+            pixel *colormap, int colors)
+#else
+do_ham_body(ifP, ofp, cols, rows, maxval, hammaxval, nPlanes, colormap, colors)
+    FILE *ifP, *ofp;
+    int cols, rows;
+    pixval maxval;      /* maxval of image color components */
+    pixval hammaxval;   /* maxval of HAM color changes */
+    int nPlanes;
+    pixel *colormap;
+    int colors;
+#endif
+{
+    register int col, row, i;
+    rawtype *raw_rowbuf;
+    ppm_fs_info *fi = NULL;
+    colorhash_table cht, cht2;
+    long bodysize = 0;
+    int *itoh;      /* table image -> ham */
+    int usehash = 1;
+    int colbits;
+    int hamcode_red, hamcode_green, hamcode_blue;
+
+    MALLOCARRAY_NOFAIL(raw_rowbuf, cols);
+
+    cht = ppm_colorrowtocolorhash(colormap, colors);
+    cht2 = ppm_alloccolorhash();
+    colbits = pm_maxvaltobits(hammaxval);
+
+    hamcode_red   = HAMCODE_RED << colbits;
+    hamcode_green = HAMCODE_GREEN << colbits;
+    hamcode_blue  = HAMCODE_BLUE << colbits;
+
+    itoh = make_val_table(maxval, hammaxval);
+
+    if( floyd )
+        fi = ppm_fs_init(cols, maxval, 0);
+
+    for( row = 0; row < rows; row++ ) {
+        int noprev;
+        int spr, spg, spb;   /* scaled values of previous pixel */
+        int upr, upg, upb;   /* unscaled values of previous pixel, for floyd */
+        pixel *prow;
+
+        noprev = 1;
+        prow = next_pixrow(ifP, row);
+        for( col = ppm_fs_startrow(fi, prow); 
+             col < cols; 
+             col = ppm_fs_next(fi, col) ) {
+
+            pixel const p = prow[col];
+
+            /* unscaled values of current pixel */
+            pixval const ur = PPM_GETR(p);
+            pixval const ug = PPM_GETG(p);
+            pixval const ub = PPM_GETB(p);
+
+            /* scaled values of current pixel */
+            int const sr = itoh[ur];
+            int const sg = itoh[ug];
+            int const sb = itoh[ub];
+
+            i = ppm_lookupcolor(cht, &p);
+            if( i == -1 ) { /* no matching color in cmap, find closest match */
+                int ucr, ucg, ucb;  /* unscaled values of colormap entry */
+
+                if(  hammapmode == HAMMODE_GRAY ) {
+                    if( maxval <= 255 ) 
+                        /* Use fast approximation to 
+                           0.299 r + 0.587 g + 0.114 b. */
+                        i = (int)ppm_fastlumin(p);
+                    else 
+                        /* Can't use fast approximation, 
+                           so fall back on floats. 
+                        */
+                        i = (int)(PPM_LUMIN(p) + 0.5); 
+                            /* -IUW added '+ 0.5' */
+                    i = itoh[i];
+                }
+                else {
+                    i = ppm_lookupcolor(cht2, &p);
+                    if( i == -1 ) {
+                        i = ppm_findclosestcolor(colormap, colors, &p);
+                        if( usehash ) {
+                            if( ppm_addtocolorhash(cht2, &p, i) < 0 ) {
+                                pm_message("out of memory "
+                                           "adding to hash table, "
+                                           "proceeding without it");
+                                usehash = 0;
+                            }
+                        }
+                    }
+                }
+                ucr = PPM_GETR(colormap[i]); 
+                ucg = PPM_GETG(colormap[i]); 
+                ucb = PPM_GETB(colormap[i]);
+
+                if( noprev ) {  /* no previous pixel, must use colormap */
+                    raw_rowbuf[col] = i;    /* + (HAMCODE_CMAP << colbits) */
+                    upr = ucr;          upg = ucg;          upb = ucb;
+                    spr = itoh[upr];    spg = itoh[upg];    spb = itoh[upb];
+                    noprev = 0;
+                } else {
+                    register long di, dr, dg, db;
+                    int scr, scg, scb;   /* scaled values of colormap entry */
+
+                    scr = itoh[ucr]; scg = itoh[ucg]; scb = itoh[ucb];
+
+                    /* compute distances for the four options */
+#if 1
+                    dr = abs(sg - spg) + abs(sb - spb);
+                    dg = abs(sr - spr) + abs(sb - spb);
+                    db = abs(sr - spr) + abs(sg - spg);
+                    di = abs(sr - scr) + abs(sg - scg) + abs(sb - scb);
+#else
+                    dr = (sg - spg)*(sg - spg) + (sb - spb)*(sb - spb);
+                    dg = (sr - spr)*(sr - spr) + (sb - spb)*(sb - spb);
+                    db = (sr - spr)*(sr - spr) + (sg - spg)*(sg - spg);
+                    di = (sr - scr)*(sr - scr) + (sg - scg)*(sg - scg) +
+                        (sb - scb)*(sb - scb);
+#endif
+
+                    if( di <= dr && di <= dg && di <= db ) {    
+                        /* prefer colormap lookup */
+                        raw_rowbuf[col] = i; 
+                        upr = ucr;  upg = ucg;  upb = ucb;
+                        spr = scr;  spg = scg;  spb = scb;
+                    }
+                    else
+                    if( db <= dr && db <= dg ) {
+                        raw_rowbuf[col] = sb + hamcode_blue; 
+                        spb = sb;
+                        upb = ub;
+                    }
+                    else
+                    if( dr <= dg ) {
+                        raw_rowbuf[col] = sr + hamcode_red;  
+                        spr = sr;
+                        upr = ur;
+                    }
+                    else {
+                        raw_rowbuf[col] = sg + hamcode_green;
+                        spg = sg;
+                        upg = ug;
+                    }
+                }
+            }
+            else {  /* prefect match in cmap */
+                raw_rowbuf[col] = i;    /* + (HAMCODE_CMAP << colbits) */
+                upr = PPM_GETR(colormap[i]); 
+                upg = PPM_GETG(colormap[i]); 
+                upb = PPM_GETB(colormap[i]);
+                spr = itoh[upr];            
+                spg = itoh[upg];            
+                spb = itoh[upb];
+            }
+            ppm_fs_update3(fi, col, upr, upg, upb);
+        }
+        bodysize += encode_row(ofp, raw_rowbuf, cols, nPlanes);
+        if( maskmethod == mskHasMask )
+            bodysize += encode_maskrow(ofp, raw_rowbuf, cols);
+        ppm_fs_endrow(fi);
+    }
+    if( ofp && ODD(bodysize) )
+        put_byte(0);
+
+    free(itoh);
+
+    /* clean up */
+    free(raw_rowbuf);
+    ppm_fs_free(fi);
+
+    return bodysize;
+}
+
+
+/************ deep (24-bit) ************/
+
+static long do_deep_body      ARGS((FILE *ifP, FILE *ofp, 
+                                    int cols, int rows, 
+                                    pixval maxval, int bitspercolor));
+
+static void
+ppm_to_deep(fp, cols, rows, maxval, bitspercolor)
+    FILE *fp;
+    int cols, rows, maxval, bitspercolor;
+{
+    int nPlanes;
+    long bodysize, oldsize, formsize;
+
+    if( maskmethod == mskHasTransparentColor ) {
+        pm_message("masking method '%s' not usable with deep ILBM - "
+                   "using '%s' instead",
+                    mskNAME[mskHasTransparentColor], mskNAME[mskHasMask]);
+        maskmethod = mskHasMask;
+    }
+
+    nPlanes = 3*bitspercolor;
+
+    bodysize = oldsize = rows * TOTALPLANES(nPlanes) * RowBytes(cols);
+    if( DO_COMPRESS ) {
+        bodysize = do_deep_body(fp, NULL, cols, rows, maxval, bitspercolor);
+        if( bodysize > oldsize )
+            pm_message("warning - %s compression increases BODY size by %ld%%",
+                       cmpNAME[compmethod], 100*(bodysize-oldsize)/oldsize);
+        else
+            pm_message("BODY compression (%s): %ld%%", 
+                       cmpNAME[compmethod], 100*(oldsize-bodysize)/oldsize);
+    }
+
+
+    formsize =
+        4 +                                 /* ILBM */
+        4 + 4 + BitMapHeaderSize +          /* BMHD size header */
+        4 + 4 + bodysize + PAD(bodysize) +  /* BODY size data */
+        length_of_text_chunks();
+    if( gen_camg )
+        formsize += 4 + 4 + CAMGChunkSize;  /* CAMG size viewportmodes */
+
+    put_big_long(ID_FORM);
+    put_big_long(formsize);
+    put_big_long(ID_ILBM);
+
+    write_bmhd(cols, rows, nPlanes);
+    write_text_chunks();
+    if( gen_camg )
+        write_camg();
+
+    /* write body */
+    put_big_long(ID_BODY);
+    put_big_long(bodysize);
+    if( DO_COMPRESS )
+        write_body_rows();
+    else
+        do_deep_body(fp, stdout, cols, rows, maxval, bitspercolor);
+}
+
+
+static long
+#if __STDC__
+do_deep_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval, 
+             int bitspercolor)
+#else
+do_deep_body(ifP, ofp, cols, rows, maxval, bitspercolor)
+    FILE *ifP, *ofp;
+    int cols, rows;
+    pixval maxval;
+    int bitspercolor;
+#endif
+{
+    register int row, col;
+    pixel *pP;
+    int *table = NULL;
+    long bodysize = 0;
+    rawtype *redbuf, *greenbuf, *bluebuf;
+    int newmaxval;
+
+    MALLOCARRAY_NOFAIL(redbuf,   cols);
+    MALLOCARRAY_NOFAIL(greenbuf, cols);
+    MALLOCARRAY_NOFAIL(bluebuf,  cols);
+
+    newmaxval = pm_bitstomaxval(bitspercolor);
+    if( maxval != newmaxval ) {
+        pm_message("maxval is not %d - automatically rescaling colors", 
+                   newmaxval);
+        table = make_val_table(maxval, newmaxval);
+    }
+
+    for( row = 0; row < rows; row++ ) {
+        pP = next_pixrow(ifP, row);
+        if( table ) {
+            for( col = 0; col < cols; col++, pP++ ) {
+                redbuf[col]     = table[PPM_GETR(*pP)];
+                greenbuf[col]   = table[PPM_GETG(*pP)];
+                bluebuf[col]    = table[PPM_GETB(*pP)];
+            }
+        }
+        else {
+            for( col = 0; col < cols; col++, pP++ ) {
+                redbuf[col]     = PPM_GETR(*pP);
+                greenbuf[col]   = PPM_GETG(*pP);
+                bluebuf[col]    = PPM_GETB(*pP);
+            }
+        }
+        bodysize += encode_row(ofp, redbuf,   cols, bitspercolor);
+        bodysize += encode_row(ofp, greenbuf, cols, bitspercolor);
+        bodysize += encode_row(ofp, bluebuf,  cols, bitspercolor);
+        if( maskmethod == mskHasMask )
+            bodysize += encode_maskrow(ofp, redbuf, cols);
+    }
+    if( ofp && ODD(bodysize) )
+        put_byte(0);
+
+    /* clean up */
+    if( table )
+        free(table);
+    free(redbuf);
+    free(greenbuf);
+    free(bluebuf);
+
+    return bodysize;
+}
+
+
+/************ direct color ************/
+
+static long do_dcol_body      ARGS((FILE *ifP, FILE *ofp, int cols, int rows, 
+                                    pixval maxval, DirectColor *dcol));
+
+static void
+ppm_to_dcol(fp, cols, rows, maxval, dcol)
+    FILE *fp;
+    int cols, rows, maxval;
+    DirectColor *dcol;
+{
+    int nPlanes;
+    long bodysize, oldsize, formsize;
+
+    if( maskmethod == mskHasTransparentColor ) {
+        pm_message("masking method '%s' not usable with deep ILBM - "
+                   "using '%s' instead",
+                   mskNAME[mskHasTransparentColor], mskNAME[mskHasMask]);
+        maskmethod = mskHasMask;
+    }
+
+    nPlanes = dcol->r + dcol->g + dcol->b;
+
+    bodysize = oldsize = rows * TOTALPLANES(nPlanes) * RowBytes(cols);
+    if( DO_COMPRESS ) {
+        bodysize = do_dcol_body(fp, NULL, cols, rows, maxval, dcol);
+        if( bodysize > oldsize )
+            pm_message("warning - %s compression increases BODY size by %ld%%",
+                       cmpNAME[compmethod], 
+                       100*(bodysize-oldsize)/oldsize);
+        else
+            pm_message("BODY compression (%s): %ld%%", cmpNAME[compmethod], 
+                       100*(oldsize-bodysize)/oldsize);
+    }
+
+
+    formsize =
+        4 +                                 /* ILBM */
+        4 + 4 + BitMapHeaderSize +          /* BMHD size header */
+        4 + 4 + DirectColorSize +           /* DCOL size dcol */
+        4 + 4 + bodysize + PAD(bodysize) +  /* BODY size data */
+        length_of_text_chunks();
+    if( gen_camg )
+        formsize += 4 + 4 + CAMGChunkSize;  /* CAMG size viewportmodes */
+
+    put_big_long(ID_FORM);
+    put_big_long(formsize);
+    put_big_long(ID_ILBM);
+
+    write_bmhd(cols, rows, nPlanes);
+    write_text_chunks();
+
+    put_big_long(ID_DCOL);
+    put_big_long(DirectColorSize);
+    put_byte(dcol->r);
+    put_byte(dcol->g);
+    put_byte(dcol->b);
+    put_byte(0);    /* pad */
+
+    if( gen_camg )
+        write_camg();
+
+    /* write body */
+    put_big_long(ID_BODY);
+    put_big_long(bodysize);
+    if( DO_COMPRESS )
+        write_body_rows();
+    else
+        do_dcol_body(fp, stdout, cols, rows, maxval, dcol);
+}
+
+
+static long
+#if __STDC__
+do_dcol_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval, 
+             DirectColor *dcol)
+#else
+do_dcol_body(ifP, ofp, cols, rows, maxval, dcol)
+    FILE *ifP, *ofp;
+    int cols, rows;
+    pixval maxval;
+    DirectColor *dcol;
+#endif
+{
+    register int row, col;
+    pixel *pP;
+    long bodysize = 0;
+    rawtype *redbuf, *greenbuf, *bluebuf;
+    int *redtable, *greentable, *bluetable;
+
+    MALLOCARRAY_NOFAIL(redbuf,   cols);
+    MALLOCARRAY_NOFAIL(greenbuf, cols);
+    MALLOCARRAY_NOFAIL(bluebuf,  cols);
+
+    redtable   = make_val_table(maxval, pm_bitstomaxval(dcol->r));
+    greentable = make_val_table(maxval, pm_bitstomaxval(dcol->g));
+    bluetable  = make_val_table(maxval, pm_bitstomaxval(dcol->b));
+
+    for( row = 0; row < rows; row++ ) {
+        pP = next_pixrow(ifP, row);
+        for( col = 0; col < cols; col++, pP++ ) {
+            redbuf[col]   = redtable[PPM_GETR(*pP)];
+            greenbuf[col] = greentable[PPM_GETG(*pP)];
+            bluebuf[col]  = bluetable[PPM_GETB(*pP)];
+        }
+        bodysize += encode_row(ofp, redbuf,   cols, dcol->r);
+        bodysize += encode_row(ofp, greenbuf, cols, dcol->g);
+        bodysize += encode_row(ofp, bluebuf,  cols, dcol->b);
+        if( maskmethod == mskHasMask )
+            bodysize += encode_maskrow(ofp, redbuf, cols);
+    }
+    if( ofp && ODD(bodysize) )
+        put_byte(0);
+
+    /* clean up */
+    free(redtable);
+    free(greentable);
+    free(bluetable);
+    free(redbuf);
+    free(greenbuf);
+    free(bluebuf);
+
+    return bodysize;
+}
+
+
+/************ normal colormapped ************/
+
+static long do_std_body     ARGS((FILE *ifP, FILE *ofp, int cols, int rows, 
+                                  pixval maxval, pixel *colormap, 
+                                  int colors, int nPlanes));
+
+static void
+ppm_to_std(fp, cols, rows, maxval, colormap, colors, cmapmaxval, 
+           maxcolors, nPlanes)
+    FILE *fp;
+    int cols, rows, maxval;
+    pixel *colormap;
+    int cmapmaxval, colors, maxcolors, nPlanes;
+{
+    long formsize, cmapsize, bodysize, oldsize;
+
+    if( maskmethod == mskHasTransparentColor ) {
+        if( transpColor ) {
+            transpIndex = 
+                ppm_addtocolorrow(colormap, &colors, maxcolors, transpColor);
+        }
+        else
+        if( colors < maxcolors )
+            transpIndex = colors;
+
+        if( transpIndex < 0 ) {
+            pm_message("too many colors for masking method '%s' - "
+                       "using '%s' instead",
+                       mskNAME[mskHasTransparentColor], mskNAME[mskHasMask]);
+            maskmethod = mskHasMask;
+        }
+    }
+
+    if( cmapmaxval != maxval ) {
+        int i, *table;
+        pixel *newcmap;
+
+        newcmap = ppm_allocrow(colors);
+        table = make_val_table(cmapmaxval, maxval);
+        for (i = 0; i < colors; ++i)
+            PPM_ASSIGN(newcmap[i], 
+                       table[PPM_GETR(colormap[i])], 
+                       table[PPM_GETG(colormap[i])], 
+                       table[PPM_GETB(colormap[i])]);
+        free(table);
+        colormap = newcmap;
+    }
+    if( sortcmap )
+        ppm_sortcolorrow(colormap, colors, PPM_STDSORT);
+
+    bodysize = oldsize = rows * TOTALPLANES(nPlanes) * RowBytes(cols);
+    if( DO_COMPRESS ) {
+        bodysize = do_std_body(fp, NULL, cols, rows, maxval, colormap, 
+                               colors, nPlanes);
+        if( bodysize > oldsize )
+            pm_message("warning - %s compression increases BODY size by %ld%%",
+                       cmpNAME[compmethod], 100*(bodysize-oldsize)/oldsize);
+        else
+            pm_message("BODY compression (%s): %ld%%", 
+                       cmpNAME[compmethod], 100*(oldsize-bodysize)/oldsize);
+    }
+
+    cmapsize = colors * 3;
+
+    formsize =
+        4 +                                 /* ILBM */
+        4 + 4 + BitMapHeaderSize +          /* BMHD size header */
+        4 + 4 + cmapsize + PAD(cmapsize) +  /* CMAP size colormap */
+        4 + 4 + bodysize + PAD(bodysize) +  /* BODY size data */
+        length_of_text_chunks();
+    if( gen_camg )
+        formsize += 4 + 4 + CAMGChunkSize;  /* CAMG size viewportmodes */
+
+    put_big_long(ID_FORM);
+    put_big_long(formsize);
+    put_big_long(ID_ILBM);
+
+    write_bmhd(cols, rows, nPlanes);
+    write_text_chunks();
+    if( gen_camg )
+        write_camg();
+    write_cmap(colormap, colors, maxval);
+
+    /* write body */
+    put_big_long(ID_BODY);
+    put_big_long(bodysize);
+    if( DO_COMPRESS )
+        write_body_rows();
+    else
+        do_std_body(fp, stdout, cols, rows, maxval, colormap, colors, nPlanes);
+}
+
+
+static long
+#if __STDC__
+do_std_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval,
+            pixel *colormap, int colors, int nPlanes)
+#else
+do_std_body(ifP, ofp, cols, rows, maxval, colormap, colors, nPlanes)
+    FILE *ifP, *ofp;
+    int cols, rows;
+    pixval maxval;
+    pixel *colormap;
+    int colors;
+    int nPlanes;
+#endif
+{
+    register int row, col, i;
+    pixel *pP;
+    rawtype *raw_rowbuf;
+    ppm_fs_info *fi = NULL;
+    long bodysize = 0;
+    int usehash = 1;
+    colorhash_table cht;
+
+    MALLOCARRAY_NOFAIL(raw_rowbuf, cols);
+    cht = ppm_colorrowtocolorhash(colormap, colors);
+    if( floyd )
+        fi = ppm_fs_init(cols, maxval, FS_ALTERNATE);
+
+    for( row = 0; row < rows; row++ ) {
+        pixel *prow;
+        prow = next_pixrow(ifP, row);
+
+        for( col = ppm_fs_startrow(fi, prow); 
+             col < cols; 
+             col = ppm_fs_next(fi, col) ) {
+            pP = &prow[col];
+
+            if( maskmethod == mskHasTransparentColor && 
+                maskrow[col] == PBM_WHITE )
+                i = transpIndex;
+            else {
+                /* Check hash table to see if we have already matched
+                   this color. 
+                */
+                i = ppm_lookupcolor(cht, pP);
+                if( i == -1 ) {
+                    i = ppm_findclosestcolor(colormap, colors, pP);    
+                        /* No; search colormap for closest match. */
+                    if( usehash ) {
+                        if( ppm_addtocolorhash(cht, pP, i) < 0 ) {
+                            pm_message("out of memory adding to hash table, "
+                                       "proceeding without it");
+                            usehash = 0;
+                        }
+                    }
+                }
+            }
+            raw_rowbuf[col] = i;
+            ppm_fs_update(fi, col, &colormap[i]);
+        }
+        bodysize += encode_row(ofp, raw_rowbuf, cols, nPlanes);
+        if( maskmethod == mskHasMask )
+            bodysize += encode_maskrow(ofp, raw_rowbuf, cols);
+        ppm_fs_endrow(fi);
+    }
+    if( ofp && ODD(bodysize) )
+        put_byte(0);
+
+    /* clean up */
+    ppm_freecolorhash(cht);
+    free(raw_rowbuf);
+    ppm_fs_free(fi);
+
+    return bodysize;
+}
+
+/************ RGB8 ************/
+
+static void
+ppm_to_rgb8(ifP, cols, rows, maxval)
+    FILE *ifP;
+    int cols, rows;
+    int maxval;
+{
+    long bodysize, oldsize, formsize;
+    pixel *pP;
+    int *table = NULL;
+    int row, col1, col2, compr_len, len;
+    unsigned char *compr_row;
+
+    maskmethod = 0;     /* no masking - RGB8 uses genlock bits */
+    compmethod = 4;     /* RGB8 files are always compressed */
+    MALLOCARRAY_NOFAIL(compr_row, cols * 4);
+
+    if( maxval != 255 ) {
+        pm_message("maxval is not 255 - automatically rescaling colors");
+        table = make_val_table(maxval, 255);
+    }
+
+    oldsize = cols * rows * 4;
+    bodysize = 0;
+    for( row = 0; row < rows; row++ ) {
+        pP = next_pixrow(ifP, row);
+        compr_len = 0;
+        for( col1 = 0; col1 < cols; col1 = col2 ) {
+            col2 = col1 + 1;
+            if( maskrow ) {
+                while( col2 < cols && PPM_EQUAL(pP[col1], pP[col2]) && 
+                       maskrow[col1] == maskrow[col2] )
+                    col2++;
+            }
+            else {
+                while( col2 < cols && PPM_EQUAL(pP[col1], pP[col2]) )
+                    col2++;
+            }
+            len = col2 - col1;
+            while( len ) {
+                int count;
+                count = (len > 127 ? 127 : len);
+                len -= count;
+                if( table ) {
+                    compr_row[compr_len++] = table[PPM_GETR(pP[col1])];
+                    compr_row[compr_len++] = table[PPM_GETG(pP[col1])];
+                    compr_row[compr_len++] = table[PPM_GETB(pP[col1])];
+                }
+                else {
+                    compr_row[compr_len++] = PPM_GETR(pP[col1]);
+                    compr_row[compr_len++] = PPM_GETG(pP[col1]);
+                    compr_row[compr_len++] = PPM_GETB(pP[col1]);
+                }
+                compr_row[compr_len] = count;
+                if( maskrow && maskrow[col1] == PBM_WHITE )
+                    compr_row[compr_len] |= 1<<7;     /* genlock bit */
+                ++compr_len;
+            }
+        }
+        store_bodyrow(compr_row, compr_len);
+        bodysize += compr_len;
+    }
+
+    pm_message("BODY compression: %ld%%", 100*(oldsize-bodysize)/oldsize);
+
+    formsize =
+        4 +                                 /* RGB8 */
+        4 + 4 + BitMapHeaderSize +          /* BMHD size header */
+        4 + 4 + CAMGChunkSize +             /* CAMG size viewportmode */
+        4 + 4 + bodysize + PAD(bodysize) +  /* BODY size data */
+        length_of_text_chunks();
+
+    /* write header */
+    put_big_long(ID_FORM);
+    put_big_long(formsize);
+    put_big_long(ID_RGB8);
+
+    write_bmhd(cols, rows, 25);
+    write_text_chunks();
+    write_camg();               /* RGB8 requires CAMG chunk */
+
+    put_big_long(ID_BODY);
+    put_big_long(bodysize);
+    write_body_rows();
+}
+
+
+/************ RGBN ************/
+
+static void
+ppm_to_rgbn(ifP, cols, rows, maxval)
+    FILE *ifP;
+    int cols, rows;
+    int maxval;
+{
+    long bodysize, oldsize, formsize;
+    pixel *pP;
+    int *table = NULL;
+    int row, col1, col2, compr_len, len;
+    unsigned char *compr_row;
+
+    maskmethod = 0;     /* no masking - RGBN uses genlock bits */
+    compmethod = 4;     /* RGBN files are always compressed */
+    MALLOCARRAY_NOFAIL(compr_row, cols * 2);
+
+    if( maxval != 15 ) {
+        pm_message("maxval is not 15 - automatically rescaling colors");
+        table = make_val_table(maxval, 15);
+    }
+
+    oldsize = cols * rows * 2;
+    bodysize = 0;
+    for( row = 0; row < rows; row++ ) {
+        pP = next_pixrow(ifP, row);
+        compr_len = 0;
+        for( col1 = 0; col1 < cols; col1 = col2 ) {
+            col2 = col1 + 1;
+            if( maskrow ) {
+                while( col2 < cols && PPM_EQUAL(pP[col1], pP[col2]) && 
+                       maskrow[col1] == maskrow[col2] )
+                    col2++;
+            }
+            else {
+                while( col2 < cols && PPM_EQUAL(pP[col1], pP[col2]) )
+                    col2++;
+            }
+            len = col2 - col1;
+            while( len ) {
+                int count;
+                count = (len > 65535 ? 65535 : len);
+                len -= count;
+                if( table ) {
+                    compr_row[compr_len]  = table[PPM_GETR(pP[col1])] << 4;
+                    compr_row[compr_len] |= table[PPM_GETG(pP[col1])];
+                    ++compr_len;
+                    compr_row[compr_len]  = table[PPM_GETB(pP[col1])] << 4;
+                }
+                else {
+                    compr_row[compr_len]  = PPM_GETR(pP[col1]) << 4;
+                    compr_row[compr_len] |= PPM_GETG(pP[col1]);
+                    ++compr_len;
+                    compr_row[compr_len]  = PPM_GETB(pP[col1]) << 4;
+                }
+                if( maskrow && maskrow[col1] == PBM_WHITE )
+                    compr_row[compr_len] |= 1<<3;   /* genlock bit */
+                if( count <= 7 )
+                    compr_row[compr_len++] |= count;  /* 3 bit repeat count */
+                else {
+                    ++compr_len;                  /* 3 bit repeat count = 0 */
+                    if( count <= 255 )
+                        compr_row[compr_len++] = (unsigned char)count;  
+                            /* byte repeat count */
+                    else {
+                        compr_row[compr_len++] = (unsigned char)0;   
+                            /* byte repeat count = 0 */
+                        compr_row[compr_len++] = (count >> 8) & 0xff; 
+                            /* word repeat count MSB */
+                        compr_row[compr_len++] = count & 0xff;    
+                            /* word repeat count LSB */
+                    }
+                }
+            }
+        }
+        store_bodyrow(compr_row, compr_len);
+        bodysize += compr_len;
+    }
+
+    pm_message("BODY compression: %ld%%", 100*(oldsize-bodysize)/oldsize);
+
+    formsize =
+        4 +                                 /* RGBN */
+        4 + 4 + BitMapHeaderSize +          /* BMHD size header */
+        4 + 4 + CAMGChunkSize +             /* CAMG size viewportmode */
+        4 + 4 + bodysize + PAD(bodysize) +  /* BODY size data */
+        length_of_text_chunks();
+
+    /* write header */
+    put_big_long(ID_FORM);
+    put_big_long(formsize);
+    put_big_long(ID_RGBN);
+
+    write_bmhd(cols, rows, 13);
+    write_text_chunks();
+    write_camg();               /* RGBN requires CAMG chunk */
+
+    put_big_long(ID_BODY);
+    put_big_long(bodysize);
+    write_body_rows();
+}
+
+
+/************ multipalette ************/
+
+#ifdef ILBM_PCHG
+static pixel *ppmslice[2];  /* need 2 for laced ILBMs, else 1 */
+
+void ppm_to_pchg()
+{
+/*
+    read first slice
+    build a colormap from this slice
+    select upto <maxcolors> colors
+    build colormap from selected colors
+    map slice to colormap
+    write slice
+    while( !finished ) {
+        read next slice
+        compute distances for each pixel and select upto
+            <maxchangesperslice> unused colors in this slice
+        modify selected colors to the ones with maximum(?) distance
+        map slice to colormap
+        write slice
+    }
+
+
+    for HAM use a different mapping:
+        compute distance to closest color in colormap
+        if( there is no matching color in colormap ) {
+            compute distances for the three "modify" cases
+            use the shortest distance from the four cases
+        }
+*/
+}
+#endif
+
+
+/************ ILBM functions ************/
+
+static int
+length_of_text_chunks ARGS((void))
+{
+    int len, n;
+
+    len = 0;
+    if( anno_chunk ) {
+        n = strlen(anno_chunk);
+        len += 4 + 4 + n + PAD(n);      /* ID chunksize text */
+    }
+    if( auth_chunk ) {
+        n = strlen(auth_chunk);
+        len += 4 + 4 + n + PAD(n);      /* ID chunksize text */
+    }
+    if( name_chunk ) {
+        n = strlen(name_chunk);
+        len += 4 + 4 + n + PAD(n);      /* ID chunksize text */
+    }
+    if( copyr_chunk ) {
+        n = strlen(copyr_chunk);
+        len += 4 + 4 + n + PAD(n);      /* ID chunksize text */
+    }
+    if( text_chunk ) {
+        n = strlen(text_chunk);
+        len += 4 + 4 + n + PAD(n);      /* ID chunksize text */
+    }
+    return len;
+}
+
+
+static void
+write_text_chunks ARGS((void))
+{
+    int n;
+
+    if( anno_chunk ) {
+        n = strlen(anno_chunk);
+        put_big_long(ID_ANNO);
+        put_big_long(n);
+        write_bytes((unsigned char *)anno_chunk, n);
+        if( ODD(n) )
+            put_byte(0);
+    }
+    if( auth_chunk ) {
+        n = strlen(auth_chunk);
+        put_big_long(ID_AUTH);
+        put_big_long(n);
+        write_bytes((unsigned char *)auth_chunk, n);
+        if( ODD(n) )
+            put_byte(0);
+    }
+    if( copyr_chunk ) {
+        n = strlen(copyr_chunk);
+        put_big_long(ID_copy);
+        put_big_long(n);
+        write_bytes((unsigned char *)copyr_chunk, n);
+        if( ODD(n) )
+            put_byte(0);
+    }
+    if( name_chunk ) {
+        n = strlen(name_chunk);
+        put_big_long(ID_NAME);
+        put_big_long(n);
+        write_bytes((unsigned char *)name_chunk, n);
+        if( ODD(n) )
+            put_byte(0);
+    }
+    if( text_chunk ) {
+        n = strlen(text_chunk);
+        put_big_long(ID_TEXT);
+        put_big_long(n);
+        write_bytes((unsigned char *)text_chunk, n);
+        if( ODD(n) )
+            put_byte(0);
+    }
+}
+
+
+static void
+write_cmap(colormap, colors, maxval)
+    pixel *colormap;
+    int colors, maxval;
+{
+    int cmapsize, i;
+
+    cmapsize = 3 * colors;
+
+    /* write colormap */
+    put_big_long(ID_CMAP);
+    put_big_long(cmapsize);
+    if( maxval != MAXCOLVAL ) {
+        int *table;
+        pm_message("maxval is not %d - automatically rescaling colors", 
+                   MAXCOLVAL);
+        table = make_val_table(maxval, MAXCOLVAL);
+        for( i = 0; i < colors; i++ ) {
+            put_byte(table[PPM_GETR(colormap[i])]);
+            put_byte(table[PPM_GETG(colormap[i])]);
+            put_byte(table[PPM_GETB(colormap[i])]);
+        }
+        free(table);
+    }
+    else {
+        for( i = 0; i < colors; i++ ) {
+            put_byte(PPM_GETR(colormap[i]));
+            put_byte(PPM_GETG(colormap[i]));
+            put_byte(PPM_GETB(colormap[i]));
+        }
+    }
+    if( ODD(cmapsize) )
+        put_byte(0);
+}
+
+
+static void
+write_bmhd(cols, rows, nPlanes)
+    int cols, rows, nPlanes;
+{
+    unsigned char xasp = 10, yasp = 10;
+
+    if( viewportmodes & vmLACE )
+        xasp *= 2;
+    if( viewportmodes & vmHIRES )
+        yasp *= 2;
+
+    put_big_long(ID_BMHD);
+    put_big_long(BitMapHeaderSize);
+
+    put_big_short(cols);
+    put_big_short(rows);
+    put_big_short(0);                       /* x-offset */
+    put_big_short(0);                       /* y-offset */
+    put_byte(nPlanes);                      /* no of planes */
+    put_byte(maskmethod);                   /* masking */
+    put_byte(compmethod);                   /* compression */
+    put_byte(BMHD_FLAGS_CMAPOK);            /* flags */
+    if( maskmethod == mskHasTransparentColor )
+        put_big_short(transpIndex);
+    else
+        put_big_short(0);
+    put_byte(xasp);                         /* x-aspect */
+    put_byte(yasp);                         /* y-aspect */
+    put_big_short(cols);                    /* pageWidth */
+    put_big_short(rows);                    /* pageHeight */
+}
+
+
+/* encode algorithm by Johan Widen (jw@jwdata.se) */
+static const unsigned char bitmask[] = {1, 2, 4, 8, 16, 32, 64, 128};
+
+static long
+encode_row(outfile, rawrow, cols, nPlanes)
+    FILE *outfile;  /* if non-NULL, write uncompressed row to this file */
+    rawtype *rawrow;
+    int cols, nPlanes;
+{
+    int plane, bytes;
+    long retbytes = 0;
+
+    bytes = RowBytes(cols);
+
+    /* Encode and write raw bytes in plane-interleaved form. */
+    for( plane = 0; plane < nPlanes; plane++ ) {
+        register int col, cbit;
+        register rawtype *rp;
+        register unsigned char *cp;
+        int mask;
+
+        mask = 1 << plane;
+        cbit = -1;
+        cp = coded_rowbuf-1;
+        rp = rawrow;
+        for( col = 0; col < cols; col++, cbit--, rp++ ) {
+            if( cbit < 0 ) {
+                cbit = 7;
+                *++cp = 0;
+            }
+            if( *rp & mask )
+                *cp |= bitmask[cbit];
+        }
+        if( outfile ) {
+            write_bytes(coded_rowbuf, bytes);
+            retbytes += bytes;
+        }
+        else
+            retbytes += compress_row(bytes);
+    }
+    return retbytes;
+}
+
+
+static long
+encode_maskrow(ofp, rawrow, cols)
+    FILE *ofp;
+    rawtype *rawrow;
+    int cols;
+{
+    int col;
+
+    for( col = 0; col < cols; col++ ) {
+        if( maskrow[col] == PBM_BLACK )
+            rawrow[col] = 1;
+        else
+            rawrow[col] = 0;
+    }
+    return encode_row(ofp, rawrow, cols, 1);
+}
+
+
+static int
+compress_row(bytes)
+    int bytes;
+{
+    int newbytes;
+
+    switch( compmethod ) {
+        case cmpByteRun1:
+            newbytes = runbyte1(bytes);
+            break;
+        default:
+            pm_error("compress_row(): unknown compression method %d", 
+                     compmethod);
+    }
+    store_bodyrow(compr_rowbuf, newbytes);
+
+    return newbytes;
+}
+
+
+static void
+store_bodyrow(row, len)
+    unsigned char *row;
+    int len;
+{
+    int idx = cur_block->used;
+    if( idx >= ROWS_PER_BLOCK ) {
+        MALLOCVAR_NOFAIL(cur_block->next);
+        cur_block = cur_block->next;
+        cur_block->used = idx = 0;
+        cur_block->next = NULL;
+    }
+    MALLOCARRAY_NOFAIL(cur_block->row[idx], len);
+    cur_block->len[idx] = len;
+    memcpy(cur_block->row[idx], row, len);
+    cur_block->used++;
+}
+
+
+static void
+write_body_rows ARGS((void))
+{
+    bodyblock *b;
+    int i;
+    long total = 0;
+
+    for( b = &firstblock; b != NULL; b = b->next ) {
+        for( i = 0; i < b->used; i++ ) {
+            write_bytes(b->row[i], b->len[i]);
+            total += b->len[i];
+        }
+    }
+    if( ODD(total) )
+        put_byte(0);
+}
+
+
+static void
+write_camg ARGS((void))
+{
+    put_big_long(ID_CAMG);
+    put_big_long(CAMGChunkSize);
+    put_big_long(viewportmodes);
+}
+
+
+/************ compression ************/
+
+
+/* runbyte1 algorithm by Robert A. Knop (rknop@mop.caltech.edu) */
+static int
+runbyte1(size)
+   int size;
+{
+    int in,out,count,hold;
+    register unsigned char *inbuf = coded_rowbuf;
+    register unsigned char *outbuf = compr_rowbuf;
+
+
+    in=out=0;
+    while( in<size ) {
+        if( (in<size-1) && (inbuf[in]==inbuf[in+1]) ) {    
+            /*Begin replicate run*/
+            for( count=0, hold=in; 
+                 in < size && inbuf[in] == inbuf[hold] && count < 128; 
+                 in++, count++)
+                ;
+            outbuf[out++]=(unsigned char)(char)(-count+1);
+            outbuf[out++]=inbuf[hold];
+        }
+        else {  /*Do a literal run*/
+            hold=out; out++; count=0;
+            while( ((in>=size-2)&&(in<size)) || 
+                   ((in<size-2) && ((inbuf[in]!=inbuf[in+1])
+                                    ||(inbuf[in]!=inbuf[in+2]))) ) {
+                outbuf[out++]=inbuf[in++];
+                if( ++count>=128 )
+                    break;
+            }
+            outbuf[hold]=count-1;
+        }
+    }
+    return(out);
+}
+
+
+
+/************ other utility functions ************/
+
+static void
+#if __STDC__
+put_big_short(short s)
+#else
+put_big_short(s)
+    short s;
+#endif
+{
+    if ( pm_writebigshort( stdout, s ) == -1 )
+        pm_error( "write error" );
+}
+
+
+static void
+put_big_long(l)
+    long l;
+{
+    if ( pm_writebiglong( stdout, l ) == -1 )
+        pm_error( "write error" );
+}
+
+
+static void
+write_bytes(buffer, bytes)
+    unsigned char *buffer;
+    int bytes;
+{
+    if( fwrite(buffer, 1, bytes, stdout) != bytes )
+        pm_error("write error");
+}
+
+
+static int *
+make_val_table(oldmaxval, newmaxval)
+    int oldmaxval, newmaxval;
+{
+    int i;
+    int *table;
+
+    MALLOCARRAY_NOFAIL(table, oldmaxval + 1);
+    for(i = 0; i <= oldmaxval; i++ )
+        table[i] = (i * newmaxval + oldmaxval/2) / oldmaxval;
+
+    return table;
+}
+
+
+
+static int  gFormat;
+static int  gCols;
+static int  gMaxval;
+
+static void
+init_read(fp, colsP, rowsP, maxvalP, formatP, readall)
+    FILE *fp;
+    int *colsP, *rowsP;
+    pixval *maxvalP;
+    int *formatP;
+    int readall;
+{
+    ppm_readppminit(fp, colsP, rowsP, maxvalP, formatP);
+    if( readall ) {
+        int row;
+
+        pixels = ppm_allocarray(*colsP, *rowsP);
+        for( row = 0; row < *rowsP; row++ )
+            ppm_readppmrow(fp, pixels[row], *colsP, *maxvalP, *formatP);
+        /* pixels = ppm_readppm(fp, colsP, rowsP, maxvalP); */
+    }
+    else {
+        pixrow = ppm_allocrow(*colsP);
+    }
+    gCols = *colsP;
+    gMaxval = *maxvalP;
+    gFormat = *formatP;
+}
+
+
+static pixel *
+next_pixrow(fp, row)
+    FILE *fp;
+    int row;
+{
+    if( pixels )
+        pixrow = pixels[row];
+    else {
+        ppm_readppmrow(fp, pixrow, gCols, gMaxval, gFormat);
+    }
+    if( maskrow ) {
+        int col;
+
+        if( maskfile )
+            pbm_readpbmrow(maskfile, maskrow, maskcols, maskformat);
+        else {
+            for( col = 0; col < gCols; col++ )
+                maskrow[col] = PBM_BLACK;
+        }
+        if( transpColor ) {
+            for( col = 0; col < gCols; col++ )
+                if( PPM_EQUAL(pixrow[col], *transpColor) )
+                    maskrow[col] = PBM_WHITE;
+        }
+    }
+    return pixrow;
+}
+
+
+
+int
+main(int argc, char ** argv) {
+
+    FILE * ifP;
+    int argn, rows, cols, format, nPlanes;
+    int ifmode, forcemode, maxplanes, fixplanes, mode;
+    int hamplanes;
+    int deepbits;   /* bits per color component in deep ILBM */
+    DirectColor dcol;
+#define MAXCOLORS       (1<<maxplanes)
+    pixval maxval;
+    pixel * colormap;
+    int colors = 0;
+    pixval cmapmaxval;      /* maxval of colors in cmap */
+    const char * mapfile;
+    const char * transpname;
+
+    ppm_init(&argc, argv);
+
+    colormap = NULL;  /* initial value */
+    ifmode = DEF_IFMODE; forcemode = MODE_NONE;
+    maxplanes = DEF_MAXPLANES; fixplanes = 0;
+    hamplanes = DEF_HAMPLANES;
+    deepbits = DEF_DEEPPLANES;
+    dcol.r = dcol.g = dcol.b = DEF_DCOLPLANES;
+    mapfile = transpname = NULL;
+
+    argn = 1;
+    while( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) {
+        if( pm_keymatch(argv[argn], "-ilbm", 5) ) {
+            if( forcemode == MODE_RGB8 || forcemode == MODE_RGBN )
+                forcemode = MODE_NONE;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-rgb8", 5) )
+            forcemode = MODE_RGB8;
+        else
+        if( pm_keymatch(argv[argn], "-rgbn", 5) )
+            forcemode = MODE_RGBN;
+        else
+        if( pm_keymatch(argv[argn], "-maxplanes", 4) || 
+            pm_keymatch(argv[argn], "-mp", 3) ) {
+            if( ++argn >= argc )
+                pm_error("-maxplanes requires a value");
+            maxplanes = get_int_val(argv[argn], argv[argn-1], 1, MAXPLANES);
+            fixplanes = 0;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-fixplanes", 4) || 
+            pm_keymatch(argv[argn], "-fp", 3) ) {
+            if( ++argn >= argc )
+                pm_error("-fixplanes requires a value");
+            fixplanes = get_int_val(argv[argn], argv[argn-1], 1, MAXPLANES);
+            maxplanes = fixplanes;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-mapfile", 4) ) {
+            if( ++argn >= argc )
+                pm_error("-mapfile requires a value");
+            mapfile = argv[argn];
+        }
+        else
+        if( pm_keymatch(argv[argn], "-mmethod", 3) ) {
+            if( ++argn >= argc )
+                pm_error("-mmethod requires a value");
+            maskmethod = get_mask_type(argv[argn]);
+            switch( maskmethod ) {
+                case mskNone:
+                case mskHasMask:
+                case mskHasTransparentColor:
+                    break;
+                default:
+                    pm_error("This program does not know how to handle "
+                             "masking method '%s'", 
+                             mskNAME[maskmethod]);
+            }
+        }
+        else
+        if( pm_keymatch(argv[argn], "-maskfile", 4) ) {
+            if( ++argn >= argc )
+                pm_error("-maskfile requires a value");
+            maskfile = pm_openr(argv[argn]);
+            if( maskmethod == mskNone )
+                maskmethod = mskHasMask;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-transparent", 3) ) {
+            if( ++argn >= argc )
+                pm_error("-transparent requires a value");
+            transpname = argv[argn];
+            if( maskmethod == mskNone )
+                maskmethod = mskHasTransparentColor;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-sortcmap", 5) )
+            sortcmap = 1;
+        else
+        if( pm_keymatch(argv[argn], "-cmaponly", 3) ) {
+            forcemode = MODE_CMAP;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-lace", 2) ) {
+            slicesize = 2;
+            viewportmodes |= vmLACE;
+            gen_camg = 1;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-nolace", 4) ) {
+            slicesize = 1;
+            viewportmodes &= ~vmLACE;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-hires", 3) ) {
+            viewportmodes |= vmHIRES;
+            gen_camg = 1;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-nohires", 5) )
+            viewportmodes &= ~vmHIRES;
+        else
+        if( pm_keymatch(argv[argn], "-camg", 5) ) {
+            char *tail;
+            long value = 0L;
+
+            if( ++argn >= argc )
+                pm_error("-camg requires a value");
+            value = strtol(argv[argn], &tail, 16);
+            /* TODO: should do some error checking here */
+            viewportmodes |= value;
+            gen_camg = 1;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-ecs", 2) ) {
+            maxplanes = ECS_MAXPLANES;
+            hamplanes = ECS_HAMPLANES;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-aga", 3) ) {
+            maxplanes = AGA_MAXPLANES;
+            hamplanes = AGA_HAMPLANES;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-hamplanes", 5) ) {
+            if( ++argn >= argc )
+                pm_error("-hamplanes requires a value");
+            hamplanes = get_int_val(argv[argn], argv[argn-1], 3, HAMMAXPLANES);
+        }
+        else
+        if( pm_keymatch(argv[argn], "-hambits", 5) ) {
+            if( ++argn >= argc )
+                pm_usage("-hambits requires a value");
+            hamplanes = 
+                get_int_val(argv[argn], argv[argn-1], 3, HAMMAXPLANES-2) +2;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-ham6", 5) ) {
+            hamplanes = ECS_HAMPLANES;
+            forcemode = MODE_HAM;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-ham8", 5) ) {
+            hamplanes = AGA_HAMPLANES;
+            forcemode = MODE_HAM;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-hammap", 5) ) {
+            if( ++argn >= argc )
+                pm_error("-hammap requires a value");
+            hammapmode = get_hammap_mode(argv[argn]);
+        }
+        else
+        if( pm_keymatch(argv[argn], "-hamif", 5) )
+            ifmode = MODE_HAM;
+        else
+        if( pm_keymatch(argv[argn], "-nohamif", 7) ) {
+            if( ifmode == MODE_HAM )
+                ifmode = MODE_NONE;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-hamforce", 4) )
+            forcemode = MODE_HAM;
+        else
+        if( pm_keymatch(argv[argn], "-nohamforce", 6) ) {
+            if( forcemode == MODE_HAM )
+                forcemode = MODE_NONE;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-24if", 4) ) {
+            ifmode = MODE_DEEP;
+            deepbits = 8;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-no24if", 6) ) {
+            if( ifmode == MODE_DEEP )
+                ifmode = MODE_NONE;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-24force", 3) ) {
+            forcemode = MODE_DEEP;
+            deepbits = 8;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-no24force", 5) ) {
+            if( forcemode == MODE_DEEP )
+                forcemode = MODE_NONE;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-deepplanes", 6) ) {
+            if( ++argn >= argc )
+                pm_error("-deepplanes requires a value");
+            deepbits = get_int_val(argv[argn], argv[argn-1], 3, 3*MAXPLANES);
+            if( deepbits % 3 != 0 )
+                pm_error("option \"%s\" argument value must be divisible by 3",
+                         argv[argn-1]);
+            deepbits /= 3;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-deepbits", 6) ) {
+            if( ++argn >= argc )
+                pm_error("-deepbits requires a value");
+            deepbits = get_int_val(argv[argn], argv[argn-1], 1, MAXPLANES);
+        }
+        else
+        if( pm_keymatch(argv[argn], "-deepif", 6) )
+            ifmode = MODE_DEEP;
+        else
+        if( pm_keymatch(argv[argn], "-nodeepif", 8) ) {
+            if( ifmode == MODE_DEEP )
+                ifmode = MODE_NONE;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-deepforce", 5) )
+            forcemode = MODE_DEEP;
+        else
+        if( pm_keymatch(argv[argn], "-nodeepforce", 7) ) {
+            if( forcemode == MODE_DEEP )
+                forcemode = MODE_NONE;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-dcif", 4) )
+            ifmode = MODE_DCOL;
+        else
+        if( pm_keymatch(argv[argn], "-nodcif", 6) ) {
+            if( ifmode == MODE_DCOL )
+                ifmode = MODE_NONE;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-dcforce", 4) )
+            forcemode = MODE_DCOL;
+        else
+        if( pm_keymatch(argv[argn], "-nodcforce", 6) ) {
+            if( forcemode == MODE_DCOL )
+                forcemode = MODE_NONE;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-dcbits", 4) || 
+            pm_keymatch(argv[argn], "-dcplanes", 4) ) {
+            if( argc - argn < 4 )
+                pm_error("-dcbits requires 4 arguments");
+            dcol.r = get_int_val(argv[argn+1], argv[argn], 1, MAXPLANES);
+            dcol.g = get_int_val(argv[argn+2], argv[argn], 1, MAXPLANES);
+            dcol.b = get_int_val(argv[argn+3], argv[argn], 1, MAXPLANES);
+            argn += 3;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-normal", 4) ) {
+            ifmode = forcemode = MODE_NONE;
+            compmethod = DEF_COMPRESSION;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-compress", 4) ) {
+            compr_force = 1;
+            if( compmethod == cmpNone )
+#if DEF_COMPRESSION == cmpNone
+                    compmethod = cmpByteRun1;
+#else
+                    compmethod = DEF_COMPRESSION;
+#endif
+        }
+        else
+        if( pm_keymatch(argv[argn], "-nocompress", 4) ) {
+            compr_force = 0;
+            compmethod = cmpNone;
+        }
+        else
+        if( pm_keymatch(argv[argn], "-cmethod", 4) ) {
+            if( ++argn >= argc )
+                pm_error("-cmethod requires a value");
+            compmethod = get_compr_method(argv[argn]);
+        }
+        else
+        if( pm_keymatch(argv[argn], "-floyd", 3) || 
+            pm_keymatch(argv[argn], "-fs", 3) )
+            floyd = 1;
+        else
+        if( pm_keymatch(argv[argn], "-nofloyd", 5) || 
+            pm_keymatch(argv[argn], "-nofs", 5) )
+            floyd = 0;
+        else
+        if( pm_keymatch(argv[argn], "-annotation", 3) ) {
+            if( ++argn >= argc )
+                pm_error("-annotation requires a value");
+            anno_chunk = argv[argn];
+        }
+        else
+        if( pm_keymatch(argv[argn], "-author", 3) ) {
+            if( ++argn >= argc )
+                pm_error("-author requires a value");
+            auth_chunk = argv[argn];
+        }
+        else
+        if( pm_keymatch(argv[argn], "-copyright", 4) ) {
+            if( ++argn >= argc )
+                pm_error("-copyright requires a value");
+            copyr_chunk = argv[argn];
+        }
+        else
+        if( pm_keymatch(argv[argn], "-name", 3) ) {
+            if( ++argn >= argc )
+                pm_error("-name requires a value");
+            name_chunk = argv[argn];
+        }
+        else
+        if( pm_keymatch(argv[argn], "-text", 3) ) {
+            if( ++argn >= argc )
+                pm_error("-text requires a value");
+            text_chunk = argv[argn];
+        }
+        else
+            pm_error("invalid option: %s", argv[argn]);
+        ++argn;
+    }
+
+    if( argn < argc ) {
+        ifP = pm_openr(argv[argn]);
+        ++argn;
+    }
+    else
+        ifP = stdin;
+
+    if( argn != argc )
+        pm_error("Program takes no arguments.");
+
+    mode = forcemode;
+    switch(forcemode) {
+        case MODE_HAM:
+            if (hammapmode == HAMMODE_RGB4 || hammapmode == HAMMODE_RGB5)
+                init_read(ifP, &cols, &rows, &maxval, &format, 1);
+            else
+                init_read(ifP, &cols, &rows, &maxval, &format, 0);
+            break;
+        case MODE_DCOL:
+        case MODE_DEEP:
+            mapfile = NULL;
+            init_read(ifP, &cols, &rows, &maxval, &format, 0);
+            break;
+        case MODE_RGB8:
+            mapfile = NULL;
+            init_read(ifP, &cols, &rows, &maxval, &format, 0);
+            break;
+        case MODE_RGBN:
+            mapfile = NULL;
+            init_read(ifP, &cols, &rows, &maxval, &format, 0);
+            break;
+        case MODE_CMAP:
+            /* Figure out the colormap. */
+            pm_message("computing colormap...");
+            colormap = ppm_mapfiletocolorrow(ifP, MAXCOLORS, &colors, 
+                                             &cmapmaxval);
+            if (colormap == NULL)
+                pm_error("too many colors - try doing a 'pnmquant %d'", 
+                         MAXCOLORS);
+            pm_message("%d colors found", colors);
+            break;
+        default:
+            if (mapfile)
+                init_read(ifP, &cols, &rows, &maxval, &format, 0);
+            else {
+                init_read(ifP, &cols, &rows, &maxval, &format, 1);  
+                    /* read file into memory */
+                pm_message("computing colormap...");
+                colormap = 
+                    ppm_computecolorrow(pixels, cols, rows, MAXCOLORS, 
+                                        &colors);
+                if (colormap) {
+                    cmapmaxval = maxval;
+                    pm_message("%d colors found", colors);
+                    nPlanes = pm_maxvaltobits(colors-1);
+                    if (fixplanes > nPlanes)
+                        nPlanes = fixplanes;
+                } else {  /* too many colors */
+                    mode = ifmode;
+                    report_too_many_colors(ifmode, maxplanes, hamplanes,
+                                           dcol, deepbits );
+                }
+            }
+    }
+
+    if (mapfile) {
+        FILE * mapfp;
+
+        pm_message("reading colormap file...");
+        mapfp = pm_openr(mapfile);
+        colormap = ppm_mapfiletocolorrow(mapfp, MAXCOLORS, &colors, 
+                                         &cmapmaxval);
+        pm_close(mapfp);
+        if (colormap == NULL)
+            pm_error("too many colors in mapfile for %d planes", maxplanes);
+        if (colors == 0)
+            pm_error("empty colormap??");
+        pm_message("%d colors found in colormap", colors);
+    }
+
+    if (maskmethod != mskNone) {
+        if (transpname) {
+            MALLOCVAR_NOFAIL(transpColor);
+            *transpColor = ppm_parsecolor(transpname, maxval);
+        }
+        if (maskfile) {
+            int maskrows;
+            pbm_readpbminit(maskfile, &maskcols, &maskrows, &maskformat);
+            if (maskcols < cols || maskrows < rows)
+                pm_error("maskfile too small - try scaling it");
+            if (maskcols > cols || maskrows > rows)
+                pm_message("warning - maskfile larger than image");
+        } else
+            maskcols = rows;
+        maskrow = pbm_allocrow(maskcols);
+    }
+
+    if (mode != MODE_CMAP) {
+        unsigned int i;
+        MALLOCARRAY_NOFAIL(coded_rowbuf, RowBytes(cols));
+        for (i = 0; i < RowBytes(cols); ++i)
+            coded_rowbuf[i] = 0;
+        if (DO_COMPRESS)
+            MALLOCARRAY_NOFAIL(compr_rowbuf, WORSTCOMPR(RowBytes(cols)));
+    }
+    
+    switch (mode) {
+        case MODE_HAM:
+            viewportmodes |= vmHAM;
+            ppm_to_ham(ifP, cols, rows, maxval, 
+                       colormap, colors, cmapmaxval, hamplanes);
+            break;
+        case MODE_DEEP:
+            ppm_to_deep(ifP, cols, rows, maxval, deepbits);
+            break;
+        case MODE_DCOL:
+            ppm_to_dcol(ifP, cols, rows, maxval, &dcol);
+            break;
+        case MODE_RGB8:
+            ppm_to_rgb8(ifP, cols, rows, maxval);
+            break;
+        case MODE_RGBN:
+            ppm_to_rgbn(ifP, cols, rows, maxval);
+            break;
+        case MODE_CMAP:
+            ppm_to_cmap(colormap, colors, cmapmaxval);
+            break;
+        default:
+            if (mapfile == NULL)
+                floyd = 0;          /* would only slow down conversion */
+            ppm_to_std(ifP, cols, rows, maxval, colormap, colors, 
+                       cmapmaxval, MAXCOLORS, nPlanes);
+            break;
+    }
+    pm_close(ifP);
+    return 0;
+}
diff --git a/converter/ppm/ppmtoleaf.c b/converter/ppm/ppmtoleaf.c
new file mode 100644
index 00000000..fa5fdaf5
--- /dev/null
+++ b/converter/ppm/ppmtoleaf.c
@@ -0,0 +1,250 @@
+/* ppmtoleaf.c - read a portable pixmap and produce a ileaf img file
+ *
+ * Copyright (C) 1994 by Bill O'Donnell.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  This software is provided "as is" without express or
+ * implied warranty.
+ *
+ * Known problems: pgms are not converted to leaf grayscales; they are
+ * converted to 8-bit color images with all gray for colors.
+ * 
+ */
+
+#include <stdio.h>
+#include "ppm.h"
+
+#define MAXCOLORS 256
+
+pixel **pixels;
+colorhash_table cht;
+
+int Red[MAXCOLORS], Green[MAXCOLORS], Blue[MAXCOLORS];
+
+
+
+static int
+colorstobpp( colors )
+int colors;
+{
+    int bpp;
+    
+    if ( colors <= 2 )
+	bpp = 1;
+    else if ( colors <= 256 )
+	bpp = 8;
+    else
+	bpp = 24;
+    return bpp;
+}
+
+
+
+static int
+GetPixel( x, y )
+int x, y;
+{
+    int color;
+    
+    color = ppm_lookupcolor( cht, &pixels[y][x] );
+    return color;
+}
+
+
+
+/* OK, this routine is not wicked efficient, but it is simple to follow
+   and it works. */
+static void
+leaf_writeimg(width, height, depth, ncolors, maxval)
+int width;
+int height;
+int depth;
+int ncolors;
+{
+    int i,row,col;
+    
+    /* NOTE: byte order in ileaf img file fmt is big-endian, always! */
+    
+    /* magic */
+    fputc(0x89, stdout);
+    fputc(0x4f, stdout);
+    fputc(0x50, stdout);
+    fputc(0x53, stdout);
+    
+    /* version 4 */
+    fputc(0x00, stdout);
+    fputc(0x04, stdout);
+    
+    /* h resolution: pixels/inch: say 75=screen resolution */
+    fputc(0x00, stdout);
+    fputc(75, stdout);
+    
+    /* v resolution: pixels/inch: say 75=screen resolution */
+    fputc(0x00, stdout);
+    fputc(75, stdout);
+    
+    /* unique id, could be anything */
+    fputc(0x01, stdout);
+    fputc(0x02, stdout);
+    fputc(0x03, stdout);
+    fputc(0x04, stdout);
+    
+    /* x offset, always zero */
+    fputc(0x00, stdout);
+    fputc(0x00, stdout);
+    
+    /* y offset, always zero */
+    fputc(0x00, stdout);
+    fputc(0x00, stdout);
+    
+    /* dimensions 64k x 64k max */
+    fputc((unsigned char)((width >> 8) & 0x00ff), stdout);
+    fputc((unsigned char)(width  & 0x00ff), stdout);
+    fputc((unsigned char)((height >> 8) & 0x00ff), stdout);
+    fputc((unsigned char)(height  & 0x00ff), stdout);
+    
+    /* depth */
+    fputc(0x00, stdout);
+    fputc((unsigned char)depth, stdout);
+    
+    /* compressed, 0=uncompressed, 1=compressed */
+    fputc(0x00, stdout);
+    
+    /* format, mono/gray = 0x20000000, RGB=0x29000000 */
+    if (depth == 1)
+	fputc(0x20, stdout);
+    else
+	fputc(0x29, stdout);
+    fputc(0x00, stdout);
+    fputc(0x00, stdout);
+    fputc(0x00, stdout);
+    
+    /* colormap size */
+    if (depth == 8)
+    {
+	fputc((unsigned char)((ncolors >> 8) & 0x00ff), stdout);
+	fputc((unsigned char)(ncolors  & 0x00ff), stdout);
+	for (i=0; i<256; i++)
+	    fputc((unsigned char) Red[i]*255/maxval, stdout);
+	for (i=0; i<256; i++)
+	    fputc((unsigned char) Green[i]*255/maxval, stdout);
+	for (i=0; i<256; i++)
+	    fputc((unsigned char) Blue[i]*255/maxval, stdout);
+	
+	for (row=0; row<height; row++) 
+	{
+	    for (col=0; col<width; col++) 
+		fputc(GetPixel(col, row), stdout);
+	    if (width % 2)
+		fputc(0x00, stdout); /* pad to 2-bytes */
+	}
+    } else if (depth == 1) {
+	/* mono image */
+	/* no colormap */
+	fputc(0x00, stdout);
+	fputc(0x00, stdout);
+
+	for (row=0; row<height; row++) 
+	{
+	    unsigned char bits = 0;
+	    for (col=0; col<width; col++) {
+		if (GetPixel(col,row))
+		    bits |= (unsigned char) (0x0080 >> (col % 8));
+		if (((col + 1) % 8) == 0)  {
+		    fputc(bits, stdout);
+		    bits = 0;
+		}
+	    }
+	    if ((width % 8) != 0)
+		fputc(bits, stdout);
+	    if ((width % 16) && (width % 16) <= 8)
+		fputc(0x00, stdout);  /* 16 bit pad */
+	}
+    } else {
+	/* no colormap, direct or true color (24 bit) image */
+	fputc(0x00, stdout);
+	fputc(0x00, stdout);
+	
+	for (row=0; row<height; row++) 
+	{
+	    for (col=0; col<width; col++) 
+		fputc(pixels[row][col].r * 255 / maxval, stdout);
+	    if (width % 2)
+		fputc(0x00, stdout); /* pad to 2-bytes */
+	    for (col=0; col<width; col++) 
+		fputc(pixels[row][col].g * 255 / maxval, stdout);
+	    if (width % 2)
+		fputc(0x00, stdout); /* pad to 2-bytes */
+	    for (col=0; col<width; col++) 
+		fputc(pixels[row][col].b * 255 / maxval, stdout);
+	    if (width % 2)
+		fputc(0x00, stdout); /* pad to 2-bytes */
+	}
+    }
+}
+
+
+
+int
+main( argc, argv )
+int argc;
+char *argv[];
+{
+    FILE *ifd;
+    int argn, rows, cols, colors, i, BitsPerPixel;
+    pixval maxval;
+    colorhist_vector chv;
+    const char * const usage = "[ppmfile]";
+    
+    ppm_init(&argc, argv);
+    
+    argn = 1;
+    
+    if ( argn < argc )
+    {
+	ifd = pm_openr( argv[argn] );
+	argn++;
+    } else
+	ifd = stdin;
+    
+    if ( argn != argc )
+	pm_usage( usage );
+    
+    pixels = ppm_readppm( ifd, &cols, &rows, &maxval );
+    
+    pm_close( ifd );
+    
+    /* Figure out the colormap. */
+    fprintf( stderr, "(Computing colormap..." );
+    fflush( stderr );
+    chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &colors );
+    if ( chv != (colorhist_vector) 0 )
+    {
+	fprintf( stderr, "  Done.  %d colors found.)\n", colors );
+	
+	for ( i = 0; i < colors; i++ )
+	{
+	    Red[i] = (int) PPM_GETR( chv[i].color );
+	    Green[i] = (int) PPM_GETG( chv[i].color );
+	    Blue[i] = (int) PPM_GETB( chv[i].color );
+	}
+	BitsPerPixel = colorstobpp( colors );
+	
+	/* And make a hash table for fast lookup. */
+	cht = ppm_colorhisttocolorhash( chv, colors );
+	ppm_freecolorhist( chv );
+    } else {
+	BitsPerPixel = 24;
+	fprintf( stderr, "  Done.  24-bit true color %d color image.)\n", colors );
+    }
+    
+    leaf_writeimg(cols, rows, BitsPerPixel, colors, maxval);
+    
+    return( 0 );
+}
+
+
+
diff --git a/converter/ppm/ppmtolj.c b/converter/ppm/ppmtolj.c
new file mode 100644
index 00000000..7ed814ed
--- /dev/null
+++ b/converter/ppm/ppmtolj.c
@@ -0,0 +1,297 @@
+/* ppmtolj.c - convert a portable pixmap to an HP PCL 5 color image
+**
+** Copyright (C) 2000 by Jonathan Melvin (jonathan.melvin@heywood.co.uk)
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <string.h>
+
+#include "ppm.h"
+
+static int compress_row_delta (unsigned char *op, unsigned char *prev_op, 
+                               unsigned char *cp, int bufsize);
+
+#define C_RESET                 "\033E"
+#define C_PRESENTATION          "\033*r%dF"
+#define C_PRESENTATION_LOGICAL  0
+#define C_PRESENTATION_PHYSICAL 3
+#define C_GAMMA                 "\033*t%dI"
+#define C_IMAGE_WIDTH           "\033*r%dS"
+#define C_IMAGE_HEIGHT          "\033*r%dT"
+#define C_DATA_PLANES           "\033*r%dU"
+#define C_TRANS_MODE            "\033*b%dM"
+#define C_TRANS_MODE_STD        0 /*compression modes*/
+#define C_TRANS_MODE_RLE        1 /*no good for rgb*/
+#define C_TRANS_MODE_TIFF       2 /*no good for rgb*/
+#define C_TRANS_MODE_DELTA      3 /*only on to use for rgb values*/
+#define C_CONFIG_IMAGE_DATA     "\033*v6W"
+#define C_SEND_ROW              "\033*b%dW"
+#define C_BEGIN_RASTER          "\033*r%dA"
+#define C_BEGIN_RASTER_CUR      1
+#define C_END_RASTER            "\033*r%dC"
+#define C_END_RASTER_UNUSED     0
+#define C_RESOLUTION            "\033*t%dR"
+#define C_RESOLUTION_300DPI     300
+#define C_MOVE_X                "\033*p+%dX"
+#define C_MOVE_Y                "\033*p+%dY"
+#define C_LEFT_MARGIN           "\033*r%dA"
+#define C_Y_OFFSET              "\033*b%dY"
+
+
+/*
+ * delta encoding. 
+ */
+/*
+op row buffer
+prev_op     previous row buffer
+bufsize     length of row
+cp          buffer for compressed data
+*/
+static int
+compress_row_delta(op, prev_op, cp, bufsize)
+unsigned char *op, *prev_op, *cp;
+int bufsize;
+{
+    int burstStart, burstEnd, burstCode, mustBurst, ptr, skip, skipped, code;
+    int deltaBufferIndex = 0;
+    if (memcmp(op, prev_op , bufsize/*rowBufferIndex*/) == 0) 
+        return 0; /* exact match, no deltas required */
+
+    ptr = 0;
+    skipped = 0;
+    burstStart = -1;
+    burstEnd = -1;
+    mustBurst = 0;
+    while (ptr < bufsize/*rowBufferIndex*/) 
+    {
+        skip = 0;
+        if (ptr == 0 || skipped == 30 || op[ptr] != prev_op[ptr] ||
+            (burstStart != -1 && ptr == bufsize - 1)) 
+        {
+            /* we want to output this byte... */
+            if (burstStart == -1) 
+            {
+                burstStart = ptr;
+            }
+            if (ptr - burstStart == 7 || ptr == bufsize - 1) 
+            {
+                /* we have to output it now... */
+                burstEnd = ptr;
+                mustBurst = 1;
+            }
+        } 
+        else 
+        {
+            /* duplicate byte, we can skip it */
+            if (burstStart != -1) 
+            {
+                burstEnd = ptr - 1;
+                mustBurst = 1;
+            }
+            skip = 1;
+        }
+        if (mustBurst) 
+        {
+            burstCode = burstEnd - burstStart; /* 0-7 means 1-8 bytes follow */
+            code = (burstCode << 5) | skipped;
+            cp[deltaBufferIndex++] = (char) code;
+            memcpy(cp+deltaBufferIndex, op+burstStart, burstCode + 1);
+            deltaBufferIndex += burstCode + 1;
+            burstStart = -1;
+            burstEnd = -1;
+            mustBurst = 0;
+            skipped = 0;
+        }
+        if (skip) 
+        {
+            skipped ++;
+        }
+        ptr ++;
+    }
+    return deltaBufferIndex;
+}
+
+
+int main(int argc, char *argv[]) {
+    pixel * pixelrow;
+    FILE *ifp;
+    int argn, rows, cols, r, c, k;
+    pixval maxval;
+    unsigned char *obuf, *op, *cbuf, *previous_obuf;
+    int format;
+    int gamma = 0;
+    int mode = C_TRANS_MODE_STD;
+    int currentmode = 0;
+    int floating = 0;  /* suppress the ``ESC & l 0 E'' ? */
+    int resets = 3;    /* bit mask for when to emit printer reset seq */
+
+    int resolution = C_RESOLUTION_300DPI;
+
+    char CID[6] =  { 0, 3, 0, 8, 8, 8 }; 
+        /*data for the configure image data command*/
+
+    const char * const usage = "[-noreset][-float][-delta][-gamma <val>] [-resolution N] "
+        "[ppmfile]\n\tresolution = [75|100|150|300|600] (dpi)";
+
+    ppm_init( &argc, argv );
+
+    argn = 1;
+    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
+    {
+        if( pm_keymatch( argv[argn], "-resolution", 2 ) && argn + 1 < argc )
+        {
+            ++argn;
+            if ( argn == argc || sscanf( argv[argn], "%d", &resolution ) != 1 )
+                pm_usage( usage );
+        }
+        else if ( pm_keymatch(argv[argn],"-gamma",2) && argn + 1 < argc )
+        {
+            ++argn;
+            if ( sscanf( argv[argn], "%d",&gamma ) != 1 )
+                pm_usage( usage );
+        }
+        else if (pm_keymatch(argv[argn],"-delta",2))
+            mode = C_TRANS_MODE_DELTA;
+        else if (pm_keymatch(argv[argn],"-float",2))
+            floating = 1;
+        else if (pm_keymatch(argv[argn],"-noreset",2))
+            resets = 0;
+
+        else
+            pm_usage( usage );
+        ++argn;
+    }
+
+    if ( argn < argc )
+    {
+        ifp = pm_openr( argv[argn] );
+        ++argn;
+    }
+    else
+        ifp = stdin;
+
+    if ( argn != argc )
+        pm_usage( usage );
+
+    ppm_readppminit( ifp, &cols, &rows, &maxval, &format );
+    pixelrow = ppm_allocrow( cols );
+
+    obuf = (unsigned char *) pm_allocrow(cols * 3, sizeof(unsigned char));
+    cbuf = (unsigned char *) pm_allocrow(cols * 6, sizeof(unsigned char));
+    if (mode == C_TRANS_MODE_DELTA)
+    {
+        previous_obuf = 
+            (unsigned char *) pm_allocrow(cols * 3, sizeof(unsigned char));
+        memset(previous_obuf, 0, cols * 3);
+    }
+
+    if(resets & 1)
+    {
+        /* Printer reset. */
+        printf(C_RESET);
+    }
+
+    if(!floating)
+    {
+        /* Ensure top margin is zero */
+        printf("\033&l0E");
+    }
+
+    /*Set Presentation mode*/
+    (void) printf(C_PRESENTATION, C_PRESENTATION_PHYSICAL);
+    /* Set the resolution */
+    (void) printf(C_RESOLUTION, resolution);
+    /* Set raster height*/
+    (void) printf(C_IMAGE_HEIGHT, rows);
+    /* Set raster width*/
+    (void) printf(C_IMAGE_WIDTH, cols);
+    /* set left margin to current x pos*/
+    /*(void) printf(C_LEFT_MARGIN, 1);*/
+    /* set the correct color mode */
+    (void) printf(C_CONFIG_IMAGE_DATA);
+    (void) fwrite(CID, 1, 6, stdout);
+    /* Start raster graphics */
+    (void) printf(C_BEGIN_RASTER, C_BEGIN_RASTER_CUR);  /*posscale);*/
+    /* set Y offset to 0 */
+    (void) printf(C_Y_OFFSET, 0);
+/*  
+    if (xoff)
+        (void) printf(C_MOVE_X, xoff);
+    if (yoff)
+        (void) printf(C_MOVE_Y, yoff);
+*/
+    /* Set raster compression */
+    (void) printf(C_TRANS_MODE, mode);
+    currentmode = mode;
+    
+    if(gamma)
+        (void) printf(C_GAMMA,   gamma);
+    
+    for (r = 0; r < rows; r++)
+    {
+        ppm_readppmrow(ifp, pixelrow, cols, maxval, format);
+
+        /* get a row of data with 3 bytes per pixel */
+        for (c = 0, op = &obuf[-1]; c < cols; c++)
+        {
+            ++op;
+            *op = (PPM_GETR(pixelrow[c])*255)/maxval;
+            ++op;
+            *op = (PPM_GETG(pixelrow[c])*255)/maxval;
+            ++op;
+            *op = (PPM_GETB(pixelrow[c])*255)/maxval;
+        }
+        ++op;
+        k = op - obuf; /*size of row*/
+        /*compress the row if required*/
+        if(mode == C_TRANS_MODE_STD)
+        {/*no compression*/
+            op = obuf;
+        }
+
+        if(mode ==  C_TRANS_MODE_DELTA)
+        {/*delta compression*/
+            int newmode = 0;
+            int deltasize = 
+                compress_row_delta(obuf, previous_obuf, cbuf, cols*3);
+            if(deltasize >= k)/*normal is best?*/
+            {
+                op = obuf;
+            }
+            else /*delta is best*/
+            {
+                k = deltasize;
+                op = cbuf;
+                newmode = C_TRANS_MODE_DELTA;
+            }
+            memcpy(previous_obuf, obuf, cols*3);
+
+            if(currentmode != newmode)
+            {
+                (void) printf(C_TRANS_MODE, newmode);
+                currentmode = newmode;
+            }
+        }
+
+        (void) printf(C_SEND_ROW, k);
+        (void) fwrite(op, 1, k, stdout);
+        
+    }
+
+    (void) printf(C_END_RASTER, C_END_RASTER_UNUSED);
+    if(resets & 2)
+    {
+        /* Printer reset. */
+        printf(C_RESET);
+    }
+    pm_close( ifp );
+    ppm_freerow(pixelrow);
+
+    return 0;
+}
diff --git a/converter/ppm/ppmtomitsu.c b/converter/ppm/ppmtomitsu.c
new file mode 100644
index 00000000..3934ae45
--- /dev/null
+++ b/converter/ppm/ppmtomitsu.c
@@ -0,0 +1,604 @@
+/* ppmtomitsu.c - read a portable pixmap and produce output for the
+**                Mitsubishi S340-10 Thermo-Sublimation Printer
+**                (or the S3410-30 parallel interface)
+**
+** Copyright (C) 1992,93 by S.Petra Zeidler
+** Minor modifications by Ingo Wilken:
+x**  - mymalloc() and check_and_rotate() functions for often used
+**    code fragments.  Reduces code size by a few KB.
+**  - use pm_error() instead of fprintf(stderr)
+**  - localized allocation of colorhastable
+**
+** This software was written for the Max Planck Institut fuer Radioastronomie,
+** Bonn, Germany, Optical Interferometry group
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "ppm.h"
+#include "nstring.h"
+#include "mallocvar.h"
+
+#include "mitsu.h"
+
+
+#include <stdio.h>
+
+#define HASHSIZE 2048
+#define myhash(x) ((PPM_GETR(x)*3 + PPM_GETG(x)*5 + PPM_GETB(x)*7) % HASHSIZE)
+
+typedef struct hashinfo {
+        pixel     color;
+        long      flag;
+        struct hashinfo *next;
+} hashinfo;
+
+#ifdef __STDC__
+static void lineputinit(int cols, int rows, int sharpness, int enlarge, int
+                        copy, struct mediasize medias);
+static void frametransferinit(int cols, int rows, int sharpness, int enlarge,
+                              int copy, struct mediasize medias);
+static void lookuptableinit(int sharpness, int enlarge, int copy,
+                            struct mediasize medias);
+static void lookuptabledata(int cols, int rows, int enlarge,
+                            struct mediasize medias);
+static void check_and_rotate(int cols, int rows, int enlarge,
+                             struct mediasize medias);
+#define CONST const
+#else /*__STDC__*/
+static int lineputinit();
+static int lookuptableinit();
+static int lookuptabledata();
+static int frametransferinit();
+static int check_and_rotate();
+#define CONST
+#endif
+
+#define cmd(arg)           fputc((arg), stdout)
+#define datum(arg)         fputc((char)(arg), stdout)
+#define data(arg,num)      fwrite((arg), sizeof(char), (num), stdout)
+
+
+#ifdef __STDC__
+int main(int argc, char *argv[] )
+#else
+int main( argc, argv )
+    int argc;
+    char* argv[];
+#endif
+    {
+    FILE             *ifp;
+    /*hashinfo         colorhashtable[HASHSIZE];*/
+    struct hashinfo  *hashrun;
+    pixel            *xP;
+    int              argn;
+    bool             dpi300;
+    int              cols, rows, format, col, row;
+    int              sharpness, enlarge, copy, tiny;
+    pixval           maxval;
+    struct mediasize medias;
+    char             media[16];
+    const char * const usage = "[-sharpness <1-4>] [-enlarge <1-3>] [-media <a,a4,as,a4s>] [-copy <1-9>] [-tiny] [-dpi300] [ppmfile]";
+
+    ppm_init(&argc, argv);
+
+    dpi300 = FALSE;
+    argn = 1;
+    sharpness = 32;
+    enlarge   = 1;
+    copy      = 1;
+    memset(media, '\0', 16);
+    tiny      = FALSE;
+
+    /* check for flags */
+    while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') {
+    if (pm_keymatch(argv[argn], "-sharpness", 2)) {
+        ++argn;
+        if (argn == argc || sscanf(argv[argn], "%d", &sharpness) != 1)
+            pm_usage(usage);
+        else if (sharpness < 1 || sharpness > 4)
+            pm_usage(usage);
+        }
+    else if (pm_keymatch(argv[argn], "-enlarge", 2)) {
+        ++argn;
+        if (argn == argc || sscanf(argv[argn], "%d", &enlarge) != 1)
+            pm_usage(usage);
+        else if (enlarge < 1 || enlarge > 3)
+            pm_usage(usage);
+        }
+    else if (pm_keymatch(argv[argn], "-media", 2)) {
+        ++argn;
+        if (argn == argc || sscanf(argv[argn], "%15s", media) < 1)
+            pm_usage(usage);
+        else if (TOUPPER(media[0]) != 'A')
+            pm_usage(usage);
+    }
+    else if (pm_keymatch(argv[argn], "-copy", 2)) {
+        ++argn;
+        if (argn == argc || sscanf(argv[argn], "%d", &copy) != 1)
+            pm_usage(usage);
+        else if (copy < 1 || copy > 9)
+            pm_usage(usage);
+        }
+    else if (pm_keymatch(argv[argn], "-dpi300", 2))
+        dpi300 = TRUE;
+    else if (pm_keymatch(argv[argn], "-tiny", 2))
+        tiny = TRUE;
+    else
+        pm_usage(usage);
+        ++argn;
+    }
+
+    if (argn < argc) {
+        ifp = pm_openr(argv[argn]);
+        ++argn;
+    }
+    else
+        ifp = stdin;
+
+    if (argn != argc)
+        pm_usage(usage);
+
+    if (TOUPPER(media[0]) == 'A')
+        switch (TOUPPER(media[1])) {
+        case 'S':
+            medias = MSize_AS;
+            break;
+        case '4':
+            if(TOUPPER(media[2]) == 'S')
+                medias = MSize_A4S;
+            else {
+                medias = MSize_A4;
+            }
+            break;
+        default:
+            medias = MSize_A;
+        }
+    else
+        medias = MSize_User;
+
+        if (dpi300) {
+                medias.maxcols *= 2;
+                medias.maxrows *= 2;
+        }
+
+    if (tiny) {
+        pixel            *pixelrow;
+        char             *redrow, *greenrow, *bluerow;
+
+        ppm_readppminit(ifp, &cols, &rows, &maxval, &format);
+        pixelrow = (pixel *) ppm_allocrow(cols);
+        MALLOCARRAY_NOFAIL(redrow, cols);
+        MALLOCARRAY_NOFAIL(greenrow, cols);
+        MALLOCARRAY_NOFAIL(bluerow, cols);
+        lineputinit(cols, rows, sharpness, enlarge, copy, medias);
+
+        for ( row = 0; row < rows; ++row ) {
+            ppm_readppmrow(ifp, pixelrow, cols, maxval, format);
+            switch(PPM_FORMAT_TYPE(format)) {
+            /* color */
+            case PPM_TYPE:
+                for (col = 0, xP = pixelrow; col < cols; col++, xP++) {
+                    /* First red. */
+                    redrow[col] = PPM_GETR(*xP);
+                    /* Then green. */
+                    greenrow[col] = PPM_GETG(*xP);
+                    /* And blue. */
+                    bluerow[col] = PPM_GETB(*xP);
+                }
+                data(redrow,   cols);
+                data(greenrow, cols);
+                data(bluerow,  cols);
+                break;
+            /* grayscale */
+            default:
+                for (col = 0, xP = pixelrow; col < cols; col++, xP++)
+                    bluerow[col] = PPM_GETB(*xP);
+                data(bluerow, cols);
+                data(bluerow, cols);
+                data(bluerow, cols);
+                break;
+            }
+        }
+        pm_close(ifp);
+    }
+    else {
+        pixel            **pixelpic;
+        int              colanz, colval;
+        int                 i;
+        colorhist_vector table;
+
+        ppm_readppminit( ifp, &cols, &rows, &maxval, &format );
+        pixelpic = ppm_allocarray( cols, rows );
+        for (row = 0; row < rows; row++)
+            ppm_readppmrow( ifp, pixelpic[row], cols, maxval, format );
+        pm_close(ifp);
+
+        /* first check wether we can use the lut transfer */
+
+        table = ppm_computecolorhist(pixelpic, cols, rows, MAXLUTCOL+1, 
+                                     &colanz);
+        if (table != NULL) {
+            hashinfo *colorhashtable;
+
+            MALLOCARRAY_NOFAIL(colorhashtable, HASHSIZE);
+            for (i=0; i<HASHSIZE; i++) {
+                colorhashtable[i].flag = -1;
+                colorhashtable[i].next = NULL;
+            }
+
+            /* we can use the lookuptable */
+            pm_message("found %d colors - using the lookuptable-method",
+                       colanz);
+            lookuptableinit(sharpness, enlarge, copy, medias);
+            switch(PPM_FORMAT_TYPE(format)) {
+            /* color */
+            case PPM_TYPE:
+                for (colval=0; colval<colanz; colval++) {
+                    cmd('$');
+                    datum(colval);
+                    datum(PPM_GETR((table[colval]).color));
+                    datum(PPM_GETG((table[colval]).color));
+                    datum(PPM_GETB((table[colval]).color));
+
+                    hashrun = &colorhashtable[myhash((table[colval]).color)];
+                    if (hashrun->flag == -1) {
+                        hashrun->color = (table[colval]).color;
+                        hashrun->flag  = colval;
+                    }
+                    else {
+                        while (hashrun->next != NULL)
+                            hashrun = hashrun->next;
+                        MALLOCVAR_NOFAIL(hashrun->next);
+                        hashrun = hashrun->next;
+                        hashrun->color = (table[colval]).color;
+                        hashrun->flag  = colval;
+                        hashrun->next  = NULL;
+                    }
+                }
+                break;
+            /* other */
+            default:
+                for (colval=0; colval<colanz; colval++) {
+                    cmd('$');
+                    datum(colval);
+                    datum(PPM_GETB((table[colval]).color));
+                    datum(PPM_GETB((table[colval]).color));
+                    datum(PPM_GETB((table[colval]).color));
+
+                    hashrun = &colorhashtable[myhash((table[colval]).color)];
+                    if (hashrun->flag == -1) {
+                        hashrun->color = (table[colval]).color;
+                        hashrun->flag  = colval;
+                    }
+                    else {
+                        while (hashrun->next != NULL)
+                            hashrun = hashrun->next;
+                        MALLOCVAR_NOFAIL(hashrun->next);
+                        hashrun = hashrun->next;
+                        hashrun->color = (table[colval]).color;
+                        hashrun->flag  = colval;
+                        hashrun->next  = NULL;
+                    }
+                }
+            }
+            lookuptabledata(cols, rows, enlarge, medias);
+            for (row=0; row<rows; row++) {
+                xP = pixelpic[row];
+                for (col=0; col<cols; col++, xP++) {
+                    hashrun = &colorhashtable[myhash(*xP)];
+                    while (!PPM_EQUAL((hashrun->color), *xP))
+                        if (hashrun->next != NULL)
+                            hashrun = hashrun->next;
+                        else {
+                            pm_error("you just found a lethal bug.");
+                        }
+                    datum(hashrun->flag);
+                }
+            }
+            free(colorhashtable);
+        }
+        else {
+        /* $#%@^!& no lut possible, so send the pic as 24bit */
+            pm_message("found too many colors for fast lookuptable mode");
+            frametransferinit(cols, rows, sharpness, enlarge, copy, medias);
+            switch(PPM_FORMAT_TYPE(format)) {
+            /* color */
+            case PPM_TYPE:
+                COLORDES(RED);
+                DATASTART;                    /* red coming */
+                for (row=0; row<rows; row++) {
+                    xP = pixelpic[row];
+                    for (col=0; col<cols; col++, xP++)
+                        datum(PPM_GETR(*xP));
+                }
+                COLORDES(GREEN);
+                DATASTART;                    /* green coming */
+                for (row=0; row<rows; row++) {
+                    xP = pixelpic[row];
+                    for (col=0; col<cols; col++, xP++)
+                        datum(PPM_GETG(*xP));
+                }
+                COLORDES(BLUE);
+                DATASTART;                    /* blue coming */
+                for (row=0; row<rows; row++) {
+                    xP = pixelpic[row];
+                    for (col=0; col<cols; col++, xP++)
+                        datum(PPM_GETB(*xP));
+                }
+                break;
+            /* grayscale */
+            default:
+                COLORDES(RED);
+                DATASTART;                    /* red coming */
+                for (row=0; row<rows; row++) {
+                    xP = pixelpic[row];
+                    for (col=0; col<cols; col++, xP++)
+                        datum(PPM_GETB(*xP));
+                }
+                COLORDES(GREEN);
+                DATASTART;                    /* green coming */
+                for (row=0; row<rows; row++) {
+                    xP = pixelpic[row];
+                    for (col=0; col<cols; col++, xP++)
+                        datum(PPM_GETB(*xP));
+                }
+                COLORDES(BLUE);
+                DATASTART;                    /* blue coming */
+                for (row=0; row<rows; row++) {
+                    xP = pixelpic[row];
+                    for (col=0; col<cols; col++, xP++)
+                        datum(PPM_GETB(*xP));
+                }
+            }
+        }
+    }
+    PRINTIT;
+    exit(0);
+}
+
+#ifdef __STDC__
+static void lineputinit(int cols, int rows,
+                        int sharpness, int enlarge, int copy,
+                        struct mediasize medias)
+#else /*__STDC__*/
+static int lineputinit(cols, rows, sharpness, enlarge, copy, medias)
+    int cols, rows;
+    int sharpness, enlarge, copy;
+    struct mediasize medias;
+#endif /*__STDC__*/
+{
+    ONLINE;
+    CLRMEM;
+    MEDIASIZE(medias);
+
+    switch (enlarge) {
+    case 2:
+        HENLARGE(ENLARGEx2); /* enlarge horizontal */
+        VENLARGE(ENLARGEx2); /* enlarge vertical */
+        break;
+    case 3:
+        HENLARGE(ENLARGEx3); /* enlarge horizontal */
+        VENLARGE(ENLARGEx3); /* enlarge vertical */
+        break;
+    default:
+        HENLARGE(NOENLARGE); /* enlarge horizontal */
+        VENLARGE(NOENLARGE); /* enlarge vertical */
+    }
+
+    COLREVERSION(DONTREVERTCOLOR);
+    NUMCOPY(copy);
+
+    HOFFINCH('\000');
+    VOFFINCH('\000');
+    CENTERING(DONTCENTER);
+
+    TRANSFERFORMAT(LINEORDER);
+    COLORSYSTEM(RGB);
+    GRAYSCALELVL(BIT_8);
+
+    switch (sharpness) {          /* sharpness :-) */
+    case 0:
+        SHARPNESS(SP_NONE);
+        break;
+    case 1:
+        SHARPNESS(SP_LOW);
+        break;
+    case 2:
+        SHARPNESS(SP_MIDLOW);
+        break;
+    case 3:
+        SHARPNESS(SP_MIDHIGH);
+        break;
+    case 4:
+        SHARPNESS(SP_HIGH);
+        break;
+    default:
+        SHARPNESS(SP_USER);
+    }
+    check_and_rotate(cols, rows, enlarge, medias);
+    DATASTART;
+    return;
+}
+
+#ifdef __STDC__
+static void lookuptableinit(int sharpness, int enlarge, int copy,
+                            struct mediasize medias)
+#else /*__STDC__*/
+static int lookuptableinit(sharpness, enlarge, copy, medias)
+    int sharpness, enlarge, copy;
+    struct mediasize medias;
+#endif /*__STDC__*/
+{
+    ONLINE;
+    CLRMEM;
+    MEDIASIZE(medias);
+
+    switch (enlarge) {
+    case 2:
+        HENLARGE(ENLARGEx2); /* enlarge horizontal */
+        VENLARGE(ENLARGEx2); /* enlarge vertical */
+        break;
+    case 3:
+        HENLARGE(ENLARGEx3); /* enlarge horizontal */
+        VENLARGE(ENLARGEx3); /* enlarge vertical */
+        break;
+    default:
+        HENLARGE(NOENLARGE); /* enlarge horizontal */
+        VENLARGE(NOENLARGE); /* enlarge vertical */
+    }
+
+    COLREVERSION(DONTREVERTCOLOR);
+    NUMCOPY(copy);
+
+    HOFFINCH('\000');
+    VOFFINCH('\000');
+    CENTERING(DONTCENTER);
+
+    TRANSFERFORMAT(LOOKUPTABLE);
+
+    switch (sharpness) {          /* sharpness :-) */
+    case 0:
+        SHARPNESS(SP_NONE);
+        break;
+    case 1:
+        SHARPNESS(SP_LOW);
+        break;
+    case 2:
+        SHARPNESS(SP_MIDLOW);
+        break;
+    case 3:
+        SHARPNESS(SP_MIDHIGH);
+        break;
+    case 4:
+        SHARPNESS(SP_HIGH);
+        break;
+    default:
+        SHARPNESS(SP_USER);
+    }
+
+    LOADLOOKUPTABLE;
+    return;
+}
+
+#ifdef __STDC__
+static void lookuptabledata(int cols, int rows, int enlarge,
+                                                        struct mediasize medias)
+#else /*__STDC__*/
+static int lookuptabledata(cols, rows, enlarge, medias)
+    int   rows, cols;
+    int   enlarge;
+    struct mediasize medias;
+#endif /*__STDC__*/
+{
+    DONELOOKUPTABLE;
+    check_and_rotate(cols, rows, enlarge, medias);
+    DATASTART;
+    return;
+}
+
+#ifdef __STDC__
+static void frametransferinit(int cols, int rows, int sharpness,
+                              int enlarge, int copy, struct mediasize medias)
+#else
+static int frametransferinit(cols, rows, sharpness, enlarge, copy, medias)
+
+    int     rows, cols;
+    int     sharpness, enlarge, copy;
+    struct mediasize medias;
+#endif
+{
+    ONLINE;
+    CLRMEM;
+    MEDIASIZE(medias);
+
+    switch (enlarge) {
+    case 2:
+        HENLARGE(ENLARGEx2); /* enlarge horizontal */
+        VENLARGE(ENLARGEx2); /* enlarge vertical */
+        break;
+    case 3:
+        HENLARGE(ENLARGEx3); /* enlarge horizontal */
+        VENLARGE(ENLARGEx3); /* enlarge vertical */
+        break;
+    default:
+        HENLARGE(NOENLARGE); /* enlarge horizontal */
+        VENLARGE(NOENLARGE); /* enlarge vertical */
+    }
+
+    COLREVERSION(DONTREVERTCOLOR);
+    NUMCOPY(copy);
+
+    HOFFINCH('\000');
+    VOFFINCH('\000');
+    CENTERING(DONTCENTER);
+
+    TRANSFERFORMAT(FRAMEORDER);
+    COLORSYSTEM(RGB);
+    GRAYSCALELVL(BIT_8);
+
+    switch (sharpness) {          /* sharpness :-) */
+    case 0:
+        SHARPNESS(SP_NONE);
+        break;
+    case 1:
+        SHARPNESS(SP_LOW);
+        break;
+    case 2:
+        SHARPNESS(SP_MIDLOW);
+        break;
+    case 3:
+        SHARPNESS(SP_MIDHIGH);
+        break;
+    case 4:
+        SHARPNESS(SP_HIGH);
+        break;
+    default:
+        SHARPNESS(SP_USER);
+    }
+    check_and_rotate(cols, rows, enlarge, medias);
+    return;
+}
+
+
+#ifdef __STDC__
+static void
+check_and_rotate(int cols, int rows, int enlarge, struct mediasize medias)
+#else
+static int
+check_and_rotate(cols, rows, enlarge, medias)
+    int cols, rows, enlarge;
+    struct mediasize medias;
+#endif
+{
+    if (cols > rows) {
+        ROTATEIMG(DOROTATE);                        /* rotate image */
+        if (enlarge*rows > medias.maxcols || enlarge*cols > medias.maxrows) {
+            pm_error("Image too large, MaxPixels = %d x %d", medias.maxrows, medias.maxcols);
+        }
+        HPIXELS(cols);
+        VPIXELS(rows);
+        HPIXELSOFF((medias.maxcols/enlarge - rows)/2);
+        VPIXELSOFF((medias.maxrows/enlarge - cols)/2);
+        pm_message("rotating image for output");
+    }
+    else {
+        ROTATEIMG(DONTROTATE);
+        if (enlarge*rows > medias.maxrows || enlarge*cols > medias.maxcols) {
+            pm_error("Image too large, MaxPixels = %d x %d", medias.maxrows, medias.maxcols);
+        }
+        HPIXELS(cols);
+        VPIXELS(rows);
+        HPIXELSOFF((medias.maxcols/enlarge - cols)/2);
+        VPIXELSOFF((medias.maxrows/enlarge - rows)/2);
+    }
+}
+
diff --git a/converter/ppm/ppmtompeg/BUGS b/converter/ppm/ppmtompeg/BUGS
new file mode 100644
index 00000000..64269dfb
--- /dev/null
+++ b/converter/ppm/ppmtompeg/BUGS
@@ -0,0 +1,43 @@
+Known BUGS:
+
+1. <fixed>
+
+2. rate control and specifics files interact badly
+
+3. using REMOTE parallel execution:
+   `command` to find INPUT files does not work
+   SPECIFICS_FILE (CDL_FILE)
+   USER_DATA 
+     dont work (should send the files over the socket, but do not
+
+4. using any sort of parallel code:
+     REFERENCE_FRAME	DECODED
+   does not work
+
+5. Cannot use both STDIN and CDL_FILEs (ok, since it doesnt make sense...)
+
+6. <fixed>
+
+7. Patterns of BBI...  seem to cause problems for parallel encoding.  
+   I believe the bugs are fixed for sequential, but if you hit one, let us
+   know!
+
+8. Sometimes parallel encoding results in a zero size being written into
+   the stream.  To fix, specify SIZE of the actual size.
+
+8.1 (related? minor, anyway) in parallel is also writes the Quant matrix
+   even when it is the default one
+
+9. It is unclear (to us anyway) if we should generate Y's from 0..255 or 16..224
+
+10. <fixed>
+
+11. <fixed>
+
+12. reading JPEG from stdin, the encoder will sometimes stop before the end
+of the stream (if the images are smaller than half the buffer size).
+
+13. Sometimes INPUT_CONVERT * is not good (requires "cat *"), in a parallel
+case reported by a user it was needed.
+
+14. Parallel encoding often generates improper temporal_ref's...
diff --git a/converter/ppm/ppmtompeg/CHANGES b/converter/ppm/ppmtompeg/CHANGES
new file mode 100644
index 00000000..fffa7a65
--- /dev/null
+++ b/converter/ppm/ppmtompeg/CHANGES
@@ -0,0 +1,180 @@
+Changes chronology
+------------------
+1.5b Release	August 1995
+	- added date to default USER_DATA string
+	- made prototypes happier
+	- renamed SPECIFICS_FILE to CDL_FILE
+	- fixed bit_rate param in constrained param setting (was too restrictive)
+	- fixed obscure P-block motion vector bug
+	- added a file name replication:
+	    foo.ppm  [1-10] will make 10 copies of foo.ppm
+	- generalized fopen calls to specify binary mode (MSDOG)
+	- generalized times calls into elapsed_time to handle different library routines
+	- fixed motion vector bug in bframes; skip block bug
+	- fixed minor bugs in parallel, mpeg, main, iframe, param, ....
+	- accepts extra whitespace at the end of lines in parameter files
+	- "PIXEL FULL" bug in B-frames fixed (caused ghosting)
+        - B-blocks "can I skip" rutine now looks at color too (more ghosting problems)
+        - switched from JPEG4 to JPEG5 (if they crash for you,
+                  do a -DJPEG4 for the encoder, and get jpeg4.tar.gz off our site)
+        - combine server (parallel) waits longer for files to exist (
+                         thanks to apian@ise.fhg.de)
+	- killing the parallel server kills the clients!!!! (apian@ise.fhg.de)
+
+1.5 Release	8 May 1995  Changes since version 1.3 release (to 1.5)
+= BUG FIXES
+	- removed SetBlocksPerSlice() from GOPStoMPEG
+	- don't access bb after freeing
+	- non-integer frame rates now work for all machines
+	- fixed parsing of -mv_histogram
+	- fixed numPadding bug (file name problem)
+	- fixed full pixel assertation bug
+        - corrected ASPECT_RATIO bug (was forced to 1)
+	- buffer size is now set correctly
+        - complains when file is too small
+	- fixed bug with non-ranged INPUT_FILE names
+	- forced MB at start/end of slice in P-frame bug fixed
+	- Actually evaluates the constrained parameter settings
+	- fixed a Cr/Cb mixup in bframe.c
+
+= NEW FEATURES
+	- rate control
+	- optional B search range different from P range
+	- can read images from stdin, allowing encoding on-the-fly
+        - Does Y files (no U or V)
+	- New way of specifying files `cmd`, like `ls jan-*-94`
+	- Can encode Abekas and Phillips style YUV formats
+	- gamma correction
+	- added -realquiet mode
+	- JMOVIE and JPEGv4 input format fully supported
+        - -float_dct uses the (slower) double precision floating point DCT 
+	- automatically identifies itself in (sequence header) userdata
+	- enforced [0][0] entry of Intra Q-table to be 8
+	- added Jim Boucher's resize code
+        - added -mse flag to print mean squared error on a per-block basis
+	- can specify Sequence header user data
+        - prints version number
+        - finds more skip blocks in GenBFrame
+	- can generate multiple sequence headers (every N GOPs)
+
+= SPEED IMPROVEMENTS
+
+
+= MAINTENANCE
+	- removed mpeg_jrevdct_sparse(), since it wasn't used
+	- added CompileTests();
+	- uses libpnmrw rather than entire pbmplus directory
+        - redid RCS
+        - reorganized [ipb]frame.c to make playing with rate controls easier
+
+= PORTABILITY
+	- added #ifdef's, #ifndef's LINUX'es to ease porting
+
+
+Changes from 1.2 to 1.3
+-----------------------
+= BUG FIXES
+	+ deletes frame files when done with them in parallel version
+	+ fixed bug in ComputeFrameTable
+	+ completion time estimation now is closer to actual time
+	+ modified ComputeSNR to avoid overflow of varOrig
+	+ fixed bug that caused I/P/B times on HP's to be wrong
+	+ fixed bug that made TwoLevel search out of the search window
+	+ fixed bug in -quiet 0
+	+ fixed memory leak when using PNM files
+	+ fixed bug:  crashed when CPU time was 0 (very rare)
+	+ fixed bug in -gop option
+	+ fixed bug in AppendFiles()
+
+= NEW FEATURES
+	+ added FORCE_ENCODE_LAST_FRAME option (allows encoding of all frames)
+	+ added PARALLEL_CHUNK_TAPER option
+	+ added -bit_rate_info option to show bit rate per frame
+	+ added -mv_histogram option to show motion vector histogram
+	+ custom quantization tables allowed
+	+ can specify frame rate, aspect ratio
+
+= SPEED IMPROVEMENTS
+	+ replaced most remaining binary multiplies/divides with shifts
+		(except those concerning negative numbers)
+
+= MAINTENANCE
+	+ got rid of & before array warning in block.c
+	+ got rid of references to srandom, random in pbmplus.h
+	+ undefine 'index' if defined as 'strchr' (since we use index as
+	  variable name)
+	+ modified frame type code to be more flexible
+
+= PORTABILITY
+	+ replaced all bzero() calls with memset() calls
+
+
+1.2 Release	20 October 1993
+
+Changes from 1.1 to 1.2
+-----------------------
+= BUG FIXES
+	+ allow comments in PPM files
+	+ first and last macroblocks in a slice may not be skipped macroblocks
+	+ fixed YUV bug:  should be able to handle dimensions which
+		aren't multiples of 16
+	+ fixed the separate I/O conversion for parallel NFS version
+	+ no_frame_summary can now be last option
+
+= NEW FEATURES
+	+ using DECODED frames as reference frames in parallel
+	  version now works
+	+ implemented multiple I/O Servers
+	+ FORCE_I_ALIGN added
+	+ reorganized Server structure slightly (only I/O Servers
+	  handle individual frame files on disk)
+	+ add option to allow exact division of frames per processor
+	  (PARALLEL_PERFECT)
+
+= SPEED IMPROVEMENTS
+	+ don't start Input server if not needed
+
+= MAINTENANCE
+	+ got rid of niceProcesses line in main.c
+	+ changed write() to SafeWrite() in parallel.c
+	+ commented stuff out after #endif's
+	+ fixed prototype warnings (eliminated non-4-byte args)
+
+
+1.1 Release	August 1993
+
+Changes from 1.0 to 1.1
+-----------------------
+= BUG FIXES
+	+ fixed bug in parallel Server code which didn't htonl() frame
+	  numbers correctly (so it didn't work on little-endian machines)
+	+ fixed bug -- B-frames were always getting just 1 slice
+	+ fixed listen() to use max connections allowed
+	+ fixed noparallel.c so it doesn't complain during non-parallel
+	  execution
+
+= NEW FEATURES
+	+ added level-2 p-search (exhaustive full, then half)
+	+ now prints signal-to-noise ratio
+	+ parallel code complains if absolute path not used for parameter
+	  file
+	+ changed single I/O server into separate Input and Output Servers
+	  (and have Output Server combine frames in parallel)
+
+= SPEED IMPROVEMENTS
+	+ slight improvement in Mpost_QuantZigBlock (.10 ms/call down to .08 ms
+	  on Sparc-10)
+	+ improvement in speed of BlockifyFrame (45.8 ms/call down to 21.2 ms)
+	+ improvement in ComputeMotionBlock (0.02 ms/call down to 0.01 ms)
+	+ improvement in ComputeMotionLumBlock (0.04 ms/call down to 0.02 ms)
+	+ improvement in LumAddMotionError (0.06 ms/call down to 0.05 ms)
+		(changed /2 to >>1)
+	
+= MAINTENANCE
+	+ removed most memory.h references (except jrevdct)
+	+ added CFLAGS lines in Makefile for SVR4.0 and SGI machines
+	+ removed mproto.h
+	+ got rid of printing connection times
+
+
+1.0 Release	July 1993
diff --git a/converter/ppm/ppmtompeg/COPYRIGHT b/converter/ppm/ppmtompeg/COPYRIGHT
new file mode 100644
index 00000000..08216f34
--- /dev/null
+++ b/converter/ppm/ppmtompeg/COPYRIGHT
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ * 
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ * 
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
diff --git a/converter/ppm/ppmtompeg/HISTORY b/converter/ppm/ppmtompeg/HISTORY
new file mode 100644
index 00000000..c9f4932a
--- /dev/null
+++ b/converter/ppm/ppmtompeg/HISTORY
@@ -0,0 +1,313 @@
+The entire ppmtojpeg directory was adapted by Bryan from the package
+mpeg_encode-1.5b-src (subdirectory mpeg_encode) on March 30, 1999.  The 
+program was called mpeg_encode in that package.  It was dated August 16,
+1995 and came from ftp://mm-ftp.cs.berkeley.edu/pub/multimedia/mpeg/
+encode/mpeg_encode-1.5b-src.tar.gz
+
+Changes for Netpbm include:
+
+  - mpeg_encode recognizes two input formats:  "PPM" and "PNM".  For
+    PPM, mpeg_encode parses it itself.  For PNM, it uses the Netpbm
+    PNM library routines.
+    
+    In the Netpbm version, PPM is gone and "PPM" is accepted as a 
+    synonym for "PNM".  For PNM, it uses the PPM (not PNM) Netpbm
+    library routines.
+
+  - mpeg_encode PNM code is broken for maxval != 255 (divides by zero
+    if maxval < 255 and overly quantize if > 255 because PNMtoYUV()
+    uses an integer divisor = maxval/256).
+
+In November 2004, Bryan rewrote large portions of the code so that he
+could read it easily enough to debug some problems.  He eliminated
+long subroutines, global variables, gotos, and the like.
+
+See the file LOGIC for some documentation on how the code works.
+
+
+The following is the README from the aforementioned mpeg_encode subdirectory.
+
+
+                 MPEG-1 Video Software Encoder
+                (Version 1.5; February 1, 1995)
+
+     Lawrence A. Rowe, Kevin Gong, Eugene Hung, Ketan Patel, Steve Smoot
+       and Dan Wallach
+    Computer Science Division-EECS, Univ. of Calif. at Berkeley
+
+This directory contains the freely distributed Berkeley MPEG-1 Video 
+Encoder.  The encoder implements the standard described in the ISO/IEC
+International Standard 11172-2.  The code has been compiled and tested 
+on the following platforms:
+
+ DECstation 5000 and Alpha
+ HP PA-RISC (HP/UX 9.X) (i.e., HP 9000/7XX and 9000/3XX)
+ SGI Indigo running IRIX 5.0.1
+ Sun Sparc (SunOS 4.X)
+
+In addition, Rainer Menes from the Technical University of Munich has
+ported the encoder and decoder to the Macintosh.  You can get that code
+directly from him (menes@statistik.tu-muenchen.de), or from the 
+Berkeley FTP archive (mm-ftp.CS.Berkeley.EDU).  If you decide to port 
+the code to a new architecture, please let us know so that we can 
+incorporate the changes into our sources.
+
+This directory contains everything required to build the encoder
+and run it.  We have included source code, makefiles, binaries
+for selected platforms, documentation, and test data.  Installation 
+instructions are given in the file named src/mpeg_encode/INSTALL.  A man 
+page is given in the file doc/mpeg_encode.1.  A detailed user 
+manual is provided in postscript format in the file doc/user-manual.ps.
+
+The encoder will accept any input file format as long as you provide 
+a script to convert the images to PPM, YUV, JPEG, or JMOVIE format.  Input 
+file processing is described in the file doc/INPUT.FORMAT.  Options to 
+control input file processing and compression parameters are specified in 
+a parameter file.  Very little error processing is done when reading 
+this file.  We suggest you start with the sample parameter file 
+examples/template.param and modify it.  See also examples/default.param.
+
+The convert directory of Mpeg-Tools contains utilities you might find 
+useful including: 
+
+programs to do PPM/YUV conversion and programs to convert Parallax
+XVideo JPEG files into PPM, YUV, or JPEG frames.
+
+The motion vector search window can be specified, including half-pixel
+block matching, in the parameter file.  We have implemented several 
+search algorithms for P-frames including: 1) exhaustive search, 
+2) subsampled search, and 3) logarithmic search.  We have also implemented
+several alternatives for B-frame block matching including: 1) interpolate
+best forward and best backward block, 2) find backward block for best
+forward or vice-versa (called CROSS2), and 3) exhaustive cross product
+(i.e., go out for coffee and a donut!). The search algorithms are controlled
+by options in the parameters file.  For tips on choosing the right search
+technique, see the user manual.
+
+The encoder can be run on one computer (i.e., sequential) or on several
+computers (i.e., parallel).  Our goal is to produce a portable, easy-to-use
+encoder that we can use to encode large volumes of video material for
+the Berkeley VOD system (see paper VodsProp93.ps.Z on the FTP archive).
+The parallelism is done on a sequence of pictures.  In other words, you 
+can spawn one or more children to encode continuous runs pictures. The 
+uncompressed data can be accessed either through NFS or TCP sockets.  
+The goal is to allow you to encode using multiple processors, think 
+spare cycles on workstations, to speed up the encoding time.  Although
+performance depends on the speed of individual processors, the file system
+and network, and the P/B frame search methods, we have encoded 3.75
+frames/second on 8 HP Snakes running in parallel as compared with 0.6
+frames/second on 1 Snake.  These are preliminary results. We are continuing 
+to experiment with and tune the code.  Instructions to run the parallel system 
+are given in the man page and the parallel.param example parameter file.
+
+We have done some tuning to produce a reasonable encoder, but there are
+many more optimizations that we would like to incorporate.  These 
+extensions are listed in the file doc/EXTENSIONS.  If you succeed in 
+implementing any of them, please let us know! 
+
+Send bug reports to:
+
+mpeg-bugs@CS.Berkeley.EDU
+   Problems, questions, or patches should be sent to this address.
+
+Anyone interested in providing financial support for this research or 
+discussing other aspects of this project should contact Larry Rowe at 
+Rowe@CS.Berkeley.EDU (+1 510-642-5117).
+
+This software is freely distributed.  That means, you may use it for 
+any non-commercial purpose.  However, patents are held by several companies 
+on various aspects of the MPEG video standard.  Companies or individuals
+who want to develop commercial products that include this code must
+acquire licenses from these companies.  For information on licensing, see
+Appendix F in the standard.
+
+ACKNOWLEDGEMENTS:
+
+We gratefully thank Hewlett-Packard and Fujitsu who provided financial
+support for this work.  We also want to thank the following people and
+organizations for their help:
+
+    Jef Poskanzer who developed the pbmplus package.
+    ---------
+    Copyright (C) 1989, 1991 by Jef Poskanzer.
+
+    Permission to use, copy, modify, and distribute this software and its
+    documentation for any purpose and without fee is hereby granted, provided
+    that the above copyright notice appear in all copies and that both that
+    copyright notice and this permission notice appear in supporting
+    documentation.  This software is provided "as is" without express or
+    implied warranty.
+    ---------
+
+    Eiichi Kowashi of Intel and Avideh Zakhor of U.C. Berkeley who
+    provided valuable suggestions on motion vector searching.
+
+    Chad Fogg of the University of Washington who has helped us 
+    understand many issues in MPEG coding and decoding.
+
+    Rainer Menes of the Technical University of Munich who has ported the
+    the Berkeley MPEG encoder and decoder to the Macintosh, and he has 
+    provided us with many suggestions to improve the code.
+
+    Robert Safranek of ATT for comments, suggestions, and most of the
+    code for custom quantization tables.
+
+    Jim Boucher of Boston University for jmovie2jpeg.
+
+    The San Diego SuperComputing Center for providing facilities to 
+    develop some of the code contained within.
+
+
+This is the TODO file from the original Berkeley package:
+
+
+TODO list for next release
+--------------------------
+
+Add option to do searches in Cr/Cb as well as Lum blocks
+jpeg5! (below)
+last-frame must be P/I error, and pattern interact badly
+add gnuconfigure so the Makefile is cake
+YUV correct (cf mail below)
+fix the "ifdef BUGGY_CODE" code in bframe.c
+Change sizing stuff so it works with non multiples of 16
+Does "RESIZE WxH" work?  Seems to for PPm but not JPG.  Fix it.  Document it
+mpeg_encode in parallel generates a "zero size warning"  It should 
+    exit(1) here, when not in parallel, and not warn when in parallel.
+     sometimes it generates a 0x0 MPEG!
+Give time estimates for parallel encoding
+Verify YUV file sizes with stat() call (when not STDIN)
+
+--------------------
+
+YUV mail:
+Please have a look on these few lines extracted from the ISO
+mpeg2encode/readpic.c/read_ppm() available on 
+ftp.netcom.com:/pub/cf/cfogg/mpeg2 :
+
+    .........
+
+     /* convert to YUV */
+
+     y = cr*r +cg*g +cb*b;
+     u = cu*(b-y);
+     v = cv*(r-y);
+     yp[j] = (219.0/256.0)*y + 16.5;  /* nominal range : 16..235 */
+     up[j] = (224.0/256.0)*u + 128.5; /* nominal range : 16..240 */
+     vp[j] = (224.0/256.0)*v + 128.5; /* nominal range : 16..240 */
+    ...........
+
+I think there is a slight misunderstanding in the Berkeley's mpeg1 codec about
+what the YUV format looks like, exactly about how to translate from PPM to YUV 
+and vice versa : the dynamic of YUV format has to be reduced as described
+above. Otherwise, on a full color display a Berkeley's MPEG bitstream has not 
+exactly the right colors if played by an ISO compliant player.
+
+Best regards
+
+
+                            Thierry GODIN
+
+--------------------
+And just for fun, kevin's list:
+To-do list	(in no particular order)
+----------
+	- should delete decoded frame files when done with them
+		(need to make sure no one else needs it)
+	- port to CM5
+	- try on Snake cluster, and other clusters (FDDI -- 100Mb/s)
+	- fix bug on snakes (look at header file for queue length)
+	- look at 24-bit display
+	- try having I/O server getting things in order, and asking Master
+		where to send them
+	- bug:  closing connections at end (on DEC 5000)
+	- GOP on space in input list
+	- pnm convolve
+	- telescopic search
+	- include system layer
+	- update documentation
+	- show error images
+	- graphical interface (showing motion vectors, etc.)
+	- use DCT-space when computing error terms
+	- vary the q-scale according to the error term
+	- modify the program to have a finer-grained parallelism option -- we
+	  can probably encode slices in parallel (this will only be useful if
+	  we want to do a few B-frames using exhaustive search)
+	- make search technique stop at x.5, not x.0
+	- pel aspect ratio in parameter file (2.4.3.2)
+	- skipped blocks options?
+	- recover from parallel machine errors?
+	- subsample B-search
+	- bug:  decoded with 1 machine can freeze
+	- malloc bug:  hello.param, with DECODED frames only
+	- portability:
+		times() function
+		popen/pclose
+
+Oh yes, I liked the concept of a spiral for your full search algorithm,
+however I thought this code a little difficult to read.  What about
+using a look up table (pre generated at compile time) to generate
+the coord offsets that would then spiral around the location in question?
+
+	+ change MAXPATHLEN to something else
+	+ put ./ in test in Makefile
+
+Currently, the IPPBPBB sequence is fixed for the entire sequence.  A later
+version should probably either test for scene changes or allow the user to
+specify them.
+
+	+ check all << and >> to make sure they are used properly
+		(note truncation of >>)
+	+ allow variable bit rate
+	+ allow size of video sequences to be set
+	+ make REMOTE usage more clear
+	+ fix bug:  when I-frame only, and decoded, does a lot of extra work
+	+ replace ZAG[i] with a pointer? (in quantization)
+		(and speed up by using 31 times the space (one for each
+		 q-value)
+	+ add interrupt handler to parallel encoder
+	+ should pad black instead of truncating
+	- graph histogram of motion vectors
+	- allow new PATTERN statements inside of file listing
+	- put in run-time checks for size of int32, int16, etc.
+	- replace pnm crap with ppm.dwallach
+	- add option to allow different p-search in b-search
+	- allow -frames option along with parallel option (shouldn't be
+		too difficult)
+	- incorrect compression rates given if do -frames option
+
+	- enforce:  >Hmmm...I will have to look at the standard.  I did find something earlier --
+>"forced updating" makes it illegal to have more than 132 P-frames without an
+>I-frame.  But "forced updating" does not disallow any number of consecutive
+>B-frames.  I'll have to check on limits for GOP lengths...
+
+	- rectangular search windows
+
+	- make parallel stats to 4 digits
+
+One of my friend just fixed the problem.....she 
+retype the whole parameter file and it is working
+now.  I think the problem was there were some 
+spacing problem......for example, if there
+are some space is the line, when the 
+program read in....it just mess everything up.
+
+It really become a problem if there is space after the image name...
+or even after the path name.
+
+	Subject: may not want to >> 4	in postdct.c
+
+------------------------------------------------------------------------
+P.S. In the future versions (is one already been released), you could add
+option for encoder to remove picture after encoding it & therefore saving
+space on disk + option to sleep while picture it's waiting for is not done.
+I've hacked this: (in readframe.c)
+
+        while ((tempfile = fopen(fullFileName, "r")) == NULL) {
+            fprintf(stderr, "Cannot open '%s', retrying...\n", fullFileName);
+            sleep(120);
+        }
+
+        fclose(tempfile);
+
+arijan@kette.fer.uni-lj.si
diff --git a/converter/ppm/ppmtompeg/LOGIC b/converter/ppm/ppmtompeg/LOGIC
new file mode 100644
index 00000000..8c19dc8d
--- /dev/null
+++ b/converter/ppm/ppmtompeg/LOGIC
@@ -0,0 +1,126 @@
+This file describes (a little) how the program works.
+
+PARALLEL MODE
+-------------
+
+In parallel mode, two processes run on the machine where you invoke
+Ppmtompeg: the master server and the combine server.  Sometimes, there's
+a third process called a decode server, but I don't know what that's for
+yet.
+
+Other processes, which are typically on separate machines, one per
+machine, are called encoder processes.  The code normally calls these
+"child" processes and the documentation normally calls them "slave"
+processes.  We really should fix that.
+
+The master server's job is to feed work assignments to the encoder
+processes.  A work assignment is a set of frames to encode.
+
+The combine server's job is to take the encoded output from the encoder
+processes and combine them into a single MPEG movie.
+
+The master process is the first process that exists.  The first thing
+it does is create the combine server.  Then it creates the encoder
+processes.  It creates each encoder process with an initial
+assignment.  The master process then waits in a loop for an encoder
+process to report that it has finished its assignment and gives a new
+assignment to it.
+
+The master process and the combine process both have a listening TCP
+port.  They choose an available port number.  When the master server
+creates the combine server, it passes the master server's TCP listen
+port number to the combine server.  It then waits to hear from the
+combine process what TCP listen port it has chosen.  The combine
+server connects to the master server's listen port and tells it.  The
+master server then passes to each encode server, as it creates it,
+the TCP listen ports of both the master server and the combine server.
+
+When the combine server has processed all the frames, it shuts down.
+It connects to the master server TCP listen port and tells the master
+server it is shutting down.
+
+When an encoder server finishes a frame, it connects to the combine
+server's TCP listen port and tells the combine server that the frame
+is ready.
+
+When an encoder server finishes an assignment, it connects to the master
+server TCP listen port and tells the master it is done, and receives over
+the same connetion its next assignment.  If there is no more work to do,
+the master server instead tells the encoder server just to terminate.
+
+When the master server has told every encoder server to terminate, it
+waits for the combine server to say that it has terminated.  The master
+server then terminates.
+
+To create the combine server, the master server forks and execs
+'ppmtompeg' with the -output_server option.
+
+To create an encoder server, the master server invokes Ssh (or something
+like it) and tells the remote shell to execute 'ppmtompeg' with the
+-child option.
+
+The various processes communicate frame data via a shared filesystem
+(e.g. NFS).  An encoder server grabs the input frames from the shared
+filesystem and writes the encoded frames to it.  The combine server
+grabs the encoded frames from it and writes the MPEG movie file to it.
+The encoded frames go in individual files.  The combine server deletes
+each file after it has copied it to the MPEG movie file.  Use the
+KEEP_TEMP_FILES parameter file statement to keep them around for 
+debugging.
+
+There's also an alternative "remote I/O" method of communication that
+doesn't require a shared filesystem.  I haven't studied that.
+
+The master server and combine server code are in parallel.c.  The
+encoder server code is roughly the same as what the single encoder
+process executes when you aren't running in parallel mode.
+
+
+THE ENCODING PROCEDURE
+----------------------
+
+Encoding is done by ppmtompeg.c, which calls GenMPEGStream in mpeg.c to
+do most of the work.
+
+The encoder goes through the input frames in movie sequence.  But the
+frames don't go into an MPEG stream in movie sequence.  A B frame not
+only can't be encoded without the subsequent I or P frame (reference
+frame); it can't be decoded without it either.  So in the MPEG stream,
+B frames come immediately after the reference frame which comes
+immediately after the B frames in the actual movie.
+
+When the input is from a sequential stream (the only way that's possible
+today is with Standard Input), the encoder saves up B frame input until
+after it has encoded and output the post reference frame.  It does
+this by chaining the input frames to the pre reference frame.  We really
+should clean this up so that a single module handles input of all kinds
+and presents only a sequential stream to the encoder.  The encoder should
+not have special cases for the different types of input.  That input
+module would let the encoder ask from Frame N even if the input is
+sequential.  The input module would buffer frames from the sequential
+input as necessary.
+
+In parallel mode, the combine server takes encoded frames from the
+encoder servers as it comes.  It can be in any order.  The combine
+server knows which frames go where in the output stream and outputs them
+in the proper order.  This is important for two reasons.  First, the
+encoder servers don't finish assignments in the same order in which they
+get them.  Second, when an encoder server gets an assignment that ends
+with a B frame, the combine server has to wait for the encoder server
+that has the next assignment to produce the post reference frame before
+the combine server can do anything with that trailing B frame.
+
+genMPEGStream() encodes a frame at a time, but it inserts a stream
+header and GOP headers where they belong.  genMPEGStream() operates in
+three modes: Whole Stream, GOP, and Just Frames.  In Whole Stream
+mode, it inserts all the required headers.  In GOP mode, it inserts
+GOP headers but not the stream header.  In Just Frames mode, it
+inserts no headers at all.  In parallel mode, genMPEGStream generates
+one frame per file, and only Just Frames mode makes any sense.  We
+really should fix this up so that the encoder always works in Just
+Frames mode and a GOP builder module takes the encoder's output and
+splices it with GOP headers and a stream builder module takes the GOP
+builder's output and adds it to a stream header.  In parallel mode,
+the combine server would run the GOP builder and stream builder
+modules.
+
diff --git a/converter/ppm/ppmtompeg/Makefile b/converter/ppm/ppmtompeg/Makefile
new file mode 100644
index 00000000..5e923fee
--- /dev/null
+++ b/converter/ppm/ppmtompeg/Makefile
@@ -0,0 +1,140 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/ppm/ppmtompeg
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+ifeq ($(JPEGLIB),NONE)
+  # 'nojpeg' is a module that implements all the jpeg access routines as
+  # error messages that tell you we don't have jpeg capability
+  JPEG_MODULE = nojpeg
+  JPEGLIBX =
+else
+  # 'jpeg' is a module that accesses J-movies via the JPEG library.
+  JPEG_MODULE = jpeg
+  JPEGLIBX = $(JPEGLIB)
+endif
+
+INCLUDES = -I$(SRCDIR)/$(SUBDIR)/headers 
+
+ifneq ($(JPEGHDR_DIR),NONE)
+  ifneq ($(JPEGHDR_DIR)x,x)
+    INCLUDES += -I$(JPEGHDR_DIR)
+  endif
+endif
+
+# use -DLONG_32 iff
+#	1) long's are 32 bits and
+#	2) int's are not
+#
+# if you are using a non-ANSI compiler, then use:
+#	-DNON_ANSI_COMPILER
+#
+# one other option:
+#	-DHEINOUS_DEBUG_MODE
+#
+
+MP_BASE_OBJS = mfwddct.o postdct.o huff.o bitio.o mheaders.o
+MP_ENCODE_OBJS = iframe.o pframe.o bframe.o psearch.o bsearch.o block.o 
+MP_OTHER_OBJS = mpeg.o subsample.o param.o rgbtoycc.o \
+	readframe.o combine.o jrevdct.o frame.o fsize.o frametype.o \
+	specifics.o rate.o opts.o input.o gethostname.o
+ifeq ($(OMIT_NETWORK),y)
+  MP_PARALLEL_OBJS = noparallel.o
+else
+  MP_PARALLEL_OBJS = parallel.o psocket.o
+endif
+NONMAIN_OBJS = $(MP_BASE_OBJS) $(MP_OTHER_OBJS) $(MP_ENCODE_OBJS) \
+	      $(MP_PARALLEL_OBJS) $(JPEG_MODULE).o
+OBJECTS = ppmtompeg.o $(NONMAIN_OBJS)
+MERGE_OBJECTS = ppmtompeg.o2 $(NONMAIN_OBJS)
+MP_INCLUDE = mproto.h mtypes.h huff.h bitio.h
+MP_MISC = Makefile huff.table parse_huff.pl
+
+BINARIES = ppmtompeg
+MERGEBINARIES = $(BINARIES)
+SCRIPTS = 
+
+.PHONY: all
+all: ppmtompeg
+
+include $(SRCDIR)/Makefile.common
+
+ifeq ($(NEED_RUNTIME_PATH),Y)
+  LIBOPTR = -runtime
+else
+  LIBOPTR =
+endif
+
+ppmtompeg: $(OBJECTS) $(NETPBMLIB) $(LIBOPT)
+	$(LD) -o $@ $(LDFLAGS) \
+          $(OBJECTS) $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR) $(JPEGLIBX)) \
+	  $(NETWORKLD) $(MATHLIB) $(LDLIBS) \
+	  $(RPATH) $(LADD)
+
+profile: $(OBJECTS) $(NETPBMLIB) $(LIBOPT)
+	$(LD) -o $@ -Bstatic -pg $(LDFLAGS) \
+          $(OBJECTS) $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR) $(JPEGLIBX)) \
+	  $(NETWORKLD) $(MATHLIB) $(LDLIBS) \
+	  $(RPATH) $(LADD)
+
+
+#########
+# OTHER #
+#########
+
+#
+# Perl is necessary if you want to modify the Huffman RLE encoding table.
+#
+PERL = perl
+
+# The following stuff is for the Huffman encoding tables.  It's commented-out
+# because you probably don't want to change this.  If you do, then uncommment
+# it.
+#
+# huff.h: huff.c
+#
+# huff.c: parse_huff.pl huff.table
+#	$(PERL) parse_huff.pl huff.table
+
+MP_ALL_SRCS = $(patsubst %.o, %.c, $(MP_ALL_OBJS))
+
+wc:;		wc -l *.[ch] headers/*.h *.pl *.table
+ci:;		ci -l $(MP_ALL_SRCS) $(MP_INCLUDE) $(MP_MISC)
+tags: $(MP_ALL_SRCS)
+	ctags -t $(MP_ALL_SRCS)
+	etags -f TAGS -t $(MP_ALL_SRCS) headers/*.h
+
+#
+# WARNING: this assumes you're using GNU indent...
+#
+indent:;	indent -T FILE -T int8 -T int16 -T int32 -T uint8 -T uint16 -T uint32  -T BitBucket -T MpegFrame -T Block -T FlatBlock $(MP_ALL_SRCS)
+
+spotless: clean	
+	rm -f huff.c huff.h *.pure.a
+
+FORCE:
+
+# 
+# Copyright (c) 1995 The Regents of the University of California.
+# All rights reserved.
+# 
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose, without fee, and without written agreement is
+# hereby granted, provided that the above copyright notice and the following
+# two paragraphs appear in all copies of this software.
+# 
+# IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+# OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+# CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# 
+# THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+# ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+# PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+# 
diff --git a/converter/ppm/ppmtompeg/bframe.c b/converter/ppm/ppmtompeg/bframe.c
new file mode 100644
index 00000000..5dfb76d3
--- /dev/null
+++ b/converter/ppm/ppmtompeg/bframe.c
@@ -0,0 +1,1347 @@
+/*===========================================================================*
+ * bframe.c
+ *
+ *  Procedures concerned with the B-frame encoding
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "all.h"
+#include <sys/param.h>
+#include <assert.h>
+
+#include "ppm.h"
+
+#include "mtypes.h"
+#include "bitio.h"
+#include "frames.h"
+#include "prototypes.h"
+#include "block.h"
+#include "fsize.h"
+#include "param.h"
+#include "mheaders.h"
+#include "postdct.h"
+#include "rate.h"
+#include "opts.h"
+#include "specifics.h"
+
+extern int **bfmvHistogram;
+extern int **bbmvHistogram;
+
+/*==================*
+ * STATIC VARIABLES *
+ *==================*/
+
+static int32 totalTime = 0;
+static int qscaleB;
+static float    totalSNR = 0.0;
+static float    totalPSNR = 0.0;
+
+static struct bframeStats {
+    unsigned int BSkipped;
+    unsigned int BIBits;
+    unsigned int BBBits;
+    unsigned int Frames;
+    unsigned int FrameBits;
+    unsigned int BIBlocks;
+    unsigned int BBBlocks;
+    unsigned int BFOBlocks;    /* forward only */
+    unsigned int BBABlocks;    /* backward only */
+    unsigned int BINBlocks;    /* interpolate */
+    unsigned int BFOBits;
+    unsigned int BBABits;
+    unsigned int BINBits;
+} bframeStats = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+
+/*====================*
+ * EXTERNAL VARIABLES *
+ *====================*/
+
+extern Block **dct, **dctr, **dctb;
+extern dct_data_type **dct_data;
+#define NO_MOTION 0
+#define MOTION 1
+#define SKIP 2  /* used in useMotion in dct_data */
+
+/*=====================*
+ * INTERNAL PROCEDURES *
+ *=====================*/
+
+static void
+zeroMotion(motion * const motionP) {
+    
+    motionP->fwd.y = motionP->fwd.x = motionP->bwd.y = motionP->bwd.x = 0;
+}
+
+
+
+static motion
+halfMotion(motion const motion) {
+    struct motion half;
+
+    half.fwd.y = motion.fwd.y / 2;
+    half.fwd.x = motion.fwd.x / 2;
+    half.bwd.y = motion.bwd.y / 2;
+    half.bwd.x = motion.bwd.x / 2;
+
+    return half;
+}
+
+
+
+
+/*===========================================================================*
+ *
+ *  compute the block resulting from motion compensation
+ *
+ * RETURNS: motionBlock is modified
+ *
+ * SIDE EFFECTS:    none
+ *
+ * PRECONDITION:    the motion vectors must be valid!
+ *
+ *===========================================================================*/
+static void
+ComputeBMotionBlock(MpegFrame * const prev,
+                    MpegFrame * const next,
+                    int         const by,
+                    int         const bx,
+                    int         const mode,
+                    motion      const motion,
+                    Block *     const motionBlockP,
+                    int         const type) {
+
+    Block prevBlock, nextBlock;
+
+    switch(mode) {
+    case MOTION_FORWARD:
+        switch (type) {
+        case LUM_BLOCK:
+            ComputeMotionBlock(prev->ref_y, by, bx, motion.fwd, motionBlockP);
+            break;
+        case CB_BLOCK:
+            ComputeMotionBlock(prev->ref_cb, by, bx, motion.fwd, motionBlockP);
+            break;
+        case CR_BLOCK:
+            ComputeMotionBlock(prev->ref_cr, by, bx, motion.fwd, motionBlockP);
+        }
+        break;
+    case MOTION_BACKWARD:
+        switch (type) {
+        case LUM_BLOCK:
+            ComputeMotionBlock(next->ref_y, by, bx, motion.bwd, motionBlockP);
+            break;
+        case CB_BLOCK:
+            ComputeMotionBlock(next->ref_cb, by, bx, motion.bwd, motionBlockP);
+            break;
+        case CR_BLOCK:
+            ComputeMotionBlock(next->ref_cr, by, bx, motion.bwd, motionBlockP);
+            break;
+        }
+        break;
+    case MOTION_INTERPOLATE:
+        switch (type) {
+        case LUM_BLOCK:
+            ComputeMotionBlock(prev->ref_y, by, bx, motion.fwd, &prevBlock);
+            ComputeMotionBlock(next->ref_y, by, bx, motion.bwd, &nextBlock);
+            break;
+        case CB_BLOCK:
+            ComputeMotionBlock(prev->ref_cb, by, bx, motion.fwd, &prevBlock);
+            ComputeMotionBlock(next->ref_cb, by, bx, motion.bwd, &nextBlock);
+            break;
+        case CR_BLOCK:
+            ComputeMotionBlock(prev->ref_cr, by, bx, motion.fwd, &prevBlock);
+            ComputeMotionBlock(next->ref_cr, by, bx, motion.bwd, &nextBlock);
+            break;
+        }
+        {
+            unsigned int y;
+            for (y = 0; y < 8; ++y) {
+                int16 * const blockRow = (*motionBlockP)[y];
+                int16 * const prevRow  = prevBlock[y];
+                int16 * const nextRow  = nextBlock[y];
+                unsigned int x;
+                for (x = 0; x < 8; ++x)
+                    blockRow[x] = (prevRow[x] + nextRow[x] + 1) / 2;
+            }
+        }
+        break;
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ *  compute the DCT of the error term
+ *
+ * RETURNS: appropriate blocks of current will contain the DCTs
+ *
+ * SIDE EFFECTS:    none
+ *
+ * PRECONDITION:    the motion vectors must be valid!
+ *
+ *===========================================================================*/
+static void
+ComputeBDiffDCTs(MpegFrame * const current,
+                 MpegFrame * const prev,
+                 MpegFrame * const next,
+                 int         const by,
+                 int         const bx,
+                 int         const mode,
+                 motion      const motion,
+                 int *       const patternP) {
+
+    Block motionBlock;
+    
+    if (*patternP & 0x20) {
+        boolean significantDiff;
+        ComputeBMotionBlock(prev, next, by, bx, mode, motion,
+                            &motionBlock, LUM_BLOCK);
+        ComputeDiffDCTBlock(current->y_blocks[by][bx], dct[by][bx],
+                            motionBlock, &significantDiff);
+        if (!significantDiff) 
+            *patternP ^=  0x20;
+    }
+
+    if (*patternP & 0x10) {
+        boolean significantDiff;
+        ComputeBMotionBlock(prev, next, by, bx+1, mode, motion,
+                            &motionBlock, LUM_BLOCK);
+        ComputeDiffDCTBlock(current->y_blocks[by][bx+1], dct[by][bx+1],
+                            motionBlock, &significantDiff);
+        if (!significantDiff) 
+            *patternP ^=  0x10;
+    }
+
+    if (*patternP & 0x8) {
+        boolean significantDiff;
+        ComputeBMotionBlock(prev, next, by+1, bx, mode, motion,
+                            &motionBlock, LUM_BLOCK);
+        ComputeDiffDCTBlock(current->y_blocks[by+1][bx], dct[by+1][bx],
+                            motionBlock, &significantDiff);
+        if (!significantDiff)
+            *patternP ^= 0x8;
+    }
+
+    if (*patternP & 0x4) {
+        boolean significantDiff;
+        ComputeBMotionBlock(prev, next, by+1, bx+1, mode, motion,
+                            &motionBlock, LUM_BLOCK);
+        ComputeDiffDCTBlock(current->y_blocks[by+1][bx+1], dct[by+1][bx+1],
+                            motionBlock, &significantDiff);
+        if (!significantDiff)
+            *patternP ^= 0x4;
+    }
+
+    if (*patternP & 0x2) {
+        boolean significantDiff;
+        ComputeBMotionBlock(prev, next, by/2, bx/2, mode,
+                            halfMotion(motion), &motionBlock, CB_BLOCK);
+        ComputeDiffDCTBlock(current->cb_blocks[by/2][bx/2],
+                            dctb[by/2][bx/2], motionBlock,
+                            &significantDiff);
+        if (!significantDiff)
+            *patternP ^= 0x2;
+    }
+
+    if (*patternP & 0x1) {
+        boolean significantDiff;
+        ComputeBMotionBlock(prev, next, by/2, bx/2, mode,
+                            halfMotion(motion),
+                            &motionBlock, CR_BLOCK);
+        ComputeDiffDCTBlock(current->cr_blocks[by/2][bx/2],
+                            dctr[by/2][bx/2], motionBlock,
+                            &significantDiff);
+        if (!significantDiff)
+            *patternP ^= 0x1;
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ *  decides if this block should be coded as intra-block
+ *
+ * RETURNS: TRUE if intra-coding should be used; FALSE otherwise
+ *
+ * SIDE EFFECTS:    none
+ *
+ * PRECONDITION:    the motion vectors must be valid!
+ *
+ *===========================================================================*/
+static boolean
+DoBIntraCode(MpegFrame * const current,
+             MpegFrame * const prev,
+             MpegFrame * const next,
+             int         const by,
+             int         const bx,
+             int         const mode,
+             motion      const motion) {
+
+    boolean retval;
+    unsigned int y;
+    int32 sum = 0, vard = 0, varc = 0;
+    LumBlock motionBlock;
+    int fy, fx;
+
+    ComputeBMotionLumBlock(prev, next, by, bx, mode, motion, &motionBlock);
+
+    MotionToFrameCoord(by, bx, 0, 0, &fy, &fx);
+
+    for (y = 0; y < 16; ++y) {
+        unsigned int x;
+        for (x = 0; x < 16; ++x) {
+            int32 const currPixel = current->orig_y[fy+y][fx+x];
+            int32 const prevPixel = motionBlock.l[y][x];
+            int32 const dif = currPixel - prevPixel;
+
+            sum += currPixel;
+            varc += SQR(currPixel);
+            vard += SQR(dif);
+        }
+    }
+
+    vard >>= 8;     /* divide by 256; assumes mean is close to zero */
+    varc = (varc>>8) - (sum>>8)*(sum>>8);
+
+    if (vard <= 64)
+        retval = FALSE;
+    else if (vard < varc)
+        retval = FALSE;
+    else
+        retval = TRUE;
+    return retval;
+}
+
+
+
+static int
+ComputeBlockColorDiff(Block current,
+                      Block motionBlock) {
+
+    unsigned int y;
+    int diffTotal;
+    
+    diffTotal = 0;
+
+    for (y = 0; y < 8; ++y) {
+        unsigned int x;
+        for (x = 0; x < 8; ++x) {
+            int const diffTmp = current[y][x] - motionBlock[y][x];
+            diffTotal += ABS(diffTmp);
+        }
+    }
+    return diffTotal;
+}
+
+
+
+/*=====================*
+ * EXPORTED PROCEDURES *
+ *=====================*/
+
+
+/*===========================================================================*
+ * MotionSufficient
+ *
+ *  decides if this motion vector is sufficient without DCT coding
+ *
+ * RETURNS: TRUE if no DCT is needed; FALSE otherwise
+ *
+ * SIDE EFFECTS:    none
+ *
+ * PRECONDITION:    the motion vectors must be valid!
+ *
+ *===========================================================================*/
+static boolean
+MotionSufficient(MpegFrame *      const curr,
+                 const LumBlock * const currBlockP,
+                 MpegFrame *      const prev,
+                 MpegFrame *      const next,
+                 int              const by,
+                 int              const bx,
+                 int              const mode,
+                 motion           const motion) {
+
+    LumBlock mLumBlock;
+    Block mColorBlock;
+    int lumErr, colorErr;
+
+    /* check bounds */
+    if ( mode != MOTION_BACKWARD ) {
+        if ( (by*DCTSIZE+(motion.fwd.y-1)/2 < 0) ||
+             ((by+2)*DCTSIZE+(motion.fwd.y+1)/2-1 >= Fsize_y) ) {
+            return FALSE;
+        }
+        if ( (bx*DCTSIZE+(motion.fwd.x-1)/2 < 0) ||
+             ((bx+2)*DCTSIZE+(motion.fwd.x+1)/2-1 >= Fsize_x) ) {
+            return FALSE;
+        }
+    }
+
+    if ( mode != MOTION_FORWARD ) {
+        if ( (by*DCTSIZE+(motion.bwd.y-1)/2 < 0) ||
+             ((by+2)*DCTSIZE+(motion.bwd.y+1)/2-1 >= Fsize_y) ) {
+            return FALSE;
+        }
+        if ( (bx*DCTSIZE+(motion.bwd.x-1)/2 < 0) ||
+             ((bx+2)*DCTSIZE+(motion.bwd.x+1)/2-1 >= Fsize_x) ) {
+            return FALSE;
+        }
+    }
+
+    /* check Lum */
+    ComputeBMotionLumBlock(prev, next, by, bx, mode, motion, &mLumBlock);
+    lumErr =  LumBlockMAD(currBlockP, &mLumBlock, 0x7fffffff);
+    if (lumErr > 512) {
+        return FALSE;
+    }
+
+    /* check color */
+    ComputeBMotionBlock(prev, next, by/2, bx/2, mode,
+                        halfMotion(motion), &mColorBlock, CR_BLOCK);
+    colorErr = ComputeBlockColorDiff(curr->cr_blocks[by/2][bx/2],
+                                     mColorBlock);
+    ComputeBMotionBlock(prev, next, by/2, bx/2, mode, halfMotion(motion),
+                        &mColorBlock, CB_BLOCK);
+    colorErr += ComputeBlockColorDiff(curr->cr_blocks[by/2][bx/2],
+                                      mColorBlock);
+    
+    return (colorErr < 256); /* lumErr checked above */
+}
+
+
+
+
+struct stats {
+    int IBlocks;
+    int BBlocks;
+    int Skipped;
+    int totalBits;
+    int IBits;
+    int BBits;
+};
+
+
+
+static void
+initializeStats(struct stats * const statsP) {
+    statsP->IBlocks = 0;
+    statsP->BBlocks = 0;
+    statsP->Skipped = 0;
+    statsP->totalBits  = 0;
+    statsP->IBits   = 0;
+    statsP->BBits   = 0;
+}
+
+
+
+static void
+checkSpecifics(MpegFrame *      const curr, 
+               int              const mbAddress,
+               int              const QScale,
+               boolean *        const skipItP,
+               boolean *        const doBsearchP,
+               int *            const modeP,
+               motion *         const motionP) {
+/*----------------------------------------------------------------------------
+   We return *modeP iff we return *doBsearchP == FALSE.
+
+   We return *motionP iff we return *skipItP == FALSE.
+-----------------------------------------------------------------------------*/
+    BlockMV * info;
+
+    SpecLookup(curr->id, 2, mbAddress, &info, QScale);
+    if (info == NULL) {
+        *doBsearchP = TRUE;
+        *skipItP = FALSE;
+    } else {
+        *doBsearchP = FALSE;
+
+        switch (info->typ) {
+        case TYP_SKIP:
+            *skipItP = TRUE;
+            break;
+        case TYP_FORW:
+            *skipItP = FALSE;
+            motionP->fwd.y = info->fy;
+            motionP->fwd.x = info->fx;
+            *modeP = MOTION_FORWARD;
+            break;
+        case TYP_BACK:
+            *skipItP = FALSE;
+            motionP->bwd.y = info->by;
+            motionP->bwd.x = info->bx;
+            *modeP = MOTION_BACKWARD;
+            break;
+        case TYP_BOTH:
+            *skipItP = FALSE;
+            motionP->fwd.y = info->fy;
+            motionP->fwd.x = info->fx;
+            motionP->bwd.y = info->by;
+            motionP->bwd.x = info->bx;
+            *modeP = MOTION_INTERPOLATE;
+            break;
+        default:
+            pm_error("Unreachable code in GenBFrame!");
+        }
+    }
+}
+
+
+
+static void
+makeNonSkipBlock(int              const y,
+                 int              const x, 
+                 MpegFrame *      const curr, 
+                 MpegFrame *      const prev, 
+                 MpegFrame *      const next,
+                 boolean          const specificsOn,
+                 int              const mbAddress,
+                 int              const QScale,
+                 const LumBlock * const currentBlockP,
+                 int *            const modeP,
+                 int *            const oldModeP,
+                 boolean          const IntrPBAllowed,
+                 boolean *        const lastIntraP,
+                 motion *         const motionP,
+                 motion *         const oldMotionP,
+                 struct stats *   const statsP) {
+
+    motion motion;
+    int mode;
+    boolean skipIt;
+    boolean doBsearch;
+
+    if (specificsOn)
+        checkSpecifics(curr, mbAddress, QScale, &skipIt, &doBsearch,
+                       &mode, &motion);
+    else {
+        skipIt = FALSE;
+        doBsearch = TRUE;
+    }
+    if (skipIt)
+        dct_data[y][x].useMotion = SKIP;
+    else {
+        if (doBsearch) {
+            motion = *motionP;  /* start with old motion */
+            mode = BMotionSearch(currentBlockP, prev, next, y, x,
+                                 &motion, mode);
+        }
+        /* STEP 2:  INTRA OR NON-INTRA CODING */
+        if (IntraPBAllowed && 
+            DoBIntraCode(curr, prev, next, y, x, mode, motion)) {
+            /* output I-block inside a B-frame */
+            ++statsP->IBlocks;
+            zeroMotion(oldMotionP);
+            *lastIntraP = TRUE;
+            dct_data[y][x].useMotion = NO_MOTION;
+            *oldModeP = MOTION_FORWARD;
+            /* calculate forward dct's */
+            if (collect_quant && (collect_quant_detailed & 1)) 
+                fprintf(collect_quant_fp, "l\n");
+            mp_fwd_dct_block2(curr->y_blocks[y][x], dct[y][x]);
+            mp_fwd_dct_block2(curr->y_blocks[y][x+1], dct[y][x+1]);
+            mp_fwd_dct_block2(curr->y_blocks[y+1][x], dct[y+1][x]);
+            mp_fwd_dct_block2(curr->y_blocks[y+1][x+1], dct[y+1][x+1]);
+            if (collect_quant && (collect_quant_detailed & 1)) {
+                fprintf(collect_quant_fp, "c\n");
+            }
+            mp_fwd_dct_block2(curr->cb_blocks[y>>1][x>>1], 
+                              dctb[y>>1][x>>1]);
+            mp_fwd_dct_block2(curr->cr_blocks[y>>1][x>>1], 
+                              dctr[y>>1][x>>1]);
+
+        } else { /* dct P/Bi/B block */
+            int pattern;
+
+            pattern = 63;
+            *lastIntraP = FALSE;
+            ++statsP->BBlocks;
+            dct_data[y][x].mode = mode;
+            *oldModeP = mode;
+            dct_data[y][x].fmotionY = motion.fwd.y;
+            dct_data[y][x].fmotionX = motion.fwd.x;
+            dct_data[y][x].bmotionY = motion.bwd.y;
+            dct_data[y][x].bmotionX = motion.bwd.x;
+
+            switch (mode) {
+            case MOTION_FORWARD:
+                ++bframeStats.BFOBlocks;
+                oldMotionP->fwd = motion.fwd;
+                break;
+            case MOTION_BACKWARD:
+                ++bframeStats.BBABlocks;
+                oldMotionP->bwd = motion.bwd;
+                break;
+            case MOTION_INTERPOLATE:
+                ++bframeStats.BINBlocks;
+                *oldMotionP = motion;
+                break;
+            default:
+                pm_error("INTERNAL ERROR:  Illegal mode: %d", mode);
+            }
+        
+            ComputeBDiffDCTs(curr, prev, next, y, x, mode, motion, &pattern);
+        
+            dct_data[y][x].pattern = pattern;
+            dct_data[y][x].useMotion = MOTION;
+            if ( computeMVHist ) {
+                assert(motion.fwd.x + searchRangeB + 1 >= 0);
+                assert(motion.fwd.y + searchRangeB + 1 >= 0);
+                assert(motion.fwd.x + searchRangeB + 1 <= 2*searchRangeB + 2);
+                assert(motion.fwd.y + searchRangeB + 1 <= 2*searchRangeB + 2);
+                assert(motion.bwd.x + searchRangeB + 1 >= 0);
+                assert(motion.bwd.y + searchRangeB + 1 >= 0);
+                assert(motion.bwd.x + searchRangeB + 1 <= 2*searchRangeB + 2);
+                assert(motion.bwd.y + searchRangeB + 1 <= 2*searchRangeB + 2);
+                
+                ++bfmvHistogram[motion.fwd.x + searchRangeB + 1]
+                    [motion.fwd.y + searchRangeB + 1];
+                ++bbmvHistogram[motion.bwd.x + searchRangeB + 1]
+                    [motion.bwd.y + searchRangeB + 1];
+            }
+        } /* motion-block */
+    }
+    *motionP = motion;
+    *modeP   = mode;
+}
+
+
+
+/*===========================================================================*
+ *
+ * GenBFrame
+ *
+ *  generate a B-frame from previous and next frames, adding the result
+ *  to the given bit bucket
+ *
+ * RETURNS: frame appended to bb
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+GenBFrame(BitBucket * const bb, 
+          MpegFrame * const curr, 
+          MpegFrame * const prev, 
+          MpegFrame * const next) {
+
+    FlatBlock fba[6], fb[6];
+    Block     dec[6];
+    int32 y_dc_pred, cr_dc_pred, cb_dc_pred;
+    int x, y;
+    struct motion motion;
+    struct motion oldMotion;
+    int oldMode = MOTION_FORWARD;
+    int mode = MOTION_FORWARD;
+    int offsetX, offsetY;
+    struct motion motionRem;
+    struct motion motionQuot;
+    struct stats stats;
+    boolean lastIntra = TRUE;
+    boolean    motionForward, motionBackward;
+    int     totalFrameBits;
+    int32    startTime, endTime;
+    int lastX, lastY;
+    int lastBlockX, lastBlockY;
+    int ix, iy;
+    LumBlock currentBlock;
+    int         fy, fx;
+    boolean make_skip_block;
+    int mbAddrInc = 1;
+    int mbAddress;
+    int     slicePos;
+    float   snr[3], psnr[3];
+    int     idx;
+    int     QScale;
+    BlockMV *info;
+    int     bitstreamMode, newQScale;
+    int     rc_blockStart=0;
+    boolean overflowChange=FALSE;
+    int overflowValue = 0;
+
+    assert(prev != NULL);
+    assert(next != NULL);
+
+    initializeStats(&stats);
+
+    if (collect_quant) {fprintf(collect_quant_fp, "# B\n");}
+    if (dct == NULL) AllocDctBlocks();
+    ++bframeStats.Frames;
+    totalFrameBits = bb->cumulativeBits;
+    startTime = time_elapsed();
+
+    /*   Rate Control */
+    bitstreamMode = getRateMode();
+    if (bitstreamMode == FIXED_RATE) {
+        targetRateControl(curr);
+    }
+ 
+    QScale = GetBQScale();
+    Mhead_GenPictureHeader(bb, B_FRAME, curr->id, fCodeB);
+    /* Check for Qscale change */
+    if (specificsOn) {
+        newQScale = SpecLookup(curr->id, 0, 0 /* junk */, &info, QScale);
+        if (newQScale != -1) {
+            QScale = newQScale;
+        }
+        /* check for slice */
+        newQScale = SpecLookup(curr->id, 1, 1, &info, QScale);
+        if (newQScale != -1) {
+            QScale = newQScale;
+        }
+    }
+
+    Mhead_GenSliceHeader(bb, 1, QScale, NULL, 0);
+
+    Frame_AllocBlocks(curr);
+    BlockifyFrame(curr);
+
+    if ( printSNR ) {
+        Frame_AllocDecoded(curr, FALSE);
+    }
+
+    /* for I-blocks */
+    y_dc_pred = cr_dc_pred = cb_dc_pred = 128;
+
+    stats.totalBits = bb->cumulativeBits;
+
+    if ( ! pixelFullSearch ) {
+        if ( ! prev->halfComputed ) {
+            ComputeHalfPixelData(prev);
+        }
+
+        if ( ! next->halfComputed ) {
+            ComputeHalfPixelData(next);
+        }
+    }
+
+    lastBlockX = Fsize_x / 8;
+    lastBlockY = Fsize_y / 8;
+    lastX = lastBlockX - 2;
+    lastY = lastBlockY - 2;
+    mbAddress = 0;
+
+    /* Start with zero motion assumption */
+    zeroMotion(&motion); 
+    zeroMotion(&oldMotion);
+    zeroMotion(&motionRem);
+    zeroMotion(&motionQuot);
+
+    /* find motion vectors and do dcts */
+    /* In this first loop, all MVs are in half-pixel scope, (if FULL
+       is set then they will be multiples of 2).  This is not true in
+       the second loop. 
+    */
+    for (y = 0;  y < lastBlockY;  y += 2) {
+        for (x = 0;  x < lastBlockX;  x += 2) {
+            slicePos = (mbAddress % blocksPerSlice);
+
+            /* compute currentBlock */
+            BLOCK_TO_FRAME_COORD(y, x, fy, fx);
+            for ( iy = 0; iy < 16; iy++ ) {
+                for ( ix = 0; ix < 16; ix++ ) {
+                    currentBlock.l[iy][ix] = (int16)curr->orig_y[fy+iy][fx+ix];
+                }
+            }
+        
+            if (slicePos == 0) {
+                zeroMotion(&oldMotion);
+                oldMode = MOTION_FORWARD;
+                lastIntra = TRUE;
+            }
+
+            /* STEP 1:  Select Forward, Backward, or Interpolated motion 
+               vectors */
+            /* see if old motion is good enough */
+            /* but force last block to be non-skipped */
+            /* can only skip if:
+             *     1)  not the last block in frame
+             *     2)  not the last block in slice
+             *     3)  not the first block in slice
+             *     4)  previous block was not intra-coded
+             */
+            if ( ((y < lastY) || (x < lastX)) &&
+                 (slicePos+1 != blocksPerSlice) &&
+                 (slicePos != 0) &&
+                 (! lastIntra) &&
+                 (BSkipBlocks) ) {
+                make_skip_block =
+                    MotionSufficient(curr, &currentBlock, 
+                                     prev, next, y, x, oldMode, oldMotion);
+            } else
+                make_skip_block = FALSE;
+
+            if (make_skip_block) {
+                /* skipped macro block */
+                dct_data[y][x].useMotion = SKIP;
+            } else
+                makeNonSkipBlock(y, x, curr, prev, next, specificsOn,
+                                 mbAddress,
+                                 QScale, &currentBlock,
+                                 &mode, &oldMode,
+                                 IntraPBAllowed,
+                                 &lastIntra, &motion, &oldMotion, &stats);
+
+            ++mbAddress;
+        }
+    }
+
+    /* reset everything */
+    zeroMotion(&oldMotion);
+    oldMode = MOTION_FORWARD;
+    lastIntra = TRUE;
+    y_dc_pred = cr_dc_pred = cb_dc_pred = 128;
+    mbAddress = 0;
+
+    /* Now generate the frame */
+    for (y = 0; y < lastBlockY; y += 2) {
+      for (x = 0; x < lastBlockX; x += 2) {
+    slicePos = (mbAddress % blocksPerSlice);
+
+    if ( (slicePos == 0) && (mbAddress != 0) ) {
+      if (specificsOn) {
+        /* Make sure no slice Qscale change */
+        newQScale = 
+            SpecLookup(curr->id,1,mbAddress/blocksPerSlice, &info, QScale);
+        if (newQScale != -1) QScale = newQScale;
+      }
+      Mhead_GenSliceEnder(bb);
+      Mhead_GenSliceHeader(bb, 1+(y>>1), QScale, NULL, 0);
+
+      /* reset everything */
+      zeroMotion(&oldMotion);
+      oldMode = MOTION_FORWARD;
+      lastIntra = TRUE;
+      y_dc_pred = cr_dc_pred = cb_dc_pred = 128;
+
+      mbAddrInc = 1+(x>>1);
+    }
+
+    /*  Determine if new Qscale needed for Rate Control purposes */
+    if (bitstreamMode == FIXED_RATE) {
+      rc_blockStart =  bb->cumulativeBits;
+      newQScale = needQScaleChange(QScale,
+                       curr->y_blocks[y][x],
+                       curr->y_blocks[y][x+1],
+                       curr->y_blocks[y+1][x],
+                       curr->y_blocks[y+1][x+1]);
+      if (newQScale > 0) {
+        QScale = newQScale;
+      }
+    }
+ 
+    if (specificsOn) {
+      newQScale = SpecLookup(curr->id, 2, mbAddress, &info, QScale);
+      if (newQScale != -1) {
+        QScale = newQScale;
+      }}
+
+    if (dct_data[y][x].useMotion == NO_MOTION) {
+
+      GEN_I_BLOCK(B_FRAME, curr, bb, mbAddrInc, QScale);
+      mbAddrInc = 1;
+      stats.IBits += (bb->cumulativeBits - stats.totalBits);
+      stats.totalBits = bb->cumulativeBits;
+          
+      /* reset because intra-coded */
+      zeroMotion(&oldMotion);
+      oldMode = MOTION_FORWARD;
+      lastIntra = TRUE;
+          
+      if ( printSNR ) {
+        /* need to decode block we just encoded */
+        /* and reverse the DCT transform */
+        for ( idx = 0; idx < 6; idx++ ) {
+          Mpost_UnQuantZigBlock(fb[idx], dec[idx], QScale, TRUE);
+          mpeg_jrevdct((int16 *)dec[idx]);
+        }
+
+        /* now, unblockify */
+        BlockToData(curr->decoded_y, dec[0], y, x);
+        BlockToData(curr->decoded_y, dec[1], y, x+1);
+        BlockToData(curr->decoded_y, dec[2], y+1, x);
+        BlockToData(curr->decoded_y, dec[3], y+1, x+1);
+        BlockToData(curr->decoded_cb, dec[4], y>>1, x>>1);
+        BlockToData(curr->decoded_cr, dec[5], y>>1, x>>1);
+      }
+    } else if (dct_data[y][x].useMotion == SKIP) {
+      ++stats.Skipped;
+      mbAddrInc++;
+          
+      /* decode skipped block */
+      if (printSNR) {
+          struct motion motion;
+        
+          for (idx = 0; idx < 6; ++idx)
+              memset((char *)dec[idx], 0, sizeof(Block)); 
+        
+          if (pixelFullSearch) {
+              motion.fwd.y = 2 * oldMotion.fwd.y;
+              motion.fwd.x = 2 * oldMotion.fwd.x;
+              motion.bwd.y = 2 * oldMotion.bwd.y;
+              motion.bwd.x = 2 * oldMotion.bwd.x;
+          } else
+              motion = oldMotion;
+          
+          /* now add the motion block */
+          AddBMotionBlock(dec[0], prev->decoded_y,
+                          next->decoded_y, y, x, mode, motion);
+          AddBMotionBlock(dec[1], prev->decoded_y,
+                          next->decoded_y, y, x+1, mode, motion);
+          AddBMotionBlock(dec[2], prev->decoded_y,
+                          next->decoded_y, y+1, x, mode, motion);
+          AddBMotionBlock(dec[3], prev->decoded_y,
+                          next->decoded_y, y+1, x+1, mode, motion);
+          AddBMotionBlock(dec[4], prev->decoded_cb,
+                          next->decoded_cb, y/2, x/2, mode,
+                          halfMotion(motion));
+          AddBMotionBlock(dec[5], prev->decoded_cr,
+                          next->decoded_cr, y/2, x/2, mode,
+                          halfMotion(motion));
+        
+          /* now, unblockify */
+          BlockToData(curr->decoded_y, dec[0], y, x);
+          BlockToData(curr->decoded_y, dec[1], y, x+1);
+          BlockToData(curr->decoded_y, dec[2], y+1, x);
+          BlockToData(curr->decoded_y, dec[3], y+1, x+1);
+          BlockToData(curr->decoded_cb, dec[4], y/2, x/2);
+          BlockToData(curr->decoded_cr, dec[5], y/2, x/2);
+      }
+    } else   /* B block */ {
+        int const fCode = fCodeB;   
+        int pattern;
+        
+        pattern = dct_data[y][x].pattern;
+        motion.fwd.y = dct_data[y][x].fmotionY;
+        motion.fwd.x = dct_data[y][x].fmotionX;
+        motion.bwd.y = dct_data[y][x].bmotionY;
+        motion.bwd.x = dct_data[y][x].bmotionX;
+
+        if (pixelFullSearch)
+            motion = halfMotion(motion);
+          
+        /* create flat blocks and update pattern if necessary */
+    calc_blocks:
+        /* Note DoQuant references QScale, overflowChange, overflowValue,
+           pattern, and the calc_blocks label                 */
+        DoQuant(0x20, dct[y][x], fba[0]);
+        DoQuant(0x10, dct[y][x+1], fba[1]);
+        DoQuant(0x08, dct[y+1][x], fba[2]);
+        DoQuant(0x04, dct[y+1][x+1], fba[3]);
+        DoQuant(0x02, dctb[y/2][x/2], fba[4]);
+        DoQuant(0x01, dctr[y/2][x/2], fba[5]);
+
+        motionForward  = (dct_data[y][x].mode != MOTION_BACKWARD);
+        motionBackward = (dct_data[y][x].mode != MOTION_FORWARD);
+        
+        /* Encode Vectors */
+        if (motionForward) {
+            /* transform the fMotion vector into the appropriate values */
+            offsetY = motion.fwd.y - oldMotion.fwd.y;
+            offsetX = motion.fwd.x - oldMotion.fwd.x;
+
+            encodeMotionVector(offsetX, offsetY,
+                               &motionQuot.fwd, &motionRem.fwd,
+                               FORW_F, fCode);
+            oldMotion.fwd = motion.fwd;
+        }
+          
+        if (motionBackward) {
+            /* transform the bMotion vector into the appropriate values */
+            offsetY = motion.bwd.y - oldMotion.bwd.y;
+            offsetX = motion.bwd.x - oldMotion.bwd.x;
+            encodeMotionVector(offsetX, offsetY,
+                               &motionQuot.bwd, &motionRem.bwd,
+                               BACK_F, fCode);
+            oldMotion.bwd = motion.bwd;
+        }
+          
+        oldMode = dct_data[y][x].mode;
+          
+        if (printSNR) { /* Need to decode */
+            if (pixelFullSearch) {
+                motion.fwd.x *= 2; motion.fwd.y *= 2;
+                motion.bwd.x *= 2; motion.bwd.y *= 2;
+            }
+            for ( idx = 0; idx < 6; idx++ ) {
+                if ( pattern & (1 << (5-idx)) ) {
+                    Mpost_UnQuantZigBlock(fba[idx], dec[idx], QScale, FALSE);
+                    mpeg_jrevdct((int16 *)dec[idx]);
+                } else {
+                    memset((char *)dec[idx], 0, sizeof(Block));
+                }
+            }
+
+            /* now add the motion block */
+            AddBMotionBlock(dec[0], prev->decoded_y,
+                            next->decoded_y, y, x, mode, motion);
+            AddBMotionBlock(dec[1], prev->decoded_y,
+                            next->decoded_y, y, x+1, mode, motion);
+            AddBMotionBlock(dec[2], prev->decoded_y,
+                            next->decoded_y, y+1, x, mode, motion);
+            AddBMotionBlock(dec[3], prev->decoded_y,
+                            next->decoded_y, y+1, x+1, mode, motion);
+            AddBMotionBlock(dec[4], prev->decoded_cb,
+                            next->decoded_cb, y/2, x/2, mode,
+                            halfMotion(motion));
+            AddBMotionBlock(dec[5], prev->decoded_cr,
+                            next->decoded_cr, y/2, x/2, mode,
+                            halfMotion(motion));
+
+            /* now, unblockify */
+            BlockToData(curr->decoded_y,  dec[0], y,   x);
+            BlockToData(curr->decoded_y,  dec[1], y,   x+1);
+            BlockToData(curr->decoded_y,  dec[2], y+1, x);
+            BlockToData(curr->decoded_y,  dec[3], y+1, x+1);
+            BlockToData(curr->decoded_cb, dec[4], y/2, x/2);
+            BlockToData(curr->decoded_cr, dec[5], y/2, x/2);
+        }
+
+        /* reset because non-intra-coded */
+        y_dc_pred = cr_dc_pred = cb_dc_pred = 128;
+        lastIntra = FALSE;
+        mode = dct_data[y][x].mode;
+        
+        /*      DBG_PRINT(("MB Header(%d,%d)\n", x, y));  */
+        Mhead_GenMBHeader(
+            bb, 3 /* pict_code_type */, mbAddrInc /* addr_incr */,
+            QScale /* q_scale */,
+            fCodeB /* forw_f_code */, fCodeB /* back_f_code */,
+            motionRem.fwd.x /* horiz_forw_r */,
+            motionRem.fwd.y /* vert_forw_r */,
+            motionRem.bwd.x /* horiz_back_r */,
+            motionRem.bwd.y /* vert_back_r */,
+            motionForward /* motion_forw */,
+            motionQuot.fwd.x /* m_horiz_forw */,
+            motionQuot.fwd.y /* m_vert_forw */,
+            motionBackward /* motion_back */,
+            motionQuot.bwd.x /* m_horiz_back */,
+            motionQuot.bwd.y /* m_vert_back */,
+            pattern /* mb_pattern */, FALSE /* mb_intra */);
+
+        mbAddrInc = 1;
+          
+        /* now output the difference */
+        {
+            unsigned int x;
+            for (x = 0; x < 6; ++x) {
+                if (GET_ITH_BIT(pattern, 5-x))
+                    Mpost_RLEHuffPBlock(fba[x], bb);
+            }
+        }
+          
+        switch (mode) {
+        case MOTION_FORWARD:
+            bframeStats.BFOBits += (bb->cumulativeBits - stats.totalBits);
+            break;
+        case MOTION_BACKWARD:
+            bframeStats.BBABits += (bb->cumulativeBits - stats.totalBits);
+            break;
+        case MOTION_INTERPOLATE:
+            bframeStats.BINBits += (bb->cumulativeBits - stats.totalBits);
+            break;
+        default:
+            pm_error("PROGRAMMER ERROR:  Illegal mode: %d", mode);
+      }
+      
+      stats.BBits += (bb->cumulativeBits - stats.totalBits);
+      stats.totalBits = bb->cumulativeBits;
+    
+      if (overflowChange) {
+        /* undo an overflow-caused Qscale change */
+        overflowChange = FALSE;
+        QScale -= overflowValue;
+        overflowValue = 0;
+      }
+    } /* if I-block, skip, or B */
+
+    mbAddress++;
+    /*   Rate Control  */
+    if (bitstreamMode == FIXED_RATE) {
+      incMacroBlockBits( bb->cumulativeBits - rc_blockStart);
+      rc_blockStart = bb->cumulativeBits;
+      MB_RateOut(TYPE_BFRAME);
+    }
+    
+      }
+    }
+
+    if ( printSNR ) {
+      BlockComputeSNR(curr,snr,psnr);
+      totalSNR += snr[0];
+      totalPSNR += psnr[0];
+    }
+    
+    Mhead_GenSliceEnder(bb);
+    /*   Rate Control  */
+    if (bitstreamMode == FIXED_RATE) {
+      updateRateControl(TYPE_BFRAME);
+    }
+    
+    endTime = time_elapsed();
+    totalTime += (endTime-startTime);
+    
+    if ( showBitRatePerFrame ) {
+      /* ASSUMES 30 FRAMES PER SECOND */
+      fprintf(bitRateFile, "%5d\t%8d\n", curr->id,
+          30*(bb->cumulativeBits-totalFrameBits));
+    }
+    
+    if ( frameSummary && !realQuiet) {
+      fprintf(stdout, "FRAME %d (B):  "
+              "I BLOCKS: %5d;  B BLOCKS: %5d   SKIPPED: %5d (%ld seconds)\n",
+              curr->id, stats.IBlocks, stats.BBlocks, stats.Skipped,
+              (long)((endTime-startTime)/TIME_RATE));
+      if ( printSNR )
+    fprintf(stdout, "FRAME %d:  "
+            "SNR:  %.1f\t%.1f\t%.1f\tPSNR:  %.1f\t%.1f\t%.1f\n",
+            curr->id, snr[0], snr[1], snr[2],
+            psnr[0], psnr[1], psnr[2]);
+    }
+    
+    bframeStats.FrameBits += (bb->cumulativeBits-totalFrameBits);
+    bframeStats.BIBlocks += stats.IBlocks;
+    bframeStats.BBBlocks += stats.BBlocks;
+    bframeStats.BSkipped += stats.Skipped;
+    bframeStats.BIBits   += stats.IBits;
+    bframeStats.BBBits   += stats.BBits;
+  }
+
+
+/*===========================================================================*
+ *
+ * SetBQScale
+ *
+ *  set the B-frame Q-scale
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    qscaleB
+ *
+ *===========================================================================*/
+void
+SetBQScale(qB)
+    int qB;
+{
+    qscaleB = qB;
+}
+
+
+/*===========================================================================*
+ *
+ * GetBQScale
+ *
+ *  get the B-frame Q-scale
+ *
+ * RETURNS: the Q-scale
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+int
+GetBQScale()
+{
+    return qscaleB;
+}
+
+
+/*===========================================================================*
+ *
+ * ResetBFrameStats
+ *
+ *  reset the B-frame stats
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+ResetBFrameStats() {
+
+    bframeStats.BIBlocks = 0;
+    bframeStats.BBBlocks = 0;
+    bframeStats.BSkipped = 0;
+    bframeStats.BIBits = 0;
+    bframeStats.BBBits = 0;
+    bframeStats.Frames = 0;
+    bframeStats.FrameBits = 0;
+    totalTime = 0;
+}
+
+
+
+float
+BFrameTotalTime(void) {
+    return (float)totalTime/(float)TIME_RATE;
+}
+
+
+
+void
+ShowBFrameSummary(unsigned int const inputFrameBits, 
+                  unsigned int const totalBits, 
+                  FILE *       const fpointer) {
+
+    if (bframeStats.Frames > 0) {
+        fprintf(fpointer, "-------------------------\n");
+        fprintf(fpointer, "*****B FRAME SUMMARY*****\n");
+        fprintf(fpointer, "-------------------------\n");
+
+        if (bframeStats.BIBlocks > 0) {
+            fprintf(fpointer,
+                    "  I Blocks:  %5d     (%6d bits)     (%5d bpb)\n",
+                    bframeStats.BIBlocks, bframeStats.BIBits,
+                    bframeStats.BIBits/bframeStats.BIBlocks);
+        } else
+            fprintf(fpointer, "  I Blocks:  %5d\n", 0);
+
+        if (bframeStats.BBBlocks > 0) {
+            fprintf(fpointer,
+                    "  B Blocks:  %5d     (%6d bits)     (%5d bpb)\n",
+                    bframeStats.BBBlocks, bframeStats.BBBits,
+                    bframeStats.BBBits/bframeStats.BBBlocks);
+            fprintf(fpointer,
+                    "  B types:   %5d     (%4d bpb) "
+                    "forw  %5d (%4d bpb) back   %5d (%4d bpb) bi\n",
+                    bframeStats.BFOBlocks,
+                    (bframeStats.BFOBlocks==0) ? 
+                        0 : bframeStats.BFOBits/bframeStats.BFOBlocks,
+                    bframeStats.BBABlocks,
+                    (bframeStats.BBABlocks==0) ? 
+                        0 : bframeStats.BBABits/bframeStats.BBABlocks,
+                    bframeStats.BINBlocks,
+                    (bframeStats.BINBlocks==0) ? 
+                        0 : bframeStats.BINBits/bframeStats.BINBlocks);
+        } else
+            fprintf(fpointer, "  B Blocks:  %5d\n", 0);
+
+        fprintf(fpointer, "  Skipped:   %5d\n", bframeStats.BSkipped);
+
+        fprintf(fpointer, "  Frames:    %5d     (%6d bits)     "
+                "(%5d bpf)     (%2.1f%% of total)\n",
+                bframeStats.Frames, bframeStats.FrameBits,
+                bframeStats.FrameBits/bframeStats.Frames,
+                100.0*(float)bframeStats.FrameBits/(float)totalBits);        
+        fprintf(fpointer, "  Compression:  %3d:1     (%9.4f bpp)\n",
+                bframeStats.Frames*inputFrameBits/bframeStats.FrameBits,
+                24.0*(float)bframeStats.FrameBits/
+                    (float)(bframeStats.Frames*inputFrameBits));
+        if (printSNR)
+            fprintf(fpointer, "  Avg Y SNR/PSNR:  %.1f     %.1f\n",
+                    totalSNR/(float)bframeStats.Frames,
+                    totalPSNR/(float)bframeStats.Frames);
+        if (totalTime == 0) {
+            fprintf(fpointer, "  Seconds:  NONE\n");
+        } else {
+            fprintf(fpointer, "  Seconds:  %9ld     (%9.4f fps)  "
+                    "(%9ld pps)  (%9ld mps)\n",
+                    (long)(totalTime/TIME_RATE),
+                    (float)((float)(TIME_RATE*bframeStats.Frames)/
+                            (float)totalTime),
+                    (long)((float)TIME_RATE*(float)bframeStats.Frames *
+                           (float)inputFrameBits/(24.0*(float)totalTime)),
+                    (long)((float)TIME_RATE*(float)bframeStats.Frames *
+                           (float)inputFrameBits/(256.0*24.0 *
+                                                  (float)totalTime)));
+        }
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ *  compute the luminance block resulting from motion compensation
+ *
+ * RETURNS: motionBlock modified
+ *
+ * SIDE EFFECTS:    none
+ *
+ * PRECONDITION:    the motion vectors must be valid!
+ *
+ *===========================================================================*/
+void
+ComputeBMotionLumBlock(MpegFrame * const prev,
+                       MpegFrame * const next,
+                       int         const by,
+                       int         const bx,
+                       int         const mode,
+                       motion      const motion,
+                       LumBlock *  const motionBlockP) {
+
+    switch(mode) {
+    case MOTION_FORWARD:
+        ComputeMotionLumBlock(prev, by, bx, motion.fwd, motionBlockP);
+        break;
+    case MOTION_BACKWARD:
+        ComputeMotionLumBlock(next, by, bx, motion.bwd, motionBlockP);
+        break;
+    case MOTION_INTERPOLATE: {
+        LumBlock prevBlock, nextBlock;
+        unsigned int y;
+
+        ComputeMotionLumBlock(prev, by, bx, motion.fwd, &prevBlock);
+        ComputeMotionLumBlock(next, by, bx, motion.bwd, &nextBlock);
+        
+        for (y = 0; y < 16; ++y) {
+            unsigned int x;
+            for (x = 0; x < 16; ++x)
+                motionBlockP->l[y][x] =
+                    (prevBlock.l[y][x] + nextBlock.l[y][x] + 1) / 2;
+        }
+    } break;
+    default:
+        pm_error("Bad mode!  Programmer error!");
+    }  /* switch */
+}
+
+
+/*===========================================================================*
+ *
+ *  estimate the seconds to compute a B-frame
+ *
+ * RETURNS: the time, in seconds
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+float
+EstimateSecondsPerBFrame() {
+    if (bframeStats.Frames == 0)
+        return 20.0;
+    else
+        return (float)totalTime/((float)TIME_RATE*(float)bframeStats.Frames);
+}
+
+
diff --git a/converter/ppm/ppmtompeg/bitio.c b/converter/ppm/ppmtompeg/bitio.c
new file mode 100644
index 00000000..e0dc4d4e
--- /dev/null
+++ b/converter/ppm/ppmtompeg/bitio.c
@@ -0,0 +1,536 @@
+/*===========================================================================*
+ * bitio.c
+ *
+ *  Procedures concerned with the bit-wise I/O
+ *
+ * EXPORTED PROCEDURES:
+ *  Bitio_New
+ *  Bitio_Free
+ *  Bitio_Write
+ *  Bitio_Flush
+ *  Bitio_WriteToSocket
+ *  Bitio_BytePad
+ *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include <assert.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "pm.h"
+#include "intcode.h"
+
+#include "all.h"
+#include "byteorder.h"
+#include "bitio.h"
+#include "mtypes.h"
+
+
+
+/*==================*
+ * STATIC VARIABLES *
+ *==================*/
+
+static uint32 lower_mask[33] = {
+    0,
+    0x1, 0x3, 0x7, 0xf,
+    0x1f, 0x3f, 0x7f, 0xff,
+    0x1ff, 0x3ff, 0x7ff, 0xfff,
+    0x1fff, 0x3fff, 0x7fff, 0xffff,
+    0x1ffff, 0x3ffff, 0x7ffff, 0xfffff,
+    0x1fffff, 0x3fffff, 0x7fffff, 0xffffff,
+    0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff,
+    0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff
+};
+
+
+extern time_t IOtime;
+
+
+/*===========================================================================*
+ *
+ * Dump
+ *
+ *  Writes out the first MAX_BITS bits of the bit bucket to the
+ *  appropriate output file
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:  none
+ *
+ *===========================================================================*/
+static void
+Dump(BitBucket * const bbPtr) {
+    struct bitBucket *ptr, *tempPtr;
+    int i, nitems;
+    int     bitsWritten = 0;
+    time_t  tempTimeStart, tempTimeEnd;
+
+    time(&tempTimeStart);
+
+    for (ptr = bbPtr->firstPtr; ptr && (bitsWritten < MAX_BITS);
+         ptr = ptr->nextPtr) {
+
+        bigend32 buffer[WORDS_PER_BUCKET];
+
+        if (ptr->bitsleftcur == 32 && ptr->currword == 0) {
+            continue;       /* empty */
+        }
+
+        for (i = 0; i <= ptr->currword; ++i)
+            buffer[i] = pm_bigendFromUint32(ptr->bits[i]);
+
+        nitems = fwrite(buffer, sizeof(buffer[0]), ptr->currword + 1, 
+                        bbPtr->filePtr);
+        if (nitems != ptr->currword+1) {
+            fprintf(stderr, 
+                    "Whoa!  Trouble writing %u words (wrote %u words)!  "
+                    "Game over, dude!\n",
+                    ptr->currword+1, nitems);
+            exit(1);
+        }
+
+        bitsWritten += ((ptr->currword + 1) * 32);
+    }
+
+    while ( bbPtr->firstPtr != ptr ) {
+        tempPtr = bbPtr->firstPtr;
+        bbPtr->firstPtr = tempPtr->nextPtr;
+        free(tempPtr);
+    }
+
+    bbPtr->totalbits -= bitsWritten;
+    bbPtr->bitsWritten += bitsWritten;
+
+    time(&tempTimeEnd);
+    IOtime += (tempTimeEnd-tempTimeStart);
+}
+
+
+/*=====================*
+ * EXPORTED PROCEDURES *
+ *=====================*/
+
+
+/*===========================================================================*
+ *
+ * Bitio_New
+ *
+ *  Create a new bit bucket; filePtr is a pointer to the open file the
+ *  bits should ultimately be written to.
+ *
+ * RETURNS: pointer to the resulting bit bucket
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+BitBucket *
+Bitio_New(FILE * const filePtr) {
+
+    BitBucket *bbPtr;
+
+    bbPtr = (BitBucket *) malloc(sizeof(BitBucket));
+    ERRCHK(bbPtr, "malloc");
+
+    bbPtr->firstPtr = bbPtr->lastPtr = malloc(sizeof(struct bitBucket));
+    ERRCHK(bbPtr->firstPtr, "malloc");
+
+    bbPtr->totalbits = 0;
+    bbPtr->cumulativeBits = 0;
+    bbPtr->bitsWritten = 0;
+    bbPtr->filePtr = filePtr;
+
+    bbPtr->firstPtr->nextPtr = NULL;
+    bbPtr->firstPtr->bitsleft = MAXBITS_PER_BUCKET;
+    bbPtr->firstPtr->bitsleftcur = 32;
+    bbPtr->firstPtr->currword = 0;
+    memset((char *)bbPtr->firstPtr->bits, 0, 
+           sizeof(uint32) * WORDS_PER_BUCKET);
+
+    return bbPtr;
+}
+
+
+
+BitBucket *
+Bitio_New_Filename(const char * const fileName) {
+
+    FILE * outputFile;
+    
+    outputFile = fopen(fileName, "wb");
+    if (outputFile == NULL)
+        pm_error("Could not open output file '%s'.  "
+                 "Errno=%d (%s)", fileName, errno, strerror(errno));
+
+    return Bitio_New(outputFile);
+}
+
+
+
+/*===========================================================================*
+ *
+ * Bitio_Free
+ *
+ *  Frees the memory associated with the given bit bucket
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+Bitio_Free(BitBucket * const bbPtr) {
+
+    struct bitBucket *tmpPtr, *nextPtr;
+
+    for (tmpPtr = bbPtr->firstPtr; tmpPtr != NULL; tmpPtr = nextPtr) {
+        nextPtr = tmpPtr->nextPtr;
+        free(tmpPtr);
+    }
+    free(bbPtr);
+}
+
+
+/*===========================================================================*
+ *
+ * Bitio_Write
+ *
+ *  Writes 'nbits' bits from 'bits' into the given bit bucket
+ *  'nbits' must be between 0 and 32
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    if the number of bits in the bit bucket surpasses
+ *          MAX_BITS, then that many bits are flushed to the
+ *          appropriate output file
+ *
+ *===========================================================================*/
+void
+Bitio_Write(BitBucket * const bbPtr, 
+            uint32      const bits_arg, 
+            int         const nbits) {
+
+    register struct bitBucket *lastPtr, *newPtr;
+    register int delta;
+    uint32 bits;
+    
+    bits=bits_arg;
+    assert(nbits <= 32 && nbits >= 0);
+
+    /*
+     * Clear top bits if not part of data, necessary due to down and
+     * dirty calls of Bitio_Write with unecessary top bits set.
+     */
+
+    bits &= lower_mask[nbits];
+    bits = bits & lower_mask[nbits];
+
+    bbPtr->totalbits += nbits;
+    bbPtr->cumulativeBits += nbits;
+    lastPtr = bbPtr->lastPtr;
+
+    delta = nbits - lastPtr->bitsleft;
+    if (delta >= 0) {
+        /*
+         * there's not enough room in the current bucket, so we're
+         * going to have to allocate another bucket
+         */
+        newPtr = lastPtr->nextPtr = (struct bitBucket *) 
+            malloc(sizeof(struct bitBucket));
+        ERRCHK(newPtr, "malloc");
+        newPtr->nextPtr = NULL;
+        newPtr->bitsleft = MAXBITS_PER_BUCKET;
+        newPtr->bitsleftcur = 32;
+        newPtr->currword = 0;
+        memset((char *)newPtr->bits, 0, sizeof(uint32) * WORDS_PER_BUCKET);
+        bbPtr->lastPtr = newPtr;
+
+        assert(lastPtr->currword == WORDS_PER_BUCKET - 1);
+        lastPtr->bits[WORDS_PER_BUCKET - 1] |= (bits >> delta);
+        lastPtr->bitsleft = 0;
+        lastPtr->bitsleftcur = 0;
+        /* lastPtr->currword++; */
+
+        if (!delta) {
+            if ( bbPtr->totalbits > MAX_BITS ) {
+                Dump(bbPtr);
+            }
+        }
+
+        assert(delta <= 32);
+        newPtr->bits[0] = (bits & lower_mask[delta]) << (32 - delta);
+        newPtr->bitsleft -= delta;
+        newPtr->bitsleftcur -= delta;
+    } else {
+        /*
+         * the current bucket will be sufficient
+         */
+        delta = nbits - lastPtr->bitsleftcur;
+        lastPtr->bitsleftcur -= nbits;
+        lastPtr->bitsleft -= nbits;
+
+        if (delta >= 0)
+        {
+            /*
+             * these bits will span more than one word
+             */
+            lastPtr->bits[lastPtr->currword] |= (bits >> delta);
+            lastPtr->currword++;
+            lastPtr->bits[lastPtr->currword] = 
+                (bits & lower_mask[delta]) << (32 - delta);
+            lastPtr->bitsleftcur = 32 - delta;
+        } else {
+            /*
+         * these bits will fit, whole
+         */
+            lastPtr->bits[lastPtr->currword] |= (bits << (-delta));
+        }
+    }
+
+    if ( bbPtr->totalbits > MAX_BITS )  /* flush bits */
+        Dump(bbPtr);
+}
+
+
+/*===========================================================================*
+ *
+ * Bitio_Flush
+ *
+ *  Flushes all of the remaining bits in the given bit bucket to the
+ *  appropriate output file.  It will generate up to the nearest 8-bit
+ *  unit of bits, which means that up to 7 extra 0 bits will be appended
+ *  to the end of the file.
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    frees the bit bucket
+ *
+ *===========================================================================*/
+void
+Bitio_Flush(BitBucket * const bbPtr) {
+
+    struct bitBucket *ptr, *tempPtr;
+    uint32  lastWord;
+    int i, nitems;
+    int     bitsWritten = 0;
+    int     bitsLeft;
+    unsigned int     numWords;
+    uint8   charBuf[4];
+    boolean    flushHere = FALSE;
+    time_t  tempTimeStart, tempTimeEnd;
+
+    time(&tempTimeStart);
+
+    bitsLeft = bbPtr->totalbits;
+
+    for (ptr = bbPtr->firstPtr; ptr; ptr = ptr->nextPtr) {
+        if (ptr->bitsleftcur == 32 && ptr->currword == 0) {
+            continue;       /* empty */
+        }
+
+        if ( bitsLeft >= 32 ) {
+            bigend32 buffer[WORDS_PER_BUCKET];
+
+            if ( ((ptr->currword + 1) * 32) > bitsLeft ) {
+                numWords = ptr->currword;
+                flushHere = TRUE;
+            } else
+                numWords = ptr->currword+1;
+
+            for (i = 0; i < numWords; ++i)
+                buffer[i] = pm_bigendFromUint32(ptr->bits[i]);
+
+            nitems = fwrite(buffer, sizeof(buffer[0]), numWords,
+                            bbPtr->filePtr);
+            if (nitems != numWords) {
+                if (ferror(bbPtr->filePtr))
+                    pm_error("Error writing %u words to flush a bit bucket.  "
+                             "fwrite() gives errno %d (%s)", 
+                             numWords, errno, strerror(errno));
+                else
+                    pm_error("Problem writing %u words "
+                             "to flush a bit bucket.  "
+                             "Only %d words transferred.", 
+                             numWords, nitems);
+            }
+
+            bitsWritten += (numWords * 32);
+            bitsLeft -= (numWords * 32);
+        } else {
+            flushHere = TRUE;
+        }
+
+        if ( (bitsLeft < 32) && flushHere ) {
+            lastWord = ptr->bits[ptr->currword];
+
+            /* output the lastPtr word in big-endian order (network) */
+
+            /* now write out lastPtr bits */
+            while ( bitsLeft > 0 ) {
+                charBuf[0] = (lastWord >> 24);
+                charBuf[0] &= lower_mask[8];
+                fwrite(charBuf, 1, sizeof(uint8), bbPtr->filePtr);
+                lastWord = (lastWord << 8);
+                bitsLeft -= 8;
+                bitsWritten += 8;
+            }
+        }
+    }
+    fflush(bbPtr->filePtr);
+    while ( bbPtr->firstPtr != ptr ) {
+        tempPtr = bbPtr->firstPtr;
+        bbPtr->firstPtr = tempPtr->nextPtr;
+        free(tempPtr);
+    }
+
+    free(bbPtr);
+
+    time(&tempTimeEnd);
+    IOtime += (tempTimeEnd-tempTimeStart);
+}
+
+
+
+void
+Bitio_Close(BitBucket * const bbPtr) {
+
+    fclose(bbPtr->filePtr);
+}
+
+
+
+/*===========================================================================*
+ *
+ * Bitio_WriteToSocket
+ *
+ *  Writes all of the remaining bits in the given bit bucket to the
+ *  given socket.  May pad the end of the socket stream with extra 0
+ *  bits as does Bitio_Flush.
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    frees the bit bucket
+ *
+ *===========================================================================*/
+void
+Bitio_WriteToSocket(BitBucket * const bbPtr, 
+                    int         const socket) {
+
+    struct bitBucket *ptr, *tempPtr;
+    uint32  lastWord;
+    int i, nitems;
+    int     bitsWritten = 0;
+    int     bitsLeft;
+    int     numWords;
+    uint8   charBuf[4];
+    boolean    flushHere = FALSE;
+
+    bitsLeft = bbPtr->totalbits;
+
+    for (ptr = bbPtr->firstPtr; ptr; ptr = ptr->nextPtr) {
+        if (ptr->bitsleftcur == 32 && ptr->currword == 0) {
+            continue;       /* empty */
+        }
+
+        if ( bitsLeft >= 32 ) {
+            bigend32 buffer[WORDS_PER_BUCKET];
+
+            if ( ((ptr->currword + 1) * 32) > bitsLeft ) {
+                numWords = ptr->currword;
+                flushHere = TRUE;
+            } else {
+                numWords = ptr->currword+1;
+            }
+
+            for (i = 0; i < numWords; ++i)
+                buffer[i] = pm_bigendFromUint32(ptr->bits[i]);
+
+            nitems = write(socket, buffer, numWords * sizeof(buffer[0]));
+            if (nitems != numWords*sizeof(uint32)) {
+                fprintf(stderr, "Whoa!  Trouble writing %u bytes "
+                        "(wrote %u bytes)!  "
+                        "Game over, dude!\n",
+                        (unsigned)(numWords*sizeof(buffer[0])), nitems);
+                exit(1);
+            }
+
+            bitsWritten += (numWords * 32);
+            bitsLeft -= (numWords * 32);
+        } else {
+            flushHere = TRUE;
+        }
+
+        if ( (bitsLeft < 32) && flushHere ) {
+            lastWord = ptr->bits[ptr->currword];
+
+            /* output the lastPtr word in big-endian order (network) */
+
+            /* now write out lastPtr bits */
+            while ( bitsLeft > 0 ) {
+                charBuf[0] = (lastWord >> 24);
+                charBuf[0] &= lower_mask[8];
+                if ( write(socket, charBuf, 1) != 1 ) {
+                    fprintf(stderr, "ERROR:  write of lastPtr bits\n");
+                    exit(1);
+                }
+                lastWord = (lastWord << 8);
+                bitsLeft -= 8;
+                bitsWritten += 8;
+            }
+        }
+    }
+
+    while ( bbPtr->firstPtr != ptr ) {
+        tempPtr = bbPtr->firstPtr;
+        bbPtr->firstPtr = tempPtr->nextPtr;
+        free(tempPtr);
+    }
+
+    free(bbPtr);
+}
+
+
+/*===========================================================================*
+ *
+ * Bitio_BytePad
+ *
+ *  Pads the end of the bit bucket to the nearest byte with 0 bits
+ *
+ * RETURNS: nothing
+ *
+ *===========================================================================*/
+void
+Bitio_BytePad(BitBucket * const bbPtr) {
+
+    struct bitBucket *lastPtrPtr = bbPtr->lastPtr;
+
+    if (lastPtrPtr->bitsleftcur % 8) {
+        Bitio_Write(bbPtr, 0, lastPtrPtr->bitsleftcur % 8);
+    }
+}
diff --git a/converter/ppm/ppmtompeg/block.c b/converter/ppm/ppmtompeg/block.c
new file mode 100644
index 00000000..913518c4
--- /dev/null
+++ b/converter/ppm/ppmtompeg/block.c
@@ -0,0 +1,1045 @@
+/*===========================================================================*
+ * block.c
+ *
+ *  Block routines
+ *
+ * NOTES:   MAD =   Mean Absolute Difference
+ *===========================================================================*/
+
+/* Copyright information is at end of file */
+
+#include <assert.h>
+
+#include "pm_c_util.h"
+#include "all.h"
+#include "mtypes.h"
+#include "frames.h"
+#include "bitio.h"
+#include "prototypes.h"
+#include "fsize.h"
+#include "opts.h"
+#include "postdct.h"
+
+#include "block.h"
+
+#define TRUNCATE_UINT8(x)   ((x < 0) ? 0 : ((x > 255) ? 255 : x))
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+
+extern Block **dct, **dctb, **dctr;
+
+
+static vector
+halfVector(vector const v) {
+    vector half;
+
+    half.y = v.y/2;
+    half.x = v.x/2;
+
+    return half;
+}
+
+/*===========================*
+ * COMPUTE DCT OF DIFFERENCE *
+ *===========================*/
+
+/*===========================================================================*
+ *
+ *  compute current-motionBlock, take the DCT, and put the difference
+ *  back into current
+ *
+ * RETURNS: current block modified
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+ComputeDiffDCTBlock(Block           current,
+                    Block           dest,
+                    Block           motionBlock,
+                    boolean * const significantDifferenceP) {
+
+    unsigned int y;
+    int diff;
+
+    diff = 0;  /* initial value */
+
+    for (y = 0; y < 8; ++y) {
+        unsigned int x;
+        for (x = 0; x < 8; ++x) {
+            current[y][x] -= motionBlock[y][x];
+            diff += ABS(current[y][x]);
+        }
+    }
+    /* Kill the block if change is too small     */
+    /* (block_bound defaults to 128, see opts.c) */
+    if (diff < block_bound)
+        *significantDifferenceP = FALSE;
+    else {
+        mp_fwd_dct_block2(current, dest);
+        *significantDifferenceP = TRUE;
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ *  appropriate (according to pattern, the coded block pattern) blocks
+ *  of 'current' are diff'ed and DCT'd.
+ *
+ * RETURNS: current blocks modified
+ *
+ * SIDE EFFECTS:    Can remove too-small difference blocks from pattern
+ *
+ * PRECONDITIONS:   appropriate blocks of 'current' have not yet been
+ *          modified
+ *
+ *===========================================================================*/
+void
+ComputeDiffDCTs(MpegFrame * const current,
+                MpegFrame * const prev,
+                int         const by,
+                int         const bx,
+                vector      const m,
+                int *       const patternP) {
+    
+    Block motionBlock;
+
+    if (collect_quant && (collect_quant_detailed & 1))
+        fprintf(collect_quant_fp, "l\n");
+    if (*patternP & 0x20) {
+        boolean significantDiff;
+        ComputeMotionBlock(prev->ref_y, by, bx, m, &motionBlock);
+        ComputeDiffDCTBlock(current->y_blocks[by][bx], dct[by][bx],
+                            motionBlock, &significantDiff);
+        if (!significantDiff)
+            *patternP ^= 0x20;
+    }
+
+    if (*patternP & 0x10) {
+        boolean significantDiff;
+        ComputeMotionBlock(prev->ref_y, by, bx+1, m, &motionBlock);
+        ComputeDiffDCTBlock(current->y_blocks[by][bx+1], dct[by][bx+1],
+                            motionBlock, &significantDiff);
+        if (!significantDiff)
+            *patternP ^= 0x10;
+    }
+
+    if (*patternP & 0x8) {
+        boolean significantDiff;
+        ComputeMotionBlock(prev->ref_y, by+1, bx, m, &motionBlock);
+        ComputeDiffDCTBlock(current->y_blocks[by+1][bx], dct[by+1][bx],
+                            motionBlock, &significantDiff);
+        if (!significantDiff)
+            *patternP ^= 0x8;
+    }
+
+    if (*patternP & 0x4) {
+        boolean significantDiff;
+        ComputeMotionBlock(prev->ref_y, by+1, bx+1, m, &motionBlock);
+        ComputeDiffDCTBlock(current->y_blocks[by+1][bx+1], dct[by+1][bx+1],
+                            motionBlock, &significantDiff);
+        if (!significantDiff)
+            *patternP ^= 0x4;
+    }
+
+    if (collect_quant && (collect_quant_detailed & 1))
+        fprintf(collect_quant_fp, "c\n");
+
+    if (*patternP & 0x2) {
+        boolean significantDiff;
+        ComputeMotionBlock(prev->ref_cb, by/2, bx/2, halfVector(m),
+                           &motionBlock);
+        ComputeDiffDCTBlock(current->cb_blocks[by/2][bx/2],
+                            dctb[by/2][bx/2], motionBlock,
+                            &significantDiff);
+        if (!significantDiff)
+            *patternP ^= 0x2;
+    }
+
+    if (*patternP & 0x1) {
+        boolean significantDiff;
+        ComputeMotionBlock(prev->ref_cr, by/2, bx/2, halfVector(m),
+                           &motionBlock);
+        ComputeDiffDCTBlock(current->cr_blocks[by/2][bx/2],
+                            dctr[by/2][bx/2], motionBlock,
+                            &significantDiff);
+        if (!significantDiff)
+            *patternP ^= 0x1;
+    }
+}
+
+
+
+static void
+computePrevFyFx(MpegFrame * const prevFrame,
+                int         const by,
+                int         const bx,
+                vector      const m,
+                uint8 ***   const prevP,
+                int *       const fyP,
+                int *       const fxP) {
+
+    boolean const xHalf = (ABS(m.x) % 2 == 1);
+    boolean const yHalf = (ABS(m.y) % 2 == 1);
+
+    MotionToFrameCoord(by, bx, m.y/2, m.x/2, fyP, fxP);
+
+    assert(*fyP >= 0); assert(*fxP >= 0);
+
+    /* C integer arithmetic rounds toward zero.  But what we need is a
+       "floor" -- i.e. round down.  So we adjust now for where the dividend
+       in the above divide by two was negative.
+    */
+
+    if (xHalf) {
+        if (m.x < 0)
+            --*fxP;
+
+        if (yHalf) {
+            if (m.y < 0)
+                --*fyP;
+        
+            *prevP = prevFrame->halfBoth;
+        } else
+            *prevP = prevFrame->halfX;
+    } else if (yHalf) {
+        if (m.y < 0)
+            --*fyP;
+        
+        *prevP = prevFrame->halfY;
+    } else
+        *prevP = prevFrame->ref_y;
+}
+
+
+
+/*======================*
+ * COMPUTE MOTION BLOCK *
+ *======================*/
+
+/*===========================================================================*
+ *
+ *  compute the motion-compensated block
+ *
+ * RETURNS: motionBlock
+ *
+ * SIDE EFFECTS:    none
+ *
+ * PRECONDITIONS:   motion vector MUST be valid
+ *
+ * NOTE:  could try to speed this up using halfX, halfY, halfBoth,
+ *    but then would have to compute for chrominance, and it's just
+ *    not worth the trouble (this procedure is not called relatively
+ *    often -- a constant number of times per macroblock)
+ *
+ *===========================================================================*/
+void
+ComputeMotionBlock(uint8 ** const prev,
+                   int      const by,
+                   int      const bx,
+                   vector   const m,
+                   Block *  const motionBlockP) {
+
+    int fy, fx;
+    boolean xHalf, yHalf;
+
+    xHalf = (ABS(m.x) % 2 == 1);
+    yHalf = (ABS(m.y) % 2 == 1);
+
+    MotionToFrameCoord(by, bx, m.y/2, m.x/2, &fy, &fx);
+
+    if (xHalf && yHalf) {
+        unsigned int y;
+        /* really should be fy+y-1 and fy+y so do (fy-1)+y = fy+y-1 and
+           (fy-1)+y+1 = fy+y
+        */
+        if (m.y < 0)
+            --fy;
+
+        if (m.x < 0)
+            --fx;
+    
+        for (y = 0; y < 8; ++y) {
+            int16 * const destPtr = (*motionBlockP)[y];
+            uint8 * const srcPtr  = &(prev[fy+y][fx]);
+            uint8 * const srcPtr2 = &(prev[fy+y+1][fx]);
+            unsigned int x;
+
+            for (x = 0; x < 8; ++x)
+                destPtr[x] =
+                    (srcPtr[x]+srcPtr[x+1]+srcPtr2[x]+srcPtr2[x+1]+2) >> 2;
+        }
+    } else if (xHalf) {
+        unsigned int y;
+        if (m.x < 0)
+            --fx;
+        
+        for (y = 0; y < 8; ++y) {
+            int16 * const destPtr = (*motionBlockP)[y];
+            uint8 * const srcPtr  = &(prev[fy+y][fx]);
+            unsigned int x;
+
+            for (x = 0; x < 8; ++x)
+                destPtr[x] = (srcPtr[x]+srcPtr[x+1]+1) >> 1;
+        }
+    } else if (yHalf) {
+        unsigned int y;
+        if ( m.y < 0 )
+            fy--;
+
+        for (y = 0; y < 8; ++y) {
+            int16 * const destPtr = (*motionBlockP)[y];
+            uint8 * const srcPtr  = &(prev[fy+y][fx]);
+            uint8 * const srcPtr2 = &(prev[fy+y+1][fx]);
+            unsigned int x;
+
+            for (x = 0; x < 8; ++x)
+                destPtr[x] = (srcPtr[x]+srcPtr2[x]+1) >> 1;
+        }
+    } else {
+        unsigned int y;
+        for (y = 0; y < 8; ++y) {
+            int16 * const destPtr = (*motionBlockP)[y];
+            uint8 * const srcPtr  = &(prev[fy+y][fx]);
+            unsigned int x;
+
+            for (x = 0; x < 8; ++x)
+                destPtr[x] = srcPtr[x];
+        }
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ *  compute the motion-compensated luminance block
+ *
+ * RETURNS: motionBlock
+ *
+ * SIDE EFFECTS:    none
+ *
+ * PRECONDITIONS:   motion vector MUST be valid
+ *
+ * NOTE:  see ComputeMotionBlock
+ *
+ *===========================================================================*/
+void
+ComputeMotionLumBlock(MpegFrame * const prevFrame,
+                      int         const by,
+                      int         const bx,
+                      vector      const m,
+                      LumBlock *  const motionBlockP) {
+
+    unsigned int y;
+    uint8 ** prev;
+    int fy, fx;
+
+    computePrevFyFx(prevFrame, by, bx, m, &prev, &fy, &fx);
+
+    for (y = 0; y < 16; ++y) {
+        uint8 * const across  = &(prev[fy+y][fx]);
+        int32 * const macross = motionBlockP->l[y];
+        unsigned int x;
+
+        for (x = 0; x < 16; ++x)
+            macross[x] = across[x];
+    }
+
+    /* this is what's really happening, in slow motion:
+     *
+     *  for (y = 0; y < 16; ++y, ++py)
+     *      for (x = 0; x < 16; ++x, ++px)
+     *          motionBlock[y][x] = prev[fy+y][fx+x];
+     *
+     */
+}
+
+
+/*=======================*
+ * BASIC ERROR FUNCTIONS *
+ *=======================*/
+
+
+/*===========================================================================*
+ *
+ *  return the MAD of two luminance blocks
+ *
+ * RETURNS: the MAD, if less than bestSoFar, or some number bigger if not
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+int32
+LumBlockMAD(const LumBlock * const currentBlockP,
+            const LumBlock * const motionBlockP,
+            int32            const bestSoFar) {
+
+    int32 diff;    /* max value of diff is 255*256 = 65280 */
+    unsigned int y;
+
+    diff = 0;  /* initial value */
+
+    for (y = 0; y < 16; ++y) {
+        const int32 * const currentRow = currentBlockP->l[y];
+        const int32 * const motionRow  = motionBlockP->l[y];
+        unsigned int x;
+        for (x = 0; x < 16; ++x)
+            diff += ABS(currentRow[x] - motionRow[x]);
+
+        if (diff > bestSoFar)
+            /* We already know the MAD won't be less than bestSoFar;
+               Caller doesn't care by how much we missed, so just return
+               this.
+            */
+            return diff;
+    }
+    /* Return the actual MAD */
+    return diff;
+}
+
+
+
+/*===========================================================================*
+ *
+ *  return the MAD of the currentBlock and the motion-compensated block
+ *      (without TUNEing)
+ *
+ *  (by, bx) is the location of the block in the frame 
+ *  (block number coordinates).  'm' is the motion of the block in pixels.
+ *  The moved block must be wholly within the frame.
+ *
+ * RETURNS: the MAD, if less than bestSoFar, or
+ *      some number bigger if not
+ *
+ * SIDE EFFECTS:    none
+ *
+ * PRECONDITIONS:  motion vector MUST be valid
+ *
+ * NOTES:  this is the procedure that is called the most, and should therefore
+ *         be the most optimized!!!
+ *
+ *===========================================================================*/
+int32
+LumMotionError(const LumBlock * const currentBlockP,
+               MpegFrame *      const prevFrame,
+               int              const by,
+               int              const bx,
+               vector           const m,
+               int32            const bestSoFar) {
+
+    int32 adiff;
+    int32 diff;    /* max value of diff is 255*256 = 65280 */
+    int y;
+    uint8 **prev;
+    int fy, fx;
+
+    computePrevFyFx(prevFrame, by, bx, m, &prev, &fy, &fx);
+
+    assert(fy >= 0); assert(fx >= 0);
+
+    adiff = 0;  /* initial value */
+    diff = 0; /* initial value */
+
+    switch (SearchCompareMode) {
+    case DEFAULT_SEARCH: /* Default. */
+        for (y = 0; y < 16; ++y) {
+            const int32 * const cacross = currentBlockP->l[y];
+            uint8 *       const across  = &prev[fy+y][fx];
+            unsigned int x;
+            
+            for (x = 0; x < 16; ++x) {
+                int32 const localDiff = across[x]-cacross[x];
+                diff += ABS(localDiff);
+            }
+            if (diff > bestSoFar)
+                return diff;
+        }
+        break;
+      
+    case LOCAL_DCT: {
+        Block     dctdiff[4], dctquant[4];
+        FlatBlock quant;
+        int x, i, tmp;
+        int distortion=0, datarate=0;
+        int pq = GetPQScale();
+      
+        for (y = 0;  y < 16;  ++y) {
+            const int32 * const cacross = currentBlockP->l[y];
+            uint8 * const across = &(prev[fy+y][fx]);
+            for (x = 0;  x < 16;  ++x) {
+                dctdiff[(x>7)+2*(y>7)][y%8][x%8] = cacross[x]-across[x];
+            }}
+
+        /* Calculate rate */
+        for (i = 0;  i < 4;  ++i) {
+            mp_fwd_dct_block2(dctdiff[i], dctdiff[i]);
+            if (Mpost_QuantZigBlock(dctdiff[i], quant, pq, FALSE) ==
+                MPOST_ZERO) {
+                /* no sense in continuing */
+                memset((char *)dctquant[i], 0, sizeof(Block));
+            } else {
+                Mpost_UnQuantZigBlock(quant, dctquant[i], pq, FALSE);
+                mpeg_jrevdct((int16 *)dctquant[i]);
+                datarate += CalcRLEHuffLength(quant);
+            }
+        }
+      
+        /* Calculate distortion */
+        for (y = 0;  y < 16;  ++y) {
+            const int32 * const cacross = currentBlockP->l[y];
+            uint8 * const across = &(prev[fy+y][fx]);
+            for (x = 0;  x < 16;  ++x) {
+                tmp = across[x] - cacross[x] +
+                    dctquant[(x>7)+2*(y>7)][y%8][x%8];
+                distortion += tmp*tmp;
+            }}
+        distortion /= 256;
+        distortion *= LocalDCTDistortScale;
+        datarate *= LocalDCTRateScale;
+        diff = (int) sqrt(distortion*distortion + datarate*datarate);
+        break;
+    }
+
+    case NO_DC_SEARCH: {
+        extern int32 niqtable[];
+        int pq = niqtable[0]*GetPQScale();
+        unsigned int y;
+        
+        for (y = 0; y < 16; ++y) {
+            const int32 * const cacross = currentBlockP->l[y];
+            uint8 * const across = &(prev[fy+y][fx]);
+            unsigned int x;
+            
+            for (x = 0; x < 16; ++x) {
+                int32 const localDiff = across[x]-cacross[x];
+                diff += localDiff;
+                adiff += ABS(localDiff);
+            }
+        }
+
+        diff /= 64*pq;  /* diff is now the DC difference (with QSCALE 1) */
+        adiff -= 64*pq*ABS(diff);
+        diff = adiff;
+    }
+    break;
+
+    case DO_Mean_Squared_Distortion:
+        for (y = 0; y < 16; ++y) {
+            const int32 * const cacross = currentBlockP->l[y];
+            uint8 * const across = &(prev[fy+y][fx]);
+            unsigned int x;
+
+            for (x = 0; x < 16; ++x) {
+                int32 const localDiff = across[x] - cacross[x];
+                diff += localDiff * localDiff;
+            }
+            if (diff > bestSoFar)
+                return diff;
+        }
+        break;
+    } /* End of Switch */
+
+    return diff;
+}
+
+
+
+/*===========================================================================*
+ *
+ *  return the MAD of the currentBlock and the average of the blockSoFar
+ *  and the motion-compensated block (this is used for B-frame searches)
+ *
+ * RETURNS: the MAD, if less than bestSoFar, or
+ *      some number bigger if not
+ *
+ * SIDE EFFECTS:    none
+ *
+ * PRECONDITIONS:  motion vector MUST be valid
+ *
+ *===========================================================================*/
+int32
+LumAddMotionError(const LumBlock * const currentBlockP,
+                  const LumBlock * const blockSoFarP,
+                  MpegFrame *      const prevFrame,
+                  int              const by,
+                  int              const bx,
+                  vector           const m,
+                  int32            const bestSoFar) {
+
+    int32 diff;    /* max value of diff is 255*256 = 65280 */
+    int y;
+    uint8 **prev;
+    int fy, fx;
+
+    computePrevFyFx(prevFrame, by, bx, m, &prev, &fy, &fx);
+
+    diff = 0; /* initial value */
+
+    /* do we add 1 before dividing by two?  Yes -- see MPEG-1 doc page 46 */
+
+    for (y = 0; y < 16; ++y) {
+        unsigned int x;
+        const uint8 * const across  = &prev[fy+y][fx];
+        const int32 * const bacross = blockSoFarP->l[y];
+        const int32 * const cacross = currentBlockP->l[y];
+
+        for (x = 0; x < 16; ++x) {
+            int32 const localDiff =
+                ((across[x] + bacross[x] + 1) / 2) - cacross[x];
+            diff += ABS(localDiff);
+        }
+
+        if (diff > bestSoFar)
+            return diff;
+    }
+    
+    /* This is what's happening:
+     *
+     *  ComputeMotionLumBlock(prevFrame, by, bx, my, mx, lumMotionBlock);
+     *
+     *  for (y = 0; y < 16; ++y)
+     *      for (x = 0; x < 16; ++x) {
+     *          localDiff = currentBlock[y][x] - lumMotionBlock[y][x];
+     *          diff += ABS(localDiff);
+     *      }
+     */
+
+    return diff;
+}
+
+
+
+/*===========================================================================*
+ *
+ *  adds the motion-compensated block to the given block
+ *
+ * RETURNS: block modified
+ *
+ * SIDE EFFECTS:    none
+ *
+ * PRECONDITIONS:  motion vector MUST be valid
+ *
+ *===========================================================================*/
+void
+AddMotionBlock(Block          block,
+               uint8 ** const prev,
+               int      const by,
+               int      const bx,
+               vector   const m) {
+
+    int     fy, fx;
+    boolean xHalf, yHalf;
+
+    xHalf = (ABS(m.x) % 2 == 1);
+    yHalf = (ABS(m.y) % 2 == 1);
+
+    MotionToFrameCoord(by, bx, (m.y/2), (m.x/2), &fy, &fx);
+
+    if (xHalf && yHalf) {
+        unsigned int y;
+        /* really should be fy+y-1 and fy+y so do (fy-1)+y = fy+y-1 and
+           (fy-1)+y+1 = fy+y
+        */
+        if (m.y < 0)
+            --fy;
+        if (m.x < 0)
+            --fx;
+
+        for (y = 0; y < 8; ++y) {
+            unsigned int x;
+            for (x = 0; x < 8; ++x)
+                block[y][x] += (prev[fy+y][fx+x]+prev[fy+y][fx+x+1]+
+                                prev[fy+y+1][fx+x]+prev[fy+y+1][fx+x+1]+2)>>2;
+        }
+    } else if (xHalf) {
+        unsigned int y;
+        if (m.x < 0)
+            --fx;
+
+        for (y = 0; y < 8; ++y) {
+            unsigned int x;
+            for (x = 0; x < 8; ++x) {
+                block[y][x] += (prev[fy+y][fx+x]+prev[fy+y][fx+x+1]+1)>>1;
+            }
+        }
+    } else if ( yHalf ) {
+        unsigned int y;
+        if (m.y < 0)
+            --fy;
+
+        for (y = 0; y < 8; ++y) {
+            unsigned int x;
+            for (x = 0; x < 8; ++x) {
+                block[y][x] += (prev[fy+y][fx+x]+prev[fy+y+1][fx+x]+1)>>1;
+            }
+        }
+    } else {
+        unsigned int y;
+        for (y = 0; y < 8; ++y) {
+            unsigned int x;
+            for (x = 0; x < 8; ++x) {
+                block[y][x] += (int16)prev[fy+y][fx+x];
+            }
+        }
+    }
+}
+
+
+/*===========================================================================*
+ *
+ *  adds the motion-compensated B-frame block to the given block
+ *
+ * RETURNS: block modified
+ *
+ * SIDE EFFECTS:    none
+ *
+ * PRECONDITIONS:  motion vectors MUST be valid
+ *
+ *===========================================================================*/
+void
+AddBMotionBlock(Block          block,
+                uint8 ** const prev,
+                uint8 ** const next,
+                int      const by,
+                int      const bx,
+                int      const mode,
+                motion   const motion) {
+
+    unsigned int y;
+    Block prevBlock, nextBlock;
+
+    switch (mode) {
+    case MOTION_FORWARD:
+        AddMotionBlock(block, prev, by, bx, motion.fwd);
+        break;
+    case MOTION_BACKWARD:
+        AddMotionBlock(block, next, by, bx, motion.bwd);
+        break;
+    default:
+        ComputeMotionBlock(prev, by, bx, motion.fwd, &prevBlock);
+        ComputeMotionBlock(next, by, bx, motion.bwd, &nextBlock);
+    }
+    for (y = 0; y < 8; ++y) {
+        unsigned int x;
+        for (x = 0; x < 8; ++x) {
+            block[y][x] += (prevBlock[y][x] + nextBlock[y][x] + 1) / 2;
+        }
+    }
+}
+
+
+/*===========================================================================*
+ *
+ *  copies the given block into the appropriate data area
+ *
+ * RETURNS: data modified
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+BlockToData(uint8 ** const data,
+            Block          block,
+            int      const by,
+            int      const bx) {
+
+    int x, y;
+    int fy, fx;
+    int16    blockItem;
+
+    BLOCK_TO_FRAME_COORD(by, bx, fy, fx);
+
+    for ( y = 0; y < 8; y++ ) {
+    for ( x = 0; x < 8; x++ ) {
+        blockItem = block[y][x];
+        data[fy+y][fx+x] = TRUNCATE_UINT8(blockItem);
+    }
+    }
+}
+
+
+/*===========================================================================*
+ *
+ *  copies data into appropriate blocks
+ *
+ * RETURNS: mf modified
+ *
+ * SIDE EFFECTS:    none
+ *
+ * NOTES:  probably shouldn't be in this file
+ *
+ *===========================================================================*/
+void
+BlockifyFrame(MpegFrame * const frameP) {
+
+    int dctx, dcty;
+    int x, y;
+    int bx, by;
+    int fy, fx;
+    int16 *destPtr;
+    uint8 *srcPtr;
+    int16 *destPtr2;
+    uint8 *srcPtr2;
+    Block *blockPtr;
+    Block *blockPtr2;
+
+    dctx = Fsize_x / DCTSIZE;
+    dcty = Fsize_y / DCTSIZE;
+
+    /*
+     * copy y data into y_blocks
+     */
+    for (by = 0; by < dcty; by++) {
+    fy = by*DCTSIZE;
+    for (bx = 0; bx < dctx; bx++) {
+        fx = bx*DCTSIZE;
+        blockPtr = (Block *) &(frameP->y_blocks[by][bx][0][0]);
+        for (y = 0; y < DCTSIZE; y++) {
+        destPtr = &((*blockPtr)[y][0]);
+        srcPtr = &(frameP->orig_y[fy+y][fx]);
+        for (x = 0; x < DCTSIZE; x++) {
+            destPtr[x] = srcPtr[x];
+        }
+        }
+    }
+    }
+
+    /*
+     * copy cr/cb data into cr/cb_blocks
+     */
+    for (by = 0; by < (dcty >> 1); by++) {
+    fy = by*DCTSIZE;
+    for (bx = 0; bx < (dctx >> 1); bx++) {
+        fx = bx*DCTSIZE;
+        blockPtr = (Block *) &(frameP->cr_blocks[by][bx][0][0]);
+        blockPtr2 = (Block *) &(frameP->cb_blocks[by][bx][0][0]);
+        for (y = 0; y < DCTSIZE; y++) {
+        destPtr = &((*blockPtr)[y][0]);
+        srcPtr = &(frameP->orig_cr[fy+y][fx]);
+        destPtr2 = &((*blockPtr2)[y][0]);
+        srcPtr2 = &(frameP->orig_cb[fy+y][fx]);
+        for (x = 0; x < DCTSIZE; x++) {
+            destPtr[x] = srcPtr[x];
+            destPtr2[x] = srcPtr2[x];
+        }
+        }
+    }
+    }
+}
+
+
+/*===========================================================================*
+ *                                       *
+ * UNUSED PROCEDURES                                 *
+ *                                       *
+ *  The following procedures are all unused by the encoder           *
+ *                                       *
+ *  They are listed here for your convenience.  You might want to use    *
+ *  them if you experiment with different search techniques          *
+ *                                       *
+ *===========================================================================*/
+
+#ifdef UNUSED_PROCEDURES
+
+/* this procedure calculates the subsampled motion block (obviously)
+ *
+ * for speed, this procedure is probably not called anywhere (it is
+ * incorporated directly into LumDiffA, LumDiffB, etc.
+ *
+ * but leave it here anyway for clarity
+ *
+ * (startY, startX) = (0,0) for A....(0,1) for B...(1,0) for C...(1,1) for D
+ *  
+ */
+void
+ComputeSubSampledMotionLumBlock(MpegFrame * const prevFrame,
+                                int         const by,
+                                int         const bx,
+                                int         const my,
+                                int         const mx,
+                                LumBlock    const motionBlock,
+                                int         const startY,
+                                int         const startX) {
+
+    uint8 *across;
+    int32 *macross;
+    int32 *lastx;
+    int y;
+    uint8 **prev;
+    int    fy, fx;
+    boolean xHalf, yHalf;
+
+    xHalf = (ABS(mx) % 2 == 1);
+    yHalf = (ABS(my) % 2 == 1);
+
+    MotionToFrameCoord(by, bx, my/2, mx/2, &fy, &fx);
+
+    if ( xHalf ) {
+    if ( mx < 0 ) {
+        fx--;
+    }
+
+    if ( yHalf ) {
+        if ( my < 0 ) {
+        fy--;
+        }
+        
+        prev = prevFrame->halfBoth;
+    } else {
+        prev = prevFrame->halfX;
+    }
+    } else if ( yHalf ) {
+    if ( my < 0 ) {
+        fy--;
+    }
+
+    prev = prevFrame->halfY;
+    } else {
+    prev = prevFrame->ref_y;
+    }
+
+    for ( y = startY; y < 16; y += 2 ) {
+    across = &(prev[fy+y][fx+startX]);
+    macross = &(motionBlock[y][startX]);
+    lastx = &(motionBlock[y][16]);
+    while ( macross < lastx ) {
+        (*macross) = (*across);
+        across += 2;
+        macross += 2;
+    }
+    }
+
+    /* this is what's really going on in slow motion:
+     *
+     *  for ( y = startY; y < 16; y += 2 )
+     *      for ( x = startX; x < 16; x += 2 )
+     *      motionBlock[y][x] = prev[fy+y][fx+x];
+     *
+     */
+}
+
+
+/*===========================================================================*
+ *
+ *  return the MAD of the currentBlock and the motion-compensated block,
+ *  subsampled 4:1 with given starting coordinates (startY, startX)
+ *
+ * RETURNS: the MAD
+ *
+ * SIDE EFFECTS:    none
+ *
+ * PRECONDITIONS:  motion vector MUST be valid
+ *
+ * NOTES:  this procedure is never called.  Instead, see subsample.c.  This
+ *         procedure is provided only for possible use in extensions
+ *
+ *===========================================================================*/
+int32
+LumMotionErrorSubSampled(LumBlock    const currentBlock,
+                         MpegFrame * const prevFrame,
+                         int         const by,
+                         int         const bx,
+                         int         const my,
+                         int         const mx,
+                         int         const startY,
+                         int         const startX) {
+
+    int32 diff;     /* max value of diff is 255*256 = 65280 */
+    int32 localDiff;
+    int32 *cacross;
+    uint8 *macross;
+    int32 *lastx;
+    int y;
+    uint8 **prev;
+    int    fy, fx;
+    boolean xHalf, yHalf;
+
+    xHalf = (ABS(mx) % 2 == 1);
+    yHalf = (ABS(my) % 2 == 1);
+
+    motionToFrameCoord(by, bx, my/2, mx/2, &fy, &fx);
+
+    if ( xHalf ) {
+    if ( mx < 0 ) {
+        fx--;
+    }
+
+    if ( yHalf ) {
+        if ( my < 0 ) {
+        fy--;
+        }
+        
+        prev = prevFrame->halfBoth;
+    } else {
+        prev = prevFrame->halfX;
+    }
+    } else if ( yHalf ) {
+    if ( my < 0 ) {
+        fy--;
+    }
+
+    prev = prevFrame->halfY;
+    } else {
+    prev = prevFrame->ref_y;
+    }
+
+    diff = 0; /* initial value */
+
+    for ( y = startY; y < 16; y += 2 ) {
+    macross = &(prev[fy+y][fx+startX]);
+    cacross = &(currentBlock[y][startX]);
+    lastx = &(currentBlock[y][16]);
+    while ( cacross < lastx ) {
+        localDiff = (*cacross)-(*macross);
+        diff += ABS(localDiff);
+        macross += 2;
+        cacross += 2;
+    }
+    }
+
+    /* this is what's really happening:
+     *
+     *  ComputeSubSampledMotionLumBlock(prevFrame, by, bx, my, mx,
+     *                  lumMotionBlock, startY, startX);
+     *
+     *  for ( y = startY; y < 16; y += 2 )
+     *      for ( x = startX; x < 16; x += 2 )
+     *      {
+     *          localDiff = currentBlock[y][x] - lumMotionBlock[y][x];
+     *      diff += ABS(localDiff);
+     *      }
+     *
+     */
+
+    return (int32)diff;
+}
+
+
+#endif /* UNUSED_PROCEDURES */
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
diff --git a/converter/ppm/ppmtompeg/bsearch.c b/converter/ppm/ppmtompeg/bsearch.c
new file mode 100644
index 00000000..142987f5
--- /dev/null
+++ b/converter/ppm/ppmtompeg/bsearch.c
@@ -0,0 +1,1088 @@
+/*===========================================================================*
+ * bsearch.c
+ *
+ *  Procedures concerned with the B-frame motion search
+ *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/RCS/bsearch.c,v 1.10 1995/08/07 21:49:01 smoot Exp $
+ *  $Log: bsearch.c,v $
+ *  Revision 1.10  1995/08/07 21:49:01  smoot
+ *  fixed bug in initial-B-frame searches
+ *
+ *  Revision 1.9  1995/06/26 21:36:07  smoot
+ *  added new ordering constraints
+ *  (B frames which are backward P's at the start of a sequence)
+ *
+ *  Revision 1.8  1995/03/27 19:17:43  smoot
+ *  killed useless type error messge (int32 defiend as int)
+ *
+ * Revision 1.7  1995/01/19  23:07:20  eyhung
+ * Changed copyrights
+ *
+ * Revision 1.6  1994/12/07  00:40:36  smoot
+ * Added seperate P and B search ranges
+ *
+ * Revision 1.5  1994/03/15  00:27:11  keving
+ * nothing
+ *
+ * Revision 1.4  1993/12/22  19:19:01  keving
+ * nothing
+ *
+ * Revision 1.3  1993/07/22  22:23:43  keving
+ * nothing
+ *
+ * Revision 1.2  1993/06/30  20:06:09  keving
+ * nothing
+ *
+ * Revision 1.1  1993/06/03  21:08:08  keving
+ * nothing
+ *
+ * Revision 1.1  1993/03/02  18:27:05  keving
+ * nothing
+ *
+ */
+
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "pm_c_util.h"
+#include "pm.h"
+#include "all.h"
+#include "mtypes.h"
+#include "frames.h"
+#include "motion_search.h"
+#include "fsize.h"
+
+
+/*==================*
+ * STATIC VARIABLES *
+ *==================*/
+
+static int  bsearchAlg;
+
+
+/*===========================*
+ * INITIALIZATION PROCEDURES *
+ *===========================*/
+
+
+/*===========================================================================*
+ *
+ * SetBSearchAlg
+ *
+ *  set the B-search algorithm
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    bsearchAlg modified
+ *
+ *===========================================================================*/
+void
+SetBSearchAlg(const char * const alg) {
+    if (strcmp(alg, "SIMPLE") == 0)
+        bsearchAlg = BSEARCH_SIMPLE;
+    else if (strcmp(alg, "CROSS2") == 0)
+        bsearchAlg = BSEARCH_CROSS2;
+    else if (strcmp(alg, "EXHAUSTIVE") == 0)
+        bsearchAlg = BSEARCH_EXHAUSTIVE;
+    else
+        pm_error("ERROR:  Impossible bsearch alg:  %s", alg);
+}
+
+
+/*===========================================================================*
+ *
+ * BSearchName
+ *
+ *  return the text of the B-search algorithm
+ *
+ * RETURNS: a pointer to the string
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+const char *
+BSearchName(void)
+{
+    const char *retval;
+
+    switch(bsearchAlg) {
+    case BSEARCH_SIMPLE:
+        retval = "SIMPLE";break;
+    case BSEARCH_CROSS2:
+        retval = "CROSS2";break;
+    case BSEARCH_EXHAUSTIVE:
+        retval = "EXHAUSTIVE";break;
+    default:
+        pm_error("ERROR:  Impossible BSEARCH ALG:  %d", psearchAlg);
+    }
+    return retval;
+}
+
+
+
+/*===========================================================================*
+ *
+ * FindBestMatchExhaust
+ *
+ *  tries to find matching motion vector
+ *  see FindBestMatch for generic description
+ *
+ * DESCRIPTION:  uses an exhaustive search
+ *
+ *===========================================================================*/
+static int32
+FindBestMatchExhaust(const LumBlock * const blockP,
+                     const LumBlock * const currentBlockP,
+                     MpegFrame *      const prev,
+                     int              const by,
+                     int              const bx,
+                     vector *         const motionP,
+                     int32            const bestSoFar,
+                     int              const searchRange) {
+
+    register int mx, my;
+    int32 bestDiff;
+    int   stepSize;
+    int   leftMY, leftMX;
+    int   rightMY, rightMX;
+    int   distance;
+    int   tempRightMY, tempRightMX;
+    boolean changed = FALSE;
+
+    stepSize = (pixelFullSearch ? 2 : 1);
+
+    COMPUTE_MOTION_BOUNDARY(by,bx,stepSize,leftMY,leftMX,rightMY,rightMX);
+
+    /* try old motion vector first */
+    if (VALID_MOTION(*motionP)) {
+        bestDiff = LumAddMotionError(currentBlockP, blockP, prev, by, bx,
+                                     *motionP, bestSoFar);
+
+        if (bestSoFar < bestDiff)
+            bestDiff = bestSoFar;
+    } else {
+        motionP->y = motionP->x = 0;
+        bestDiff = bestSoFar;
+    }
+
+    /* maybe should try spiral pattern centered around  prev motion vector? */
+
+    /* try a spiral pattern */    
+    for (distance = stepSize;
+         distance <= searchRange;
+         distance += stepSize) {
+        tempRightMY = MIN(distance, rightMY);
+        tempRightMX = MIN(distance, rightMX);
+
+        /* do top, bottom */
+        for (my = -distance; my < tempRightMY;
+             my += max(tempRightMY+distance-stepSize, stepSize)) {
+            if (my >= leftMY) {
+                for (mx = -distance; mx < tempRightMX; mx += stepSize) {
+                    if (mx >= leftMX) {
+                        int diff;
+                        vector m;
+                        m.y = my; m.x = mx;
+                        diff = LumAddMotionError(currentBlockP, blockP, prev,
+                                                 by, bx, m, bestDiff);
+                        
+                        if (diff < bestDiff) {
+                            *motionP = m;
+                            bestDiff = diff;
+                        }
+                    }
+                }
+            }
+        }
+
+        /* do left, right */
+        for (mx = -distance;
+             mx < tempRightMX;
+             mx += max(tempRightMX+distance-stepSize, stepSize)) {
+            if (mx >= leftMX) {
+                for (my = -distance+stepSize;
+                     my < tempRightMY-stepSize;
+                     my += stepSize) {
+                    if (my >= leftMY) {
+                        int diff;
+                        vector m;
+                        m.y = my; m.x = mx;
+                        diff = LumAddMotionError(currentBlockP, blockP, prev,
+                                                 by, bx,
+                                                 m, bestDiff);
+                        
+                        if (diff < bestDiff) {
+                            *motionP = m;
+                            bestDiff = diff;
+                            changed = TRUE;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    if (!changed)
+        ++bestDiff;
+
+    return bestDiff;
+}
+
+
+/*===========================================================================*
+ *
+ * FindBestMatchTwoLevel
+ *
+ *  tries to find matching motion vector
+ *  see FindBestMatch for generic description
+ *
+ * DESCRIPTION:  uses an exhaustive full-pixel search, then looks at
+ *       neighboring half-pixels
+ *
+ *===========================================================================*/
+static int32
+FindBestMatchTwoLevel(const LumBlock * const blockP,
+                      const LumBlock * const currentBlockP,
+                      MpegFrame *      const prev,
+                      int              const by,
+                      int              const bx,
+                      vector *         const motionP,
+                      int32            const bestSoFar,
+                      int              const searchRange) {
+
+    int mx, my;
+    int32 bestDiff;
+    int leftMY, leftMX;
+    int rightMY, rightMX;
+    int distance;
+    int tempRightMY, tempRightMX;
+    boolean changed = FALSE;
+    int yOffset, xOffset;
+
+    /* exhaustive full-pixel search first */
+
+    COMPUTE_MOTION_BOUNDARY(by,bx,2,leftMY,leftMX,rightMY,rightMX);
+
+    --rightMY;
+    --rightMX;
+
+    /* convert vector into full-pixel vector */
+    if (motionP->y > 0 ) {
+        if ((motionP->y % 2) == 1)
+            --motionP->y;
+    } else if ((-motionP->y % 2) == 1)
+        ++motionP->y;
+
+    if (motionP->x > 0 ) {
+        if ((motionP->x % 2) == 1 )
+            --motionP->x;
+    } else if ((-motionP->x % 2) == 1)
+        ++motionP->x;
+
+    /* try old motion vector first */
+    if (VALID_MOTION(*motionP)) {
+        bestDiff = LumAddMotionError(currentBlockP, blockP, prev, by, bx,
+                                     *motionP, bestSoFar);
+        
+        if (bestSoFar < bestDiff)
+            bestDiff = bestSoFar;
+    } else {
+        motionP->y = motionP->x = 0;
+        bestDiff = bestSoFar;
+    }
+
+    ++rightMY;
+    ++rightMX;
+
+    /* maybe should try spiral pattern centered around  prev motion vector? */
+
+    /* try a spiral pattern */    
+    for ( distance = 2; distance <= searchRange; distance += 2 ) {
+        tempRightMY = MIN(distance, rightMY);
+        tempRightMX = MIN(distance, rightMX);
+
+        /* do top, bottom */
+        for (my = -distance; my < tempRightMY;
+             my += max(tempRightMY+distance-2, 2)) {
+            if (my >= leftMY) {
+                for ( mx = -distance; mx < tempRightMX; mx += 2 ) {
+                    if (mx >= leftMX) {
+                        int diff;
+                        vector m;
+                        m.y = my; m.x = mx;
+                        diff = LumAddMotionError(currentBlockP, blockP, prev,
+                                                 by, bx, m, bestDiff);
+                        
+                        if (diff < bestDiff) {
+                            *motionP = m;
+                            bestDiff = diff;
+                        }
+                    }
+                }
+            }
+        }
+
+        /* do left, right */
+        for (mx = -distance;
+             mx < tempRightMX;
+             mx += max(tempRightMX+distance-2, 2)) {
+            if (mx >= leftMX) {
+                for (my = -distance+2; my < tempRightMY-2; my += 2) {
+                    if (my >= leftMY) {
+                        int diff;
+                        vector m;
+                        m.y = my; m.x = mx;
+                        diff = LumAddMotionError(currentBlockP, blockP, prev,
+                                                 by, bx, m, bestDiff);
+                        
+                        if (diff < bestDiff) {
+                            *motionP = m;
+                            bestDiff = diff;
+                            changed = TRUE;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /* now look at neighboring half-pixels */
+    my = motionP->y;
+    mx = motionP->x;
+
+    --rightMY;
+    --rightMX;
+
+    for (yOffset = -1; yOffset <= 1; ++yOffset) {
+        for (xOffset = -1; xOffset <= 1; ++xOffset) {
+            if ((yOffset != 0) || (xOffset != 0)) {
+                vector m;
+                m.y = my + yOffset;
+                m.x = mx + xOffset;
+                if (VALID_MOTION(m)) {
+                    int diff;
+                    diff = LumAddMotionError(currentBlockP, blockP,
+                                             prev, by, bx, m, bestDiff);
+                    if (diff < bestDiff) {
+                        *motionP = m;
+                        bestDiff = diff;
+                        changed = TRUE;
+                    }
+                }
+            }
+        }
+    }
+
+    if (!changed)
+        ++bestDiff;
+
+    return bestDiff;
+}
+
+
+
+static void
+trySpacing(int              const spacing,
+           vector           const center,
+           int              const bestDiffSoFar,
+           vector *         const newCenterP,
+           int32 *          const newBestDiffP,
+           int              const leftMY,
+           int              const leftMX,
+           int              const rightMY,
+           int              const rightMX,
+           const LumBlock * const currentBlockP,
+           const LumBlock * const blockP,
+           MpegFrame *      const prev,
+           int              const by,
+           int              const bx) {
+           
+    int tempRightMY, tempRightMX;
+    int my;
+    int bestDiff;
+    vector newCenter;
+
+    /* Initial values */
+    newCenter = center;
+    bestDiff   = bestDiffSoFar;
+
+    tempRightMY = MIN(rightMY, center.y + spacing + 1);
+    tempRightMX = MIN(rightMX, center.x + spacing + 1);
+    
+    for (my = center.y - spacing; my < tempRightMY; my += spacing) {
+        if (my >= leftMY) {
+            int mx;
+            for (mx = center.x - spacing; mx < tempRightMX; mx += spacing) {
+                if (mx >= leftMX) {
+                    int32 diff;
+                    vector m;
+                    m.y = my; m.x = mx;
+                    diff = LumAddMotionError(currentBlockP, blockP, prev,
+                                             by, bx, m, bestDiff);
+                    
+                    if (diff < bestDiff) {
+                        /* We have a new best */
+                        newCenter = m;
+                        bestDiff   = diff;
+                    }
+                }
+            }
+        }
+    }
+    *newCenterP  = newCenter;
+    *newBestDiffP = bestDiff;
+}
+
+
+
+static void
+chooseNewSpacing(int   const oldSpacing,
+                 int   const stepSize,
+                 int * const newSpacingP) {
+        
+    if (stepSize == 2) {  /* make sure spacing is even */
+        if (oldSpacing == 2)
+            *newSpacingP = 0;
+        else {
+            int const trialSpacing = (oldSpacing + 1) / 2;
+            if ((trialSpacing % 2) != 0)
+                *newSpacingP = trialSpacing + 1;
+            else
+                *newSpacingP = trialSpacing;
+        }
+    } else {
+        if (oldSpacing == 1)
+            *newSpacingP = 0;
+        else
+            *newSpacingP = (oldSpacing + 1) / 2;
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * FindBestMatchLogarithmic
+ *
+ *  tries to find matching motion vector
+ *  see FindBestMatch for generic description
+ *
+ * DESCRIPTION:  uses a logarithmic search
+ *
+ *===========================================================================*/
+static int32
+FindBestMatchLogarithmic(const LumBlock * const blockP,
+                         const LumBlock * const currentBlockP,
+                         MpegFrame *      const prev,
+                         int              const by,
+                         int              const bx,
+                         vector *         const motionP,
+                         int32            const bestSoFar,
+                         int              const searchRange) {
+
+    int const stepSize = (pixelFullSearch ? 2 : 1);
+
+    int32 diff, bestDiff;
+    int leftMY, leftMX;
+    int rightMY, rightMX;
+    int spacing;
+    vector center;
+
+    COMPUTE_MOTION_BOUNDARY(by, bx, stepSize,
+                            leftMY, leftMX, rightMY, rightMX);
+
+    bestDiff = 0x7fffffff;
+
+    /* grid spacing */
+    if (stepSize == 2) {  /* make sure spacing is even */
+        spacing = (searchRange + 1) / 2;
+        if ((spacing % 2) != 0)
+            ++spacing;
+    } else
+        spacing = (searchRange + 1) / 2;
+
+    /* Start at (0,0) */
+    center.y = center.x = 0;
+    
+    while (spacing >= stepSize) {
+        trySpacing(spacing, center, bestDiff,
+                   &center, &bestDiff,
+                   leftMY, leftMX, rightMY, rightMX,
+                   currentBlockP, blockP, prev, by, bx);
+
+        chooseNewSpacing(spacing, stepSize, &spacing);
+    }
+
+    /* check old motion -- see if it's better */
+    if ((motionP->y >= leftMY) && (motionP->y < rightMY) &&
+        (motionP->x >= leftMX) && (motionP->x < rightMX)) {
+        diff = LumAddMotionError(currentBlockP, blockP, prev, by, bx,
+                                 *motionP, bestDiff);
+    } else
+        diff = 0x7fffffff;
+
+    if (bestDiff < diff)
+        *motionP = center;
+    else
+        bestDiff = diff;
+
+    return bestDiff;
+}
+
+
+
+/*===========================================================================*
+ *
+ * FindBestMatchSubSample
+ *
+ *  tries to find matching motion vector
+ *  see FindBestMatch for generic description
+ *
+ * DESCRIPTION:  should use subsampling method, but too lazy to write all
+ *       the code for it (so instead just calls FindBestMatchExhaust)
+ *
+ *===========================================================================*/
+static int32
+FindBestMatchSubSample(const LumBlock * const blockP,
+                       const LumBlock * const currentBlockP,
+                       MpegFrame *      const prev,
+                       int              const by,
+                       int              const bx,
+                       vector *         const motionP,
+                       int32            const bestSoFar,
+                       int              const searchRange) {
+
+    /* too lazy to write the code for this... */
+    
+    return FindBestMatchExhaust(blockP, currentBlockP, prev,
+                                by, bx, motionP, bestSoFar,
+                                searchRange);
+}
+
+
+/*===========================================================================*
+ *
+ * FindBestMatch
+ *
+ *  given a motion-compensated block in one direction, tries to find
+ *  the best motion vector in the opposite direction to match it
+ *
+ * RETURNS: the best vector (*motionY, *motionX), and the corresponding
+ *      error is returned if it is better than bestSoFar.  If not,
+ *      then a number greater than bestSoFar is returned and
+ *      (*motionY, *motionX) has no meaning.
+ *
+ * SIDE EFFECTS:  none
+ *
+ *===========================================================================*/
+static int32
+FindBestMatch(const LumBlock * const blockP,
+              const LumBlock * const currentBlockP,
+              MpegFrame *      const prev,
+              int              const by,
+              int              const bx,
+              vector *         const motionP,
+              int32            const bestSoFar,
+              int              const searchRange) {
+
+    int32 result;
+
+    switch(psearchAlg) {
+    case PSEARCH_SUBSAMPLE:
+        result = FindBestMatchSubSample(
+            blockP, currentBlockP, prev, by, bx,
+            motionP, bestSoFar, searchRange);
+        break;
+    case PSEARCH_EXHAUSTIVE:
+        result = FindBestMatchExhaust(
+            blockP, currentBlockP, prev, by, bx,
+            motionP, bestSoFar, searchRange);
+        break;
+    case PSEARCH_LOGARITHMIC:
+        result = FindBestMatchLogarithmic(
+            blockP, currentBlockP, prev, by, bx,
+            motionP, bestSoFar, searchRange);
+        break;
+    case PSEARCH_TWOLEVEL:
+        result = FindBestMatchTwoLevel(
+            blockP, currentBlockP, prev, by, bx,
+            motionP, bestSoFar, searchRange);
+        break;
+    default:
+        pm_error("ERROR:  Impossible P-search alg %d", psearchAlg);
+    }
+    return result;
+}
+
+
+
+/*===========================================================================*
+ *
+ *  finds the best backward and forward motion vectors
+ *  if backNeeded == FALSE, then won't find best backward vector if it
+ *  is worse than the best forward vector
+ *
+ * *motionP is input as well as output.  We do not update it
+ * if it would make the error worse than the existing value.
+ *
+ * RETURNS: *motionP and associated errors *forwardErrP and *backErrP.
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+BMotionSearchNoInterp(const LumBlock * const currentBlockP,
+                      MpegFrame *      const prev,
+                      MpegFrame *      const next,
+                      int              const by,
+                      int              const bx,
+                      motion *         const motionP,
+                      int32 *          const forwardErrP,
+                      int32 *          const backErrP,
+                      boolean          const backNeeded) {
+
+    /* CALL SEARCH PROCEDURE */
+    switch(psearchAlg) {
+    case PSEARCH_SUBSAMPLE:
+        *forwardErrP = PSubSampleSearch(currentBlockP, prev, by, bx, 
+                                        &motionP->fwd,searchRangeB);
+        *backErrP = PSubSampleSearch(currentBlockP, next, by, bx, 
+                                     &motionP->bwd, searchRangeB);
+        break;
+    case PSEARCH_EXHAUSTIVE:
+        *forwardErrP = PLocalSearch(currentBlockP, prev, by, bx,
+                                    &motionP->fwd,
+                                    0x7fffffff, searchRangeB);
+        if (backNeeded)
+            *backErrP = PLocalSearch(currentBlockP, next, by, bx,
+                                     &motionP->bwd,
+                                     0x7fffffff, searchRangeB);
+        else
+            *backErrP = PLocalSearch(currentBlockP, next, by, bx,
+                                     &motionP->bwd,
+                                     *forwardErrP, searchRangeB);
+        break;
+    case PSEARCH_LOGARITHMIC:
+        *forwardErrP = PLogarithmicSearch(currentBlockP, prev, by, bx, 
+                                          &motionP->fwd, searchRangeB);
+        *backErrP = PLogarithmicSearch(currentBlockP, next, by, bx, 
+                                       &motionP->bwd, searchRangeB);
+        break;
+    case PSEARCH_TWOLEVEL:
+        *forwardErrP =
+            PTwoLevelSearch(currentBlockP, prev, by, bx,
+                            &motionP->fwd, 0x7fffffff, searchRangeB);
+        if ( backNeeded ) {
+            *backErrP =
+                PTwoLevelSearch(currentBlockP, next, by, bx,
+                                &motionP->bwd, 0x7fffffff, searchRangeB);
+        } else {
+            *backErrP =
+                PTwoLevelSearch(currentBlockP, next, by, bx,
+                                &motionP->bwd, *forwardErrP, searchRangeB);
+        }
+        break;
+    default:
+        pm_error("ERROR:  Impossible PSEARCH ALG:  %d", psearchAlg);
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * BMotionSearchSimple
+ *
+ *  does a simple search for B-frame motion vectors
+ *  see BMotionSearch for generic description
+ *
+ * DESCRIPTION:
+ *  1)  find best backward and forward vectors
+ *  2)  compute interpolated error using those two vectors
+ *  3)  return the best of the three choices
+ *
+ * *fmyP,fmxP,bmyP,bmxP are inputs as well as outputs.  We do not update
+ * them if it would make the error worse than the existing values.  Otherwise,
+ * we update them to the vectors we find to be best.
+ * 
+ *===========================================================================*/
+static int
+BMotionSearchSimple(const LumBlock * const currentBlockP,
+                    MpegFrame *      const prev,
+                    MpegFrame *      const next,
+                    int              const by,
+                    int              const bx,
+                    motion *         const motionP,
+                    int              const oldMode) {
+
+    int retval;
+    int32 forwardErr, backErr, interpErr;
+    LumBlock interpBlock;
+    int32 bestSoFar;
+
+    /* STEP 1 */
+    BMotionSearchNoInterp(currentBlockP, prev, next, by, bx, motionP,
+                          &forwardErr, &backErr, TRUE);
+              
+    /* STEP 2 */
+
+    ComputeBMotionLumBlock(prev, next, by, bx, MOTION_INTERPOLATE,
+                           *motionP, &interpBlock);
+    bestSoFar = min(backErr, forwardErr);
+    interpErr =
+        LumBlockMAD(currentBlockP, &interpBlock, bestSoFar);
+
+    /* STEP 3 */
+
+    if (interpErr <= forwardErr) {
+        if (interpErr <= backErr)
+            retval = MOTION_INTERPOLATE;
+        else
+            retval = MOTION_BACKWARD;
+    } else if (forwardErr <= backErr)
+        retval = MOTION_FORWARD;
+    else
+        retval = MOTION_BACKWARD;
+
+    return retval;
+}
+
+
+/*===========================================================================*
+ *
+ * BMotionSearchCross2
+ *
+ *  does a cross-2 search for B-frame motion vectors
+ *  see BMotionSearch for generic description
+ *
+ * DESCRIPTION:
+ *  1)  find best backward and forward vectors
+ *  2)  find best matching interpolating vectors
+ *  3)  return the best of the 4 choices
+ *
+ * *fmyP,fmxP,bmyP,bmxP are inputs as well as outputs.  We do not update
+ * them if it would make the error worse than the existing values.  Otherwise,
+ * we update them to the vectors we find to be best.
+ *===========================================================================*/
+static int
+BMotionSearchCross2(const LumBlock * const currentBlockP,
+                    MpegFrame *      const prev,
+                    MpegFrame *      const next,
+                    int              const by,
+                    int              const bx,
+                    motion *         const motionP,
+                    int              const oldMode) {
+    
+    int retval;
+    LumBlock forwardBlock, backBlock;
+    int32   forwardErr, backErr;
+    motion newMotion;
+    int32   interpErrF, interpErrB, interpErr;
+    int32   bestErr;
+
+    /* STEP 1 */
+
+    BMotionSearchNoInterp(currentBlockP, prev, next, by, bx, motionP,
+                          &forwardErr, &backErr, TRUE);
+
+    bestErr = min(forwardErr, backErr);
+
+    {
+        /* STEP 2 */
+        
+        struct motion motion;
+        motion.fwd = motionP->fwd;
+        motion.bwd.y = motion.bwd.x = 0;
+        ComputeBMotionLumBlock(prev, next, by, bx, MOTION_FORWARD, motion,
+                               &forwardBlock);
+        
+        motion.fwd.y = motion.fwd.x = 0;
+        motion.bwd = motionP->bwd;
+        ComputeBMotionLumBlock(prev, next, by, bx, MOTION_BACKWARD, motion,
+                               &backBlock);
+    }
+    /* try a cross-search; total of 4 local searches */    
+    newMotion = *motionP;
+
+    interpErrF = FindBestMatch(&forwardBlock, currentBlockP,
+                               next, by, bx, &newMotion.bwd,
+                               bestErr, searchRangeB);
+    bestErr = min(bestErr, interpErr);
+    interpErrB = FindBestMatch(&backBlock, currentBlockP,
+                               prev, by, bx, &newMotion.fwd,
+                               bestErr, searchRangeB);
+
+    /* STEP 3 */
+
+    if (interpErrF <= interpErrB) {
+        newMotion.fwd = motionP->fwd;
+        interpErr = interpErrF;
+    } else {
+        newMotion.bwd = motionP->bwd;
+        interpErr = interpErrB;
+    }
+
+    if (interpErr <= forwardErr) {
+        if (interpErr <= backErr) {
+            *motionP = newMotion;
+            retval = MOTION_INTERPOLATE;
+        } else
+            retval = MOTION_BACKWARD;
+    } else if (forwardErr <= backErr)
+        retval = MOTION_FORWARD;
+    else
+        retval = MOTION_BACKWARD;
+
+    return retval;
+}
+
+
+/*===========================================================================*
+ *
+ * BMotionSearchExhaust
+ *
+ *  does an exhaustive search for B-frame motion vectors
+ *  see BMotionSearch for generic description
+ *
+ * DESCRIPTION:
+ *  1)  find best backward and forward vectors
+ *  2)  use exhaustive search to find best interpolating vectors
+ *  3)  return the best of the 3 choices
+ *
+ * *fmyP,fmxP,bmyP,bmxP are inputs as well as outputs.  We do not update
+ * them if it would make the error worse than the existing values.  Otherwise,
+ * we update them to the vectors we find to be best.
+ *===========================================================================*/
+static int
+BMotionSearchExhaust(const LumBlock * const currentBlockP,
+                     MpegFrame *      const prev,
+                     MpegFrame *      const next,
+                     int              const by,
+                     int              const bx,
+                     motion *         const motionP,
+                     int              const oldMode) {
+
+    int mx, my;
+    int32 bestDiff;
+    int     stepSize;
+    LumBlock    forwardBlock;
+    int32   forwardErr, backErr;
+    int     leftMY, leftMX;
+    int     rightMY, rightMX;
+    boolean result;
+
+    /* STEP 1 */
+
+    BMotionSearchNoInterp(currentBlockP, prev, next, by, bx, motionP,
+                          &forwardErr, &backErr, FALSE);
+
+    if (forwardErr <= backErr) {
+        bestDiff = forwardErr;
+        result = MOTION_FORWARD;
+    } else {
+        bestDiff = backErr;
+        result = MOTION_BACKWARD;
+    }
+
+    /* STEP 2 */
+
+    stepSize = (pixelFullSearch ? 2 : 1);
+
+    COMPUTE_MOTION_BOUNDARY(by,bx,stepSize,leftMY,leftMX,rightMY,rightMX);
+
+    if (searchRangeB < rightMY)
+        rightMY = searchRangeB;
+    if (searchRangeB < rightMX)
+        rightMX = searchRangeB;
+
+    for (my = -searchRangeB; my < rightMY; my += stepSize) {
+        if (my >= leftMY) {
+            for (mx = -searchRangeB; mx < rightMX; mx += stepSize) {
+                if (mx >= leftMX) {
+                    struct motion motion;
+                    vector newMotion;
+                    int32 diff;
+                    motion.fwd.y = my; motion.fwd.x = mx;
+                    motion.bwd.y = 0;  motion.bwd.x = 0;
+                    ComputeBMotionLumBlock(prev, next, by, bx, MOTION_FORWARD,
+                                           motion, &forwardBlock);
+
+                    newMotion = motion.fwd;
+                    
+                    diff = FindBestMatch(&forwardBlock,
+                                         currentBlockP, next, by, bx,
+                                         &newMotion, bestDiff, searchRangeB);
+                    
+                    if (diff < bestDiff) {
+                        motionP->fwd = motion.fwd;
+                        motionP->bwd = newMotion;
+                        bestDiff = diff;
+                        result = MOTION_INTERPOLATE;
+                    }
+                }
+            }
+        }
+    }
+    return result;
+}
+
+
+
+/*===========================================================================*
+ *
+ *  search for the best B-frame motion vectors
+ *
+ * RETURNS: MOTION_FORWARD      forward motion should be used
+ *      MOTION_BACKWARD     backward motion should be used
+ *      MOTION_INTERPOLATE  both should be used and interpolated
+ *
+ * OUTPUTS: *motionP  =   TWICE the forward motion vectors
+ *
+ * SIDE EFFECTS:    none
+ *
+ * PRECONDITIONS:   The relevant block in 'current' is valid (it has not
+ *          been dct'd).  Thus, the data in 'current' can be
+ *          accesed through y_blocks, cr_blocks, and cb_blocks.
+ *          This is not the case for the blocks in 'prev' and
+ *          'next.'  Therefore, references into 'prev' and 'next'
+ *          should be done
+ *          through the struct items ref_y, ref_cr, ref_cb
+ *
+ * POSTCONDITIONS:  current, prev, next should be unchanged.
+ *          Some computation could be saved by requiring
+ *          the dct'd difference to be put into current's block
+ *          elements here, depending on the search technique.
+ *          However, it was decided that it mucks up the code
+ *          organization a little, and the saving in computation
+ *          would be relatively little (if any).
+ *
+ * NOTES:   the search procedure MAY return (0,0) motion vectors
+ *
+ *===========================================================================*/
+int
+BMotionSearch(const LumBlock * const currentBlockP,
+              MpegFrame *      const prev,
+              MpegFrame *      const next,
+              int              const by,
+              int              const bx,
+              motion *         const motionP,
+              int              const oldMode) {
+
+    int retval;
+
+    /* If we are an initial B frame, no possibility of forward motion */
+    if (prev == (MpegFrame *) NULL) {
+        PMotionSearch(currentBlockP, next, by, bx, &motionP->bwd);
+        return MOTION_BACKWARD;
+    }
+  
+    /* otherwise simply call the appropriate algorithm, based on user
+       preference
+    */
+
+    switch(bsearchAlg) {
+    case BSEARCH_SIMPLE:
+        retval = BMotionSearchSimple(currentBlockP, prev, next, by, bx,
+                                     motionP, oldMode);
+        break;
+    case BSEARCH_CROSS2:
+        retval = BMotionSearchCross2(currentBlockP, prev, next, by, bx,
+                                     motionP, oldMode);
+        break;
+    case BSEARCH_EXHAUSTIVE:
+        retval = BMotionSearchExhaust(currentBlockP, prev, next, by, bx,
+                                      motionP, oldMode);
+        break;
+    default:
+        pm_error("Impossible B-frame motion search algorithm:  %d",
+                 bsearchAlg);
+    }
+    return retval;
+}
+
+
+/*===========================================================================*
+ *                                       *
+ * UNUSED PROCEDURES                                 *
+ *                                       *
+ *  The following procedures are all unused by the encoder           *
+ *                                       *
+ *  They are listed here for your convenience.  You might want to use    *
+ *  them if you experiment with different search techniques          *
+ *                                       *
+ *===========================================================================*/
+
+#ifdef UNUSED_PROCEDURES
+
+/*===========================================================================*
+ *
+ * ValidBMotion
+ *
+ *  decides if the given B-frame motion is valid
+ *
+ * RETURNS: TRUE if the motion is valid, FALSE otherwise
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+boolean
+ValidBMotion(by, bx, mode, fmy, fmx, bmy, bmx)
+    int by;
+    int bx;
+    int mode;
+    int fmy;
+    int fmx;
+    int bmy;
+    int bmx;
+{
+    if ( mode != MOTION_BACKWARD ) {
+    /* check forward motion for bounds */
+    if ( (by*DCTSIZE+(fmy-1)/2 < 0) || ((by+2)*DCTSIZE+(fmy+1)/2-1 >= Fsize_y) ) {
+        return FALSE;
+    }
+    if ( (bx*DCTSIZE+(fmx-1)/2 < 0) || ((bx+2)*DCTSIZE+(fmx+1)/2-1 >= Fsize_x) ) {
+        return FALSE;
+    }
+    }
+
+    if ( mode != MOTION_FORWARD ) {
+    /* check backward motion for bounds */
+    if ( (by*DCTSIZE+(bmy-1)/2 < 0) || ((by+2)*DCTSIZE+(bmy+1)/2-1 >= Fsize_y) ) {
+        return FALSE;
+    }
+    if ( (bx*DCTSIZE+(bmx-1)/2 < 0) || ((bx+2)*DCTSIZE+(bmx+1)/2-1 >= Fsize_x) ) {
+        return FALSE;
+    }
+    }
+
+    return TRUE;
+}
+
+
+#endif /* UNUSED_PROCEDURES */
diff --git a/converter/ppm/ppmtompeg/combine.c b/converter/ppm/ppmtompeg/combine.c
new file mode 100644
index 00000000..341bb5dc
--- /dev/null
+++ b/converter/ppm/ppmtompeg/combine.c
@@ -0,0 +1,357 @@
+/*===========================================================================*
+ * combine.c
+ *
+ *  Procedures to combine frames or GOPS into an MPEG sequence
+ *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "all.h"
+#include <time.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "ppm.h"
+#include "nstring.h"
+
+#include "mtypes.h"
+#include "frames.h"
+#include "frametype.h"
+#include "motion_search.h"
+#include "mpeg.h"
+#include "prototypes.h"
+#include "parallel.h"
+#include "param.h"
+#include "readframe.h"
+#include "mheaders.h"
+#include "fsize.h"
+#include "input.h"
+#include "combine.h"
+
+
+#define READ_ATTEMPTS 5 /* number of times (seconds) to retry an input file */
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+extern int  yuvWidth, yuvHeight;
+
+
+/*===========================================================================*
+ *
+ * AppendFile
+ *
+ *  appends the output file with the contents of the given input file
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+AppendFile(FILE * const ofP,
+           FILE * const ifP) {
+
+    uint8 data[9999];
+    unsigned int readItems;
+
+    readItems = 9999;
+    while (readItems == 9999) {
+        readItems = fread(data, sizeof(uint8), 9999, ifP);
+        if (readItems > 0)
+            fwrite(data, sizeof(uint8), readItems, ofP);
+    }
+    fclose(ifP);
+}
+
+
+
+static void
+appendSpecifiedGopFiles(struct inputSource * const inputSourceP,
+                        const char *         const currentGopPath,
+                        FILE *               const ofP) {
+
+    unsigned int fileSeq;
+
+    for (fileSeq = 0; fileSeq < inputSourceP->numInputFiles; ++fileSeq) {
+        const char * inputFileName;
+        const char * fileName;
+        unsigned int nAttempts;
+        FILE * ifP;
+
+        GetNthInputFileName(inputSourceP, fileSeq, &inputFileName);
+        asprintfN(&fileName, "%s/%s", currentGOPPath, inputFileName);
+
+        for (nAttempts = 0, ifP = NULL;
+             nAttempts < READ_ATTEMPTS && !ifP;
+             ++nAttempts) {
+
+            FILE * ifP;
+            ifP = fopen(fileName, "rb");
+            if (ifP == NULL) {
+                pm_message("ERROR:  Couldn't read file '%s'.  retry %u", 
+                           fileName, nAttempts);
+                sleep(1);
+            }
+        }
+        if (ifP) {
+            if (!realQuiet)
+                pm_message("Appending file %u '%s'", fileSeq, fileName);
+            AppendFile(ofP, ifP);
+        } else
+            pm_error("Unable to read file '%s' after %u attempts.",
+                     fileName, READ_ATTEMPTS);
+        strfree(fileName);
+        strfree(inputFileName);
+    }
+} 
+
+
+
+static void
+appendDefaultGopFiles(const char * const outputFileName,
+                      FILE *       const ofP) {
+
+    unsigned int fileSeq;
+    bool endOfFiles;
+    
+    for (fileSeq = 0, endOfFiles = FALSE; !endOfFiles; ++fileSeq) {
+        const char * fileName;
+        FILE * ifP;
+
+        asprintfN(&fileName, "%s.gop.%u", outputFileName, fileSeq);
+        
+        ifP = fopen(fileName, "rb");
+        if (ifP == NULL)
+            endOfFiles = TRUE;
+        else {
+            if (!realQuiet)
+                pm_message("appending file:  %s", fileName);
+            
+            AppendFile(ofP, ifP);
+        }
+        strfree(fileName);
+    }
+}
+
+
+
+void
+GOPsToMPEG(struct inputSource * const inputSourceP,
+           const char *         const outputFileName, 
+           FILE *               const ofP) {
+/*----------------------------------------------------------------------------
+   Combine individual GOPs (one per file) into a single MPEG sequence file.
+-----------------------------------------------------------------------------*/
+    BitBucket * bb;
+
+    {
+        /* Why is this reset called? */
+        int x=Fsize_x, y=Fsize_y;
+        Fsize_Reset();
+        Fsize_Note(0, yuvWidth, yuvHeight);
+        if (Fsize_x == 0 || Fsize_y == 0)
+            Fsize_Note(0, x, y);
+    }
+    
+    bb = Bitio_New(ofP);
+
+    Mhead_GenSequenceHeader(bb, Fsize_x, Fsize_y, /* pratio */ aspectRatio,
+                            /* pict_rate */ frameRate, /* bit_rate */ -1,
+                            /* buf_size */ -1, /*c_param_flag */ 1,
+                            /* iq_matrix */ customQtable, 
+                            /* niq_matrix */ customNIQtable,
+                            /* ext_data */ NULL, /* ext_data_size */ 0,
+                            /* user_data */ NULL, /* user_data_size */ 0);
+
+    /* it's byte-padded, so we can dump it now */
+    Bitio_Flush(bb);
+
+    if (inputSourceP->stdinUsed)
+        AppendFile(ofP, stdin);
+    else {
+        if (inputSourceP->numInputFiles > 0)
+            appendSpecifiedGopFiles(inputSourceP, currentGOPPath, ofP);
+        else
+            appendDefaultGopFiles(outputFileName, ofP);
+    }
+    bb = Bitio_New(ofP);
+
+    /* SEQUENCE END CODE */
+    Mhead_GenSequenceEnder(bb);
+
+    Bitio_Flush(bb);
+
+    fclose(ofP);
+}
+
+
+
+static void
+makeGOPHeader(FILE *       const outputFileP,
+              unsigned int const totalFramesSent,
+              unsigned int const frameNumber,
+              unsigned int const seqWithinGop) {
+
+    boolean closed = (totalFramesSent == frameNumber);
+
+    BitBucket * bbP;
+
+    if (!realQuiet)
+        fprintf(stdout, "Creating new GOP (closed = %d) after %d frames\n",
+                closed, seqWithinGop);
+
+    /* new GOP */
+    bbP = Bitio_New(outputFileP);
+    Mhead_GenGOPHeader(bbP, /* drop_frame_flag */ 0,
+                       tc_hrs, tc_min, tc_sec, tc_pict,
+                       closed, /* broken_link */ 0,
+                       /* ext_data */ NULL, /* ext_data_size */ 0,
+                       /* user_data */ NULL, 
+                       /* user_data_size */ 0);
+    Bitio_Flush(bbP);
+    SetGOPStartTime(frameNumber);
+}        
+
+
+
+void
+FramesToMPEG(FILE *               const outputFile, 
+             void *               const inputHandle,
+             fileAcquisitionFn          acquireInputFile,
+             fileDispositionFn          disposeInputFile) {
+/*----------------------------------------------------------------------------
+   Combine a bunch of frames, one per file, into a single MPEG
+   sequence file.
+
+   acquireInputFile() opens a file that contains an encoded frame,
+   identified by frame number.  It returns NULL when the frame number
+   is beyond the frames available.
+-----------------------------------------------------------------------------*/
+    unsigned int frameNumber;
+    BitBucket *bb;
+    FILE *inputFile;
+    int pastRefNum = -1;
+    int futureRefNum = -1;
+    boolean inputLeft;
+    unsigned int seqWithinGop;
+        /* The sequence of the current frame within its GOP.  0 is the
+           first frame of a GOP, etc.
+        */
+
+    tc_hrs = 0; tc_min = 0; tc_sec = 0; tc_pict = 0; tc_extra = 0;
+
+    {
+        /* Why is this reset called? */
+        int x=Fsize_x, y=Fsize_y;
+        Fsize_Reset();
+        Fsize_Note(0, yuvWidth, yuvHeight);
+        if (Fsize_x == 0 || Fsize_y == 0)
+            Fsize_Note(0, x, y);
+    }
+    SetBlocksPerSlice();
+
+    bb = Bitio_New(outputFile);
+    Mhead_GenSequenceHeader(bb, Fsize_x, Fsize_y, /* pratio */ aspectRatio,
+                            /* pict_rate */ frameRate, /* bit_rate */ -1,
+                            /* buf_size */ -1, /*c_param_flag */ 1,
+                            /* iq_matrix */ qtable, /* niq_matrix */ niqtable,
+                            /* ext_data */ NULL, /* ext_data_size */ 0,
+                            /* user_data */ NULL, /* user_data_size */ 0);
+    /* it's byte-padded, so we can dump it now */
+    Bitio_Flush(bb);
+
+    /* need to do these in the right order!!! */
+    /* also need to add GOP headers */
+
+    seqWithinGop = 0;
+    totalFramesSent = 0;
+
+    inputLeft = TRUE;
+    frameNumber = 0;
+
+    makeGOPHeader(outputFile, totalFramesSent, frameNumber, seqWithinGop);
+    
+    while (inputLeft) {
+        if (FType_Type(frameNumber) != 'b') {
+            pastRefNum = futureRefNum;
+            futureRefNum = frameNumber;
+
+            if ((FType_Type(frameNumber) == 'i') && seqWithinGop >= gopSize) {
+                makeGOPHeader(outputFile,
+                              totalFramesSent, frameNumber, seqWithinGop);
+                seqWithinGop -= gopSize;
+            }
+
+            acquireInputFile(inputHandle, frameNumber, &inputFile);
+            if (inputFile == NULL)
+                inputLeft = FALSE;
+            else {
+                AppendFile(outputFile, inputFile);
+
+                disposeInputFile(inputHandle, frameNumber);
+
+                ++seqWithinGop;
+                IncrementTCTime();
+
+                /* now, output the B-frames */
+                if (pastRefNum != -1) {
+                    unsigned int bNum;
+                    
+                    for (bNum = pastRefNum+1; bNum < futureRefNum; ++bNum) {
+                        acquireInputFile(inputHandle, bNum, &inputFile);
+
+                        if (inputFile) {
+                            AppendFile(outputFile, inputFile);
+
+                            disposeInputFile(inputHandle, bNum);
+            
+                            ++seqWithinGop;
+                            IncrementTCTime();
+                        }
+                    }
+                }
+            }
+        }
+        ++frameNumber;
+    }
+
+    if (!realQuiet)
+        fprintf(stdout, "Wrote %d frames\n", totalFramesSent);
+
+    bb = Bitio_New(outputFile);
+
+    /* SEQUENCE END CODE */
+    Mhead_GenSequenceEnder(bb);
+
+    Bitio_Flush(bb);
+
+    fclose(outputFile);
+}
+
+
+
diff --git a/converter/ppm/ppmtompeg/docs/EXTENSIONS b/converter/ppm/ppmtompeg/docs/EXTENSIONS
new file mode 100644
index 00000000..c683fbf8
--- /dev/null
+++ b/converter/ppm/ppmtompeg/docs/EXTENSIONS
@@ -0,0 +1,41 @@
+EXTENSIONS
+----------
+
+This is a list of things that we'd like to incorporate into the encoder.
+If you succeed in implementing any of them, please let us know!
+
+*  better B-frame search technique
+*  use DCT-space when computing error terms
+*  vary the q-scale according to the error term
+*  other motion vector search techniques
+*  modify the program to have a finer-grained parallelism option -- we
+   can probably encode slices in parallel (this will only be useful if we
+   want to do a few B-frames using exhaustive search)
+*  include system layer
+*  VBV delay with rate control
+
+
+CREATING YOUR OWN MOTION SEARCH ROUTINES
+----------------------------------------
+
+Adding your own special motion search routine is very easy.  We'll explain
+adding a P-frame search routine; adding a B-frame routine is similar.
+
+First, edit the procedures PMotionSearch and SetPSearchAlg (both in the
+file psearch.c) to recognize your new search routine.  You probably want
+to define a constant
+    PSEARCH_<your search name> in headers/search.h
+
+Have PMotionSearch call your search procedure just as it calls the other
+standard search procedures.  Make sure your procedure follows the guidelines
+in the comments for PMotionSearch.
+
+Note:  The encoder uses MAD as its search criterion.  The reason for this:
+	"Among the various criteria that can be used as a measure of the
+	 match between the two blocks, the mean absolute difference (MAD)
+	 is favored because it requires no multiplication and gives
+	 similar performance as the mean squared error (MSE)."
+	- Liu and Zaccarin,
+	  "New Fast Algorithms for the Estimation of Block Motion Vectors,"
+	  IEEE Transactions on Circuits and Systems for Video Technology
+	  Volume 3 No. 2 (April 1993)
diff --git a/converter/ppm/ppmtompeg/docs/INPUT.FORMAT b/converter/ppm/ppmtompeg/docs/INPUT.FORMAT
new file mode 100644
index 00000000..8a663d3a
--- /dev/null
+++ b/converter/ppm/ppmtompeg/docs/INPUT.FORMAT
@@ -0,0 +1,79 @@
+			----------------
+			| FILE FORMATS |
+			----------------
+
+PPM FORMAT
+----------
+
+See http://netpbm.sourceforge.net/doc/ppm.html .
+
+
+UCB YUV FORMAT
+--------------
+
+You should be aware that the YUV format used in the MPEG encoder is DIFFERENT
+than the Abekas YUV format.  The reason for this is that in MPEG, the U and
+V components are subsampled 4:1.
+
+To give you an idea of what format the YUV file must be in, the following
+code will read in a YUV file:
+
+    unsigned char **y_data, **cr_data, **cb_data;
+
+    void	ReadYUV(char *fileName, int width, int height)
+    {
+	FILE *fpointer;
+	register int y;
+
+	/* should allocate memory for y_data, cr_data, cb_data here */
+
+	fpointer = fopen(fileName, "r");
+
+	for (y = 0; y < height; y++)			/* Y */
+	    fread(y_data[y], 1, width, fpointer);
+
+	for (y = 0; y < height / 2; y++)			/* U */
+	    fread(cb_data[y], 1, width / 2, fpointer);
+
+	for (y = 0; y < height / 2; y++)			/* V */
+	    fread(cr_data[y], 1, width / 2, fpointer);
+
+	fclose(fpointer);
+    }
+
+There are two reasons why you'd want to use YUV files rather than PPM files:
+	1)  The YUV files are 50% the size of the corresponding PPM files
+	2)  The ENCODER will run slightly faster, since it doesn't have to
+	    do the RGB to YUV conversion itself.
+
+
+ABEKAS YUV FORMAT 
+-----------------
+
+The Abekas YUV Format interlaces the Y, U, and V values in a 4:2:2 format.
+The interlacing pattern is
+
+UYVY
+
+for each group of 4 bytes in the file.
+
+
+PHILLIPS YUV FORMAT 
+-------------------
+
+The Phillips YUV Format interlaces the Y, U, and V values in a 4:2:2 format.
+The interlacing pattern is
+
+YVYU
+
+for each group of 4 bytes in the file.
+
+
+You may specify either ABEKAS, PHILLIPS, or UCB as the YUV_FORMAT when
+encoding ; the encoder defaults to UCB YUV_FORMAT if not specified.
+In addition, if you've got a weird interlacing format, you can also
+try and de-interlace it by giving the YUV pattern in the YUV_FORMAT.
+So a YUV 4:4:4 format would be
+
+YUVYUV
+
diff --git a/converter/ppm/ppmtompeg/docs/parallel.param b/converter/ppm/ppmtompeg/docs/parallel.param
new file mode 100644
index 00000000..0702d32c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/docs/parallel.param
@@ -0,0 +1,142 @@
+# parameter file template with parallel execution
+#
+# you can use this as a template, copying it to a separate file then modifying
+# the copy
+#
+#
+# any line beginning with '#' is a comment
+#
+# no line should be longer than 255 characters
+#
+#
+# general format of each line is:
+#	<option> <spaces and/or tabs> <value>
+#
+# lines can generally be in any order
+#
+# only exception is the option 'INPUT' which must be followed by input
+# files in the order in which they must appear, followed by 'END_INPUT'
+#
+# <option> MUST be in UPPER CASE
+#
+
+PATTERN		IBBPBBI
+OUTPUT		/n/picasso/users/keving/encode/output.mpg
+
+# mpeg_encode really only accepts 3 different file formats, but using a
+# conversion statement it can effectively handle ANY file format
+#
+# you must specify whether you will convert to PNM or PPM or YUV format
+#	(must be upper case)
+#
+BASE_FILE_FORMAT	YUV
+
+#
+# if YUV format (or using parallel version), must provide width and height
+# YUV_SIZE	widthxheight
+# this option is ignored if BASE_FILE_FORMAT is PPM or PNM and you're running
+# on just one machine
+#
+YUV_SIZE	352x240
+
+# the conversion statement
+#
+# Each occurrence of '*' will be replaced by the input file
+#
+# e.g., if you have a bunch of GIF files, then this might be:
+#	INPUT_CONVERT	giftoppm *
+#
+# e.g., if you have a bunch of files like a.Y a.U a.V, etc., then:
+#	INPUT_CONVERT	cat *.Y *.U *.V
+#
+# e.g., if you are grabbing from laser disc you might have something like
+#	INPUT_CONVERT	goto frame *; grabppm
+# 'INPUT_CONVERT *' means the files are already in the base file format
+#
+INPUT_CONVERT	*
+
+# number of frames in a GOP.
+#
+# since each GOP must have at least one I-frame, the encoder will find the
+# the first I-frame after GOP_SIZE frames to start the next GOP
+#
+# later, will add more flexible GOP signalling
+#
+GOP_SIZE	6
+
+# number of slices in a frame
+#
+# 1 is a good number.  another possibility is the number of macroblock rows
+# (which is the height divided by 16)
+#
+SLICES_PER_FRAME	1
+
+# directory to get all input files from (makes this file easier to read)
+# can't use stdin with parallel encoding
+INPUT_DIR	/n/picasso/users/keving/encode/input/tennis
+
+INPUT
+# '*' is replaced by the numbers 01, 02, 03, 04
+# if I instead do [01-11], it would be 01, 02, ..., 09, 10, 11
+# if I instead do [1-11], it would be 1, 2, 3, ..., 9, 10, 11
+# if I instead do [1-11+3], it would be 1, 4, 7, 10
+# the program assumes none of your input files has a name ending in ']'
+# if you do, too bad!!!
+#
+#
+stennis.*.yuv	[0-7]
+# can have more files here if you want...there is no limit on the number
+# of files
+END_INPUT
+
+
+
+# all of the remaining options have to do with the motion search and qscale
+
+# FULL or HALF -- must be upper case
+PIXEL		HALF
+
+# means +/- this many pixels
+RANGE		10
+
+# this must be one of {EXHAUSTIVE, SUBSAMPLE, LOGARITHMIC}
+PSEARCH_ALG	LOGARITHMIC
+
+# this must be one of {SIMPLE, CROSS2, EXHAUSTIVE}
+#
+# note that EXHAUSTIVE is really, really, really slow
+#
+BSEARCH_ALG	CROSS2
+
+#
+# these specify the q-scale for I, P, and B frames
+# (values must be between 1 and 31)
+#
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# this must be ORIGINAL or DECODED
+REFERENCE_FRAME	ORIGINAL
+
+
+# the following two are optional  (default = 10, 60)
+
+# number of frames to do initially to gauge speed of machine
+PARALLEL_TEST_FRAMES	3
+
+# number of seconds per chunk thereafter
+PARALLEL_TIME_CHUNKS	30
+
+
+PARALLEL
+# lines must be of form "machine <whitespace> username <whitespace> executable"
+charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#REMOTE charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode ~keving/encode/examples/parallel.param
+# remote machine: "REMOTE machine username executable param_file"
+# mickey		keving	~keving/encode/bin/dec-5000/mpeg_encode
+#REMOTE mickey		keving	~keving/encode/bin/dec-5000/mpeg_encode ~keving/encode/examples/parallel.param
+#REMOTE mickey		keving	~keving/encode/bin/dec-5000/mpeg_encode ~keving/encode/examples/parallel.param
+REMOTE woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode ~keving/encode/examples/parallel.param
+END_PARALLEL
diff --git a/converter/ppm/ppmtompeg/docs/param-summary b/converter/ppm/ppmtompeg/docs/param-summary
new file mode 100644
index 00000000..d8322bfb
--- /dev/null
+++ b/converter/ppm/ppmtompeg/docs/param-summary
@@ -0,0 +1,14 @@
+Different MPEG coding strategies
+--------------------------------
+
+Strategy			Speed	Compression	Quality
+--------			-----	-----------	-------
+Increase MV Search Range	Down	Up		Up?
+Increasing Q-Scale		Same	Up		Down
+Decreasing Q-Scale		Same	Down		Up
+Use half-pixel search		Down	Up		Up?
+Use pixel subsampling		Up	Down		Down?
+Use motion vector subsampling	Up	Down		Down?
+Use decoded frame as reference	Same	Unknown		Up?
+Varying q-scale			Down	Up		Down
+
diff --git a/converter/ppm/ppmtompeg/docs/template.param b/converter/ppm/ppmtompeg/docs/template.param
new file mode 100644
index 00000000..66b6dd98
--- /dev/null
+++ b/converter/ppm/ppmtompeg/docs/template.param
@@ -0,0 +1,154 @@
+# parameter file template with lots of comments to assist you
+#
+# you can use this as a template, copying it to a separate file then modifying
+# the copy
+#
+#
+# any line beginning with '#' is a comment
+#
+# no line should be longer than 255 characters
+#
+#
+# general format of each line is:
+#	<option> <spaces and/or tabs> <value>
+#
+# lines can generally be in any order
+#
+# an exception is the option 'INPUT' which must be followed by input
+# files in the order in which they must appear, followed by 'END_INPUT'
+#
+# Also, if you use the `command` method of generating input file names,
+# the command will only be executed in the INPUT_DIR if INPUT_DIR preceeds
+# the INPUT parameter.
+#
+# <option> MUST be in UPPER CASE
+#
+
+PATTERN		IBBPBBPBBPBBPBBP
+OUTPUT		output.mpg
+
+# mpeg_encode really only accepts 3 different file formats, but using a
+# conversion statement it can effectively handle ANY file format
+#
+# You must specify the type of the input files.  The choices are:
+#    YUV, PPM, JMOVIE, Y, JPEG, PNM
+#	(must be upper case)
+#
+BASE_FILE_FORMAT	YUV
+
+#
+# if YUV format (or using parallel version), must provide width and height
+# YUV_SIZE	widthxheight
+# this option is ignored if BASE_FILE_FORMAT is not YUV and you're running
+# on just one machine
+#
+YUV_SIZE	352x240
+
+# If you are using YUV, there are different supported file formats.
+# EYUV or UCB are the same as previous versions of this encoder.
+# (All the Y's, then U's then V's, in 4:2:0 subsampling.)
+# Other formats, such as Abekas, Phillips, or a general format are
+# permissible, the general format is a string of Y's, U's, and V's
+# to specify the file order.
+
+INPUT_FORMAT UCB
+
+# the conversion statement
+#
+# Each occurrence of '*' will be replaced by the input file
+#
+# e.g., if you have a bunch of GIF files, then this might be:
+#	INPUT_CONVERT	giftoppm *
+#
+# e.g., if you have a bunch of files like a.Y a.U a.V, etc., then:
+#	INPUT_CONVERT	cat *.Y *.U *.V
+#
+# e.g., if you are grabbing from laser disc you might have something like
+#	INPUT_CONVERT	goto frame *; grabppm
+# 'INPUT_CONVERT *' means the files are already in the base file format
+#
+INPUT_CONVERT	*
+
+# number of frames in a GOP.
+#
+# since each GOP must have at least one I-frame, the encoder will find the
+# the first I-frame after GOP_SIZE frames to start the next GOP
+#
+# later, will add more flexible GOP signalling
+#
+GOP_SIZE	16
+
+# number of slices in a frame
+#
+# 1 is a good number.  another possibility is the number of macroblock rows
+# (which is the height divided by 16)
+#
+SLICES_PER_FRAME	1
+
+# directory to get all input files from (makes this file easier to read)
+INPUT_DIR	../input/tennis
+
+# There are a bunch of ways to specify the input files.
+# from a simple one-per-line listing, to the following 
+# way of numbering them.  See the manual for more information.
+INPUT
+# '*' is replaced by the numbers 01, 02, 03, 04
+# if I instead do [01-11], it would be 01, 02, ..., 09, 10, 11
+# if I instead do [1-11], it would be 1, 2, 3, ..., 9, 10, 11
+# if I instead do [1-11+3], it would be 1, 4, 7, 10
+# the program assumes none of your input files has a name ending in ']'
+# if you do, too bad!!!
+#
+#
+stennis.*.yuv	[0-23]
+# can have more files here if you want...there is no limit on the number
+# of files
+END_INPUT
+
+
+
+# Many of the remaining options have to do with the motion search and qscale
+
+# FULL or HALF -- must be upper case
+PIXEL		HALF
+
+# means +/- this many pixels for both P and B frame searches
+# specify two numbers if you wish to serc different ranges in the two.
+RANGE		10
+
+# this must be one of {EXHAUSTIVE, SUBSAMPLE, LOGARITHMIC}
+PSEARCH_ALG	LOGARITHMIC
+
+# this must be one of {SIMPLE, CROSS2, EXHAUSTIVE}
+#
+# note that EXHAUSTIVE is really, really, really slow
+#
+BSEARCH_ALG	CROSS2
+
+#
+# these specify the q-scale for I, P, and B frames
+# (values must be between 1 and 31)
+# These are the Qscale values for the entire frame in variable bit-rate
+# mode, and starting points (but not important) for constant bit rate
+#
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# this must be ORIGINAL or DECODED
+REFERENCE_FRAME	ORIGINAL
+
+# for parallel parameters see parallel.param in the exmaples subdirectory
+
+# if you want constant bit-rate mode, specify it as follows (number is bits/sec):
+BIT_RATE  1000000
+
+# To specify the buffer size (327680 is default, measused in bits, for 16bit words)
+BUFFER_SIZE 327680
+
+# The frame rate is the number of frames/second (legal values:
+# 23.976, 24, 25, 29.97, 30, 50 ,59.94, 60
+FRAME_RATE 30
+
+# There are many more options, see the users manual for examples....
+# ASPECT_RATIO, USER_DATA, GAMMA, IQTABLE, etc.
diff --git a/converter/ppm/ppmtompeg/docs/users-guide.ps b/converter/ppm/ppmtompeg/docs/users-guide.ps
new file mode 100644
index 00000000..2e39be0a
--- /dev/null
+++ b/converter/ppm/ppmtompeg/docs/users-guide.ps
@@ -0,0 +1,3233 @@
+%!
+%%BoundingBox: (atend)
+%%Pages: (atend)
+%%DocumentFonts: (atend)
+%%EndComments
+%
+% FrameMaker PostScript Prolog 3.0, for use with FrameMaker 3.0
+% Copyright (c) 1986,87,89,90,91 by Frame Technology Corporation.
+% All rights reserved.
+%
+% Known Problems:
+%	Due to bugs in Transcript, the 'PS-Adobe-' is omitted from line 1
+/FMversion (3.0) def 
+% Set up Color vs. Black-and-White
+	/FMPrintInColor systemdict /colorimage known
+		systemdict /currentcolortransfer known or def
+% Uncomment this line to force b&w on color printer
+%   /FMPrintInColor false def
+/FrameDict 195 dict def 
+systemdict /errordict known not {/errordict 10 dict def
+		errordict /rangecheck {stop} put} if
+% The readline in 23.0 doesn't recognize cr's as nl's on AppleTalk
+FrameDict /tmprangecheck errordict /rangecheck get put 
+errordict /rangecheck {FrameDict /bug true put} put 
+FrameDict /bug false put 
+mark 
+% Some PS machines read past the CR, so keep the following 3 lines together!
+currentfile 5 string readline
+00
+0000000000
+cleartomark 
+errordict /rangecheck FrameDict /tmprangecheck get put 
+FrameDict /bug get { 
+	/readline {
+		/gstring exch def
+		/gfile exch def
+		/gindex 0 def
+		{
+			gfile read pop 
+			dup 10 eq {exit} if 
+			dup 13 eq {exit} if 
+			gstring exch gindex exch put 
+			/gindex gindex 1 add def 
+		} loop
+		pop 
+		gstring 0 gindex getinterval true 
+		} def
+	} if
+/FMVERSION {
+	FMversion ne {
+		/Times-Roman findfont 18 scalefont setfont
+		100 100 moveto
+		(FrameMaker version does not match postscript_prolog!)
+		dup =
+		show showpage
+		} if
+	} def 
+/FMLOCAL {
+	FrameDict begin
+	0 def 
+	end 
+	} def 
+	/gstring FMLOCAL
+	/gfile FMLOCAL
+	/gindex FMLOCAL
+	/orgxfer FMLOCAL
+	/orgproc FMLOCAL
+	/organgle FMLOCAL
+	/orgfreq FMLOCAL
+	/yscale FMLOCAL
+	/xscale FMLOCAL
+	/manualfeed FMLOCAL
+	/paperheight FMLOCAL
+	/paperwidth FMLOCAL
+/FMDOCUMENT { 
+	array /FMfonts exch def 
+	/#copies exch def
+	FrameDict begin
+	0 ne dup {setmanualfeed} if
+	/manualfeed exch def
+	/paperheight exch def
+	/paperwidth exch def
+	/yscale exch def
+	/xscale exch def
+	currenttransfer cvlit /orgxfer exch def
+	currentscreen cvlit /orgproc exch def
+	/organgle exch def /orgfreq exch def
+	setpapername 
+	manualfeed {true} {papersize} ifelse 
+	{manualpapersize} {false} ifelse 
+	{desperatepapersize} if
+	end 
+	} def 
+	/pagesave FMLOCAL
+	/orgmatrix FMLOCAL
+	/landscape FMLOCAL
+/FMBEGINPAGE { 
+	FrameDict begin 
+	/pagesave save def
+	3.86 setmiterlimit
+	/landscape exch 0 ne def
+	landscape { 
+		90 rotate 0 exch neg translate pop 
+		}
+		{pop pop}
+		ifelse
+	xscale yscale scale
+	/orgmatrix matrix def
+	gsave 
+	} def 
+/FMENDPAGE {
+	grestore 
+	pagesave restore
+	end 
+	showpage
+	} def 
+/FMFONTDEFINE { 
+	FrameDict begin
+	findfont 
+	ReEncode 
+	1 index exch 
+	definefont 
+	FMfonts 3 1 roll 
+	put
+	end 
+	} def 
+/FMFILLS {
+	FrameDict begin
+	array /fillvals exch def
+	end 
+	} def 
+/FMFILL {
+	FrameDict begin
+	 fillvals 3 1 roll put
+	end 
+	} def 
+/FMNORMALIZEGRAPHICS { 
+	newpath
+	0.0 0.0 moveto
+	1 setlinewidth
+	0 setlinecap
+	0 0 0 sethsbcolor
+	0 setgray 
+	} bind def
+	/fx FMLOCAL
+	/fy FMLOCAL
+	/fh FMLOCAL
+	/fw FMLOCAL
+	/llx FMLOCAL
+	/lly FMLOCAL
+	/urx FMLOCAL
+	/ury FMLOCAL
+/FMBEGINEPSF { 
+	end 
+	/FMEPSF save def 
+	/showpage {} def 
+	FMNORMALIZEGRAPHICS 
+	[/fy /fx /fh /fw /ury /urx /lly /llx] {exch def} forall 
+	fx fy translate 
+	rotate
+	fw urx llx sub div fh ury lly sub div scale 
+	llx neg lly neg translate 
+	} bind def
+/FMENDEPSF {
+	FMEPSF restore
+	FrameDict begin 
+	} bind def
+FrameDict begin 
+/setmanualfeed {
+%%BeginFeature *ManualFeed True
+	 statusdict /manualfeed true put
+%%EndFeature
+	} def
+/max {2 copy lt {exch} if pop} bind def
+/min {2 copy gt {exch} if pop} bind def
+/inch {72 mul} def
+/pagedimen { 
+	paperheight sub abs 16 lt exch 
+	paperwidth sub abs 16 lt and
+	{/papername exch def} {pop} ifelse
+	} def
+	/papersizedict FMLOCAL
+/setpapername { 
+	/papersizedict 14 dict def 
+	papersizedict begin
+	/papername /unknown def 
+		/Letter 8.5 inch 11.0 inch pagedimen
+		/LetterSmall 7.68 inch 10.16 inch pagedimen
+		/Tabloid 11.0 inch 17.0 inch pagedimen
+		/Ledger 17.0 inch 11.0 inch pagedimen
+		/Legal 8.5 inch 14.0 inch pagedimen
+		/Statement 5.5 inch 8.5 inch pagedimen
+		/Executive 7.5 inch 10.0 inch pagedimen
+		/A3 11.69 inch 16.5 inch pagedimen
+		/A4 8.26 inch 11.69 inch pagedimen
+		/A4Small 7.47 inch 10.85 inch pagedimen
+		/B4 10.125 inch 14.33 inch pagedimen
+		/B5 7.16 inch 10.125 inch pagedimen
+	end
+	} def
+/papersize {
+	papersizedict begin
+		/Letter {lettertray letter} def
+		/LetterSmall {lettertray lettersmall} def
+		/Tabloid {11x17tray 11x17} def
+		/Ledger {ledgertray ledger} def
+		/Legal {legaltray legal} def
+		/Statement {statementtray statement} def
+		/Executive {executivetray executive} def
+		/A3 {a3tray a3} def
+		/A4 {a4tray a4} def
+		/A4Small {a4tray a4small} def
+		/B4 {b4tray b4} def
+		/B5 {b5tray b5} def
+		/unknown {unknown} def
+	papersizedict dup papername known {papername} {/unknown} ifelse get
+	end
+	/FMdicttop countdictstack 1 add def 
+	statusdict begin stopped end 
+	countdictstack -1 FMdicttop {pop end} for 
+	} def
+/manualpapersize {
+	papersizedict begin
+		/Letter {letter} def
+		/LetterSmall {lettersmall} def
+		/Tabloid {11x17} def
+		/Ledger {ledger} def
+		/Legal {legal} def
+		/Statement {statement} def
+		/Executive {executive} def
+		/A3 {a3} def
+		/A4 {a4} def
+		/A4Small {a4small} def
+		/B4 {b4} def
+		/B5 {b5} def
+		/unknown {unknown} def
+	papersizedict dup papername known {papername} {/unknown} ifelse get
+	end
+	stopped 
+	} def
+/desperatepapersize {
+	statusdict /setpageparams known
+		{
+		paperwidth paperheight 0 1 
+		statusdict begin
+		{setpageparams} stopped pop 
+		end
+		} if
+	} def
+/savematrix {
+	orgmatrix currentmatrix pop
+	} bind def
+/restorematrix {
+	orgmatrix setmatrix
+	} bind def
+/dmatrix matrix def
+/dpi    72 0 dmatrix defaultmatrix dtransform
+    dup mul exch   dup mul add   sqrt def
+/freq dpi 18.75 div 8 div round dup 0 eq {pop 1} if 8 mul dpi exch div def
+/sangle 1 0 dmatrix defaultmatrix dtransform exch atan def
+/DiacriticEncoding [
+/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+/.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl
+/numbersign /dollar /percent /ampersand /quotesingle /parenleft
+/parenright /asterisk /plus /comma /hyphen /period /slash /zero /one
+/two /three /four /five /six /seven /eight /nine /colon /semicolon
+/less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K
+/L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash
+/bracketright /asciicircum /underscore /grave /a /b /c /d /e /f /g /h
+/i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar
+/braceright /asciitilde /.notdef /Adieresis /Aring /Ccedilla /Eacute
+/Ntilde /Odieresis /Udieresis /aacute /agrave /acircumflex /adieresis
+/atilde /aring /ccedilla /eacute /egrave /ecircumflex /edieresis
+/iacute /igrave /icircumflex /idieresis /ntilde /oacute /ograve
+/ocircumflex /odieresis /otilde /uacute /ugrave /ucircumflex
+/udieresis /dagger /.notdef /cent /sterling /section /bullet
+/paragraph /germandbls /registered /copyright /trademark /acute
+/dieresis /.notdef /AE /Oslash /.notdef /.notdef /.notdef /.notdef
+/yen /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+/ordfeminine /ordmasculine /.notdef /ae /oslash /questiondown
+/exclamdown /logicalnot /.notdef /florin /.notdef /.notdef
+/guillemotleft /guillemotright /ellipsis /.notdef /Agrave /Atilde
+/Otilde /OE /oe /endash /emdash /quotedblleft /quotedblright
+/quoteleft /quoteright /.notdef /.notdef /ydieresis /Ydieresis
+/fraction /currency /guilsinglleft /guilsinglright /fi /fl /daggerdbl
+/periodcentered /quotesinglbase /quotedblbase /perthousand
+/Acircumflex /Ecircumflex /Aacute /Edieresis /Egrave /Iacute
+/Icircumflex /Idieresis /Igrave /Oacute /Ocircumflex /.notdef /Ograve
+/Uacute /Ucircumflex /Ugrave /dotlessi /circumflex /tilde /macron
+/breve /dotaccent /ring /cedilla /hungarumlaut /ogonek /caron
+] def
+/ReEncode { 
+	dup 
+	length 
+	dict begin 
+	{
+	1 index /FID ne 
+		{def} 
+		{pop pop} ifelse 
+	} forall 
+	0 eq {/Encoding DiacriticEncoding def} if 
+	currentdict 
+	end 
+	} bind def
+/graymode true def
+	/bwidth FMLOCAL
+	/bpside FMLOCAL
+	/bstring FMLOCAL
+	/onbits FMLOCAL
+	/offbits FMLOCAL
+	/xindex FMLOCAL
+	/yindex FMLOCAL
+	/x FMLOCAL
+	/y FMLOCAL
+/setpattern {
+	 /bwidth  exch def
+	 /bpside  exch def
+	 /bstring exch def
+	 /onbits 0 def  /offbits 0 def
+	 freq sangle landscape {90 add} if 
+		{/y exch def
+		 /x exch def
+		 /xindex x 1 add 2 div bpside mul cvi def
+		 /yindex y 1 add 2 div bpside mul cvi def
+		 bstring yindex bwidth mul xindex 8 idiv add get
+		 1 7 xindex 8 mod sub bitshift and 0 ne
+		 {/onbits  onbits  1 add def 1}
+		 {/offbits offbits 1 add def 0}
+		 ifelse
+		}
+		setscreen
+	 {} settransfer
+	 offbits offbits onbits add div FMsetgray
+	/graymode false def
+	} bind def
+/grayness {
+	FMsetgray
+	graymode not {
+		/graymode true def
+		orgxfer cvx settransfer
+		orgfreq organgle orgproc cvx setscreen
+		} if
+	} bind def
+	/HUE FMLOCAL
+	/SAT FMLOCAL
+	/BRIGHT FMLOCAL
+	/Colors FMLOCAL
+FMPrintInColor 
+	
+	{
+	/HUE 0 def
+	/SAT 0 def
+	/BRIGHT 0 def
+	% array of arrays Hue and Sat values for the separations [HUE BRIGHT]
+	/Colors   
+	[[0    0  ]    % black
+	 [0    0  ]    % white
+	 [0.00 1.0]    % red
+	 [0.37 1.0]    % green
+	 [0.60 1.0]    % blue
+	 [0.50 1.0]    % cyan
+	 [0.83 1.0]    % magenta
+	 [0.16 1.0]    % comment / yellow
+	 ] def
+      
+	/BEGINBITMAPCOLOR { 
+		BITMAPCOLOR} def
+	/BEGINBITMAPCOLORc { 
+		BITMAPCOLORc} def
+	/BEGINBITMAPTRUECOLOR { 
+		BITMAPTRUECOLOR } def
+	/BEGINBITMAPTRUECOLORc { 
+		BITMAPTRUECOLORc } def
+	/K { 
+		Colors exch get dup
+		0 get /HUE exch store 
+		1 get /BRIGHT exch store
+		  HUE 0 eq BRIGHT 0 eq and
+			{1.0 SAT sub setgray}
+			{HUE SAT BRIGHT sethsbcolor} 
+		  ifelse
+		} def
+	/FMsetgray { 
+		/SAT exch 1.0 exch sub store 
+		  HUE 0 eq BRIGHT 0 eq and
+			{1.0 SAT sub setgray}
+			{HUE SAT BRIGHT sethsbcolor} 
+		  ifelse
+		} bind def
+	}
+	
+	{
+	/BEGINBITMAPCOLOR { 
+		BITMAPGRAY} def
+	/BEGINBITMAPCOLORc { 
+		BITMAPGRAYc} def
+	/BEGINBITMAPTRUECOLOR { 
+		BITMAPTRUEGRAY } def
+	/BEGINBITMAPTRUECOLORc { 
+		BITMAPTRUEGRAYc } def
+	/FMsetgray {setgray} bind def
+	/K { 
+		pop
+		} def
+	}
+ifelse
+/normalize {
+	transform round exch round exch itransform
+	} bind def
+/dnormalize {
+	dtransform round exch round exch idtransform
+	} bind def
+/lnormalize { 
+	0 dtransform exch cvi 2 idiv 2 mul 1 add exch idtransform pop
+	} bind def
+/H { 
+	lnormalize setlinewidth
+	} bind def
+/Z {
+	setlinecap
+	} bind def
+	/fillvals FMLOCAL
+/X { 
+	fillvals exch get
+	dup type /stringtype eq
+	{8 1 setpattern} 
+	{grayness}
+	ifelse
+	} bind def
+/V { 
+	gsave eofill grestore
+	} bind def
+/N { 
+	stroke
+	} bind def
+/M {newpath moveto} bind def
+/E {lineto} bind def
+/D {curveto} bind def
+/O {closepath} bind def
+	/n FMLOCAL
+/L { 
+ 	/n exch def
+	newpath
+	normalize
+	moveto 
+	2 1 n {pop normalize lineto} for
+	} bind def
+/Y { 
+	L 
+	closepath
+	} bind def
+	/x1 FMLOCAL
+	/x2 FMLOCAL
+	/y1 FMLOCAL
+	/y2 FMLOCAL
+	/rad FMLOCAL
+/R { 
+	/y2 exch def
+	/x2 exch def
+	/y1 exch def
+	/x1 exch def
+	x1 y1
+	x2 y1
+	x2 y2
+	x1 y2
+	4 Y 
+	} bind def
+/RR { 
+	/rad exch def
+	normalize
+	/y2 exch def
+	/x2 exch def
+	normalize
+	/y1 exch def
+	/x1 exch def
+	newpath
+	x1 y1 rad add moveto
+	x1 y2 x2 y2 rad arcto
+	x2 y2 x2 y1 rad arcto
+	x2 y1 x1 y1 rad arcto
+	x1 y1 x1 y2 rad arcto
+	closepath
+	16 {pop} repeat
+	} bind def
+/C { 
+	grestore
+	gsave
+	R 
+	clip
+	} bind def
+	/FMpointsize FMLOCAL
+/F { 
+	FMfonts exch get
+	FMpointsize scalefont
+	setfont
+	} bind def
+/Q { 
+	/FMpointsize exch def
+	F 
+	} bind def
+/T { 
+	moveto show
+	} bind def
+/RF { 
+	rotate
+	0 ne {-1 1 scale} if
+	} bind def
+/TF { 
+	gsave
+	moveto 
+	RF
+	show
+	grestore
+	} bind def
+/P { 
+	moveto
+	0 32 3 2 roll widthshow
+	} bind def
+/PF { 
+	gsave
+	moveto 
+	RF
+	0 32 3 2 roll widthshow
+	grestore
+	} bind def
+/S { 
+	moveto
+	0 exch ashow
+	} bind def
+/SF { 
+	gsave
+	moveto
+	RF
+	0 exch ashow
+	grestore
+	} bind def
+/B { 
+	moveto
+	0 32 4 2 roll 0 exch awidthshow
+	} bind def
+/BF { 
+	gsave
+	moveto
+	RF
+	0 32 4 2 roll 0 exch awidthshow
+	grestore
+	} bind def
+/G { 
+	gsave
+	newpath
+	normalize translate 0.0 0.0 moveto 
+	dnormalize scale 
+	0.0 0.0 1.0 5 3 roll arc 
+	closepath fill
+	grestore
+	} bind def
+/A { 
+	gsave
+	savematrix
+	newpath
+	2 index 2 div add exch 3 index 2 div sub exch 
+	normalize 2 index 2 div sub exch 3 index 2 div add exch 
+	translate 
+	scale 
+	0.0 0.0 1.0 5 3 roll arc 
+	restorematrix
+	stroke
+	grestore
+	} bind def
+	/x FMLOCAL
+	/y FMLOCAL
+	/w FMLOCAL
+	/h FMLOCAL
+	/xx FMLOCAL
+	/yy FMLOCAL
+	/ww FMLOCAL
+	/hh FMLOCAL
+	/FMsaveobject FMLOCAL
+	/FMoptop FMLOCAL
+	/FMdicttop FMLOCAL
+/BEGINPRINTCODE { 
+	/FMdicttop countdictstack 1 add def 
+	/FMoptop count 4 sub def 
+	/FMsaveobject save def
+	userdict begin 
+	/showpage {} def 
+	FMNORMALIZEGRAPHICS 
+	3 index neg 3 index neg translate
+	} bind def
+/ENDPRINTCODE {
+	count -1 FMoptop {pop pop} for 
+	countdictstack -1 FMdicttop {pop end} for 
+	FMsaveobject restore 
+	} bind def
+/gn { 
+	0 
+	{	46 mul 
+		cf read pop 
+		32 sub 
+		dup 46 lt {exit} if 
+		46 sub add 
+		} loop
+	add 
+	} bind def
+	/str FMLOCAL
+/cfs { 
+	/str sl string def 
+	0 1 sl 1 sub {str exch val put} for 
+	str def 
+	} bind def
+/ic [ 
+	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0223
+	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0223
+	0
+	{0 hx} {1 hx} {2 hx} {3 hx} {4 hx} {5 hx} {6 hx} {7 hx} {8 hx} {9 hx}
+	{10 hx} {11 hx} {12 hx} {13 hx} {14 hx} {15 hx} {16 hx} {17 hx} {18 hx}
+	{19 hx} {gn hx} {0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} {11} {12}
+	{13} {14} {15} {16} {17} {18} {19} {gn} {0 wh} {1 wh} {2 wh} {3 wh}
+	{4 wh} {5 wh} {6 wh} {7 wh} {8 wh} {9 wh} {10 wh} {11 wh} {12 wh}
+	{13 wh} {14 wh} {gn wh} {0 bl} {1 bl} {2 bl} {3 bl} {4 bl} {5 bl} {6 bl}
+	{7 bl} {8 bl} {9 bl} {10 bl} {11 bl} {12 bl} {13 bl} {14 bl} {gn bl}
+	{0 fl} {1 fl} {2 fl} {3 fl} {4 fl} {5 fl} {6 fl} {7 fl} {8 fl} {9 fl}
+	{10 fl} {11 fl} {12 fl} {13 fl} {14 fl} {gn fl}
+	] def
+	/sl FMLOCAL
+	/val FMLOCAL
+	/ws FMLOCAL
+	/im FMLOCAL
+	/bs FMLOCAL
+	/cs FMLOCAL
+	/len FMLOCAL
+	/pos FMLOCAL
+/ms { 
+	/sl exch def 
+	/val 255 def 
+	/ws cfs 
+	/im cfs 
+	/val 0 def 
+	/bs cfs 
+	/cs cfs 
+	} bind def
+400 ms 
+/ip { 
+	is 
+	0 
+	cf cs readline pop 
+	{	ic exch get exec 
+		add 
+		} forall 
+	pop 
+	
+	} bind def
+/wh { 
+	/len exch def 
+	/pos exch def 
+	ws 0 len getinterval im pos len getinterval copy pop
+	pos len 
+	} bind def
+/bl { 
+	/len exch def 
+	/pos exch def 
+	bs 0 len getinterval im pos len getinterval copy pop
+	pos len 
+	} bind def
+/s1 1 string def
+/fl { 
+	/len exch def 
+	/pos exch def 
+	/val cf s1 readhexstring pop 0 get def
+	pos 1 pos len add 1 sub {im exch val put} for
+	pos len 
+	} bind def
+/hx { 
+	3 copy getinterval 
+	cf exch readhexstring pop pop 
+	} bind def
+	/h FMLOCAL
+	/w FMLOCAL
+	/d FMLOCAL
+	/lb FMLOCAL
+	/bitmapsave FMLOCAL
+	/is FMLOCAL
+	/cf FMLOCAL
+/wbytes { 
+	dup 
+	8 eq {pop} {1 eq {7 add 8 idiv} {3 add 4 idiv} ifelse} ifelse
+	} bind def
+/BEGINBITMAPBWc { 
+	1 {} COMMONBITMAPc
+	} bind def
+/BEGINBITMAPGRAYc { 
+	8 {} COMMONBITMAPc
+	} bind def
+/BEGINBITMAP2BITc { 
+	2 {} COMMONBITMAPc
+	} bind def
+/COMMONBITMAPc { 
+	/r exch def
+	/d exch def
+	gsave
+	translate rotate scale /h exch def /w exch def
+	/lb w d wbytes def 
+	sl lb lt {lb ms} if 
+	/bitmapsave save def 
+	r                    
+	/is im 0 lb getinterval def 
+	ws 0 lb getinterval is copy pop 
+	/cf currentfile def 
+	w h d [w 0 0 h neg 0 h] 
+	{ip} image 
+	bitmapsave restore 
+	grestore
+	} bind def
+/BEGINBITMAPBW { 
+	1 {} COMMONBITMAP
+	} bind def
+/BEGINBITMAPGRAY { 
+	8 {} COMMONBITMAP
+	} bind def
+/BEGINBITMAP2BIT { 
+	2 {} COMMONBITMAP
+	} bind def
+/COMMONBITMAP { 
+	/r exch def
+	/d exch def
+	gsave
+	translate rotate scale /h exch def /w exch def
+	/bitmapsave save def 
+	r                    
+	/is w d wbytes string def
+	/cf currentfile def 
+	w h d [w 0 0 h neg 0 h] 
+	{cf is readhexstring pop} image
+	bitmapsave restore 
+	grestore
+	} bind def
+	/proc1 FMLOCAL
+	/proc2 FMLOCAL
+	/newproc FMLOCAL
+/Fmcc {
+    /proc2 exch cvlit def
+    /proc1 exch cvlit def
+    /newproc proc1 length proc2 length add array def
+    newproc 0 proc1 putinterval
+    newproc proc1 length proc2 putinterval
+    newproc cvx
+} bind def
+/ngrayt 256 array def
+/nredt 256 array def
+/nbluet 256 array def
+/ngreent 256 array def
+	/gryt FMLOCAL
+	/blut FMLOCAL
+	/grnt FMLOCAL
+	/redt FMLOCAL
+	/indx FMLOCAL
+	/cynu FMLOCAL
+	/magu FMLOCAL
+	/yelu FMLOCAL
+	/k FMLOCAL
+	/u FMLOCAL
+/colorsetup {
+	currentcolortransfer
+	/gryt exch def
+	/blut exch def
+	/grnt exch def
+	/redt exch def
+	0 1 255 {
+		/indx exch def
+		/cynu 1 red indx get 255 div sub def
+		/magu 1 green indx get 255 div sub def
+		/yelu 1 blue indx get 255 div sub def
+		/k cynu magu min yelu min def
+		/u k currentundercolorremoval exec def
+		nredt indx 1 0 cynu u sub max sub redt exec put
+		ngreent indx 1 0 magu u sub max sub grnt exec put
+		nbluet indx 1 0 yelu u sub max sub blut exec put
+		ngrayt indx 1 k currentblackgeneration exec sub gryt exec put
+	} for
+	{255 mul cvi nredt exch get}
+	{255 mul cvi ngreent exch get}
+	{255 mul cvi nbluet exch get}
+	{255 mul cvi ngrayt exch get}
+	setcolortransfer
+	{pop 0} setundercolorremoval
+	{} setblackgeneration
+	} bind def
+	/tran FMLOCAL
+/fakecolorsetup {
+	/tran 256 string def
+	0 1 255 {/indx exch def 
+		tran indx
+		red indx get 77 mul
+		green indx get 151 mul
+		blue indx get 28 mul
+		add add 256 idiv put} for
+	currenttransfer
+	{255 mul cvi tran exch get 255.0 div}
+	exch Fmcc settransfer
+} bind def
+/BITMAPCOLOR { 
+	/d 8 def
+	gsave
+	translate rotate scale /h exch def /w exch def
+	/bitmapsave save def 
+	colorsetup
+	/is w d wbytes string def
+	/cf currentfile def 
+	w h d [w 0 0 h neg 0 h] 
+	{cf is readhexstring pop} {is} {is} true 3 colorimage 
+	bitmapsave restore 
+	grestore
+	} bind def
+/BITMAPCOLORc { 
+	/d 8 def
+	gsave
+	translate rotate scale /h exch def /w exch def
+	/lb w d wbytes def 
+	sl lb lt {lb ms} if 
+	/bitmapsave save def 
+	colorsetup
+	/is im 0 lb getinterval def 
+	ws 0 lb getinterval is copy pop 
+	/cf currentfile def 
+	w h d [w 0 0 h neg 0 h] 
+	{ip} {is} {is} true 3 colorimage
+	bitmapsave restore 
+	grestore
+	} bind def
+/BITMAPTRUECOLORc { 
+        gsave
+        translate rotate scale /h exch def /w exch def
+        /bitmapsave save def 
+        
+        /is w string def
+        
+        ws 0 w getinterval is copy pop 
+        /cf currentfile def 
+        w h 8 [w 0 0 h neg 0 h] 
+        {ip} {gip} {bip} true 3 colorimage
+        bitmapsave restore 
+        grestore
+        } bind def
+/BITMAPTRUECOLOR { 
+        gsave
+        translate rotate scale /h exch def /w exch def
+        /bitmapsave save def 
+        /is w string def
+        /gis w string def
+        /bis w string def
+        /cf currentfile def 
+        w h 8 [w 0 0 h neg 0 h] 
+        { cf is readhexstring pop } 
+        { cf gis readhexstring pop } 
+        { cf bis readhexstring pop } 
+        true 3 colorimage 
+        bitmapsave restore 
+        grestore
+        } bind def
+/BITMAPTRUEGRAYc { 
+        gsave
+        translate rotate scale /h exch def /w exch def
+        /bitmapsave save def 
+        
+        /is w string def
+        
+        ws 0 w getinterval is copy pop 
+        /cf currentfile def 
+        w h 8 [w 0 0 h neg 0 h] 
+        {ip gip bip w gray} image
+        bitmapsave restore 
+        grestore
+        } bind def
+/ww FMLOCAL
+/r FMLOCAL
+/g FMLOCAL
+/b FMLOCAL
+/i FMLOCAL
+/gray { 
+        /ww exch def
+        /b exch def
+        /g exch def
+        /r exch def
+        0 1 ww 1 sub { /i exch def r i get .299 mul g i get .587 mul
+			b i get .114 mul add add r i 3 -1 roll floor cvi put } for
+        r
+        } bind def
+/BITMAPTRUEGRAY { 
+        gsave
+        translate rotate scale /h exch def /w exch def
+        /bitmapsave save def 
+        /is w string def
+        /gis w string def
+        /bis w string def
+        /cf currentfile def 
+        w h 8 [w 0 0 h neg 0 h] 
+        { cf is readhexstring pop 
+          cf gis readhexstring pop 
+          cf bis readhexstring pop w gray}  image
+        bitmapsave restore 
+        grestore
+        } bind def
+/BITMAPGRAY { 
+	8 {fakecolorsetup} COMMONBITMAP
+	} bind def
+/BITMAPGRAYc { 
+	8 {fakecolorsetup} COMMONBITMAPc
+	} bind def
+/ENDBITMAP {
+	} bind def
+end 
+	/ALDsave FMLOCAL
+	/ALDmatrix matrix def ALDmatrix currentmatrix pop
+/StartALD {
+	/ALDsave save def
+	 savematrix
+	 ALDmatrix setmatrix
+	} bind def
+/InALD {
+	 restorematrix
+	} bind def
+/DoneALD {
+	 ALDsave restore
+	} bind def
+%%EndProlog
+%%BeginSetup
+(3.0) FMVERSION
+1 1 612 792 0 1 12 FMDOCUMENT
+0 0 /Times-Roman FMFONTDEFINE
+1 0 /Times-Bold FMFONTDEFINE
+2 0 /Courier FMFONTDEFINE
+3 0 /Times-Italic FMFONTDEFINE
+32 FMFILLS
+0 0 FMFILL
+1 .1 FMFILL
+2 .3 FMFILL
+3 .5 FMFILL
+4 .7 FMFILL
+5 .9 FMFILL
+6 .97 FMFILL
+7 1 FMFILL
+8 <0f1e3c78f0e1c387> FMFILL
+9 <0f87c3e1f0783c1e> FMFILL
+10 <cccccccccccccccc> FMFILL
+11 <ffff0000ffff0000> FMFILL
+12 <8142241818244281> FMFILL
+13 <03060c183060c081> FMFILL
+14 <8040201008040201> FMFILL
+16 1 FMFILL
+17 .9 FMFILL
+18 .7 FMFILL
+19 .5 FMFILL
+20 .3 FMFILL
+21 .1 FMFILL
+22 0.03 FMFILL
+23 0 FMFILL
+24 <f0e1c3870f1e3c78> FMFILL
+25 <f0783c1e0f87c3e1> FMFILL
+26 <3333333333333333> FMFILL
+27 <0000ffff0000ffff> FMFILL
+28 <7ebddbe7e7dbbd7e> FMFILL
+29 <fcf9f3e7cf9f3f7e> FMFILL
+30 <7fbfdfeff7fbfdfe> FMFILL
+%%EndSetup
+%%Page: "11" 11
+%%BeginPaperSize: Letter
+%%EndPaperSize
+612 792 0 FMBEGINPAGE
+57.97 26.65 554.03 40.82 R
+7 X
+0 K
+V
+0 10 Q
+0 X
+(11) 301 34.15 T
+56.69 54.99 297.64 735.31 R
+7 X
+V
+1 12 Q
+0 X
+(7. Fr) 56.69 727.31 T
+(equently Asked Questions) 81.12 727.31 T
+0 F
+(7.1 Questions) 56.69 699.31 T
+(1. How do I encode a sequence that can be) 82.49 672.31 T
+(played by the Xing player?) 56.69 658.31 T
+(2. I\325m using the Parallax XV) 82.49 644.31 T
+(ideo card to) 220.34 644.31 T
+(digitize; how do I MPEG-encode the resulting) 56.69 630.31 T
+(data?) 56.69 616.31 T
+(3. How do I convert the MPEG-encoder) 82.49 602.31 T
+(YUV \336les into PPM \336les?) 56.69 588.31 T
+(7.2 Answers) 56.69 546.31 T
+-0.76 (1. The XING player samples video at 160x120 and) 56.69 518.31 P
+(expands to output 320x240. This is where their) 56.69 504.31 T
+(speed comes from. The player cannot buf) 56.69 490.31 T
+(fer a) 255.32 490.31 T
+(320x240 and thus had data overruns. The xing) 56.69 476.31 T
+(player would \325) 56.69 462.31 T
+(theoretically\325 handle 160x120 I) 126.42 462.31 T
+(frames.) 56.69 448.31 T
+(Thus, to encode, use P) 56.69 420.31 T
+(A) 163.52 420.31 T
+(TTERN I and 160x120) 170.85 420.31 T
+(frames.) 56.69 406.31 T
+(\050jboucher@\337ash.bu.edu\051) 56.69 392.31 T
+(2. Use the type JMOVIE, or use the jmovie2jpeg) 56.69 364.31 T
+(utility in the misc/ directory) 56.69 350.31 T
+(.) 189.84 350.31 T
+(3. Stanford\325) 56.69 322.31 T
+(s CVv1.2.2.tar) 113.33 322.31 T
+(.Z includes) 182.62 322.31 T
+(cyuv2ppm.c.) 56.69 308.31 T
+-0.2 (Which after you split the Y) 56.69 294.31 P
+-0.2 (, U, and V components) 185.05 294.31 P
+(out, works \336ne. \050curly@hsn.cftnet.com\051) 56.69 280.31 T
+(This can be ftp\325d from) 56.69 252.31 T
+2 F
+(havefun.stanford.edu) 56.69 238.31 T
+0 F
+(, in the directory) 200.61 238.31 T
+2 F
+(/) 282.89 238.31 T
+(pub/cv/) 56.69 224.31 T
+0 F
+(.) 107.07 224.31 T
+314.36 54.99 552.76 735.31 R
+7 X
+V
+FMENDPAGE
+%%EndPage: "11" 10
+%%Page: "10" 10
+612 792 0 FMBEGINPAGE
+57.97 26.65 554.03 40.82 R
+7 X
+0 K
+V
+0 10 Q
+0 X
+(10) 301 34.15 T
+56.69 54.99 297.64 735.31 R
+7 X
+V
+0 X
+(Usage:) 85.04 728.64 T
+2 F
+(ppmtoeyuv < input.ppm > output.yuv) 85.04 715.64 T
+0 F
+0.33 (This takes as input a ppm file and outputs a subsam-) 85.04 689.64 P
+(pled yuv file suitable for the encoder.) 56.69 678.64 T
+0 12 Q
+(6.2 jmovie2jpeg) 56.69 636.31 T
+0 10 Q
+(Usage:) 85.04 610.64 T
+2 F
+4.9 (jmovie2jpeg infile outfile start-) 85.04 597.64 P
+(frame end-frame) 56.69 586.64 T
+(infile) 85.04 560.64 T
+0 F
+( is a version 2 Parallax J_Movie) 121.02 560.64 T
+2 F
+(outfile) 85.04 547.64 T
+0 F
+( is a base file name for the output files) 127.02 547.64 T
+2 F
+5.44 (start-frame) 85.04 534.64 P
+0 F
+2.27 ( and) 151 534.64 P
+2 F
+5.44 (end-frame) 174.97 534.64 P
+0 F
+2.27 ( are the starting) 228.94 534.64 P
+(and ending frame numbers) 56.69 523.64 T
+1.39 (This takes as input a J_Movie and creates separate) 85.04 497.64 P
+0.09 (JFIF compatible JPEG files with the names base<num>.jpg,) 56.69 486.64 P
+(where base is outfile, and <num> are the frame numbers.) 56.69 475.64 T
+9.6 (jmovie2jpeg was written by Jim Boucher) 85.04 462.64 P
+(\050jboucher@\337ash.bu.edu\051.) 56.69 451.64 T
+0 12 Q
+(6.3 movieT) 56.69 424.31 T
+(oV) 111.15 424.31 T
+(id) 125.09 424.31 T
+0 10 Q
+(Usage:) 85.04 398.64 T
+2 F
+22.34 (movieToVid movieFile dataDir) 85.04 385.64 P
+(indexDir srcHostName) 56.69 374.64 T
+0 F
+0.95 (This program is used to convert a Parallax J Movie) 85.04 348.64 P
+0.58 (into a \322.vid\323 file, which is video only. vid files are used by) 56.69 337.64 P
+(some of the programs described later.) 56.69 326.64 T
+0.55 (See the README file in misc/mtv/ for more details) 85.04 313.64 P
+(on usage.) 56.69 302.64 T
+0.58 (movieToVid was written by Brian Smith \050bsmith@-) 85.04 289.64 P
+(cs.berkeley.edu\051) 56.69 278.64 T
+0 12 Q
+(6.4 eyuvtojpeg) 56.69 236.31 T
+0 10 Q
+(Usage:) 85.04 210.64 T
+2 F
+(eyuvtojpeg infile outfile) 85.04 197.64 T
+0 F
+0.27 (This takes as input an encoder yuv file and outputs a) 85.04 171.64 P
+(jpeg file. It uses cjpeg to do the compression.) 56.69 160.64 T
+0 12 Q
+(6.5 blockrun) 56.69 118.31 T
+0 10 Q
+(Usage:) 85.04 92.64 T
+2 F
+2.9 (blockrun command num_args firstnum) 85.04 79.64 P
+(lastnum skip arg1 ... argn) 56.69 68.64 T
+314.36 54.99 552.76 735.31 R
+7 X
+V
+0 F
+0 X
+7.53 (This runs the given command \050which has) 342.71 715.64 P
+2 F
+0.32 (num_args) 314.36 704.64 P
+0 F
+0.13 ( args\051, with the args) 362.34 704.64 P
+2 F
+0.32 (arg1 ... argn) 444.61 704.64 P
+0 F
+0.13 (, where) 523.21 704.64 P
+-0.04 (any \325=\325 character is replaced by a number from) 314.36 693.64 P
+2 F
+-0.09 (firstnum) 504.78 693.64 P
+0 F
+(to) 314.36 682.64 T
+2 F
+(lastnum) 324.64 682.64 T
+0 F
+( skipping by) 366.61 682.64 T
+2 F
+(skip) 418.54 682.64 T
+0 F
+(. For example:) 442.52 682.64 T
+2 F
+7.23 (blockrun eyuvtojpeg 2 13 19 3) 342.71 669.64 P
+(flow=.yuv flow=.jpg) 314.36 658.64 T
+0 F
+(will run:) 342.71 632.64 T
+2 F
+(eyuvtojpeg flow13.yuv flow13.jpg) 342.71 606.64 T
+(eyuvtojpeg flow16.yuv flow16.jpg) 342.71 593.64 T
+(eyuvtojpeg flow19.yuv flow19.jpg) 342.71 580.64 T
+0 12 Q
+(6.6 vidtoppm) 314.36 553.31 T
+0 10 Q
+(Usage:) 342.71 527.64 T
+2 F
+10.05 (vidtoppm filename width height) 342.71 514.64 P
+(start end outbase [quality]) 314.36 503.64 T
+0 F
+0.39 (This takes as input a .vid file of given) 342.71 477.64 P
+2 F
+0.94 (height) 499.45 477.64 P
+0 F
+0.39 ( and) 535.43 477.64 P
+2 F
+2.91 (width) 314.36 466.64 P
+0 F
+1.22 (, and turns them into a bunch of ppm files named) 344.35 466.64 P
+2 F
+(outbase) 314.36 455.64 T
+0 F
+(.N, where N is a number from) 356.34 455.64 T
+2 F
+(start) 478.74 455.64 T
+0 F
+( to) 508.73 455.64 T
+2 F
+(end) 521.5 455.64 T
+0 F
+(.) 539.49 455.64 T
+0 12 Q
+(6.7 vidtojpeg) 314.36 428.31 T
+0 10 Q
+(Usage:) 342.71 402.64 T
+2 F
+8.05 (vidtojpeg filename width height) 342.71 389.64 P
+(start end outbase [quality]) 314.36 378.64 T
+0 F
+2.83 (This is the same as vidtoppm, except it outputs) 342.71 352.64 P
+(JPEG files instead of PPM files.) 314.36 341.64 T
+0 12 Q
+(6.8 vidtoeyuv) 314.36 314.31 T
+0 10 Q
+(Usage:) 342.71 288.64 T
+2 F
+8.05 (vidtoeyuv filename width height) 342.71 275.64 P
+(start nth outbase [quality]) 314.36 264.64 T
+0 F
+0.39 (This takes as input a .vid file of given) 342.71 238.64 P
+2 F
+0.94 (height) 499.45 238.64 P
+0 F
+0.39 ( and) 535.43 238.64 P
+2 F
+3.58 (width) 314.36 227.64 P
+0 F
+1.49 (, and turns them into a bunch of yuv files named) 344.35 227.64 P
+2 F
+2.87 (outbase) 314.36 216.64 P
+0 F
+1.2 (.N, where N is a number from) 356.34 216.64 P
+2 F
+2.87 (start) 487.12 216.64 P
+0 F
+1.2 ( to) 517.1 216.64 P
+2 F
+2.87 (end) 532.27 216.64 P
+0 F
+1.2 (,) 550.26 216.64 P
+(skipping by) 314.36 205.64 T
+2 F
+(nth) 363.79 205.64 T
+0 F
+(.) 381.77 205.64 T
+0 12 Q
+(6.8 PBMPLUS) 314.36 176.31 T
+0 10 Q
+-0.04 (There is a very useful package called pbmplus avail-) 342.71 150.64 P
+1.46 (able for ftp \050ee.utah.edu:/pbmplus for example\051. This has) 314.36 139.64 P
+0.31 (conversions from TIFF, GIF, and many other common for-) 314.36 128.64 P
+0.86 (mats to PPMs, which the encoder can read. You can even) 314.36 117.64 P
+0.58 (keep the originals in their own format, and do conversions) 314.36 106.64 P
+(via) 314.36 95.64 T
+2 F
+(INPUT_CONVERT.) 329.07 95.64 T
+FMENDPAGE
+%%EndPage: "10" 9
+%%Page: "9" 9
+612 792 0 FMBEGINPAGE
+57.97 26.65 554.03 40.82 R
+7 X
+0 K
+V
+0 10 Q
+0 X
+(9) 303.5 34.15 T
+56.69 54.99 297.64 735.31 R
+7 X
+V
+0 X
+0.08 (ating input files must work on the remote machine. Also the) 56.69 728.64 P
+1.78 (USER_DATA and CDL_FILE files must be available on) 56.69 717.64 P
+2.53 (the remote sites as specified in the parameter file. This) 56.69 706.64 P
+(should be fixed in future versions.) 56.69 695.64 T
+1 12 Q
+(4. Performance) 56.69 668.31 T
+0 10 Q
+1.23 (Table seven shows a comparison of sequential per-) 85.04 641.64 P
+(formance on different machine types.) 56.69 630.64 T
+1.31 (Parallel performance is dependent not only on pro-) 85.04 422.64 P
+1.87 (cessor performance, but network performance. If you are) 56.69 411.64 P
+-0.21 (using a 10 Mb/s Ethernet, don\325t expect to get better than 4 or) 56.69 400.64 P
+0.89 (5 frames per second -- no matter how fast your processors) 56.69 389.64 P
+(are.) 56.69 378.64 T
+2.5 (Parallel performance is also greatly dependent on) 85.04 365.64 P
+1.16 (how big the input files are \050YUV is better than PPM, and) 56.69 354.64 P
+0.81 (JPEG is better than both\051, and how big the output files are) 56.69 343.64 P
+(\050better compression will lead to less I/O\051.) 56.69 332.64 T
+1 12 Q
+(5. Other Options) 56.69 305.31 T
+0 10 Q
+-0 (This section gives example of some more rarely used) 85.04 278.64 P
+0.12 (options in the parameter \336le, such as customizing the Quan-) 56.69 267.64 P
+(tization tables, or setting the aspect ratio.) 56.69 256.64 T
+0 12 Q
+(5.1 Custom Quantization T) 56.69 229.31 T
+(ables \050parameter \336le\051) 186.45 229.31 T
+0 10 Q
+(You can specify your own custom quantization tables.) 56.69 206.64 T
+-0.31 (Currently you can only do this once per MPEG file.) 56.69 194.64 P
+-0.31 ( Y) 260.99 194.64 P
+-0.31 (ou can) 269.39 194.64 P
+-0.06 (specify both Intra- and Non-intra quantization tables. If you) 56.69 182.64 P
+-0.44 (don\325) 56.69 170.64 P
+-0.44 (t specify them, then the default tables are used \050c.f. page) 74.83 170.64 P
+(D-16, D-17 of the standard\051.) 56.69 158.64 T
+(Usage:) 85.04 141.64 T
+2 F
+(IQTABLE) 85.04 115.64 T
+(table row 1) 85.04 102.64 T
+(table row 2) 85.04 89.64 T
+(...) 85.04 76.64 T
+(table row 8) 85.04 63.64 T
+100.63 478.31 253.7 484.31 C
+0 0 612 792 C
+0 10 Q
+0 X
+0 K
+0.06 (a. Macroblocks per second; a) 118.63 471.64 P
+1.5 (320x240 pixel image is 300) 118.63 459.64 P
+(macroblocks per frame.) 118.63 447.64 T
+1 12 Q
+(T) 99.59 594.31 T
+(able 7: Machine Comparison) 106.49 594.31 T
+0 10 Q
+(Machine) 121.41 572.64 T
+(MPS) 203.66 572.64 T
+0 8 Q
+(a) 223.66 576.64 T
+0 10 Q
+(HP 9000/755) 120.13 554.64 T
+(280) 207.94 554.64 T
+(DEC 3000/400) 112.92 538.64 T
+(247) 207.94 538.64 T
+(HP 9000/750) 120.13 522.64 T
+(191) 207.94 522.64 T
+(Sparc 10) 137.91 506.64 T
+(104) 207.94 506.64 T
+(DEC 5000) 130.69 490.64 T
+(68) 210.44 490.64 T
+100.63 584.06 100.63 484.56 2 L
+V
+0.5 H
+0 Z
+N
+177.16 584.56 177.16 484.06 2 L
+V
+N
+253.7 584.06 253.7 484.56 2 L
+V
+N
+100.38 584.31 253.95 584.31 2 L
+V
+N
+100.88 565.56 253.45 565.56 2 L
+V
+N
+100.88 563.06 253.45 563.06 2 L
+V
+N
+100.38 548.31 253.95 548.31 2 L
+V
+N
+100.38 532.31 253.95 532.31 2 L
+V
+N
+100.38 516.31 253.95 516.31 2 L
+V
+N
+100.38 500.31 253.95 500.31 2 L
+V
+N
+100.38 484.31 253.95 484.31 2 L
+V
+N
+314.36 54.99 552.76 735.31 R
+7 X
+V
+0 X
+0.96 (This specifies the intra-coding quantization table \050I) 342.71 715.64 P
+0.01 (frames and I-blocks in P and B frames\051. Each) 314.36 704.64 P
+2 F
+0.03 (table row) 498.76 704.64 P
+0 F
+(is simply 8 integers, separated by tabs and/or spaces.) 314.36 693.64 T
+(Usage:) 342.71 680.64 T
+2 F
+(NIQTABLE) 342.71 654.64 T
+(table row 1) 342.71 641.64 T
+(table row 2) 342.71 628.64 T
+(...) 342.71 615.64 T
+(table row 8) 342.71 602.64 T
+0 F
+4.63 (This specifies the non-intra-coding quantization) 342.71 576.64 P
+(table \050difference vectors in P and B frames\051.) 314.36 565.64 T
+0 12 Q
+(5.2 Aspect Ratio \050parameter \336le\051) 314.36 538.31 T
+0 10 Q
+(Y) 340.16 512.64 T
+(ou can specify the aspect ratio to be one of the) 346.37 512.64 T
+(fourteen legal values as speci\336ed in the standard \050c.f.) 314.36 500.64 T
+(Section 2.4.3.2\051. This sets the requested aspect ration for) 314.36 488.64 T
+(playback.) 314.36 476.64 T
+0 12 Q
+(Usage:) 340.16 463.31 T
+(ASPECT_RA) 340.16 449.31 T
+(TIO \337oat) 406.13 449.31 T
+2 10 Q
+8.03 (float) 342.71 436.64 P
+0 F
+3.34 ( is one of {1.0, 0.6735, 0.7031, 0.7615,) 372.69 436.64 P
+2.25 (0.8055, 0.8437, 0.8935, 0.9157, 0.9815, 1.0255, 1.0695,) 314.36 425.64 P
+(1.0950, 1.1575, 1.2015}.) 314.36 414.64 T
+0 12 Q
+(5.3 Frame Rate \050parameter \336le\051) 314.36 374.31 T
+0 10 Q
+-0 (You can specify the frame rate to be one of the eight) 342.71 348.64 P
+1.97 (legal values \050c.f. Section 2.4.3.2\051. This is used by some) 314.36 337.64 P
+(playback systems to gauge the playback rate.) 314.36 326.64 T
+(Usage:) 342.71 313.64 T
+2 F
+(FRAME_RATE float) 342.71 300.64 T
+4.91 (float) 342.71 287.64 P
+0 F
+2.05 ( is one of {23.976, 24, 25, 29.97, 30, 50,) 372.69 287.64 P
+(59.94, 60}.) 314.36 276.64 T
+0 12 Q
+(5.4 Floating Point DCT \050command line\051) 314.36 249.31 T
+0 10 Q
+2.13 (The encoder normally uses a quick algorithm for) 342.71 236.64 P
+2.06 (forward and reverse DCTs. However, in sequences with) 314.36 225.64 P
+0.5 (many P frames, this can result in errors when decoded ref-) 314.36 214.64 P
+0.4 (erence frames are used. To use the \050slow\051 double precision) 314.36 203.64 P
+(accurate dcts, use the following flag:) 314.36 192.64 T
+(Usage:) 342.71 179.64 T
+(mpeg_encode -float_dct) 342.71 166.64 T
+1 12 Q
+(6. Other T) 314.36 139.31 T
+(ools) 366.89 139.31 T
+0 10 Q
+(The misc/ directory contains several useful tools.) 314.36 112.64 T
+0 12 Q
+(6.1 ppmtoeyuv) 314.36 83.31 T
+FMENDPAGE
+%%EndPage: "9" 8
+%%Page: "8" 8
+612 792 0 FMBEGINPAGE
+57.97 26.65 554.03 40.82 R
+7 X
+0 K
+V
+0 10 Q
+0 X
+(8) 303.5 34.15 T
+56.69 54.99 297.64 735.31 R
+7 X
+V
+0 X
+(frame-to-I-frame scale.) 56.69 728.64 T
+(Usage:) 85.04 715.64 T
+2 F
+(-bit_rate_info rate_file) 85.04 702.64 T
+0 F
+(This puts the bit rate info into the specified file) 85.04 689.64 T
+(\050order of info, etc.\051) 85.04 676.64 T
+1 12 Q
+(3. Parallel Usage) 56.69 641.31 T
+0 10 Q
+1.03 (In parallel execution there are slave processes. You) 85.04 610.64 P
+(can have those processes run nicely if you want.) 56.69 599.64 T
+(Usage:) 85.04 586.64 T
+2 F
+(-nice) 85.04 573.64 T
+0 F
+3.56 (This makes all slave processes run nicely. This) 85.04 560.64 P
+0.8 (means that interactive users take precedence, so they don\325t) 56.69 549.64 P
+-0.2 (feel like they\325re running in molasses. If you want to be mean) 56.69 538.64 P
+(to them, don\325t use this option. :-\051) 56.69 527.64 T
+0 12 Q
+(3.1 Architecture Overview) 56.69 500.31 T
+0 10 Q
+1.3 (Figure 1 shows a diagram of the system architecture. The) 56.69 474.64 P
+-0.03 (slaves exist on the different slave machines which you spec-) 56.69 463.64 P
+1.5 (ify \050see Section 3.2\051. The server processes all live on the) 56.69 452.64 P
+(machine you run the encoder on.) 56.69 441.64 T
+0 12 Q
+(3.2 Specifying Slave Machines \050both\051) 56.69 412.31 T
+0 10 Q
+0.03 (You specify the slave machines in the parameter file.) 85.04 386.64 P
+-0.2 (For each slave you must specify the username to use, as well) 56.69 375.64 P
+0.06 (as the executable mpeg_encode program. If a slave does not) 56.69 364.64 P
+1.52 (have NFS access, then it is REMOTE and you must also) 56.69 353.64 P
+(specify where the parameter file is.) 56.69 342.64 T
+(Usage:) 85.04 316.64 T
+2 F
+(PARALLEL) 85.04 303.64 T
+(slave_specification) 85.04 290.64 T
+(END_PARALLEL) 85.04 277.64 T
+(slave_specification) 85.04 251.64 T
+0 F
+( can be either:) 198.98 251.64 T
+2 F
+(machine username executable) 85.04 238.64 T
+0 F
+(or) 85.04 225.64 T
+2 F
+2.9 (REMOTE machine username executable) 85.04 212.64 P
+(param_file) 56.69 201.64 T
+0 F
+0.76 (You must have an account with the given username) 85.04 175.64 P
+0.06 (on each machine, and you must place your machine/login in) 56.69 164.64 P
+(the appropriate .rhosts files.) 56.69 153.64 T
+1.59 (To make it easier to run experiments with varying) 85.04 127.64 P
+1.35 (numbers of processors, there is a command-line argument) 56.69 116.64 P
+(which limits the number of slave machines.) 56.69 105.64 T
+(Usage:) 85.04 79.64 T
+2 F
+(-max_machines num_machines) 85.04 66.64 T
+314.36 54.99 552.76 735.31 R
+7 X
+V
+0 F
+0 X
+0.98 (This means that the encoder will use no more than) 342.71 728.64 P
+2 F
+(num_machines) 314.36 717.64 T
+0 F
+( machines as slaves.) 386.32 717.64 T
+0 12 Q
+(3.3 Remote Shell \050parameter \336le\051) 314.36 677.31 T
+0 10 Q
+0.98 (To run processes on the slave machines, mpeg_en-) 342.71 651.64 P
+1.52 (code uses the remote shell command. On most machines) 314.36 640.64 P
+-0.09 (this is the command) 314.36 629.64 P
+2 F
+-0.2 (rsh) 396.76 629.64 P
+0 F
+-0.09 (. On HP machines, however, rsh is) 414.75 629.64 P
+0.39 (the restricted shell; on HP machines, the right command to) 314.36 618.64 P
+(use is) 314.36 607.64 T
+2 F
+(remsh) 339.35 607.64 T
+0 F
+(, rather than) 369.33 607.64 T
+2 F
+(rsh) 419.84 607.64 T
+0 F
+(.) 437.83 607.64 T
+(Usage:) 342.71 594.64 T
+2 F
+(RSH <rsh command>) 342.71 568.64 T
+0 12 Q
+(3.4 Scheduling Algorithms \050parameter \336le\051) 314.36 528.31 T
+0 10 Q
+0.87 (The encoder provides 3 different scheduling algorithms to) 314.36 502.64 P
+(schedule which processors get which frames.) 314.36 491.64 T
+2.03 (The first scheduling algorithm simply assigns N/P) 340.16 478.64 P
+0.28 (frames to each processor, where N is the number of frames) 314.36 467.64 P
+0.72 (and P is the number of processors. This has the advantage) 314.36 456.64 P
+-0.01 (of minimal overhead, but only works well when all the pro-) 314.36 445.64 P
+0.3 (cessors run at nearly the same speed. Also, since most pro-) 314.36 434.64 P
+0.37 (cessors will finish at about the same time, you will have to) 314.36 423.64 P
+1.41 (wait at the end while the Combine Server gathers all the) 314.36 412.64 P
+(frame files together.) 314.36 401.64 T
+(Usage:) 340.16 388.64 T
+(PARALLEL_PERFECT) 340.16 375.64 T
+3.6 (The second scheduling algorithm first assigns S) 340.16 349.64 P
+-0.09 (frames to each processor. When a processor is finished, it is) 314.36 338.64 P
+1.49 (assigned T seconds of work \050the scheduler estimates this) 314.36 327.64 P
+1.97 (based on previous performance\051. S should be at least 3,) 314.36 316.64 P
+0.37 (preferably at least 5 or 6, to insure a good estimate of each) 314.36 305.64 P
+(processor\325s speed.) 314.36 294.64 T
+(Usage:) 340.16 281.64 T
+(PARALLEL_TEST_FRAMES S) 340.16 268.64 T
+(PARALLEL_TIME_CHUNKS T) 340.16 255.64 T
+0.79 (The third scheduling algorithm is probably the best.) 340.16 229.64 P
+2.43 (It also first assigns S frames to each processor. Subse-) 314.36 218.64 P
+3.44 (quently, however, whenever a processor finishes, it is) 314.36 207.64 P
+1.1 (assigned enough work to keep it busy until almost every-) 314.36 196.64 P
+2.04 (thing is done. Effectively, a processor is assigned many) 314.36 185.64 P
+-0.06 (frames, and then fewer and fewer frames as more work gets) 314.36 174.64 P
+2.92 (done. This insures good load balancing, while limiting) 314.36 163.64 P
+(scheduling overhead.) 314.36 152.64 T
+(Usage:) 340.16 139.64 T
+(PARALLEL_TEST_FRAMES S) 340.16 126.64 T
+(PARALLEL_CHUNK_TAPER) 340.16 113.64 T
+0 12 Q
+(3.5 Parallel problems \050parameter \336le\051) 314.36 86.31 T
+0 10 Q
+8.16 (There are some unsupported features using) 340.16 73.64 P
+-0.18 (REMOTE to specify slaves: The \324command\324 form of gener-) 314.36 62.64 P
+FMENDPAGE
+%%EndPage: "8" 7
+%%Page: "7" 7
+612 792 0 FMBEGINPAGE
+57.97 26.65 554.03 40.82 R
+7 X
+0 K
+V
+0 10 Q
+0 X
+(7) 303.5 34.15 T
+56.69 54.99 297.64 477.36 R
+7 X
+V
+0 X
+(you can specify frame files to combine.) 56.69 470.69 T
+1.11 (The parameter file may specify input frame files in) 85.04 457.69 P
+0.94 (the same manner as normal input files -- except instead of) 56.69 446.69 P
+7.11 (using INPUT_DIR, INPUT, and END_INPUT, use) 56.69 435.69 P
+28.3 (FRAME_INPUT_DIR, FRAME_INPUT, and) 56.69 424.69 P
+1.83 (FRAME_END_INPUT. If no input frame files are speci-) 56.69 413.69 P
+0.16 (fied, then the default is to use the output file name with suf-) 56.69 402.69 P
+(fix \322.frame.<frame_num>\323 starting from 0 as the input files.) 56.69 391.69 T
+0 12 Q
+(2.15 Stats and Other Options \050command line\051) 56.69 364.36 T
+0 10 Q
+0.43 (There are several options for printing or suppressing) 85.04 338.69 P
+(useful information.) 56.69 327.69 T
+0.75 (The encoder always prints \050to stdout\051 parameter file) 85.04 314.69 P
+2.87 (information and statistics about how many I, P, and B) 56.69 303.69 P
+0.8 (frames there were, and information about compression and) 56.69 292.69 P
+2.31 (quality. You can send these statistics, in addition to the) 56.69 281.69 P
+(screen, to a file.) 56.69 270.69 T
+(Usage:) 85.04 257.69 T
+2 F
+( -stat stat_file) 110.83 244.69 T
+0 F
+2.56 (This appends the parameter file info and stats to) 85.04 231.69 P
+2 F
+(stat_file) 56.69 220.69 T
+0 F
+0.87 (Normally, the statistics do not include any informa-) 85.04 194.69 P
+1.77 (tion about quality. This is because computing the quality) 56.69 183.69 P
+2.02 (takes a little more time. If you wish to have the quality) 56.69 172.69 P
+1.17 (included in the statistics, use the -snr command line argu-) 56.69 161.69 P
+(ment.) 56.69 150.69 T
+(Usage:) 85.04 137.69 T
+2 F
+(-snr) 85.04 124.69 T
+0 F
+1.78 (This prints the signal-to-noise ratio \050snr\051 and peak) 85.04 111.69 P
+(snr.) 56.69 100.69 T
+3.69 (An additional statistic measure is mean squared) 85.04 74.69 P
+0.78 (error. If you wish to see the per-block mean squared error,) 56.69 63.69 P
+314.36 54.99 552.76 477.36 R
+7 X
+V
+0 X
+(use the -mse command line flag \050sets -snr as a side effect\051.) 314.36 470.69 T
+(Usage:) 342.71 457.69 T
+2 F
+(-mse) 342.71 444.69 T
+0 F
+(This prints the MSE for each block encoded) 342.71 431.69 T
+1.02 (Another set of data which can be useful is a histo-) 342.71 418.69 P
+0.44 (gram of the motion vectors. The encoder can keep track of) 314.36 407.69 P
+-0.16 (P-frame motion vectors and forward and backward B-frame) 314.36 396.69 P
+0.4 (motion vectors. The output is in the form of a matrix, each) 314.36 385.69 P
+0.98 (entry corresponding to a motion vector in the search win-) 314.36 374.69 P
+0.49 (dow. The center of the matrix represents \0500,0\051 motion vec-) 314.36 363.69 P
+(tors.) 314.36 352.69 T
+(Usage:) 342.71 339.69 T
+2 F
+(-mv_histogram) 342.71 326.69 T
+0 F
+1.47 (During normal execution, the encoder outputs two) 342.71 300.69 P
+0.58 (kinds of information. It prints a single line for each frame,) 314.36 289.69 P
+0.86 (summarizing block type and time info. It also prints, after) 314.36 278.69 P
+-0.07 (each frame, an estimate of the remaining running time. You) 314.36 267.69 P
+(can modify how often or if this information is to be shown.) 314.36 256.69 T
+(Usage:) 342.71 243.69 T
+2 F
+( -quiet num) 342.71 230.69 T
+( -no_frame_summary) 342.71 217.69 T
+(-realquiet) 349.8 204.69 T
+0 F
+-0.13 (If) 342.71 191.69 P
+2 F
+-0.31 (num) 351.74 191.69 P
+0 F
+-0.13 ( is negative, the time estimate is never shown;) 369.73 191.69 P
+1.94 (otherwise, it reports a time estimate no more often than) 314.36 180.69 P
+1.22 (every) 314.36 169.69 P
+2 F
+2.92 (num) 340.27 169.69 P
+0 F
+1.22 ( seconds \050unless the time estimate rises, which) 358.26 169.69 P
+1 (will happen near the beginning of the run\051. The default is) 314.36 158.69 P
+2 F
+(num) 314.36 147.69 T
+0 F
+( = 0, which means report after every frame.) 332.35 147.69 T
+1.08 (If) 342.71 134.69 P
+2 F
+2.59 (-no_frame_summary) 352.94 134.69 P
+0 F
+1.08 ( is given, then informa-) 454.89 134.69 P
+(tion about each frame is not printed.) 314.36 123.69 T
+2 F
+10.05 (-realquiet stops all printing,) 342.71 110.69 P
+(other than error messages.) 314.36 99.69 T
+0 F
+1 (Another nice feature is that the encoder can output) 342.71 73.69 P
+0.75 (the bit rate, on both a frame-to-frame scale, and also an I-) 314.36 62.69 P
+385.51 518.46 470.55 575.15 R
+7 X
+V
+0.5 H
+0 Z
+0 X
+N
+141.05 721.49 294.05 766.49 18 RR
+7 X
+V
+0 X
+N
+1 18 Q
+(Master Server) 165.05 738.43 T
+102.05 623.49 181.05 660.49 R
+7 X
+V
+0 X
+N
+(Slave) 121.05 637.49 T
+452.05 625.49 531.05 662.49 R
+7 X
+V
+0 X
+N
+(Slave) 471.05 639.49 T
+333.05 624.49 412.05 661.49 R
+7 X
+V
+0 X
+N
+(Slave) 352.05 638.49 T
+218.05 623.49 297.05 660.49 R
+7 X
+V
+0 X
+N
+(Slave) 237.05 637.49 T
+147.47 523.06 300.47 568.06 18 RR
+7 X
+V
+0 X
+N
+(Combine Server) 165.31 539.33 T
+498.64 673.25 501.04 661.49 492.77 670.19 495.71 671.72 4 Y
+V
+472.44 709.73 470.04 721.49 478.31 712.79 475.37 711.26 4 Y
+V
+495.71 671.72 475.38 711.26 2 L
+7 X
+V
+0 X
+N
+137.07 670.41 129.04 661.49 131.12 673.31 134.09 671.86 4 Y
+V
+149.02 710.57 157.04 719.49 154.97 707.67 151.99 709.12 4 Y
+V
+134.1 671.86 152 709.12 2 L
+7 X
+V
+0 X
+N
+164.04 661.55 152.04 661.49 162.18 667.9 163.11 664.73 4 Y
+V
+344.03 719.43 356.03 719.49 345.89 713.08 344.96 716.26 4 Y
+V
+163.12 664.72 344.98 716.25 2 L
+7 X
+V
+0 X
+N
+392.55 667.26 382.02 661.5 387.89 671.96 390.22 669.61 4 Y
+V
+430.5 714.73 441.02 720.49 435.15 710.03 432.82 712.38 4 Y
+V
+390.24 669.6 432.85 712.37 2 L
+7 X
+V
+0 X
+N
+277.89 664.38 266.04 662.49 275.09 670.37 276.49 667.37 4 Y
+V
+377.19 717.6 389.04 719.49 379.98 711.61 378.59 714.61 4 Y
+V
+276.5 667.37 378.6 714.61 2 L
+7 X
+V
+0 X
+N
+241.78 672.73 248.04 662.49 237.31 667.86 239.55 670.29 4 Y
+V
+192.3 709.25 186.04 719.49 196.78 714.12 194.54 711.69 4 Y
+V
+239.55 670.29 194.54 711.69 2 L
+7 X
+V
+0 X
+N
+353.94 670.32 363.03 662.49 351.17 664.31 352.56 667.32 4 Y
+V
+245.13 712.66 236.04 720.49 247.9 718.67 246.52 715.67 4 Y
+V
+352.57 667.31 246.52 715.66 2 L
+7 X
+V
+0 X
+N
+470.93 667.95 481.05 661.49 469.05 661.61 469.99 664.78 4 Y
+V
+287.16 714.03 277.05 720.49 289.05 720.37 288.1 717.2 4 Y
+V
+469.99 664.78 288.1 717.2 2 L
+7 X
+V
+0 X
+N
+336.05 720.49 489.05 765.49 18 RR
+7 X
+V
+0 X
+N
+(Decode Server) 360.05 737.43 T
+134.71 613.56 130.39 624.76 139.99 617.55 137.35 615.56 4 Y
+V
+168.59 579.26 172.91 568.07 163.31 575.27 165.95 577.27 4 Y
+V
+137.35 615.56 165.96 577.26 2 L
+7 X
+V
+0 X
+N
+234.18 617.55 243.77 624.76 239.45 613.56 236.82 615.56 4 Y
+V
+210.85 575.27 201.26 568.07 205.58 579.26 208.21 577.27 4 Y
+V
+236.82 615.56 208.22 577.26 2 L
+7 X
+V
+0 X
+N
+352.39 622.81 364.23 624.77 355.23 616.83 353.81 619.82 4 Y
+V
+255.6 570.03 243.76 568.08 252.77 576.01 254.18 573.02 4 Y
+V
+353.83 619.81 254.2 573.01 2 L
+7 X
+V
+0 X
+N
+465.63 624.68 477.63 624.76 467.5 618.33 466.56 621.51 4 Y
+V
+291.21 568.14 279.21 568.07 289.34 574.49 290.28 571.32 4 Y
+V
+466.57 621.51 290.28 571.31 2 L
+7 X
+V
+0 X
+N
+393.94 616.24 385.49 624.76 397.17 622.01 395.55 619.12 4 Y
+V
+440.83 597.86 449.27 589.33 437.59 592.09 439.21 594.98 4 Y
+V
+395.57 619.11 439.23 594.97 2 L
+7 X
+V
+0 X
+N
+281.34 617.08 272.12 624.76 284.01 623.14 282.67 620.11 4 Y
+V
+376.28 582.84 385.5 575.16 373.61 576.78 374.94 579.81 4 Y
+V
+282.68 620.1 374.96 579.8 2 L
+7 X
+V
+0 X
+N
+168.9 618.38 158.74 624.76 170.74 624.73 169.82 621.56 4 Y
+V
+375.34 567.36 385.51 560.98 373.51 561 374.43 564.18 4 Y
+V
+169.82 621.55 374.43 564.18 2 L
+7 X
+V
+0 X
+N
+312.01 543.5 300.47 546.8 312.01 550.11 312.01 546.8 4 Y
+V
+373.98 550.11 385.51 546.8 373.98 543.5 373.98 546.8 4 Y
+V
+312.01 546.8 373.98 546.8 2 L
+7 X
+V
+0 X
+N
+302.9 498.61 259.93 498.61 2 L
+V
+1.14 H
+N
+1 12 Q
+(Figure 1) 259.93 499.8 T
+385.51 498.49 302.9 498.49 2 L
+V
+0.59 H
+N
+0 F
+(: Network Model) 302.9 499.8 T
+495.43 619 505.95 624.77 500.08 614.3 497.76 616.65 4 Y
+V
+473.96 588.02 463.44 582.25 469.3 592.72 471.63 590.37 4 Y
+V
+497.79 616.64 471.66 590.35 2 L
+7 X
+V
+0.5 H
+0 X
+N
+1 18 Q
+(Disk) 410.52 530.49 T
+7 X
+90 450 42.52 14.17 428.03 575.15 G
+0 X
+90 450 42.52 14.17 428.03 575.15 A
+7 X
+180 270 42.94 21.26 428.45 521.28 G
+2 Z
+0 X
+180 270 42.94 21.26 428.45 521.28 A
+7 X
+270 360 42.94 21.26 427.68 521.61 G
+0 X
+270 360 42.94 21.26 427.68 521.61 A
+FMENDPAGE
+%%EndPage: "7" 6
+%%Page: "6" 6
+612 792 0 FMBEGINPAGE
+57.97 26.65 554.03 40.82 R
+7 X
+0 K
+V
+0 10 Q
+0 X
+(6) 303.5 34.15 T
+56.69 54.99 297.64 735.31 R
+7 X
+V
+0 X
+3.03 (number\051. This field entry can be changed by adding a) 56.69 728.64 P
+1.27 (USER_DATA parameter whose vale is the name of a file) 56.69 717.64 P
+(continuing the data to add to the header.) 56.69 706.64 T
+(Usage:) 85.04 693.64 T
+(USER_DATA ./user_data.txt) 85.04 667.64 T
+0 12 Q
+(2.1) 56.69 640.31 T
+(1 Compression Decision List \050CDL, also) 71.24 640.31 T
+(Speci\336cs File \050parameter \336le\051\051) 56.69 626.31 T
+0 10 Q
+0.18 (If you want to be very exact in what is set during the) 85.04 599.64 P
+1.71 (encoding, use CDL_FILE \050the older SPECIFICS_FILE is) 56.69 588.64 P
+0.18 (supported as well\051 to point to a file describing the exact set-) 56.69 577.64 P
+0.22 (tings wished for the encoding. The version 1.0 of CDL sup-) 56.69 566.64 P
+(port has the following format:) 56.69 555.64 T
+(version 1) 85.04 542.64 T
+(frame FN T Q) 85.04 529.64 T
+(slice SN Q) 85.04 516.64 T
+(block BN Q | BN Q skip | BN Q bi fx fy bx by |) 85.04 503.64 T
+(BN Q forw fx fy | BN Q back bx by) 109.7 490.64 T
+3.51 (FN, SN, and BN signal which frame/slice/block) 85.04 477.64 P
+1.64 (number the command applies to. Note that if you have a) 56.69 466.64 P
+2.22 (block or slice command, must be proceeded by a frame) 56.69 455.64 P
+0.63 (command for that frame. T sets the type of the frame \050I, P,) 56.69 444.64 P
+-0.06 (B, or - to not set\051. Q sets the q-scale \0501-31 or +N -N for rela-) 56.69 433.64 P
+0.17 (tive scaling, or 0 for no change\051. The detailed block specifi-) 56.69 422.64 P
+2.81 (cations set the motion vectors \050in half-pixel units\051. See) 56.69 411.64 P
+(specifications.c for more information.) 56.69 400.64 T
+2.25 (Version 2 CDL files have relative Qscales, so \3222) 85.04 387.64 P
+1.89 (\322means decrease the Qscale by 2, \3222\323 means increase it.) 56.69 376.64 P
+(Unsigned numbers like \3224\323 set the Qscale \050to 4\051.) 56.69 365.64 T
+(Usage:) 85.04 352.64 T
+(CDL_FILE filename) 85.04 326.64 T
+(CDL_DEFINES string) 85.04 313.64 T
+3 (where filename contains the specifics, and string) 85.04 300.64 P
+0.61 (\050optional\051 are defines to be passed to the C preprocessor to) 56.69 289.64 P
+(use on the file \050-Db=block for example\051.) 56.69 278.64 T
+0 12 Q
+(2.12 Gamma Correction \050parameter \336le\051) 56.69 251.31 T
+0 10 Q
+-0.13 (If your movies are too light or too dark for your play-) 85.04 224.64 P
+(back system, you can pre-gamma correct them.) 56.69 213.64 T
+(Usage:) 85.04 200.64 T
+2 F
+(GAMMA gamma-val) 85.04 174.64 T
+0 F
+1.5 (gamma-corrects by raising each luminance fraction) 85.04 161.64 P
+(to the power) 56.69 150.64 T
+2 F
+(gamma-val) 109.15 150.64 T
+0 F
+(\050a float\051) 169.12 150.64 T
+0.18 (This works by converting the luminance \050brightness\051) 85.04 137.64 P
+-0.17 (of the input image to a fraction zero to one, and then raises it) 56.69 126.64 P
+-0.17 (to the power) 56.69 115.64 P
+2 F
+-0.4 (gamma-val) 108.65 115.64 P
+0 F
+-0.17 (. Thus values less than 1 brighten,) 162.62 115.64 P
+2.36 (and greater than 1 dim. If your output device has good) 56.69 104.64 P
+0.92 (brightness controls, it is better to control brightness at that) 56.69 93.64 P
+(end.) 56.69 82.64 T
+314.36 54.99 552.76 735.31 R
+7 X
+V
+0 12 Q
+0 X
+(2.13 Encoding GOPs at a T) 314.36 727.31 T
+(ime \050command line\051) 445.86 727.31 T
+0 10 Q
+2.65 (Instead of encoding an entire sequence, you can) 342.71 700.64 P
+1.57 (encode a single GOP. GOPs can later be joined together) 314.36 689.64 P
+(with the encoder to form an MPEG file.) 314.36 678.64 T
+(Usage:) 342.71 665.64 T
+2 F
+(-gop num) 342.71 652.64 T
+0 F
+1.81 (This only encodes the numbered GOP \050which are) 342.71 639.64 P
+(numbered beginning at 0.) 314.36 628.64 T
+1.2 (The output file will be the normal output filename) 342.71 615.64 P
+(with the suffix \322.gop.<gop_num>\323) 314.36 604.64 T
+1.18 (GOP files can be joined at any time using the fol-) 342.71 578.64 P
+(lowing command-line argument.) 314.36 567.64 T
+(Usage:) 342.71 554.64 T
+2 F
+(-combine_gops) 342.71 541.64 T
+0 F
+2.05 (This causes the encoder to simply combine some) 342.71 528.64 P
+0.52 (GOP files into a single MPEG stream. A sequence header/) 314.36 517.64 P
+0.03 (ender are inserted. In this case, the parameter file need only) 314.36 506.64 P
+0.18 (contain the YUV_SIZE value, an output file, and perhaps a) 314.36 495.64 P
+0.21 (list of input GOP files. If no list of input GOP files is used,) 314.36 484.64 P
+0.45 (then the encoder assumes you\325re using the same parameter) 314.36 473.64 P
+-0.08 (file you used with the) 314.36 462.64 P
+2 F
+-0.19 (-gop) 403.08 462.64 P
+0 F
+-0.08 (option, and calculates the cor-) 432.88 462.64 P
+0.52 (responding gop filenames itself. If this is not the case, you) 314.36 451.64 P
+0.58 (can specify input GOP files in the same manner as normal) 314.36 440.64 P
+0.44 (input files -- except instead of using INPUT_DIR, INPUT,) 314.36 429.64 P
+1.79 (and END_INPUT, use GOP_INPUT_DIR, GOP_INPUT,) 314.36 418.64 P
+1.38 (and GOP_END_INPUT. If no input GOP files are speci-) 314.36 407.64 P
+-0.05 (fied, then the default is to use the output file name with suf-) 314.36 396.64 P
+(fix \322.gop.<gop_num>\323 starting from 0 as the input files.) 314.36 385.64 T
+4.1 (Thus, to summarize, unless you\325re mixing and) 342.71 372.64 P
+0.2 (matching GOP files from different sources, you can simply) 314.36 361.64 P
+1.37 (use the same parameter file for the) 314.36 350.64 P
+2 F
+3.28 (-gop) 464.63 350.64 P
+0 F
+1.37 ( and) 488.62 350.64 P
+2 F
+3.28 (-combi-) 510.78 350.64 P
+(ne_gops) 314.36 339.64 T
+0 F
+( options.) 356.34 339.64 T
+0 12 Q
+(2.14 Encoding Frames at a T) 314.36 299.31 T
+(ime \050command line\051) 452.51 299.31 T
+0 10 Q
+2.65 (Instead of encoding an entire sequence, you can) 342.71 273.64 P
+0.34 (encode individual frames. These frames can later be joined) 314.36 262.64 P
+(together to form an MPEG file.) 314.36 251.64 T
+(Usage:) 342.71 238.64 T
+(-frames first_frame last_frame) 342.71 225.64 T
+2.37 (This causes the encoder to encode the numbered) 342.71 212.64 P
+(frames in the given range, inclusive.) 314.36 201.64 T
+0.89 (The output will be placed in separate files, one per) 342.71 188.64 P
+0.21 (frame, with the filenames being the normal output file with) 314.36 177.64 P
+(the suffix \322.frame.<frame num>\323) 314.36 166.64 T
+(The frame files can later be combined as follows:) 342.71 140.64 T
+(Usage:) 342.71 127.64 T
+(-combine_frames) 342.71 114.64 T
+2.05 (This causes the encoder to simply combine some) 342.71 101.64 P
+2.21 (frames into a single MPEG stream. Sequence and GOP) 314.36 90.64 P
+1.11 (headers are inserted appropriately. You can either use the) 314.36 79.64 P
+1.39 (same parameter file for -frames and -combine_frames, or) 314.36 68.64 P
+FMENDPAGE
+%%EndPage: "6" 5
+%%Page: "5" 5
+612 792 0 FMBEGINPAGE
+57.97 26.65 554.03 40.82 R
+7 X
+0 K
+V
+0 10 Q
+0 X
+(5) 303.5 34.15 T
+72 72 297 732.47 R
+7 X
+V
+2 F
+0 X
+(sflowg.07.yuv) 100.35 725.81 T
+(sflowg.08.yuv) 100.35 712.81 T
+(sflowg.09.yuv) 100.35 699.81 T
+(sflowg.10.yuv) 100.35 686.81 T
+0 F
+1.37 (If there is no star, then the file name is simple) 100.35 660.81 P
+0.99 (repeated the appropriate number of times \050[1-10] is 10) 72 649.81 P
+(times\051.) 72 638.81 T
+-0.18 (Commands can be used to dynamically create the) 100.35 625.81 P
+(list of files, for example:) 72 614.81 T
+2 F
+(INPUT) 100.35 588.81 T
+(\324ls July-*.ppm\324) 100.35 575.81 T
+(\324cat file-list\324) 100.35 562.81 T
+(END_INPUT) 100.35 549.81 T
+0 F
+1.41 (The command\050s\051 will be executed in the direc-) 100.35 523.81 P
+0.44 (tory named by INPUT_DIR if it appears before INPUT) 72 512.81 P
+2.09 (in the parameter file. Note that the encoder-provided) 72 501.81 P
+(filling in of *\325s is not supported in this mode.) 72 490.81 T
+0.36 (The encoder allows you to use other file formats) 100.35 477.81 P
+1.61 (by providing an input conversion specifier. You must) 72 466.81 P
+0.18 (describe how to convert the input format into one of the) 72 455.81 P
+(base file types.) 72 444.81 T
+2 F
+(Usage:) 100.35 431.81 T
+(INPUT_CONVERT conversion) 100.35 405.81 T
+3.25 (conversion) 100.35 379.81 P
+0 F
+1.35 ( must be a multi-star expression.) 160.31 379.81 P
+-0.16 (If) 72 368.81 P
+2 F
+-0.37 (conversion) 81 368.81 P
+0 F
+-0.16 ( is simply \324*\325, then no conversion takes) 140.97 368.81 P
+0.92 (place. Otherwise, each of the file lines are replaced by) 72 357.81 P
+0.3 (the conversion line with the file name wherever there is) 72 346.81 P
+0.88 (a \324*\325. The conversion line must send the output to std-) 72 335.81 P
+-0.12 (out. For example, suppose we have a bunch of GIF files.) 72 324.81 P
+(Then we would do:) 72 313.81 T
+2 F
+(BASE_FILE_FORMAT) 100.35 300.81 T
+( PPM) 196.29 300.81 T
+(INPUT) 100.35 287.81 T
+(pictures.*.gif [0-10]) 100.35 274.81 T
+(END_INPUT) 100.35 261.81 T
+(INPUT_CONVERT giftoppm *) 100.35 248.81 T
+0 F
+0.9 (Another example: Suppose we have separate Y,) 100.35 222.81 P
+1.18 (U, and V files \050where the U and V have already been) 72 211.81 P
+(subsampled\051. Then we might have:) 72 200.81 T
+2 F
+(BASE_FILE_FORMAT) 100.35 187.81 T
+( YUV) 196.29 187.81 T
+(INPUT) 100.35 174.81 T
+(pictures.* [0-10]) 100.35 161.81 T
+(END_INPUT) 100.35 148.81 T
+(INPUT_CONVERT cat *.Y *.U *.V) 100.35 135.81 T
+(YUV_FORMAT UCB) 100.35 122.81 T
+0 F
+0.68 (As you can see, the \322files\323 between) 100.35 109.81 P
+2 F
+1.63 (INPUT) 249.41 109.81 P
+0 F
+0.68 ( and) 279.39 109.81 P
+2 F
+2.41 (END_INPUT) 72 98.81 P
+0 F
+1 ( don\325t have to be files at all! This can be) 125.97 98.81 P
+(very useful.) 72 87.81 T
+315 72 540 732.47 R
+7 X
+V
+0 X
+(To read data from standard input, set:) 343.35 712.81 T
+2 F
+(INPUT_DIR stdin) 343.35 699.81 T
+0 F
+1.05 (Note that you cannot use the stdin option when) 343.35 686.81 P
+0.07 (coding in parallel. \050Use GOPINPUTDIR or FRAMEIN-) 315 675.81 P
+(PUTDIR if combining frames/GOPs.\051) 315 664.81 T
+(The output file is specified by:) 343.35 638.81 T
+2 F
+(OUTPUT filename) 343.35 625.81 T
+0 F
+(for example:) 343.35 612.81 T
+2 F
+(OUTPUT /u/keving/mpg/flowers.mpg) 343.35 599.81 T
+0 12 Q
+(2.8 Original or Decoded \050parameter \336le\051) 315 559.47 T
+0 10 Q
+0.09 (The encoder can use either the original frames as) 343.35 532.81 P
+3 (reference frames, or the decoded frames. Using the) 315 521.81 P
+2.48 (decoded frames gives better playback quality, but is) 315 510.81 P
+1.93 (slower and seems to give worse compression. It also) 315 499.81 P
+0.63 (causes some complications with parallel encoding. \050see) 315 488.81 P
+0.95 (the section on parallel encoding\051 One recommendation) 315 477.81 P
+0.52 (is to use original, and lower the q-scale if the quality is) 315 466.81 P
+(not good enough. Table six shows the trade-offs.) 315 455.81 T
+(Usage:) 343.35 429.81 T
+2 F
+(REFERENCE_FRAME ORIGINAL) 343.35 403.81 T
+0 12 Q
+(2.9 Bit-rate Control \050parameter \336le\051) 315 272.47 T
+0 10 Q
+1.53 (The default encoding uses variable bit rate. To) 343.35 245.81 P
+0.33 (limit the bit rate, the MPEG-2 Standard\325s algorithm has) 315 234.81 P
+2.39 (been implemented \050suitably adjusted\051. There are two) 315 223.81 P
+(parameters which must be set to use bit-rate control:) 315 212.81 T
+2 F
+(BUFFER_SIZE N \050in bits\051) 343.35 199.81 T
+(BIT_RATE M \050in bytes/sec\051) 343.35 186.81 T
+0 F
+-0.09 (N sets the largest required buffer, M specifies the) 343.35 173.81 P
+1 (continual rate. N is set in number of bits, the buffer is) 315 162.81 P
+(actually in 16bit ints.) 315 151.81 T
+0 12 Q
+(2.10 Userdata \050parameter \336le\051) 315 124.47 T
+0 10 Q
+0.01 (An identification string is added by default to the) 343.35 97.81 P
+2.96 (Sequence layer user-data field. It is \322UCB Encoder) 315 86.81 P
+1.41 (Vers\323 \050where Vers is replaced by the encoder version) 315 75.81 P
+1 12 Q
+-0.15 (T) 315 380.47 P
+-0.15 (able 6: Original or Decoded? \050Normalized\051) 321.9 380.47 P
+0 10 Q
+(Reference) 323.62 352.81 T
+(Compress) 373.49 358.81 T
+(ion) 387.1 346.81 T
+(Speed) 429.46 352.81 T
+(Quality) 486.21 358.81 T
+(I/P/B) 490.65 346.81 T
+(Decoded) 329.87 328.81 T
+(1000) 383.49 328.81 T
+(1000) 431.68 328.81 T
+(1000/969/919) 473.44 328.81 T
+(Original) 332.08 312.81 T
+(885) 385.99 312.81 T
+(1373) 431.68 312.81 T
+(1000/912/884) 473.44 312.81 T
+318.37 370.22 318.37 306.72 2 L
+V
+0.5 H
+0 Z
+N
+369.39 370.72 369.39 306.22 2 L
+V
+N
+417.58 370.72 417.58 306.22 2 L
+V
+N
+465.77 370.72 465.77 306.22 2 L
+V
+N
+536.63 370.22 536.63 306.72 2 L
+V
+N
+318.12 370.47 536.88 370.47 2 L
+V
+N
+318.62 339.72 536.38 339.72 2 L
+V
+N
+318.62 337.22 536.38 337.22 2 L
+V
+N
+318.12 322.47 536.88 322.47 2 L
+V
+N
+318.12 306.47 536.88 306.47 2 L
+V
+N
+FMENDPAGE
+%%EndPage: "5" 4
+%%Page: "4" 4
+612 792 0 FMBEGINPAGE
+57.97 26.65 554.03 40.82 R
+7 X
+0 K
+V
+0 10 Q
+0 X
+(4) 303.5 34.15 T
+56.69 54.99 297.64 735.31 R
+7 X
+V
+0 X
+(A standard sequence is IBBPBBPBBPBBPB) 85.04 715.64 T
+(Usage:) 85.04 702.64 T
+2 F
+(PATTERN) 85.04 676.64 T
+( <IPB pattern>) 127.02 676.64 T
+0 F
+1.84 (Note that if the last frame in an encoding is a B-) 85.04 650.64 P
+0.07 (frame, it will not be encoded \050since it has no future frame to) 56.69 639.64 P
+1.81 (reference from\051. Pre-I patters like BBIBBP are legal, but) 56.69 628.64 P
+0 (seem to have bugs, so watch out! To insure that every frame) 56.69 617.64 P
+0.63 (is encoded, the encoder can force the last frame to be an I-) 56.69 606.64 P
+(frame.) 56.69 595.64 T
+(Usage:) 85.04 582.64 T
+2 F
+(FORCE_ENCODE_LAST_FRAME) 85.04 556.64 T
+0 12 Q
+(2.7 Specifying Input Files \050parameter \336le\051) 56.69 516.31 T
+0 10 Q
+0.03 (The encoder can accept five base types of input files:) 85.04 489.64 P
+0.08 (PPM, PNM, JMOVIE, JPEG, and YUV. Note that PPM is a) 56.69 478.64 P
+1.38 (subset of PNM; the PPM option is available because it is) 56.69 467.64 P
+0.49 (faster to read if the files are known to be PPM. JMOVIE is) 56.69 456.64 P
+0.19 (the format created by the Parallax video grabber. JPEGs are) 56.69 445.64 P
+-0.06 (a standard image format. YUV formats are described below.) 56.69 434.64 P
+0.98 (If you use YUV format, you must specify the pixel) 85.04 421.64 P
+0.61 (size of the image in the parameter file and the YUV_FOR-) 56.69 410.64 P
+(MAT.) 56.69 399.64 T
+(Usage:) 85.04 386.64 T
+2 F
+(BASE_FILE_FORMAT format) 85.04 360.64 T
+(YUV_SIZE widthxheight) 85.04 347.64 T
+(YUV_FORMAT yuv_format) 85.04 334.64 T
+0.1 (format) 85.04 308.64 P
+0 F
+0.04 ( is one of {) 121.02 308.64 P
+2 F
+0.1 (YUV, PPM, PNM, JMOVIE,) 165.4 308.64 P
+(JPEG) 56.69 297.64 T
+0 F
+(}) 80.68 297.64 T
+2 F
+(width) 85.04 284.64 T
+0 F
+( and) 115.02 284.64 T
+2 F
+(height) 134.45 284.64 T
+0 F
+( are integers \050like 320x240\051) 170.43 284.64 T
+2 F
+5.81 (yuv_format) 85.04 271.64 P
+0 F
+2.42 (is one of) 156.81 271.64 P
+2 F
+5.81 ( {ABEKAS, EYUV,) 196.07 271.64 P
+1.66 (PHILLIPS, UCB, {SPECIAL}},) 56.69 260.64 P
+0 F
+0.69 (where) 223.59 260.64 P
+2 F
+1.66 ( SPECIAL) 248 260.64 P
+0 F
+1.83 (is a specification of the pattern of Y, U, and V, such as) 56.69 249.64 P
+2 F
+-0.57 (UYVY) 56.69 238.64 P
+0 F
+-0.24 (for) 86.1 238.64 P
+2 F
+-0.57 ( ABEKAS. The pattern can be of any) 97.76 238.64 P
+1.18 (length, or order, but must consist only) 56.69 227.64 P
+0.15 (of Ys, Us, andVs, and must represent two) 56.69 216.64 P
+3.18 (pixels of data \050thus YUVYUV for 4:4:4) 56.69 205.64 P
+(source\051.) 56.69 194.64 T
+0 F
+1.73 (You must specify the directory in which the input) 85.04 168.64 P
+-0.12 (files are located. You can use \324.\325 to specify the current direc-) 56.69 157.64 P
+(tory.) 56.69 146.64 T
+(Usage:) 85.04 133.64 T
+(INPUT_DIR directory) 85.04 107.64 T
+1.08 (You must also specify the names of the files them-) 85.04 81.64 P
+1.11 (selves. You list them sequentially, one per line, in display) 56.69 70.64 P
+2.12 (order. There are shortcuts, however, which allow you to) 56.69 59.64 P
+314.36 54.99 552.76 735.31 R
+7 X
+V
+0 X
+(condense many files into one line.) 314.36 728.64 T
+(Usage:) 342.71 715.64 T
+2 F
+(INPUT) 342.71 689.64 T
+(file) 342.71 676.64 T
+2 8 Q
+(1) 366.7 674.14 T
+2 10 Q
+(file) 342.71 663.64 T
+2 8 Q
+(2) 366.7 661.14 T
+2 10 Q
+(...) 342.71 650.64 T
+(file) 342.71 637.64 T
+2 8 Q
+(n) 366.7 635.14 T
+2 10 Q
+(END_INPUT) 342.71 624.64 T
+8.17 (file) 342.71 598.64 P
+2 8 Q
+6.53 (i) 366.7 596.14 P
+0 10 Q
+3.4 ( can be either a file name, a single-star) 371.49 598.64 P
+0.28 (expression followed by a bracketed expansion for star, or a) 314.36 587.64 P
+0.28 (command to be executed. There are two types of bracketed) 314.36 576.64 P
+(expansions. For example:) 314.36 565.64 T
+2 F
+(sflowg.*.yuv [0-10]) 342.71 539.64 T
+0 F
+(is expanded to:) 342.71 513.64 T
+2 F
+(sflowg.0.yuv) 342.71 487.64 T
+(sflowg.1.yuv) 342.71 474.64 T
+(sflowg.2.yuv) 342.71 461.64 T
+(sflowg.3.yuv) 342.71 448.64 T
+(sflowg.4.yuv) 342.71 435.64 T
+(sflowg.5.yuv) 342.71 422.64 T
+(sflowg.6.yuv) 342.71 409.64 T
+(sflowg.7.yuv) 342.71 396.64 T
+(sflowg.8.yuv) 342.71 383.64 T
+(sflowg.9.yuv) 342.71 370.64 T
+(sflowg.10.yuv) 342.71 357.64 T
+(sflowg.*.yuv [0-10+3]) 342.71 331.64 T
+0 F
+(is expanded to:) 342.71 305.64 T
+2 F
+(sflowg.0.yuv) 342.71 279.64 T
+(sflowg.3.yuv) 342.71 266.64 T
+(sflowg.6.yuv) 342.71 253.64 T
+(sflowg.9.yuv) 342.71 240.64 T
+0 F
+(Also, the encoder will pad with 0\325s if necessary:) 342.71 214.64 T
+2 F
+(sflowg.*.yuv [00-10]) 342.71 188.64 T
+0 F
+(is expanded to:) 342.71 162.64 T
+2 F
+(sflowg.00.yuv) 342.71 136.64 T
+(sflowg.01.yuv) 342.71 123.64 T
+(sflowg.02.yuv) 342.71 110.64 T
+(sflowg.03.yuv) 342.71 97.64 T
+(sflowg.04.yuv) 342.71 84.64 T
+(sflowg.05.yuv) 342.71 71.64 T
+(sflowg.06.yuv) 342.71 58.64 T
+FMENDPAGE
+%%EndPage: "4" 3
+%%Page: "3" 3
+612 792 0 FMBEGINPAGE
+57.97 26.65 554.03 40.82 R
+7 X
+0 K
+V
+0 10 Q
+0 X
+(3) 303.5 34.15 T
+56.69 54.99 297.64 735.31 R
+7 X
+V
+0 X
+1.31 (For some reason Simple seems to give better com-) 85.04 616.64 P
+(pression, but it depends on the image sequence.) 56.69 605.64 T
+(Usage:) 85.04 579.64 T
+2 F
+(PSEARCH_ALG ptechnique) 85.04 553.64 T
+(BSEARCH_ALG btechnique) 85.04 540.64 T
+0 F
+0.87 (where) 85.04 514.64 P
+2 F
+2.08 (ptechnique) 112.82 514.64 P
+0 F
+0.87 (is one of {LOGARITHMIC,) 180.87 514.64 P
+(SUBSAMPLE, TWOLEVEL, EXHAUSTIVE}) 56.69 503.64 T
+3.14 (where) 85.04 490.64 P
+2 F
+7.54 (btechnique) 115.1 490.64 P
+0 F
+3.14 ( is one of {EXHAUSTIVE,) 175.07 490.64 P
+(CROSS2, SIMPLE}) 56.69 479.64 T
+0 12 Q
+(2.3 GOP \050parameter \336le\051) 56.69 439.31 T
+0 10 Q
+1.94 (A Group of Pictures \050GOP\051 is a roughly indepen-) 85.04 412.64 P
+3.02 (dently decodable sequence of frames. An MPEG video) 56.69 401.64 P
+0 (stream is made of one or more GOPs. You may specify how) 56.69 390.64 P
+0.8 (many frames each GOP should be. A GOP must start with) 56.69 379.64 P
+0.2 (an I-frame, and the encoder will enforce that by taking your) 56.69 368.64 P
+(number as the) 56.69 357.64 T
+3 F
+(minimum) 115.26 357.64 T
+0 F
+( number of frames in a GOP.) 152.46 357.64 T
+(Usage:) 85.04 331.64 T
+2 F
+(GOP_SIZE) 85.04 305.64 T
+( num) 133.01 305.64 T
+0 F
+(where) 85.04 292.64 T
+2 F
+(num) 111.96 292.64 T
+0 F
+( = the number of frames in a GOP) 129.95 292.64 T
+0 12 Q
+(2.4 Slice \050parameter \336le\051) 56.69 252.31 T
+0 10 Q
+-0.25 (A slice is an independently decodable unit in a frame.) 85.04 226.64 P
+0.54 (It can be as small as one macroblock, or it can be as big as) 56.69 215.64 P
+1.47 (the entire frame. Barring transmission error, adding slices) 56.69 204.64 P
+0.55 (does not change quality or speed; the only effect is slightly) 56.69 193.64 P
+-0.1 (worse compression. More slices are used for noisy transmis-) 56.69 182.64 P
+2.81 (sion so that errors are more recoverable. Because most) 56.69 171.64 P
+0.28 (transmission systems have more sophisticated error correct-) 56.69 160.64 P
+(ing routines, we usually just use one slice per frame.) 56.69 149.64 T
+(Usage:) 85.04 123.64 T
+2 F
+(SLICES_PER_FRAME) 85.04 97.64 T
+( num) 180.99 97.64 T
+0 F
+(where) 85.04 84.64 T
+2 F
+(num) 111.96 84.64 T
+0 F
+( is the number of slices in a frame) 129.95 84.64 T
+2.31 (Note: Some MPEG playback systems require that) 85.04 58.64 P
+1 12 Q
+(T) 76.29 727.31 T
+(able 4: B-frame Motion V) 83.18 727.31 T
+(ector Sear) 213.99 727.31 T
+(ch) 266.05 727.31 T
+(\050Normalized\051) 143.19 713.31 T
+0 10 Q
+(T) 84.14 691.64 T
+(echnique) 89.54 691.64 T
+(Compression) 139.45 691.64 T
+(Speed) 204.64 691.64 T
+(Quality) 242.96 691.64 T
+(Exhaustive) 86.23 673.64 T
+(1000) 155.83 673.64 T
+(?) 214.63 673.64 T
+(1000) 247.96 673.64 T
+(Cross2) 102.88 657.64 T
+(975) 158.33 657.64 T
+(1000) 206.86 657.64 T
+(996) 250.46 657.64 T
+(Simple) 102.32 641.64 T
+(938) 158.33 641.64 T
+(1765) 206.86 641.64 T
+(991) 250.46 641.64 T
+75.12 703.06 75.12 635.56 2 L
+V
+0.5 H
+0 Z
+N
+134.65 703.56 134.65 635.06 2 L
+V
+N
+197.01 703.56 197.01 635.06 2 L
+V
+N
+236.69 703.56 236.69 635.06 2 L
+V
+N
+279.21 703.06 279.21 635.56 2 L
+V
+N
+74.87 703.31 279.46 703.31 2 L
+V
+N
+75.37 684.56 278.96 684.56 2 L
+V
+N
+75.37 682.06 278.96 682.06 2 L
+V
+N
+74.87 667.31 279.46 667.31 2 L
+V
+N
+74.87 651.31 279.46 651.31 2 L
+V
+N
+74.87 635.31 279.46 635.31 2 L
+V
+N
+314.36 54.99 552.76 735.31 R
+7 X
+V
+0 X
+1.26 (each slice must consist of whole rows of macroblocks. If) 314.36 728.64 P
+0.55 (this is the case, then if the height of the image is H pixels,) 314.36 717.64 P
+1.97 (then you should set the SLICES_PER_FRAME to some) 314.36 706.64 P
+0.32 (number which divides H/16. For example, if H = 240, then) 314.36 695.64 P
+0.82 (you should only use SLICES_PER_FRAME values of 15,) 314.36 684.64 P
+(5, 3, or 1.) 314.36 673.64 T
+0.3 (Note to the note: these MPEG playback systems are) 342.71 660.64 P
+1.07 (really at fault, since the MPEG standard says this doesn\325t) 314.36 649.64 P
+(have to be so.) 314.36 638.64 T
+0 12 Q
+(2.5 Search W) 314.36 611.31 T
+(indow \050parameter \336le\051) 378.83 611.31 T
+0 10 Q
+0.65 (The search window is the window in which motion) 342.71 585.64 P
+0.47 (vectors are searched for. The window is a square. You can) 314.36 574.64 P
+1.22 (specify the size of the square, and whether to allow half-) 314.36 563.64 P
+(pixel motion vectors or not.) 314.36 552.64 T
+(Usage:) 342.71 526.64 T
+2 F
+(PIXEL) 342.71 500.64 T
+( <FULL or HALF>) 372.69 500.64 T
+(RANGE num [numB]) 342.71 487.64 T
+-0.54 (HALF) 342.71 461.64 P
+0 F
+-0.22 ( means that half-pixel vectors are allowed. The) 366.7 461.64 P
+0.19 (search window is +/-) 314.36 450.64 P
+2 F
+0.47 (num) 401.27 450.64 P
+0 F
+0.19 ( pixels in the X and Y directions.) 419.26 450.64 P
+-0.02 (It is usually important that you use) 314.36 439.64 P
+2 F
+-0.05 (HALF) 455.54 439.64 P
+0 F
+-0.02 (, because it results) 479.53 439.64 P
+2.12 (in both better quality and better compression. It is only) 314.36 428.64 P
+(undesirable for computer-generated images.) 314.36 417.64 T
+2 F
+-0.25 (num) 342.71 404.64 P
+0 F
+-0.1 ( should probably be set to at least 8 or 10 pixels.) 360.7 404.64 P
+2.35 (This number depends on the image. Using much larger) 314.36 393.64 P
+0.68 (numbers such as 20 or 30 doesn\325t seem to help much, and) 314.36 382.64 P
+0.02 (increases the CPU cost drastically. The optional numB is in) 314.36 371.64 P
+2.67 (case you wish to specify different ranges for predicted) 314.36 360.64 P
+3.37 (frames \050P-frames, num\051, and Bi-directional frames \050B-) 314.36 349.64 P
+0.31 (frames, numB\051. B-frame limits are optional as indicated by) 314.36 338.64 P
+-0 (the braces above \050so \322RANGE 10 6\323 is a valid command as) 314.36 327.64 P
+(is \322RANGE 9\323\051.) 314.36 316.64 T
+0 12 Q
+(2.6 IPB Pattern \050parameter \336le\051) 314.36 276.31 T
+0 10 Q
+2.24 (You can set the sequence of I, P, and B-frames.) 342.71 249.64 P
+0.45 (Later versions will allow you to do more than set a repeat-) 314.36 238.64 P
+2.49 (ing IPB pattern. The pattern affects speed, quality, and) 314.36 227.64 P
+(compression. Table five shows some of the trade-offs.) 314.36 216.64 T
+(\050this is given a certain Q-scale\051) 342.71 59.64 T
+1 12 Q
+(T) 337.17 193.31 T
+(able 5: Comparison of I/P/B-Frames) 344.06 193.31 T
+(\050Normalized\051) 399.59 179.31 T
+0 F
+(Frame) 331.84 156.31 T
+(T) 335.26 142.31 T
+(ype) 341.74 142.31 T
+(Compress) 384.37 156.31 T
+(ion) 400.7 142.31 T
+(Speed) 451.3 149.31 T
+(Quality) 505.57 149.31 T
+(I-frames) 334.94 122.31 T
+(1000) 396.36 122.31 T
+(1000) 453.96 122.31 T
+(1000) 511.57 122.31 T
+(P-frames) 332.26 104.31 T
+(409) 399.36 104.31 T
+(601) 456.96 104.31 T
+(969) 514.56 104.31 T
+(B-frames) 330.93 86.31 T
+(72) 402.36 86.31 T
+(260) 456.96 86.31 T
+(919) 514.56 86.31 T
+314.76 169.06 314.76 79.56 2 L
+V
+N
+379.56 169.56 379.56 79.06 2 L
+V
+N
+437.16 169.56 437.16 79.06 2 L
+V
+N
+494.76 169.56 494.76 79.06 2 L
+V
+N
+552.36 169.06 552.36 79.56 2 L
+V
+N
+314.51 169.31 552.61 169.31 2 L
+V
+N
+315.01 134.56 552.11 134.56 2 L
+V
+N
+315.01 132.06 552.11 132.06 2 L
+V
+N
+314.51 115.31 552.61 115.31 2 L
+V
+N
+314.51 97.31 552.61 97.31 2 L
+V
+N
+314.51 79.31 552.61 79.31 2 L
+V
+N
+FMENDPAGE
+%%EndPage: "3" 2
+%%Page: "2" 2
+612 792 0 FMBEGINPAGE
+57.97 26.65 554.03 40.82 R
+7 X
+0 K
+V
+0 10 Q
+0 X
+(2) 303.5 34.15 T
+72 72 297 720 R
+7 X
+V
+0 X
+2.84 (Here is a description of the different command-line) 72 700.33 P
+0.03 (options available and parameter-file options available in) 72 689.33 P
+2.37 (sequential \050one-machine\051 encoding. You should defi-) 72 678.33 P
+3.43 (nitely read sections 2.1-2.9. The other sections are) 72 667.33 P
+(optional.) 72 656.33 T
+0.21 (In the following, whenever a space in the parameter file) 72 630.33 P
+4.29 (appears, it can be represented by any amount of) 72 619.33 P
+(whitespace \050tabs or spaces\051.) 72 608.33 T
+0 12 Q
+(2.1 Q-Scale \050parameter \336le\051) 72 581 T
+0 10 Q
+1.78 (The quantization scale values \050Q-Scale\051 give a) 100.35 554.33 P
+1.37 (trade-off between quality and compression. Using dif-) 72 543.33 P
+-0.16 (ferent Q-Scale values has very little effect on speed. The) 72 532.33 P
+1.74 (Q-Scale values can be set separately for I, P, and B-) 72 521.33 P
+(frames.) 72 510.33 T
+(Usage:) 100.35 484.33 T
+2 F
+(IQ-Scale) 126.21 471.33 T
+( num) 174.18 471.33 T
+(PQ-Scale num) 125.5 458.33 T
+(BQ-Scale num) 126.21 445.33 T
+0 F
+(num in all three cases is a number from 1 to 31.) 100.35 432.33 T
+3.47 (Larger numbers give better compression, but) 100.35 406.33 P
+0.3 (worse quality. In the following, the quality numbers are) 72 395.33 P
+(peak signal-to-noise ratio, defined as:) 72 384.33 T
+(where MSE is the mean squared error.) 100.35 332.18 T
+0.12 (Tables one and two show the Q-scale vs. Quality) 100.35 306.18 P
+(relationship for the flower-garden sequence.) 72 295.18 T
+1.84 (Note that when rate-control \050Section 2.9\051 is in) 100.35 282.18 P
+0.19 (use, the rate control mechanism will change the Q-scale) 72 271.18 P
+1.37 (throughout the blocks of the frame, so these specified) 72 260.18 P
+(values are merely starting points.) 72 249.18 T
+1 12 Q
+(T) 95.77 212.85 T
+(able 1: Q-Scale vs. Quality \050SNR\051) 102.66 212.85 T
+0 10 Q
+(Q-Scale) 91.87 191.18 T
+(I-Frames) 140.95 191.18 T
+(P-Frames) 190.86 191.18 T
+(B-Frames) 241.33 191.18 T
+(1) 124.48 173.18 T
+(43.2) 150.24 173.18 T
+(46.3) 201.27 173.18 T
+(46.5) 252.29 173.18 T
+(6) 124.48 157.18 T
+(32.6) 150.24 157.18 T
+(34.6) 201.27 157.18 T
+(34.3) 252.29 157.18 T
+(1) 119.85 141.18 T
+(1) 124.48 141.18 T
+(28.6) 150.24 141.18 T
+(29.5) 201.27 141.18 T
+(30.0) 252.29 141.18 T
+(16) 119.48 125.18 T
+(26.3) 150.24 125.18 T
+(26.8) 201.27 125.18 T
+(28.6) 252.29 125.18 T
+(21) 119.48 109.18 T
+(24.7) 150.24 109.18 T
+(25.0) 201.27 109.18 T
+(27.9) 252.29 109.18 T
+(26) 119.48 93.18 T
+(23.5) 150.24 93.18 T
+(23.9) 201.27 93.18 T
+(27.5) 252.29 93.18 T
+82.45 202.6 82.45 87.1 2 L
+V
+0.5 H
+0 Z
+N
+133.48 203.1 133.48 86.6 2 L
+V
+N
+184.5 203.1 184.5 86.6 2 L
+V
+N
+235.52 203.1 235.52 86.6 2 L
+V
+N
+286.55 202.6 286.55 87.1 2 L
+V
+N
+82.2 202.85 286.8 202.85 2 L
+V
+N
+82.7 184.1 286.3 184.1 2 L
+V
+N
+82.7 181.6 286.3 181.6 2 L
+V
+N
+82.2 166.85 286.8 166.85 2 L
+V
+N
+82.2 150.85 286.8 150.85 2 L
+V
+N
+82.2 134.85 286.8 134.85 2 L
+V
+N
+82.2 118.85 286.8 118.85 2 L
+V
+N
+82.2 102.85 286.8 102.85 2 L
+V
+N
+82.2 86.85 286.8 86.85 2 L
+V
+N
+72 72 297 720 C
+72 341.85 297 381 C
+0 12 Q
+0 X
+0 K
+(2) 148.86 360.39 T
+(0) 154.85 360.39 T
+3 F
+(l) 161.56 360.39 T
+(o) 165.6 360.39 T
+(g) 172.3 360.39 T
+0 9 Q
+(1) 178.76 356.63 T
+(0) 183.25 356.63 T
+0 12 Q
+(2) 196.32 367.58 T
+(5) 202.32 367.58 T
+(5) 208.31 367.58 T
+3 F
+(M) 196.45 349.78 T
+(S) 207.15 349.78 T
+(E) 213.85 349.78 T
+221.18 360.98 196.45 360.98 2 L
+0.33 H
+0 Z
+N
+196.45 360.98 193.45 347.78 2 L
+N
+193.45 347.78 191.45 351.58 2 L
+N
+191.45 351.58 190.45 349.68 2 L
+N
+189.45 362.98 220.93 362.98 2 L
+N
+72 72 297 720 C
+0 0 612 792 C
+315 72 540 720 R
+7 X
+0 K
+V
+0 12 Q
+0 X
+(2.2 Search T) 315 471 T
+(echniques \050parameter \336le\051) 375.11 471 T
+0 10 Q
+0.77 (There are several different motion vector search) 343.35 445.33 P
+2.44 (techniques available for both P-frame search and B-) 315 434.33 P
+0.54 (frame search. Using different search techniques present) 315 423.33 P
+-0.04 (little difference in quality, but a large difference in com-) 315 412.33 P
+(pression and speed.) 315 401.33 T
+0.26 (There are 4 types of P-frame search: Exhaustive,) 343.35 388.33 P
+(TwoLevel, SubSample, and Logarithmic.) 315 377.33 T
+0.1 (There are 3 types of B-frame search: Exhaustive,) 343.35 364.33 P
+(Cross2, and Simple.) 315 353.33 T
+1.53 (The suggested search techniques are TwoLevel) 343.35 340.33 P
+2.17 (and Logarithmic for P-frame search, and Cross2 and) 315 329.33 P
+0.68 (Simple for B-frame search. Tables three and four com-) 315 318.33 P
+(pare the different search methods:) 315 307.33 T
+318.37 170 536.63 176 C
+0 0 612 792 C
+0 10 Q
+0 X
+0 K
+(a. Smaller numbers mean better compression) 336.37 163.33 T
+(b. Larger numbers mean faster execution) 336.37 151.33 T
+(c. Larger numbers mean better quality) 336.37 139.33 T
+(31) 362.48 672.33 T
+(22.6) 393.24 672.33 T
+(23.0) 444.27 672.33 T
+(27.3) 495.29 672.33 T
+1 12 Q
+(T) 342.04 646 T
+(able 2: Q-Scale vs. Compr) 348.94 646 T
+(ession) 482.3 646 T
+0 10 Q
+(Q-Scale) 334.87 624.33 T
+(I-Frames) 383.95 624.33 T
+(P-Frames) 433.86 624.33 T
+(B-Frames) 484.33 624.33 T
+(1) 367.48 606.33 T
+(2:1) 395.6 606.33 T
+(2:1) 446.62 606.33 T
+(2:1) 497.65 606.33 T
+(6) 367.48 590.33 T
+(7) 399.49 590.33 T
+(10) 448.01 590.33 T
+(15) 499.04 590.33 T
+(1) 362.85 574.33 T
+(1) 367.48 574.33 T
+(1) 397.18 574.33 T
+(1) 401.8 574.33 T
+(18) 448.01 574.33 T
+(43) 499.04 574.33 T
+(16) 362.48 558.33 T
+(15) 396.99 558.33 T
+(29) 448.01 558.33 T
+(97) 499.04 558.33 T
+(21) 362.48 542.33 T
+(19) 396.99 542.33 T
+(41) 448.01 542.33 T
+(173) 496.54 542.33 T
+(26) 362.48 526.33 T
+(24) 396.99 526.33 T
+(56) 448.01 526.33 T
+(256) 496.54 526.33 T
+(31) 362.48 510.33 T
+(28) 396.99 510.33 T
+(73) 448.01 510.33 T
+(330) 496.54 510.33 T
+1 12 Q
+(T) 326.96 284 T
+(able 3: P-frame Motion V) 333.86 284 T
+(ector Sear) 463.98 284 T
+(ch) 516.05 284 T
+(\050Normalized\051) 393.53 270 T
+0 10 Q
+(T) 327.39 248.33 T
+(echnique) 332.79 248.33 T
+(Compression) 382.34 248.33 T
+0 8 Q
+(a) 435.09 252.33 T
+0 10 Q
+(Speed) 454.39 248.33 T
+0 8 Q
+(b) 478.82 252.33 T
+0 10 Q
+(Quality) 498.61 248.33 T
+0 8 Q
+(c) 528.59 252.33 T
+0 10 Q
+(Exhaustive) 329.48 230.33 T
+(1000) 400.5 230.33 T
+(1000) 458.61 230.33 T
+(1000) 505.38 230.33 T
+(SubSample) 328.36 214.33 T
+(1008) 400.5 214.33 T
+(2456) 458.61 214.33 T
+(1000) 505.38 214.33 T
+(T) 333.52 198.33 T
+(woLevel) 338.92 198.33 T
+(1009) 400.5 198.33 T
+(3237) 458.61 198.33 T
+(1000) 505.38 198.33 T
+(Logarithmic) 324.48 182.33 T
+(1085) 400.5 182.33 T
+(8229) 458.61 182.33 T
+(998) 507.88 182.33 T
+1 12 Q
+(T) 338.77 712 T
+(able 1: Q-Scale vs. Quality \050SNR\051) 345.66 712 T
+0 10 Q
+(Q-Scale) 334.87 690.33 T
+(I-Frames) 383.95 690.33 T
+(P-Frames) 433.86 690.33 T
+(B-Frames) 484.33 690.33 T
+325.45 701.75 325.45 666.25 2 L
+V
+0.5 H
+0 Z
+N
+376.48 702.25 376.48 665.75 2 L
+V
+N
+427.5 702.25 427.5 665.75 2 L
+V
+N
+478.52 702.25 478.52 665.75 2 L
+V
+N
+529.55 701.75 529.55 666.25 2 L
+V
+N
+325.2 702 529.8 702 2 L
+V
+N
+325.7 683.25 529.3 683.25 2 L
+V
+N
+325.7 680.75 529.3 680.75 2 L
+V
+N
+325.2 666 529.8 666 2 L
+V
+N
+325.45 635.75 325.45 504.25 2 L
+V
+N
+376.48 636.25 376.48 503.75 2 L
+V
+N
+427.5 636.25 427.5 503.75 2 L
+V
+N
+478.52 636.25 478.52 503.75 2 L
+V
+N
+529.55 635.75 529.55 504.25 2 L
+V
+N
+325.2 636 529.8 636 2 L
+V
+N
+325.7 617.25 529.3 617.25 2 L
+V
+N
+325.7 614.75 529.3 614.75 2 L
+V
+N
+325.2 600 529.8 600 2 L
+V
+N
+325.2 584 529.8 584 2 L
+V
+N
+325.2 568 529.8 568 2 L
+V
+N
+325.2 552 529.8 552 2 L
+V
+N
+325.2 536 529.8 536 2 L
+V
+N
+325.2 520 529.8 520 2 L
+V
+N
+325.2 504 529.8 504 2 L
+V
+N
+318.37 259.75 318.37 176.25 2 L
+V
+N
+377.89 260.25 377.89 175.75 2 L
+V
+N
+443.09 260.25 443.09 175.75 2 L
+V
+N
+494.11 260.25 494.11 175.75 2 L
+V
+N
+536.63 259.75 536.63 176.25 2 L
+V
+N
+318.12 260 536.88 260 2 L
+V
+N
+318.62 241.25 536.38 241.25 2 L
+V
+N
+318.62 238.75 536.38 238.75 2 L
+V
+N
+318.12 224 536.88 224 2 L
+V
+N
+318.12 208 536.88 208 2 L
+V
+N
+318.12 192 536.88 192 2 L
+V
+N
+318.12 176 536.88 176 2 L
+V
+N
+FMENDPAGE
+%%EndPage: "2" 1
+%%Page: "1" 1
+612 792 0 FMBEGINPAGE
+56.69 111.69 297.64 565.23 R
+7 X
+0 K
+V
+1 12 Q
+0 X
+(Contents) 56.69 557.23 T
+0 10 Q
+(0. Contacts/History) 56.69 539.56 T
+(1. Installation) 56.69 526.56 T
+(2. Sequential Usage) 56.69 513.56 T
+(2.1 Q-Scale) 85.04 500.56 T
+(2.2 Search Techniques) 85.04 487.56 T
+(2.3 GOP) 85.04 474.56 T
+(2.4 Slices) 85.04 461.56 T
+(2.5) 85.04 448.56 T
+( Search window) 97.53 448.56 T
+(2.6 IPB pattern) 85.04 435.56 T
+(2.7 Specifying Input Files) 85.04 422.56 T
+(2.8) 85.04 409.56 T
+( Original or Decoded) 97.53 409.56 T
+(2.9 Bit-rate Control) 85.04 396.56 T
+(2.10 User-data) 85.04 383.56 T
+(2.11 Compression Decision List \050CDL\051) 85.04 370.56 T
+(2.12 Gamma Correction) 85.04 357.56 T
+(2.13 Encoding GOPs at a time) 85.04 344.56 T
+(2.14) 85.04 331.56 T
+( Encoding Frames at a time) 102.53 331.56 T
+(2.15) 85.04 318.56 T
+( Stats and other info) 102.53 318.56 T
+(3. Parallel Usage) 56.69 305.56 T
+(3.1) 85.04 292.56 T
+( Architecture) 97.53 292.56 T
+(3.2 Specifying slave machines) 85.04 279.56 T
+(3.3) 85.04 266.56 T
+( Remote Shell) 97.53 266.56 T
+(3.4 Scheduling Algorithms) 85.04 253.56 T
+(3.5 Parallel Problems) 85.04 240.56 T
+0 12 Q
+(4. Performance) 56.69 226.23 T
+0 10 Q
+(5. Other Options) 56.69 213.56 T
+(5.1) 85.04 200.56 T
+( Custom Quantization Tables) 97.53 200.56 T
+(5.2 Aspect Ratio) 85.04 187.56 T
+(5.3) 85.04 174.56 T
+( Frame Rate) 97.53 174.56 T
+(6. Other Tools) 56.69 161.56 T
+(6.1) 85.04 148.56 T
+(ppmtoeyuv) 100.03 148.56 T
+(6.2 jmovie2jpeg) 85.04 135.56 T
+(6.3 movieToVid) 85.04 122.56 T
+314.36 54.99 552.76 565.23 R
+7 X
+V
+0 X
+(6.4) 342.71 558.56 T
+(yuvtojpeg) 357.7 558.56 T
+(6.5) 342.71 545.56 T
+( blockrun) 355.2 545.56 T
+(6.6) 342.71 532.56 T
+( vidtoppm) 355.2 532.56 T
+(6.7 vidtojpeg) 342.71 519.56 T
+(6.8 vidtoeyuv) 342.71 506.56 T
+(7. Frequently Asked Questions) 314.36 493.56 T
+(7.1 Questions) 342.71 480.56 T
+(7.2 Answers) 342.71 467.56 T
+1 12 Q
+(0. Contacts/History) 314.36 432.23 T
+0 10 Q
+2.56 (The Berkeley MPEG encoder was written by Kevin L.) 314.36 414.56 P
+0.59 (Gong in the Berkeley Plateau Multimedia Research group,) 314.36 403.56 P
+2.29 (headed by Professor Lawrence Rowe. It has since been) 314.36 392.56 P
+2.69 (modified by Stephen Smoot, Eugene Hung, and Darryl) 314.36 381.56 P
+0.69 (Brown. Please use the following e-mail addresses to reach) 314.36 370.56 P
+(us:) 314.36 359.56 T
+-0.23 (mpeg-bugs@plateau.cs.berkeley.edu \050bug reports and ques-) 314.36 346.56 P
+(tions\051 larry@cs.berkeley.edu \050research funding!\051) 314.36 335.56 T
+1 12 Q
+(1. Installation) 314.36 300.23 T
+0 10 Q
+(To install, read the directions in doc/INSTALL.) 342.71 282.56 T
+1.32 (Note that the) 342.71 269.56 P
+2 F
+3.17 (bin/) 400.8 269.56 P
+0 F
+1.32 ( directory contains binaries for) 424.79 269.56 P
+0.35 (several different platforms. The program has been success-) 314.36 258.56 P
+(fully ported to the following platforms:) 314.36 247.56 T
+(SunOS 4.x) 342.71 234.56 T
+(DEC Alpha running OSF1) 342.71 221.56 T
+(DECstation 5000 running Ultrix) 342.71 208.56 T
+(HP 9000 series) 342.71 195.56 T
+-0.06 (If you are successful in porting to a new platform, or) 342.71 169.56 P
+0.15 (have problems installing, please let us know \050at the address) 314.36 158.56 P
+(above\051.) 314.36 147.56 T
+1 12 Q
+(2. Sequential Usage) 314.36 112.23 T
+0 10 Q
+(The encoder is invoked in the following manner:) 314.36 81.56 T
+1 F
+(mpeg_encode) 352.7 68.56 T
+0 F
+( <options> parameter_file) 410.44 68.56 T
+56.69 570.9 552.76 735.31 R
+7 X
+V
+1 18 Q
+0 X
+(Berkeley MPEG-1 V) 174.89 723.31 T
+(ideo Encoder) 333.11 723.31 T
+290.64 685.53 254.68 685.53 2 L
+V
+1.71 H
+0 Z
+N
+(User) 254.68 687.31 T
+296.95 685.53 290.96 685.53 2 L
+V
+N
+(\325) 290.96 687.31 T
+354.77 685.53 296.29 685.53 2 L
+V
+N
+(s Guide) 296.29 687.31 T
+3 12 Q
+(Plateau Resear) 245.54 657.31 T
+(ch Gr) 318.71 657.31 T
+(oup) 345.92 657.31 T
+(Computer Science Division) 239.1 643.31 T
+(University of California) 247.08 629.31 T
+(Berkeley) 239.59 615.31 T
+(, California 94720) 280.89 615.31 T
+(mpeg-bugs@cs.berkeley) 236.26 596.31 T
+(.edu) 352.87 596.31 T
+56.69 54.99 297.64 104.6 R
+7 X
+V
+56.69 94.59 297.64 104.6 C
+56.69 94.59 297.64 104.6 R
+7 X
+0 K
+V
+56.69 103.59 525.7 103.59 2 L
+V
+1 H
+2 Z
+0 X
+N
+0 0 612 792 C
+0 9 Q
+0 X
+0 K
+(Last Updated: 30 January 1995) 56.69 88.59 T
+FMENDPAGE
+%%EndPage: "1" 0
+%%Trailer
+%%BoundingBox: 0 0 612 792
+%%Pages: 11 -1
+%%DocumentFonts: Times-Roman
+%%+ Times-Bold
+%%+ Courier
+%%+ Times-Italic
diff --git a/converter/ppm/ppmtompeg/examples/basicspeed.param b/converter/ppm/ppmtompeg/examples/basicspeed.param
new file mode 100644
index 00000000..94b03de7
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/basicspeed.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/speed.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/decoded.test b/converter/ppm/ppmtompeg/examples/decoded.test
new file mode 100644
index 00000000..7496524c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/decoded.test
@@ -0,0 +1,81 @@
+# speed test parameter file
+
+PATTERN		ibpbpb
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	cat *.Y *.U *.V
+
+INPUT_DIR	/n/picasso/users/keving/links/flowg
+
+INPUT
+sflowg.*	[0-6]
+END_INPUT
+
+# the following two are optional  (default = 10, 60)
+
+# number of frames to do initially to gauge speed of machine
+PARALLEL_TEST_FRAMES	3
+
+# number of seconds per chunk thereafter
+PARALLEL_TIME_CHUNKS	30
+
+
+PARALLEL
+# lines must be of form "machine <whitespace> username <whitespace> executable"
+# these guys are sorta slow:
+#elmer-fudd	keving	~keving/encode/bin/sun/mpeg_encode
+#zonker		keving	~keving/encode/bin/sun/mpeg_encode
+#roger-rabbit	keving	~keving/encode/bin/sun/mpeg_encode
+#tweety		keving	~keving/encode/bin/sun/mpeg_encode
+#mickey		keving	~keving/encode/bin/mickey/mpeg_encode
+#
+# these guys are pretty fast:
+#
+#big-bird	keving	~keving/encode/bin/hp/mpeg_encode
+#gumby		keving	~keving/encode/bin/hp/mpeg_encode
+#charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#bugs-bunny	keving	~keving/encode/bin/sun/mpeg_encode
+#
+# remotes
+#
+#REMOTE anaconda	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE adder	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE moccasin	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE cobra	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE boa	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE asp	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE rattler	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE viper	keving	~keving/mpeg_encode ~keving/parallel.test
+# mamba doesn't seem to work for whatever reason...don't know why
+#REMOTE mamba	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE cse.lbl.gov kevin	~kevin/mpeg_encode ~kevin/parallel.test
+#REMOTE roger-rabbit keving	~keving/src/encode/bin/sun/mpeg_encode ~keving/src/encode/ParamExamples/parallel.test
+END_PARALLEL
+
+
+# motion vector search parameters
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		FULL
+
+# means +/- this many pixels
+RANGE		8
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+IQSCALE		1
+PQSCALE		1
+BQSCALE		25
+
+REFERENCE_FRAME		DECODED
+#REFERENCE_FRAME		ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/decoded2.test b/converter/ppm/ppmtompeg/examples/decoded2.test
new file mode 100644
index 00000000..b5e9a865
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/decoded2.test
@@ -0,0 +1,81 @@
+# speed test parameter file
+
+PATTERN		ibbbpbbbbbp
+OUTPUT		/tmp/ts.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	512x512
+
+INPUT_CONVERT	giftoppm *
+
+INPUT_DIR	../test/ts
+
+INPUT
+med*.gif [030-039]
+END_INPUT
+
+# the following two are optional  (default = 10, 60)
+
+# number of frames to do initially to gauge speed of machine
+PARALLEL_TEST_FRAMES	3
+
+# number of seconds per chunk thereafter
+PARALLEL_TIME_CHUNKS	30
+
+
+PARALLEL
+# lines must be of form "machine <whitespace> username <whitespace> executable"
+# these guys are sorta slow:
+#elmer-fudd	keving	~keving/encode/bin/sun/mpeg_encode
+#zonker		keving	~keving/encode/bin/sun/mpeg_encode
+#roger-rabbit	keving	~keving/encode/bin/sun/mpeg_encode
+#tweety		keving	~keving/encode/bin/sun/mpeg_encode
+#mickey		keving	~keving/encode/bin/mickey/mpeg_encode
+#
+# these guys are pretty fast:
+#
+#big-bird	keving	~keving/encode/bin/hp/mpeg_encode
+#gumby		keving	~keving/encode/bin/hp/mpeg_encode
+#charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#bugs-bunny	keving	~keving/encode/bin/sun/mpeg_encode
+#
+# remotes
+#
+#REMOTE anaconda	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE adder	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE moccasin	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE cobra	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE boa	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE asp	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE rattler	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE viper	keving	~keving/mpeg_encode ~keving/parallel.test
+# mamba doesn't seem to work for whatever reason...don't know why
+#REMOTE mamba	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE cse.lbl.gov kevin	~kevin/mpeg_encode ~kevin/parallel.test
+#REMOTE roger-rabbit keving	~keving/src/encode/bin/sun/mpeg_encode ~keving/src/encode/ParamExamples/parallel.test
+END_PARALLEL
+
+
+# motion vector search parameters
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		FULL
+
+# means +/- this many pixels
+RANGE		8
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+REFERENCE_FRAME		DECODED
+#REFERENCE_FRAME		ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/decorig.param b/converter/ppm/ppmtompeg/examples/decorig.param
new file mode 100644
index 00000000..3c570bcd
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/decorig.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBP
+
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-3]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/default.param b/converter/ppm/ppmtompeg/examples/default.param
new file mode 100644
index 00000000..17d8fe5d
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/default.param
@@ -0,0 +1,34 @@
+# parameter file with good default values
+#
+# use this as a guideline for any parameters you don't really understand
+# or don't care about
+#
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		output.mpg
+
+BASE_FILE_FORMAT	YUV
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+PIXEL		HALF
+RANGE		10
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+REFERENCE_FRAME	ORIGINAL
+
+#
+# you really need to understand the following
+#
+YUV_SIZE	352x240
+INPUT_CONVERT	*
+
+INPUT_DIR	input/flowg
+
+INPUT
+sflowg.*.yuv	[0-23]
+END_INPUT
diff --git a/converter/ppm/ppmtompeg/examples/fastspeed.param b/converter/ppm/ppmtompeg/examples/fastspeed.param
new file mode 100644
index 00000000..c45e0341
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/fastspeed.param
@@ -0,0 +1,46 @@
+# speed test parameter file
+
+PATTERN		ibbpbbpbbpbbpbb
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	cat *.Y *.U *.V
+
+INPUT_DIR	/n/picasso/project/mm/mpeg/mpeg_encode/input/flowg
+
+INPUT
+sflowg.*	[0-149]
+END_INPUT
+
+PARALLEL_TEST_FRAMES	3
+PARALLEL_TIME_CHUNKS	15
+
+PARALLEL
+charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+END_PARALLEL
+
+# motion vector search parameters
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		FULL
+
+# means +/- this many pixels
+RANGE		8
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+REFERENCE_FRAME	ORIGINAL
+
diff --git a/converter/ppm/ppmtompeg/examples/foobar.param b/converter/ppm/ppmtompeg/examples/foobar.param
new file mode 100644
index 00000000..1ba6f029
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/foobar.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard20.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[000-027]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		FULL
+
+RANGE		10
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/frame.test b/converter/ppm/ppmtompeg/examples/frame.test
new file mode 100644
index 00000000..fbefb8c4
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/frame.test
@@ -0,0 +1,37 @@
+# speed test parameter file
+
+PATTERN		IBPB
+OUTPUT		output/food
+GOP_SIZE	20
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	cat *.Y *.U *.V
+
+INPUT_DIR	links/flowg
+
+INPUT
+sflowg.*	[0-4]
+END_INPUT
+
+
+# motion vector search parameters
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		FULL
+
+# means +/- this many pixels
+RANGE		8
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+IQSCALE		10
+PQSCALE		31
+BQSCALE		31
+
diff --git a/converter/ppm/ppmtompeg/examples/gop.combine b/converter/ppm/ppmtompeg/examples/gop.combine
new file mode 100644
index 00000000..6b5c3e89
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/gop.combine
@@ -0,0 +1,11 @@
+# speed test parameter file
+
+OUTPUT		output/gop.mpg
+
+YUV_SIZE	352x240
+
+INPUT_DIR	.
+
+INPUT
+output/food.gop.*	[0-2]
+END_INPUT
diff --git a/converter/ppm/ppmtompeg/examples/gop.test b/converter/ppm/ppmtompeg/examples/gop.test
new file mode 100644
index 00000000..c2ae346b
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/gop.test
@@ -0,0 +1,43 @@
+# speed test parameter file
+
+PATTERN		IBPBP
+
+OUTPUT		output/food
+GOP_SIZE	5
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	cat *.Y *.U *.V
+
+INPUT_DIR	links/flowg
+
+INPUT
+sflowg.*	[0-14]
+END_INPUT
+
+
+GOP_INPUT_DIR	output
+GOP_INPUT
+food.gop.*	[1-2]
+GOP_END_INPUT
+
+# motion vector search parameters
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		FULL
+
+# means +/- this many pixels
+RANGE		8
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+IQSCALE		10
+PQSCALE		31
+BQSCALE		31
+
diff --git a/converter/ppm/ppmtompeg/examples/gop1.param b/converter/ppm/ppmtompeg/examples/gop1.param
new file mode 100644
index 00000000..c89a5b9a
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/gop1.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	6
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/hello.param b/converter/ppm/ppmtompeg/examples/hello.param
new file mode 100644
index 00000000..579b01ed
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/hello.param
@@ -0,0 +1,60 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/hello.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-49]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		FULL
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+#REFERENCE_FRAME	DECODED
+REFERENCE_FRAME	ORIGINAL
+
+PARALLEL_TEST_FRAMES	3
+
+# number of seconds per chunk thereafter
+PARALLEL_TIME_CHUNKS	30
+
+PARALLEL
+#big-bird	keving	~keving/encode/bin/hp/mpeg_encode
+#gumby		keving	~keving/encode/bin/hp/mpeg_encode
+charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#REMOTE cory keving ~keving/bin/mpeg_encode ~keving/hello.param
+REMOTE scorpius.EECS.Berkeley.EDU keving ~keving/bin/mpeg_encode ~keving/hello.param
+REMOTE monoceros.EECS.Berkeley.EDU keving ~keving/bin/mpeg_encode ~keving/hello.param
+REMOTE lepus.EECS.Berkeley.EDU keving ~keving/bin/mpeg_encode ~keving/hello.param
+REMOTE fornax.EECS.Berkeley.EDU keving ~keving/bin/mpeg_encode ~keving/hello.param
+REMOTE delphinus.EECS.Berkeley.EDU keving ~keving/bin/mpeg_encode ~keving/hello.param
+REMOTE cephus.EECS.Berkeley.EDU keving ~keving/bin/mpeg_encode ~keving/hello.param
+REMOTE carina.EECS.Berkeley.EDU keving ~keving/bin/mpeg_encode ~keving/hello.param
+REMOTE bootes.EECS.Berkeley.EDU keving ~keving/bin/mpeg_encode ~keving/hello.param
+REMOTE aquila.EECS.Berkeley.EDU keving ~keving/bin/mpeg_encode ~keving/hello.param
+#
+END_PARALLEL
+
+# FORCE_I_ALIGN
diff --git a/converter/ppm/ppmtompeg/examples/i.test b/converter/ppm/ppmtompeg/examples/i.test
new file mode 100644
index 00000000..af62afae
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/i.test
@@ -0,0 +1,37 @@
+# speed test parameter file
+
+PATTERN		I
+OUTPUT		output/food
+GOP_SIZE	20
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	cat *.Y *.U *.V
+
+INPUT_DIR	links/flowg
+
+INPUT
+sflowg.*	[0-5]
+END_INPUT
+
+
+# motion vector search parameters
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		FULL
+
+# means +/- this many pixels
+RANGE		8
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+IQSCALE		10
+PQSCALE		31
+BQSCALE		31
+
diff --git a/converter/ppm/ppmtompeg/examples/ispeed.jpeg.param b/converter/ppm/ppmtompeg/examples/ispeed.jpeg.param
new file mode 100644
index 00000000..ce3d4ed3
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/ispeed.jpeg.param
@@ -0,0 +1,48 @@
+# speed test parameter file
+
+PATTERN		I
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard1.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	djpeg *
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg/jpeg
+
+INPUT
+sflowg.*.jpeg	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		FULL
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
+
+
+PARALLEL_TEST_FRAMES	3
+PARALLEL_TIME_CHUNKS	30
+
+PARALLEL
+big-bird	keving	~keving/encode/bin/hp/mpeg_encode
+gumby		keving	~keving/encode/bin/hp/mpeg_encode
+charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+END_PARALLEL
+
+IO_SERVER_CONVERT       *
+SLAVE_CONVERT           djpeg *
diff --git a/converter/ppm/ppmtompeg/examples/ispeed.param b/converter/ppm/ppmtompeg/examples/ispeed.param
new file mode 100644
index 00000000..01381e85
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/ispeed.param
@@ -0,0 +1,45 @@
+# speed test parameter file
+
+PATTERN		I
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard1.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		FULL
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
+
+
+PARALLEL_TEST_FRAMES	3
+PARALLEL_TIME_CHUNKS	30
+
+PARALLEL
+big-bird	keving	~keving/encode/bin/hp/mpeg_encode
+gumby		keving	~keving/encode/bin/hp/mpeg_encode
+charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+END_PARALLEL
diff --git a/converter/ppm/ppmtompeg/examples/jpeg.test b/converter/ppm/ppmtompeg/examples/jpeg.test
new file mode 100644
index 00000000..75e3eb8f
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/jpeg.test
@@ -0,0 +1,66 @@
+# speed test parameter file
+
+PATTERN		ibbpbbpbbpbbpbb
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.jpeg.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	352x240
+
+INPUT_CONVERT	djpeg *
+
+INPUT_DIR	/n/picasso/project/mm/mpeg/mpeg_encode/input/flowg/jpeg
+
+INPUT
+sflowg.*.jpeg	[1-20]
+END_INPUT
+
+# the following two are optional  (default = 10, 60)
+
+# number of frames to do initially to gauge speed of machine
+PARALLEL_TEST_FRAMES	3
+
+# number of seconds per chunk thereafter
+PARALLEL_TIME_CHUNKS	30
+
+# specifies what the IO server must do before transmitting to remote sites
+IO_SERVER_CONVERT	*
+
+# specifies what the remote slave must do after receiving from IO server
+SLAVE_CONVERT		djpeg *
+
+PARALLEL
+# lines must be of form "machine <whitespace> username <whitespace> executable"
+# these guys are sorta slow:
+#elmer-fudd	keving	~keving/encode/bin/sun/mpeg_encode
+#roger-rabbit	keving	~keving/encode/bin/sun/mpeg_encode
+#tweety		keving	~keving/encode/bin/sun/mpeg_encode
+#mickey		keving	~keving/encode/bin/mickey/mpeg_encode
+#
+# these guys are pretty fast:
+#
+#big-bird	keving	~keving/encode/bin/hp/mpeg_encode
+#gumby		keving	~keving/encode/bin/hp/mpeg_encode
+charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#bugs-bunny	keving	~keving/encode/bin/sun/mpeg_encode
+#
+# remotes
+#
+END_PARALLEL
+
+
+# motion vector search parameters
+
+PIXEL		HALF
+RANGE		8
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+REFERENCE_FRAME	ORIGINAL
+
diff --git a/converter/ppm/ppmtompeg/examples/jpeg19.param b/converter/ppm/ppmtompeg/examples/jpeg19.param
new file mode 100644
index 00000000..15013895
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/jpeg19.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	JPEG
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg/jpeg
+
+INPUT
+sflowg.*.jpeg	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/jpegout19.param b/converter/ppm/ppmtompeg/examples/jpegout19.param
new file mode 100644
index 00000000..d31383d3
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/jpegout19.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	352x240
+
+INPUT_CONVERT	djpeg *
+
+INPUT_DIR	../input/flowg/jpeg
+
+INPUT
+sflowg.*.jpeg	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/localdisk.param b/converter/ppm/ppmtompeg/examples/localdisk.param
new file mode 100644
index 00000000..e7f49f33
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/localdisk.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		I
+OUTPUT		/usr/tmp/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/usr/tmp
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		FULL
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/localremote.test b/converter/ppm/ppmtompeg/examples/localremote.test
new file mode 100644
index 00000000..6c91721f
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/localremote.test
@@ -0,0 +1,80 @@
+# speed test parameter file
+
+#PATTERN		ibbpbbpbbpbbpbb
+PATTERN		iiiiiiii
+OUTPUT		/n/picasso/users/keving/src/encode/output/good
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	cat *.Y *.U *.V
+
+INPUT_DIR	/n/picasso/users/keving/links/flowg
+
+INPUT
+sflowg.*	[0-149]
+END_INPUT
+
+# the following two are optional  (default = 10, 60)
+
+# number of frames to do initially to gauge speed of machine
+PARALLEL_TEST_FRAMES	3
+
+# number of seconds per chunk thereafter
+PARALLEL_TIME_CHUNKS	30
+
+
+PARALLEL
+# lines must be of form "machine <whitespace> username <whitespace> executable"
+# these guys are sorta slow:
+#REMOTE charlie-brown	keving	~keving/src/encode/bin/dec/mpeg_encode ~keving/src/encode/ParamExamples/localremote.test
+#REMOTE woodstock	keving	~keving/src/encode/bin/dec/mpeg_encode ~keving/src/encode/ParamExamples/localremote.test
+#REMOTE gumby		keving	~keving/src/encode/bin/hp/mpeg_encode ~keving/src/encode/ParamExamples/localremote.test
+#REMOTE big-bird		keving	~keving/src/encode/bin/hp/mpeg_encode ~keving/src/encode/ParamExamples/localremote.test
+#REMOTE elmer-fudd	keving	~keving/src/encode/bin/sun/mpeg_encode ~keving/src/encode/ParamExamples/localremote.test
+#REMOTE mickey		keving	~keving/src/encode/bin/mickey/mpeg_encode ~keving/src/encode/ParamExamples/localremote.test
+#zonker		keving	~keving/src/encode/bin/sun/mpeg_encode
+#roger-rabbit	keving	~keving/src/encode/bin/sun/mpeg_encode
+#tweety		keving	~keving/src/encode/bin/sun/mpeg_encode
+#
+# these guys are pretty fast:
+#
+#bugs-bunny	keving	~keving/src/encode/bin/sun/mpeg_encode
+#
+# remotes
+#
+#REMOTE anaconda	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE adder	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE moccasin	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE cobra	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE boa	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE asp	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE rattler	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE viper	keving	~keving/mpeg_encode ~keving/parallel.test
+# mamba doesn't seem to work for whatever reason...don't know why
+#REMOTE mamba	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE cse.lbl.gov kevin	~kevin/mpeg_encode ~kevin/parallel.test
+#REMOTE roger-rabbit keving	~keving/src/encode/bin/sun/mpeg_encode ~keving/src/encode/ParamExamples/parallel.test
+END_PARALLEL
+
+
+# motion vector search parameters
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		FULL
+
+# means +/- this many pixels
+RANGE		8
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
diff --git a/converter/ppm/ppmtompeg/examples/luxo.param b/converter/ppm/ppmtompeg/examples/luxo.param
new file mode 100644
index 00000000..6c92dca1
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/luxo.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/luxo.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	320x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/luxo
+
+INPUT
+luxo*.yuv	[1201-1400]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		20
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/luxo18.param b/converter/ppm/ppmtompeg/examples/luxo18.param
new file mode 100644
index 00000000..4a020325
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/luxo18.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/luxo19.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	320x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/luxo
+
+INPUT
+luxo*.yuv	[1201-1400]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/luxo19.5.param b/converter/ppm/ppmtompeg/examples/luxo19.5.param
new file mode 100644
index 00000000..edacc7c7
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/luxo19.5.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/luxo19.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	320x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/luxo
+
+INPUT
+luxo*.yuv	[1201-1216]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/luxo19.param b/converter/ppm/ppmtompeg/examples/luxo19.param
new file mode 100644
index 00000000..7accb23a
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/luxo19.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/luxo19.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	320x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/luxo
+
+INPUT
+luxo*.yuv	[1201-1400]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/luxo2.param b/converter/ppm/ppmtompeg/examples/luxo2.param
new file mode 100644
index 00000000..f75b7e1c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/luxo2.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		I
+OUTPUT		/n/picasso/users/keving/encode/output/luxo2.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	320x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/luxo
+
+INPUT
+luxo1399.yuv
+luxo1400.yuv
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		20
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/luxo48.param b/converter/ppm/ppmtompeg/examples/luxo48.param
new file mode 100644
index 00000000..599f6d8c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/luxo48.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/luxo19.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	320x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/luxo
+
+INPUT
+luxo*.yuv	[1201-1400]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	SUBSAMPLE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/luxo49.param b/converter/ppm/ppmtompeg/examples/luxo49.param
new file mode 100644
index 00000000..5a493e40
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/luxo49.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/luxo19.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	320x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/luxo
+
+INPUT
+luxo*.yuv	[1201-1400]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/luxo50.param b/converter/ppm/ppmtompeg/examples/luxo50.param
new file mode 100644
index 00000000..f3afe8e8
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/luxo50.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/luxo19.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	320x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/luxo
+
+INPUT
+luxo*.yuv	[1201-1400]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/luxo52.5.param b/converter/ppm/ppmtompeg/examples/luxo52.5.param
new file mode 100644
index 00000000..f3f16854
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/luxo52.5.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/luxo19.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	320x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/luxo
+
+INPUT
+luxo*.yuv	[1201-1216]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/luxo52.param b/converter/ppm/ppmtompeg/examples/luxo52.param
new file mode 100644
index 00000000..8874ad97
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/luxo52.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/luxo19.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	320x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/luxo
+
+INPUT
+luxo*.yuv	[1201-1400]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/luxo53.param b/converter/ppm/ppmtompeg/examples/luxo53.param
new file mode 100644
index 00000000..d084b18b
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/luxo53.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/luxo19.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	320x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/luxo
+
+INPUT
+luxo*.yuv	[1201-1216]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	EXHAUSTIVE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/luxobug.param b/converter/ppm/ppmtompeg/examples/luxobug.param
new file mode 100644
index 00000000..73e9de9f
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/luxobug.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/luxobug.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	320x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/luxo-skip10
+
+INPUT
+luxo*.yuv		[1351-1701+5]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/luxoskip.param b/converter/ppm/ppmtompeg/examples/luxoskip.param
new file mode 100644
index 00000000..12768ed4
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/luxoskip.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/luxoskip.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	320x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/luxo-skip10
+
+INPUT
+luxo*1 		[1-307]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		20
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		20
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/med.param b/converter/ppm/ppmtompeg/examples/med.param
new file mode 100644
index 00000000..0e3bbd8d
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/med.param
@@ -0,0 +1,36 @@
+# 'med' parameter file
+
+PATTERN		IBBPBBPBBPBBPBBI
+OUTPUT		output/med.mpg
+GOP_SIZE	20
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+INPUT_CONVERT	giftoppm *
+
+INPUT_DIR	links/gcmovie
+
+INPUT
+med*.gif	[001-073]
+END_INPUT
+
+
+# motion vector search parameters
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		HALF
+
+# means +/- this many pixels
+RANGE		8
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	CROSS2
+
+
+IQSCALE		13
+PQSCALE		16
+BQSCALE		26
+
diff --git a/converter/ppm/ppmtompeg/examples/med18.param b/converter/ppm/ppmtompeg/examples/med18.param
new file mode 100644
index 00000000..b8ad664c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/med18.param
@@ -0,0 +1,34 @@
+# 'med' parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/med18.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+INPUT_CONVERT	giftoppm *
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/gcmovie
+
+INPUT
+med*.gif	[001-073]
+END_INPUT
+
+
+# motion vector search parameters
+
+# FULL or HALF -- must be upper case
+PIXEL		HALF
+
+# means +/- this many pixels
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/medspeed.param b/converter/ppm/ppmtompeg/examples/medspeed.param
new file mode 100644
index 00000000..d91f4dda
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/medspeed.param
@@ -0,0 +1,38 @@
+# speed test parameter file
+
+PATTERN		ibbpbbpbbpbbpbb
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	cat *.Y *.U *.V
+
+INPUT_DIR	/n/picasso/project/mm/mpeg/mpeg_encode/input/flowg
+
+INPUT
+sflowg.*	[0-149]
+END_INPUT
+
+# motion vector search parameters
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		FULL
+
+# means +/- this many pixels
+RANGE		10
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	CROSS2
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+REFERENCE_FRAME	ORIGINAL
+
diff --git a/converter/ppm/ppmtompeg/examples/nametest.param b/converter/ppm/ppmtompeg/examples/nametest.param
new file mode 100644
index 00000000..445622fb
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/nametest.param
@@ -0,0 +1,60 @@
+# test suite parameter file
+
+PATTERN		IBBBPBBBBP
+OUTPUT		/tmp/foobar.mpg
+
+YUV_SIZE	352x240
+
+BASE_FILE_FORMAT	YUV
+INPUT_CONVERT	cat *.Y *.U *.V
+GOP_SIZE	30
+SLICES_PER_FRAME  1
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/tennis
+
+INPUT
+stennis.0
+stennis.*	[1-3]
+stennis.*	[4-5]
+stennis.*	[6-6]
+stennis.7
+stennis.*	[8-15]
+stennis.16
+stennis.17
+stennis.*	[18-34]
+END_INPUT
+
+
+
+# all of the remaining options have to do with the motion search and qscale
+#
+# change this only if you're unsatisfied with the CPU time or quality, or
+# are experimenting
+#
+
+# if this appears in the file, then in addition to testing luminance when
+# computing motion vectors, program will also take into account chrominance
+#
+# this option MUST appear before ERROR option, or it will be ignored
+#
+# CHROMINANCE
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		FULL
+
+# means +/- this many pixels
+RANGE		8
+
+PSEARCH_ALG	LOGARITHMIC
+
+BSEARCH_ALG	SIMPLE
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/nosearch.param b/converter/ppm/ppmtompeg/examples/nosearch.param
new file mode 100644
index 00000000..453356c0
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/nosearch.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		I
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard1.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		FULL
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/par3.param b/converter/ppm/ppmtompeg/examples/par3.param
new file mode 100644
index 00000000..ca664334
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/par3.param
@@ -0,0 +1,43 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-39]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
+
+PARALLEL_TEST_FRAMES	3
+PARALLEL_TIME_CHUNKS	30
+
+PARALLEL
+bugs-bunny	keving	~keving/encode/bin/sun/mpeg_encode
+linus		keving	~keving/encode/bin/sun/mpeg_encode
+END_PARALLEL
diff --git a/converter/ppm/ppmtompeg/examples/par4.param b/converter/ppm/ppmtompeg/examples/par4.param
new file mode 100644
index 00000000..95a72c34
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/par4.param
@@ -0,0 +1,45 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-49]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
+
+PARALLEL_TEST_FRAMES	3
+# PARALLEL_TIME_CHUNKS	20
+PARALLEL_CHUNK_TAPER
+
+PARALLEL
+bugs-bunny	keving	~keving/encode/bin/sun/mpeg_encode
+linus		keving	~keving/encode/bin/sun/mpeg_encode
+zonker		keving	~keving/encode/bin/sun/mpeg_encode
+END_PARALLEL
diff --git a/converter/ppm/ppmtompeg/examples/par5.param b/converter/ppm/ppmtompeg/examples/par5.param
new file mode 100644
index 00000000..ad51a519
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/par5.param
@@ -0,0 +1,47 @@
+# speed test parameter file
+
+#PATTERN		IBBPBBPBBPBBPBB
+PATTERN		I
+
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
+
+PARALLEL_TEST_FRAMES	3
+PARALLEL_CHUNK_TAPER
+
+PARALLEL
+woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+gumby		keving	~keving/encode/bin/hp/mpeg_encode
+big-bird	keving	~keving/encode/bin/hp/mpeg_encode
+roger-rabbit	keving	~keving/encode/bin/sun/mpeg_encode
+END_PARALLEL
diff --git a/converter/ppm/ppmtompeg/examples/parallel.2 b/converter/ppm/ppmtompeg/examples/parallel.2
new file mode 100644
index 00000000..0ab153fb
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/parallel.2
@@ -0,0 +1,87 @@
+# speed test parameter file
+
+PATTERN		ibbpbbpbbpbbpbb
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	352x240
+
+INPUT_CONVERT	djpeg *
+
+INPUT_DIR	/n/picasso/project/mm/mpeg/mpeg_encode/input/flowg/jpeg
+
+INPUT
+sflowg.*.jpeg	[1-30]
+END_INPUT
+
+# the following are optional
+
+PARALLEL_PERFECT
+
+
+
+# specifies what the IO server must do before transmitting to remote sites
+IO_SERVER_CONVERT	*
+
+# specifies what the remote slave must do after receiving from IO server
+SLAVE_CONVERT		djpeg *
+
+PARALLEL
+# lines must be of form "machine <whitespace> username <whitespace> executable"
+# these guys are sorta slow:
+#elmer-fudd	keving	~keving/encode/bin/sun/mpeg_encode
+#elmer-fudd	keving	~keving/encode/bin/sun/mpeg_encode
+#bugs-bunny	keving	~keving/encode/bin/sun/mpeg_encode
+#REMOTE bugs-bunny keving ~keving/encode/src/mpeg_encode ~keving/encode/examples/parallel.test
+#roger-rabbit	keving	~keving/encode/bin/sun/mpeg_encode
+#tweety		keving	~keving/encode/bin/sun/mpeg_encode
+#mickey		keving	~keving/encode/bin/mickey/mpeg_encode
+#
+# these guys are pretty fast:
+#
+#big-bird	keving	~keving/encode/bin/hp/mpeg_encode
+#gumby		keving	~keving/encode/bin/hp/mpeg_encode
+charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#bugs-bunny	keving	~keving/encode/bin/sun/mpeg_encode
+#
+# remotes
+#
+#REMOTE anaconda	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE adder	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE moccasin	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE cobra	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE boa	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE asp	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE rattler	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE viper	keving	~keving/mpeg_encode ~keving/parallel.test
+# mamba doesn't seem to work for whatever reason...don't know why
+#REMOTE mamba	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE cse.lbl.gov kevin	~kevin/mpeg_encode ~kevin/parallel.test
+#REMOTE roger-rabbit keving	~keving/src/encode/bin/sun/mpeg_encode ~keving/src/encode/ParamExamples/parallel.test
+END_PARALLEL
+
+
+# motion vector search parameters
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		FULL
+
+# means +/- this many pixels
+RANGE		8
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+
+REFERENCE_FRAME	ORIGINAL
+
diff --git a/converter/ppm/ppmtompeg/examples/parallel.param b/converter/ppm/ppmtompeg/examples/parallel.param
new file mode 100644
index 00000000..a158eae5
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/parallel.param
@@ -0,0 +1,141 @@
+# parameter file template with parallel execution
+#
+# you can use this as a template, copying it to a separate file then modifying
+# the copy
+#
+#
+# any line beginning with '#' is a comment
+#
+# no line should be longer than 255 characters
+#
+#
+# general format of each line is:
+#	<option> <spaces and/or tabs> <value>
+#
+# lines can generally be in any order
+#
+# only exception is the option 'INPUT' which must be followed by input
+# files in the order in which they must appear, followed by 'END_INPUT'
+#
+# <option> MUST be in UPPER CASE
+#
+
+PATTERN		IBBPBBPBBPBBP
+OUTPUT		/n/picasso/users/keving/encode/output.mpg
+
+# mpeg_encode really only accepts 3 different file formats, but using a
+# conversion statement it can effectively handle ANY file format
+#
+# you must specify whether you will convert to PNM or PPM or YUV format
+#	(must be upper case)
+#
+BASE_FILE_FORMAT	YUV
+
+#
+# if YUV format (or using parallel version), must provide width and height
+# YUV_SIZE	widthxheight
+# this option is ignored if BASE_FILE_FORMAT is PPM or PNM and you're running
+# on just one machine
+#
+YUV_SIZE	352x240
+
+# the conversion statement
+#
+# Each occurrence of '*' will be replaced by the input file
+#
+# e.g., if you have a bunch of GIF files, then this might be:
+#	INPUT_CONVERT	giftoppm *
+#
+# e.g., if you have a bunch of files like a.Y a.U a.V, etc., then:
+#	INPUT_CONVERT	cat *.Y *.U *.V
+#
+# e.g., if you are grabbing from laser disc you might have something like
+#	INPUT_CONVERT	goto frame *; grabppm
+# 'INPUT_CONVERT *' means the files are already in the base file format
+#
+INPUT_CONVERT	*
+
+# number of frames in a GOP.
+#
+# since each GOP must have at least one I-frame, the encoder will find the
+# the first I-frame after GOP_SIZE frames to start the next GOP
+#
+# later, will add more flexible GOP signalling
+#
+GOP_SIZE	6
+
+# number of slices in a frame
+#
+# 1 is a good number.  another possibility is the number of macroblock rows
+# (which is the height divided by 16)
+#
+SLICES_PER_FRAME	1
+
+# directory to get all input files from (makes this file easier to read)
+INPUT_DIR	/n/picasso/users/keving/encode/input/tennis
+
+INPUT
+# '*' is replaced by the numbers 01, 02, 03, 04
+# if I instead do [01-11], it would be 01, 02, ..., 09, 10, 11
+# if I instead do [1-11], it would be 1, 2, 3, ..., 9, 10, 11
+# if I instead do [1-11+3], it would be 1, 4, 7, 10
+# the program assumes none of your input files has a name ending in ']'
+# if you do, too bad!!!
+#
+#
+stennis.*.yuv	[0-7]
+# can have more files here if you want...there is no limit on the number
+# of files
+END_INPUT
+
+
+
+# all of the remaining options have to do with the motion search and qscale
+
+# FULL or HALF -- must be upper case
+PIXEL		HALF
+
+# means +/- this many pixels
+RANGE		10
+
+# this must be one of {EXHAUSTIVE, SUBSAMPLE, LOGARITHMIC}
+PSEARCH_ALG	LOGARITHMIC
+
+# this must be one of {SIMPLE, CROSS2, EXHAUSTIVE}
+#
+# note that EXHAUSTIVE is really, really, really slow
+#
+BSEARCH_ALG	CROSS2
+
+#
+# these specify the q-scale for I, P, and B frames
+# (values must be between 1 and 31)
+#
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# this must be ORIGINAL or DECODED
+REFERENCE_FRAME	ORIGINAL
+
+
+# the following two are optional  (default = 10, 60)
+
+# number of frames to do initially to gauge speed of machine
+PARALLEL_TEST_FRAMES	3
+
+# number of seconds per chunk thereafter
+PARALLEL_TIME_CHUNKS	30
+
+
+PARALLEL
+# lines must be of form "machine <whitespace> username <whitespace> executable"
+charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#REMOTE charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode ~keving/encode/examples/parallel.param
+# remote machine: "REMOTE machine username executable param_file"
+# mickey		keving	~keving/encode/bin/dec-5000/mpeg_encode
+#REMOTE mickey		keving	~keving/encode/bin/dec-5000/mpeg_encode ~keving/encode/examples/parallel.param
+#REMOTE mickey		keving	~keving/encode/bin/dec-5000/mpeg_encode ~keving/encode/examples/parallel.param
+REMOTE woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode ~keving/encode/examples/parallel.param
+END_PARALLEL
diff --git a/converter/ppm/ppmtompeg/examples/parallel.test b/converter/ppm/ppmtompeg/examples/parallel.test
new file mode 100644
index 00000000..2489d847
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/parallel.test
@@ -0,0 +1,85 @@
+# speed test parameter file
+
+PATTERN		ibbpbbpbbpbbpbb
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	352x240
+
+INPUT_CONVERT	djpeg *
+
+INPUT_DIR	/n/picasso/project/mm/mpeg/mpeg_encode/input/flowg/jpeg
+
+INPUT
+sflowg.*.jpeg	[1-30]
+END_INPUT
+
+# the following two are optional  (default = 10, 60)
+
+# number of frames to do initially to gauge speed of machine
+PARALLEL_TEST_FRAMES	3
+
+# number of seconds per chunk thereafter
+PARALLEL_TIME_CHUNKS	30
+
+# specifies what the IO server must do before transmitting to remote sites
+IO_SERVER_CONVERT	*
+
+# specifies what the remote slave must do after receiving from IO server
+SLAVE_CONVERT		djpeg *
+
+PARALLEL
+# lines must be of form "machine <whitespace> username <whitespace> executable"
+# these guys are sorta slow:
+#REMOTE bugs-bunny keving ~keving/encode/src/mpeg_encode ~keving/encode/examples/parallel.test
+#mickey		keving	~keving/encode/bin/mickey/mpeg_encode
+#
+# these guys are pretty fast:
+#
+#big-bird	keving	~keving/encode/bin/hp/mpeg_encode
+#gumby		keving	~keving/encode/bin/hp/mpeg_encode
+#charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+bugs-bunny	keving	~keving/encode/bin/sun/mpeg_encode
+#roger-rabbit	keving	~keving/encode/bin/sun/mpeg_encode
+#
+# remotes
+#
+#REMOTE anaconda	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE adder	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE moccasin	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE cobra	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE boa	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE asp	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE rattler	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE viper	keving	~keving/mpeg_encode ~keving/parallel.test
+# mamba doesn't seem to work for whatever reason...don't know why
+#REMOTE mamba	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE cse.lbl.gov kevin	~kevin/mpeg_encode ~kevin/parallel.test
+#REMOTE roger-rabbit keving	~keving/src/encode/bin/sun/mpeg_encode ~keving/src/encode/ParamExamples/parallel.test
+END_PARALLEL
+
+
+# motion vector search parameters
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		FULL
+
+# means +/- this many pixels
+RANGE		8
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+
+REFERENCE_FRAME	ORIGINAL
+
diff --git a/converter/ppm/ppmtompeg/examples/param.template b/converter/ppm/ppmtompeg/examples/param.template
new file mode 100644
index 00000000..ce81e474
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/param.template
@@ -0,0 +1,107 @@
+# parameter file template
+#
+# any line beginning with '#' is a comment
+#
+# no line should be longer than 255 characters
+#
+#
+# general format of each line is:
+#	<option> <spaces and/or tabs> <value>
+#
+# lines can generally be in any order
+#
+# only exception is the option 'INPUT' which must be followed by input
+# files in the order in which they must appear, followed by 'END_INPUT'
+#
+# <option> MUST be in UPPER CASE
+#
+
+PATTERN		IBBPBBPBBPBBP
+OUTPUT		output/food
+
+# mpeg_encode really only accepts 2 different file formats, but using a
+# conversion statement it can effectively handle ANY file format
+#
+# you must specify whether you will convert to PPM or YUV format
+#	(must be upper case)
+#
+BASE_FILE_FORMAT	YUV
+
+#
+# if YUV format, must provide width and height
+# YUV_SIZE	width height
+# this option is ignored if BASE_FILE_FORMAT is PPM
+#
+YUV_SIZE	352x240
+
+# the conversion statement
+#
+# Each occurrence of '*' will be replaced by the input file
+#
+# e.g., if you have a bunch of GIF files, then this might be:
+#	INPUT_CONVERT	giftoppm *
+#
+# e.g., if you have a bunch of files like a.Y a.U a.V, etc., then:
+#	INPUT_CONVERT	cat *.Y *.U *.V
+#
+# e.g., if you are grabbing from laser disc you might have something like
+#	INPUT_CONVERT	goto frame *; grabppm
+# 'INPUT_CONVERT *' means the files are already in the base file format
+#
+INPUT_CONVERT	cat *.Y *.U *.V
+
+# number of frames in a GOP.
+#
+# since each GOP must have at least one I-frame, the encoder will find the
+# the first I-frame after GOP_SIZE frames to start the next GOP
+#
+# later, will add more flexible GOP signalling
+#
+GOP_SIZE	30
+
+# directory to get all input files from (makes this file easier to read)
+INPUT_DIR	links/payam
+
+INPUT
+# '*' is replaced by the numbers 01, 02, 03, 04
+# if I instead do [01-11], it would be 01, 02, ..., 09, 10, 11
+# if I instead do [1-11], it would be 1, 2, 3, ..., 9, 10, 11
+# the program assumes none of your input files has a name ending in ']'
+# if you do, too bad!!!
+#
+#
+stennis.*	[0-23]
+# can have more files here if you want...as many as you want
+END_INPUT
+
+
+
+# all of the remaining options have to do with the motion search and qscale
+#
+# change this only if you're unsatisfied with the CPU time or quality, or
+# are experimenting
+#
+
+# if this appears in the file, then in addition to testing luminance when
+# computing motion vectors, program will also take into account chrominance
+#
+# this option MUST appear before ERROR option, or it will be ignored
+#
+# CHROMINANCE
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		FULL
+
+# means +/- this many pixels
+RANGE		8
+
+# YES or NO -- must be upper case
+SUBSAMPLE	NO
+
+IQSCALE		10
+PQSCALE		15
+BQSCALE		25
+
diff --git a/converter/ppm/ppmtompeg/examples/payam.param b/converter/ppm/ppmtompeg/examples/payam.param
new file mode 100644
index 00000000..ce886914
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/payam.param
@@ -0,0 +1,37 @@
+# parameter file for payam's images
+
+#PATTERN		IBBPBBPBBPBBPBBI
+PATTERN		II
+OUTPUT		output/payam.mpg
+GOP_SIZE	20
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PNM
+
+INPUT_CONVERT *
+INPUT_DIR	links/payam
+
+INPUT
+kh*.pnm	[1-3]
+END_INPUT
+
+
+# motion vector search paramters
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		FULL
+
+# means +/- this many pixels
+RANGE		8
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+
+IQSCALE		10
+PQSCALE		15
+BQSCALE		25
+
diff --git a/converter/ppm/ppmtompeg/examples/payam18.param b/converter/ppm/ppmtompeg/examples/payam18.param
new file mode 100644
index 00000000..7f7b767e
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/payam18.param
@@ -0,0 +1,34 @@
+# parameter file for payam's images
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/payam18.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PNM
+
+INPUT_CONVERT *
+INPUT_DIR	/n/picasso/users/keving/encode/input/payam
+
+INPUT
+kh*.pnm	[1-39]
+END_INPUT
+
+
+# motion vector search paramters
+
+# FULL or HALF -- must be upper case
+PIXEL		HALF
+
+# means +/- this many pixels
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/pnm.param b/converter/ppm/ppmtompeg/examples/pnm.param
new file mode 100644
index 00000000..6fce2be4
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/pnm.param
@@ -0,0 +1,37 @@
+# speed test parameter file
+
+PATTERN		I
+OUTPUT		output/food
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	links/flowg
+
+INPUT
+sflowg.*.ppm	[01-10]
+END_INPUT
+
+
+# motion vector search parameters
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		FULL
+
+# means +/- this many pixels
+RANGE		8
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+IQSCALE		31
+PQSCALE		10
+BQSCALE		25
+
diff --git a/converter/ppm/ppmtompeg/examples/ppm.param b/converter/ppm/ppmtompeg/examples/ppm.param
new file mode 100644
index 00000000..6fce2be4
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/ppm.param
@@ -0,0 +1,37 @@
+# speed test parameter file
+
+PATTERN		I
+OUTPUT		output/food
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	links/flowg
+
+INPUT
+sflowg.*.ppm	[01-10]
+END_INPUT
+
+
+# motion vector search parameters
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		FULL
+
+# means +/- this many pixels
+RANGE		8
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+IQSCALE		31
+PQSCALE		10
+BQSCALE		25
+
diff --git a/converter/ppm/ppmtompeg/examples/prof.b.param b/converter/ppm/ppmtompeg/examples/prof.b.param
new file mode 100644
index 00000000..2b8253fd
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/prof.b.param
@@ -0,0 +1,48 @@
+# test suite parameter file
+
+PATTERN		IBBBBBBBBI
+OUTPUT		output/food
+
+BASE_FILE_FORMAT	PPM
+INPUT_CONVERT	giftoppm *
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+INPUT_DIR	Test/ts
+
+INPUT
+med*.gif	[030-039]
+END_INPUT
+
+
+
+# all of the remaining options have to do with the motion search and qscale
+#
+# change this only if you're unsatisfied with the CPU time or quality, or
+# are experimenting
+#
+
+# if this appears in the file, then in addition to testing luminance when
+# computing motion vectors, program will also take into account chrominance
+#
+# this option MUST appear before ERROR option, or it will be ignored
+#
+# CHROMINANCE
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		FULL
+
+# means +/- this many pixels
+RANGE		8
+
+# YES or NO -- must be upper case
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
diff --git a/converter/ppm/ppmtompeg/examples/prof.bhalf.param b/converter/ppm/ppmtompeg/examples/prof.bhalf.param
new file mode 100644
index 00000000..8d6de83a
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/prof.bhalf.param
@@ -0,0 +1,48 @@
+# test suite parameter file
+
+PATTERN		IBBBBBBBBI
+OUTPUT		output/food
+
+BASE_FILE_FORMAT	PPM
+INPUT_CONVERT	giftoppm *
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+INPUT_DIR	Test/ts
+
+INPUT
+med*.gif	[030-039]
+END_INPUT
+
+
+
+# all of the remaining options have to do with the motion search and qscale
+#
+# change this only if you're unsatisfied with the CPU time or quality, or
+# are experimenting
+#
+
+# if this appears in the file, then in addition to testing luminance when
+# computing motion vectors, program will also take into account chrominance
+#
+# this option MUST appear before ERROR option, or it will be ignored
+#
+# CHROMINANCE
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		HALF
+
+# means +/- this many pixels
+RANGE		8
+
+# YES or NO -- must be upper case
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	CROSS2
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
diff --git a/converter/ppm/ppmtompeg/examples/prof.param b/converter/ppm/ppmtompeg/examples/prof.param
new file mode 100644
index 00000000..591eaa67
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/prof.param
@@ -0,0 +1,36 @@
+# speed test parameter file
+
+PATTERN I
+#PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	CROSS2
+
+#REFERENCE_FRAME	DECODED
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/prof.ss.param b/converter/ppm/ppmtompeg/examples/prof.ss.param
new file mode 100644
index 00000000..ef339e7a
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/prof.ss.param
@@ -0,0 +1,48 @@
+# test suite parameter file
+
+PATTERN		IPPPPPPPPP
+OUTPUT		output/food
+
+BASE_FILE_FORMAT	PPM
+INPUT_CONVERT	giftoppm *
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+INPUT_DIR	Test/ts
+
+INPUT
+med*.gif	[030-039]
+END_INPUT
+
+
+
+# all of the remaining options have to do with the motion search and qscale
+#
+# change this only if you're unsatisfied with the CPU time or quality, or
+# are experimenting
+#
+
+# if this appears in the file, then in addition to testing luminance when
+# computing motion vectors, program will also take into account chrominance
+#
+# this option MUST appear before ERROR option, or it will be ignored
+#
+# CHROMINANCE
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		FULL
+
+# means +/- this many pixels
+RANGE		16
+
+# YES or NO -- must be upper case
+PSEARCH_ALG	SUBSAMPLE
+BSEARCH_ALG	SIMPLE
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
diff --git a/converter/ppm/ppmtompeg/examples/prof2.param b/converter/ppm/ppmtompeg/examples/prof2.param
new file mode 100644
index 00000000..4073606a
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/prof2.param
@@ -0,0 +1,36 @@
+# speed test parameter file
+
+PATTERN IPPP
+#PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
+
diff --git a/converter/ppm/ppmtompeg/examples/prof3.param b/converter/ppm/ppmtompeg/examples/prof3.param
new file mode 100644
index 00000000..4581eade
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/prof3.param
@@ -0,0 +1,36 @@
+# speed test parameter file
+
+PATTERN IBB
+#PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
+
diff --git a/converter/ppm/ppmtompeg/examples/prof4.param b/converter/ppm/ppmtompeg/examples/prof4.param
new file mode 100644
index 00000000..583fb43c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/prof4.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
+
diff --git a/converter/ppm/ppmtompeg/examples/remote.test b/converter/ppm/ppmtompeg/examples/remote.test
new file mode 100644
index 00000000..d548510f
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/remote.test
@@ -0,0 +1,85 @@
+# speed test parameter file
+
+PATTERN		ibbpbbpbbpbbpbb
+OUTPUT		/n/picasso/users/keving/encode/output/good.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	cat *.Y *.U *.V
+
+INPUT_DIR	/n/picasso/users/keving/links/flowg
+
+INPUT
+sflowg.*	[0-40]
+END_INPUT
+
+# the following two are optional  (default = 10, 60)
+
+# number of frames to do initially to gauge speed of machine
+PARALLEL_TEST_FRAMES	3
+
+# number of seconds per chunk thereafter
+PARALLEL_TIME_CHUNKS	30
+
+
+PARALLEL
+# lines must be of form "machine <whitespace> username <whitespace> executable"
+# these guys are sorta slow:
+#elmer-fudd	keving	~keving/src/encode/bin/sun/mpeg_encode
+#zonker		keving	~keving/src/encode/bin/sun/mpeg_encode
+#roger-rabbit	keving	~keving/src/encode/bin/sun/mpeg_encode
+#tweety		keving	~keving/src/encode/bin/sun/mpeg_encode
+#mickey		keving	~keving/src/encode/bin/mickey/mpeg_encode
+#
+REMOTE zonker keving ~keving/encode/src/mpeg_encode ~keving/encode/examples/remote.test
+REMOTE linus keving ~keving/encode/src/mpeg_encode ~keving/encode/examples/remote.test
+REMOTE roger-rabbit keving ~keving/encode/src/mpeg_encode ~keving/encode/examples/remote.test
+REMOTE bugs-bunny keving ~keving/encode/src/mpeg_encode ~keving/encode/examples/remote.test
+REMOTE elmer-fudd keving ~keving/encode/src/mpeg_encode ~keving/encode/examples/remote.test
+# these guys are pretty fast:
+#
+#big-bird	keving	~keving/src/encode/bin/hp/mpeg_encode
+#gumby		keving	~keving/src/encode/bin/hp/mpeg_encode
+#charlie-brown	keving	~keving/src/encode/bin/dec/mpeg_encode
+#woodstock	keving	~keving/src/encode/bin/dec/mpeg_encode
+#bugs-bunny	keving	~keving/src/encode/bin/sun/mpeg_encode
+#
+# remotes
+#
+#REMOTE anaconda	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE adder	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE moccasin	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE cobra	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE boa	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE asp	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE rattler	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE viper	keving	~keving/mpeg_encode ~keving/parallel.test
+# mamba doesn't seem to work for whatever reason...don't know why
+#REMOTE mamba	keving	~keving/mpeg_encode ~keving/parallel.test
+#REMOTE cse.lbl.gov kevin	~kevin/mpeg_encode ~kevin/parallel.test
+#REMOTE roger-rabbit keving	~keving/src/encode/bin/sun/mpeg_encode ~keving/src/encode/ParamExamples/parallel.test
+END_PARALLEL
+
+
+# motion vector search parameters
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		FULL
+
+# means +/- this many pixels
+RANGE		4
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search1.param b/converter/ppm/ppmtompeg/examples/search1.param
new file mode 100644
index 00000000..779ffa0f
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search1.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard1.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		FULL
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search10.param b/converter/ppm/ppmtompeg/examples/search10.param
new file mode 100644
index 00000000..c44c7c72
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search10.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard10.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search11.param b/converter/ppm/ppmtompeg/examples/search11.param
new file mode 100644
index 00000000..78935268
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search11.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard11.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		FULL
+
+RANGE		20
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/search12.param b/converter/ppm/ppmtompeg/examples/search12.param
new file mode 100644
index 00000000..d7a115c3
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search12.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard12.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search13.param b/converter/ppm/ppmtompeg/examples/search13.param
new file mode 100644
index 00000000..80bb57f5
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search13.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard13.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		20
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search14.param b/converter/ppm/ppmtompeg/examples/search14.param
new file mode 100644
index 00000000..af0ebb9c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search14.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/search15.param b/converter/ppm/ppmtompeg/examples/search15.param
new file mode 100644
index 00000000..1df18350
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search15.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard15.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		FULL
+
+RANGE		20
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/search16.param b/converter/ppm/ppmtompeg/examples/search16.param
new file mode 100644
index 00000000..3523dce3
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search16.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard16.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		20
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search17.param b/converter/ppm/ppmtompeg/examples/search17.param
new file mode 100644
index 00000000..89c3cb91
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search17.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard17.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		20
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/search18.param b/converter/ppm/ppmtompeg/examples/search18.param
new file mode 100644
index 00000000..d659d914
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search18.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard18.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search19.5.param b/converter/ppm/ppmtompeg/examples/search19.5.param
new file mode 100644
index 00000000..22827fce
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search19.5.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-15]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/search19.param b/converter/ppm/ppmtompeg/examples/search19.param
new file mode 100644
index 00000000..88f9d613
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search19.param
@@ -0,0 +1,42 @@
+# speed test parameter file
+
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/charlie-brown/project/mm/mpeg/mpeg_encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+
+
+
+PATTERN		IBBPBBPBBPBBPBB
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+# LOGARITHMIC, TWOLEVEL, SUBSAMPLE, EXHAUSTIVE
+PSEARCH_ALG	LOGARITHMIC
+
+# CROSS2, SIMPLE
+BSEARCH_ALG	CROSS2
+
+# DECODED or ORIGINAL
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/search2.param b/converter/ppm/ppmtompeg/examples/search2.param
new file mode 100644
index 00000000..c4f6300c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search2.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard2.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		FULL
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/search20.param b/converter/ppm/ppmtompeg/examples/search20.param
new file mode 100644
index 00000000..efd05f6d
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search20.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard20.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		FULL
+
+RANGE		10
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search21.param b/converter/ppm/ppmtompeg/examples/search21.param
new file mode 100644
index 00000000..6acb79ac
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search21.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard21.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search22.param b/converter/ppm/ppmtompeg/examples/search22.param
new file mode 100644
index 00000000..252cb25b
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search22.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard22.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		FULL
+
+RANGE		20
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search23.param b/converter/ppm/ppmtompeg/examples/search23.param
new file mode 100644
index 00000000..5e7a7620
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search23.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard23.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		20
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search24.param b/converter/ppm/ppmtompeg/examples/search24.param
new file mode 100644
index 00000000..0ca71e6d
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search24.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard24.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	SUBSAMPLE
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search25.param b/converter/ppm/ppmtompeg/examples/search25.param
new file mode 100644
index 00000000..724ce5a4
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search25.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard25.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	SUBSAMPLE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search26.param b/converter/ppm/ppmtompeg/examples/search26.param
new file mode 100644
index 00000000..1a7081cd
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search26.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard26.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		FULL
+
+RANGE		10
+
+PSEARCH_ALG	SUBSAMPLE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search27.param b/converter/ppm/ppmtompeg/examples/search27.param
new file mode 100644
index 00000000..49da7e7a
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search27.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard27.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		FULL
+
+RANGE		20
+
+PSEARCH_ALG	SUBSAMPLE
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search28.param b/converter/ppm/ppmtompeg/examples/search28.param
new file mode 100644
index 00000000..f78331aa
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search28.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard28.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		20
+
+PSEARCH_ALG	SUBSAMPLE
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search29.param b/converter/ppm/ppmtompeg/examples/search29.param
new file mode 100644
index 00000000..52f89a90
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search29.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard29.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search3.param b/converter/ppm/ppmtompeg/examples/search3.param
new file mode 100644
index 00000000..4c00af6c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search3.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard3.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		FULL
+
+RANGE		20
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search30.param b/converter/ppm/ppmtompeg/examples/search30.param
new file mode 100644
index 00000000..5664dbb8
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search30.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard30.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		20
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search31.param b/converter/ppm/ppmtompeg/examples/search31.param
new file mode 100644
index 00000000..3d8bc0cd
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search31.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search32.param b/converter/ppm/ppmtompeg/examples/search32.param
new file mode 100644
index 00000000..a4675cee
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search32.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard32.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		4
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search33.param b/converter/ppm/ppmtompeg/examples/search33.param
new file mode 100644
index 00000000..465132c6
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search33.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard33.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		4
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search34.param b/converter/ppm/ppmtompeg/examples/search34.param
new file mode 100644
index 00000000..f53a91f3
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search34.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard34.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		4
+
+PSEARCH_ALG	SUBSAMPLE
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search35.param b/converter/ppm/ppmtompeg/examples/search35.param
new file mode 100644
index 00000000..5a7fa27f
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search35.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard35.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		4
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search36.param b/converter/ppm/ppmtompeg/examples/search36.param
new file mode 100644
index 00000000..66fb8e7f
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search36.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard36.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		4
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search37.param b/converter/ppm/ppmtompeg/examples/search37.param
new file mode 100644
index 00000000..f5f5d4d3
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search37.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard37.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		4
+
+PSEARCH_ALG	SUBSAMPLE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search38.param b/converter/ppm/ppmtompeg/examples/search38.param
new file mode 100644
index 00000000..b8186ce2
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search38.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard38.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		4
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search39.param b/converter/ppm/ppmtompeg/examples/search39.param
new file mode 100644
index 00000000..ab18d28b
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search39.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard39.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		4
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search4.param b/converter/ppm/ppmtompeg/examples/search4.param
new file mode 100644
index 00000000..68285e6d
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search4.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard4.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		20
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/search40.param b/converter/ppm/ppmtompeg/examples/search40.param
new file mode 100644
index 00000000..3fd65b6e
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search40.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard40.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		8
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search41.param b/converter/ppm/ppmtompeg/examples/search41.param
new file mode 100644
index 00000000..3a47baf7
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search41.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard41.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		8
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search42.param b/converter/ppm/ppmtompeg/examples/search42.param
new file mode 100644
index 00000000..3ade2c85
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search42.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard42.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		8
+
+PSEARCH_ALG	SUBSAMPLE
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search43.param b/converter/ppm/ppmtompeg/examples/search43.param
new file mode 100644
index 00000000..e2c044a4
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search43.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard43.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		8
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search44.param b/converter/ppm/ppmtompeg/examples/search44.param
new file mode 100644
index 00000000..47c97e89
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search44.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard44.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		8
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search45.param b/converter/ppm/ppmtompeg/examples/search45.param
new file mode 100644
index 00000000..c4131d33
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search45.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard45.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		8
+
+PSEARCH_ALG	SUBSAMPLE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search46.param b/converter/ppm/ppmtompeg/examples/search46.param
new file mode 100644
index 00000000..cc9c67bf
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search46.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard46.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		8
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search47.param b/converter/ppm/ppmtompeg/examples/search47.param
new file mode 100644
index 00000000..7527228e
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search47.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard47.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		8
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search48.param b/converter/ppm/ppmtompeg/examples/search48.param
new file mode 100644
index 00000000..3d75a6a2
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search48.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	SUBSAMPLE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/search49.param b/converter/ppm/ppmtompeg/examples/search49.param
new file mode 100644
index 00000000..c25ab724
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search49.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/search5.param b/converter/ppm/ppmtompeg/examples/search5.param
new file mode 100644
index 00000000..5a1ad629
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search5.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard5.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		20
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/search50.param b/converter/ppm/ppmtompeg/examples/search50.param
new file mode 100644
index 00000000..6711ebb7
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search50.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/search51.param b/converter/ppm/ppmtompeg/examples/search51.param
new file mode 100644
index 00000000..6609e381
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search51.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-19]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	EXHAUSTIVE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/search52.5.param b/converter/ppm/ppmtompeg/examples/search52.5.param
new file mode 100644
index 00000000..be34be4b
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search52.5.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-15]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/search52.param b/converter/ppm/ppmtompeg/examples/search52.param
new file mode 100644
index 00000000..246aa978
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search52.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/search53.param b/converter/ppm/ppmtompeg/examples/search53.param
new file mode 100644
index 00000000..81afb3bc
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search53.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-15]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	EXHAUSTIVE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/search6.param b/converter/ppm/ppmtompeg/examples/search6.param
new file mode 100644
index 00000000..47c36a0c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search6.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard6.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		20
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/search7.param b/converter/ppm/ppmtompeg/examples/search7.param
new file mode 100644
index 00000000..f5d1224d
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search7.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard7.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		FULL
+
+RANGE		10
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search8.param b/converter/ppm/ppmtompeg/examples/search8.param
new file mode 100644
index 00000000..f3b17e96
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search8.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard8.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		20
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/search9.param b/converter/ppm/ppmtompeg/examples/search9.param
new file mode 100644
index 00000000..80051112
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/search9.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard9.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		FULL
+
+RANGE		20
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/seq.param b/converter/ppm/ppmtompeg/examples/seq.param
new file mode 100644
index 00000000..665c22a7
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/seq.param
@@ -0,0 +1,36 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/zonker/video/sequoia.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	320x240
+
+INPUT_CONVERT	/n/zonker/video/getframe *
+
+INPUT_DIR	/n/zonker/video/seqframes
+
+INPUT
+# really goes up to 200
+*	[0-1000]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		15
+PQSCALE		18
+BQSCALE		30
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/sequoia.param b/converter/ppm/ppmtompeg/examples/sequoia.param
new file mode 100644
index 00000000..1b93d880
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/sequoia.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/sequoia.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	352x240
+
+INPUT_CONVERT	djpeg *
+
+INPUT_DIR	/n/zonker/video/seqframes
+
+INPUT
+sequoia*.jpeg	[378-600]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		22
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/sequoia19.param b/converter/ppm/ppmtompeg/examples/sequoia19.param
new file mode 100644
index 00000000..52e5a72b
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/sequoia19.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/charlie-brown/project/mm/mpeg/mpeg_encode/output/seq.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	352x240
+
+INPUT_CONVERT	/n/zonker/video/seqframes/gframe /n/video/video/sequoia * 640x480.100 352 240
+
+INPUT_DIR	/n/zonker/video
+
+INPUT
+*	[0-199]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/sequoia2.param b/converter/ppm/ppmtompeg/examples/sequoia2.param
new file mode 100644
index 00000000..676cc2e4
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/sequoia2.param
@@ -0,0 +1,36 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/sequoia.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	352x240
+
+INPUT_CONVERT	djpeg *
+
+INPUT_DIR	/n/zonker/video/seqframes
+
+INPUT
+# really goes up to 200
+sequoia*.jpeg	[001-200]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		15
+PQSCALE		18
+BQSCALE		30
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/simple.param b/converter/ppm/ppmtompeg/examples/simple.param
new file mode 100644
index 00000000..14964829
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/simple.param
@@ -0,0 +1,40 @@
+# Simple parameter file
+
+OUTPUT		./test.mpg
+GOP_SIZE	15
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/charlie-brown/project/mm/mpeg/mpeg_encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-14]
+END_INPUT
+
+PATTERN		IBBPBBPBBPBBPBBPBBPBBP
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10 4
+
+# LOGARITHMIC, TWOLEVEL, SUBSAMPLE, EXHAUSTIVE
+PSEARCH_ALG	 LOGARITHMIC
+
+# CROSS2, SIMPLE
+BSEARCH_ALG	CROSS2
+
+# DECODED or ORIGINAL
+REFERENCE_FRAME	ORIGINAL
+
diff --git a/converter/ppm/ppmtompeg/examples/slice.param b/converter/ppm/ppmtompeg/examples/slice.param
new file mode 100644
index 00000000..308d2053
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/slice.param
@@ -0,0 +1,47 @@
+# test suite parameter file
+
+PATTERN		IIIIIIIII
+OUTPUT		ts.mpg
+
+BASE_FILE_FORMAT	PPM
+INPUT_CONVERT	giftoppm *
+GOP_SIZE		1
+SLICES_PER_FRAME	170000
+
+INPUT_DIR	ts
+
+INPUT
+med*.gif	[030-034]
+END_INPUT
+
+
+
+# all of the remaining options have to do with the motion search and qscale
+#
+# change this only if you're unsatisfied with the CPU time or quality, or
+# are experimenting
+#
+
+# if this appears in the file, then in addition to testing luminance when
+# computing motion vectors, program will also take into account chrominance
+#
+# this option MUST appear before ERROR option, or it will be ignored
+#
+# CHROMINANCE
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		HALF
+
+# means +/- this many pixels
+RANGE		4
+
+# YES or NO -- must be upper case
+SUBSAMPLE	NO
+
+IQSCALE		31
+PQSCALE		10
+BQSCALE		25
+
diff --git a/converter/ppm/ppmtompeg/examples/slimed.param b/converter/ppm/ppmtompeg/examples/slimed.param
new file mode 100644
index 00000000..b5938b94
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/slimed.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		I
+OUTPUT		/n/picasso/users/keving/encode/output/slimed.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	480x360
+
+INPUT_CONVERT	djpeg *
+
+INPUT_DIR	../input/slimed
+
+INPUT
+slimed.*.jpeg	[1-100]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		20
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/slowspeed.param b/converter/ppm/ppmtompeg/examples/slowspeed.param
new file mode 100644
index 00000000..321313be
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/slowspeed.param
@@ -0,0 +1,38 @@
+# speed test parameter file
+
+PATTERN		ibbpbbpbbpbbpbb
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	cat *.Y *.U *.V
+
+INPUT_DIR	/n/picasso/project/mm/mpeg/mpeg_encode/input/flowg
+
+INPUT
+sflowg.*	[0-149]
+END_INPUT
+
+# motion vector search parameters
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		HALF
+
+# means +/- this many pixels
+RANGE		12
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	CROSS2
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+REFERENCE_FRAME	ORIGINAL
+
diff --git a/converter/ppm/ppmtompeg/examples/stanford.param b/converter/ppm/ppmtompeg/examples/stanford.param
new file mode 100644
index 00000000..4c87334c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/stanford.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/stanford.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		4
+PQSCALE		4
+BQSCALE		8
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		8
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/subtest.param b/converter/ppm/ppmtompeg/examples/subtest.param
new file mode 100644
index 00000000..7fb4f585
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/subtest.param
@@ -0,0 +1,34 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard18.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	SUB4
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg/yuvsub
+
+INPUT
+*	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/temp.param b/converter/ppm/ppmtompeg/examples/temp.param
new file mode 100644
index 00000000..209fdfb4
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/temp.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/flow.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/flowg
+
+INPUT
+sflowg.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/template.param b/converter/ppm/ppmtompeg/examples/template.param
new file mode 100644
index 00000000..66b6dd98
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/template.param
@@ -0,0 +1,154 @@
+# parameter file template with lots of comments to assist you
+#
+# you can use this as a template, copying it to a separate file then modifying
+# the copy
+#
+#
+# any line beginning with '#' is a comment
+#
+# no line should be longer than 255 characters
+#
+#
+# general format of each line is:
+#	<option> <spaces and/or tabs> <value>
+#
+# lines can generally be in any order
+#
+# an exception is the option 'INPUT' which must be followed by input
+# files in the order in which they must appear, followed by 'END_INPUT'
+#
+# Also, if you use the `command` method of generating input file names,
+# the command will only be executed in the INPUT_DIR if INPUT_DIR preceeds
+# the INPUT parameter.
+#
+# <option> MUST be in UPPER CASE
+#
+
+PATTERN		IBBPBBPBBPBBPBBP
+OUTPUT		output.mpg
+
+# mpeg_encode really only accepts 3 different file formats, but using a
+# conversion statement it can effectively handle ANY file format
+#
+# You must specify the type of the input files.  The choices are:
+#    YUV, PPM, JMOVIE, Y, JPEG, PNM
+#	(must be upper case)
+#
+BASE_FILE_FORMAT	YUV
+
+#
+# if YUV format (or using parallel version), must provide width and height
+# YUV_SIZE	widthxheight
+# this option is ignored if BASE_FILE_FORMAT is not YUV and you're running
+# on just one machine
+#
+YUV_SIZE	352x240
+
+# If you are using YUV, there are different supported file formats.
+# EYUV or UCB are the same as previous versions of this encoder.
+# (All the Y's, then U's then V's, in 4:2:0 subsampling.)
+# Other formats, such as Abekas, Phillips, or a general format are
+# permissible, the general format is a string of Y's, U's, and V's
+# to specify the file order.
+
+INPUT_FORMAT UCB
+
+# the conversion statement
+#
+# Each occurrence of '*' will be replaced by the input file
+#
+# e.g., if you have a bunch of GIF files, then this might be:
+#	INPUT_CONVERT	giftoppm *
+#
+# e.g., if you have a bunch of files like a.Y a.U a.V, etc., then:
+#	INPUT_CONVERT	cat *.Y *.U *.V
+#
+# e.g., if you are grabbing from laser disc you might have something like
+#	INPUT_CONVERT	goto frame *; grabppm
+# 'INPUT_CONVERT *' means the files are already in the base file format
+#
+INPUT_CONVERT	*
+
+# number of frames in a GOP.
+#
+# since each GOP must have at least one I-frame, the encoder will find the
+# the first I-frame after GOP_SIZE frames to start the next GOP
+#
+# later, will add more flexible GOP signalling
+#
+GOP_SIZE	16
+
+# number of slices in a frame
+#
+# 1 is a good number.  another possibility is the number of macroblock rows
+# (which is the height divided by 16)
+#
+SLICES_PER_FRAME	1
+
+# directory to get all input files from (makes this file easier to read)
+INPUT_DIR	../input/tennis
+
+# There are a bunch of ways to specify the input files.
+# from a simple one-per-line listing, to the following 
+# way of numbering them.  See the manual for more information.
+INPUT
+# '*' is replaced by the numbers 01, 02, 03, 04
+# if I instead do [01-11], it would be 01, 02, ..., 09, 10, 11
+# if I instead do [1-11], it would be 1, 2, 3, ..., 9, 10, 11
+# if I instead do [1-11+3], it would be 1, 4, 7, 10
+# the program assumes none of your input files has a name ending in ']'
+# if you do, too bad!!!
+#
+#
+stennis.*.yuv	[0-23]
+# can have more files here if you want...there is no limit on the number
+# of files
+END_INPUT
+
+
+
+# Many of the remaining options have to do with the motion search and qscale
+
+# FULL or HALF -- must be upper case
+PIXEL		HALF
+
+# means +/- this many pixels for both P and B frame searches
+# specify two numbers if you wish to serc different ranges in the two.
+RANGE		10
+
+# this must be one of {EXHAUSTIVE, SUBSAMPLE, LOGARITHMIC}
+PSEARCH_ALG	LOGARITHMIC
+
+# this must be one of {SIMPLE, CROSS2, EXHAUSTIVE}
+#
+# note that EXHAUSTIVE is really, really, really slow
+#
+BSEARCH_ALG	CROSS2
+
+#
+# these specify the q-scale for I, P, and B frames
+# (values must be between 1 and 31)
+# These are the Qscale values for the entire frame in variable bit-rate
+# mode, and starting points (but not important) for constant bit rate
+#
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# this must be ORIGINAL or DECODED
+REFERENCE_FRAME	ORIGINAL
+
+# for parallel parameters see parallel.param in the exmaples subdirectory
+
+# if you want constant bit-rate mode, specify it as follows (number is bits/sec):
+BIT_RATE  1000000
+
+# To specify the buffer size (327680 is default, measused in bits, for 16bit words)
+BUFFER_SIZE 327680
+
+# The frame rate is the number of frames/second (legal values:
+# 23.976, 24, 25, 29.97, 30, 50 ,59.94, 60
+FRAME_RATE 30
+
+# There are many more options, see the users manual for examples....
+# ASPECT_RATIO, USER_DATA, GAMMA, IQTABLE, etc.
diff --git a/converter/ppm/ppmtompeg/examples/tennis.param b/converter/ppm/ppmtompeg/examples/tennis.param
new file mode 100644
index 00000000..978e1904
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/tennis.param
@@ -0,0 +1,37 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		output/tennis.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	cat *.Y *.U *.V
+
+INPUT_DIR	links/tennis
+
+INPUT
+stennis.*	[0-149]
+END_INPUT
+
+
+# motion vector search parameters
+
+# MAD or MSE -- must be upper case
+ERROR		MAD
+
+# FULL or HALF -- must be upper case
+PIXEL		HALF
+
+# means +/- this many pixels
+RANGE		8
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	CROSS2
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
diff --git a/converter/ppm/ppmtompeg/examples/tennis18.param b/converter/ppm/ppmtompeg/examples/tennis18.param
new file mode 100644
index 00000000..48979f3e
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/tennis18.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/tennis.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/tennis
+
+INPUT
+stennis.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/tennis19.5.param b/converter/ppm/ppmtompeg/examples/tennis19.5.param
new file mode 100644
index 00000000..196b7a28
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/tennis19.5.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/tennis.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/tennis
+
+INPUT
+stennis.*.yuv	[0-15]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/tennis19.param b/converter/ppm/ppmtompeg/examples/tennis19.param
new file mode 100644
index 00000000..542c237c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/tennis19.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/tennis.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/tennis
+
+INPUT
+stennis.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/tennis48.param b/converter/ppm/ppmtompeg/examples/tennis48.param
new file mode 100644
index 00000000..9db63638
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/tennis48.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/tennis.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/tennis
+
+INPUT
+stennis.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	SUBSAMPLE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/tennis49.param b/converter/ppm/ppmtompeg/examples/tennis49.param
new file mode 100644
index 00000000..cbecb035
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/tennis49.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/tennis.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/tennis
+
+INPUT
+stennis.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/tennis50.param b/converter/ppm/ppmtompeg/examples/tennis50.param
new file mode 100644
index 00000000..bbfe3cb0
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/tennis50.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/tennis.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/tennis
+
+INPUT
+stennis.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/tennis52.5.param b/converter/ppm/ppmtompeg/examples/tennis52.5.param
new file mode 100644
index 00000000..a00e4322
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/tennis52.5.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/tennis.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/tennis
+
+INPUT
+stennis.*.yuv	[0-15]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/tennis52.param b/converter/ppm/ppmtompeg/examples/tennis52.param
new file mode 100644
index 00000000..0ae84f00
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/tennis52.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/tennis.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/tennis
+
+INPUT
+stennis.*.yuv	[0-149]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/tennis53.param b/converter/ppm/ppmtompeg/examples/tennis53.param
new file mode 100644
index 00000000..bc311376
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/tennis53.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/tennis.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/tennis
+
+INPUT
+stennis.*.yuv	[0-15]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	EXHAUSTIVE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/tennisbug.param b/converter/ppm/ppmtompeg/examples/tennisbug.param
new file mode 100644
index 00000000..4a655b4a
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/tennisbug.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IPPPPPPPPPPP
+
+OUTPUT		/n/picasso/users/keving/encode/output/tennis.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	../input/tennis
+
+INPUT
+stennis.*.yuv	[0-49]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		1
+PQSCALE		1
+BQSCALE		1
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/testp.param b/converter/ppm/ppmtompeg/examples/testp.param
new file mode 100644
index 00000000..ea8dee18
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/testp.param
@@ -0,0 +1,45 @@
+# speed test parameter file
+
+PATTERN		IPPPPPPPPP
+
+OUTPUT		/n/picasso/users/keving/encode/output/testp.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input
+
+INPUT
+me.0.ppm
+me.0.ppm
+me.0.ppm
+me.0.ppm
+me.0.ppm
+me.0.ppm
+me.0.ppm
+me.0.ppm
+me.0.ppm
+me.0.ppm
+me.0.ppm
+END_INPUT
+
+# quality parameters
+
+IQSCALE		4
+PQSCALE		4
+BQSCALE		4
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/walk.param b/converter/ppm/ppmtompeg/examples/walk.param
new file mode 100644
index 00000000..52be9d2e
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/walk.param
@@ -0,0 +1,61 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/walk1.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	320x240
+
+INPUT_CONVERT	zcat *
+
+INPUT_DIR	/n/zonker/cluster/keving/walk
+
+INPUT
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+f.*.Z	[1-1800]
+END_INPUT
+
+PARALLEL_TEST_FRAMES	10
+PARALLEL_TIME_CHUNKS	30
+
+
+IO_SERVER_CONVERT *
+SLAVE_CONVERT zcat *
+
+PARALLEL
+#charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+END_PARALLEL
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/walk1.param b/converter/ppm/ppmtompeg/examples/walk1.param
new file mode 100644
index 00000000..52be9d2e
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/walk1.param
@@ -0,0 +1,61 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/walk1.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	320x240
+
+INPUT_CONVERT	zcat *
+
+INPUT_DIR	/n/zonker/cluster/keving/walk
+
+INPUT
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+f.*.Z	[1-1800]
+END_INPUT
+
+PARALLEL_TEST_FRAMES	10
+PARALLEL_TIME_CHUNKS	30
+
+
+IO_SERVER_CONVERT *
+SLAVE_CONVERT zcat *
+
+PARALLEL
+#charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+END_PARALLEL
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/walk18.param b/converter/ppm/ppmtompeg/examples/walk18.param
new file mode 100644
index 00000000..80ed48f3
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/walk18.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/walk19.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	320x240
+
+INPUT_CONVERT	zcat *
+
+INPUT_DIR	/n/zonker/cluster/keving/walk
+
+INPUT
+f.*.Z	[1-200]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/walk19.5.param b/converter/ppm/ppmtompeg/examples/walk19.5.param
new file mode 100644
index 00000000..7fb724d1
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/walk19.5.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/walk19.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	320x240
+
+INPUT_CONVERT	zcat *
+
+INPUT_DIR	/n/zonker/cluster/keving/walk
+
+INPUT
+f.*.Z	[1-16]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/walk19.param b/converter/ppm/ppmtompeg/examples/walk19.param
new file mode 100644
index 00000000..acc8f627
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/walk19.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/walk19.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	320x240
+
+INPUT_CONVERT	zcat *
+
+INPUT_DIR	/n/zonker/cluster/keving/walk
+
+INPUT
+f.*.Z	[1-200]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/walk2.param b/converter/ppm/ppmtompeg/examples/walk2.param
new file mode 100644
index 00000000..cbb28705
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/walk2.param
@@ -0,0 +1,61 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/walk2.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	320x240
+
+INPUT_CONVERT	zcat *
+
+INPUT_DIR	/n/zonker/cluster/keving/walk
+
+INPUT
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+f.*.Z	[1-1800]
+END_INPUT
+
+PARALLEL_TEST_FRAMES	10
+PARALLEL_TIME_CHUNKS	30
+
+
+IO_SERVER_CONVERT *
+SLAVE_CONVERT zcat *
+
+PARALLEL
+#charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+END_PARALLEL
+
+# quality parameters
+
+IQSCALE		6
+PQSCALE		8
+BQSCALE		15
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/examples/walk3.param b/converter/ppm/ppmtompeg/examples/walk3.param
new file mode 100644
index 00000000..86a30812
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/walk3.param
@@ -0,0 +1,61 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/walk3.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	320x240
+
+INPUT_CONVERT	zcat *
+
+INPUT_DIR	/n/zonker/cluster/keving/walk
+
+INPUT
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+f.*.Z	[1-1800]
+END_INPUT
+
+PARALLEL_TEST_FRAMES	10
+PARALLEL_TIME_CHUNKS	30
+
+
+IO_SERVER_CONVERT *
+SLAVE_CONVERT zcat *
+
+PARALLEL
+#charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+END_PARALLEL
+
+# quality parameters
+
+IQSCALE		6
+PQSCALE		8
+BQSCALE		15
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/walk4.param b/converter/ppm/ppmtompeg/examples/walk4.param
new file mode 100644
index 00000000..12d1f505
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/walk4.param
@@ -0,0 +1,61 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+OUTPUT		/n/picasso/users/keving/encode/output/walk4.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	320x240
+
+INPUT_CONVERT	zcat *
+
+INPUT_DIR	/n/zonker/cluster/keving/walk
+
+INPUT
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+f.*.Z	[1-1800]
+END_INPUT
+
+PARALLEL_TEST_FRAMES	10
+PARALLEL_TIME_CHUNKS	30
+
+
+IO_SERVER_CONVERT *
+SLAVE_CONVERT zcat *
+
+PARALLEL
+#charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+END_PARALLEL
+
+# quality parameters
+
+IQSCALE		4
+PQSCALE		6
+BQSCALE		10
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		7
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/walk48.param b/converter/ppm/ppmtompeg/examples/walk48.param
new file mode 100644
index 00000000..2c6df697
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/walk48.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/walk19.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	320x240
+
+INPUT_CONVERT	zcat *
+
+INPUT_DIR	/n/zonker/cluster/keving/walk
+
+INPUT
+f.*.Z	[1-200]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	SUBSAMPLE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/walk49.param b/converter/ppm/ppmtompeg/examples/walk49.param
new file mode 100644
index 00000000..72c17a94
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/walk49.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/walk19.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	320x240
+
+INPUT_CONVERT	zcat *
+
+INPUT_DIR	/n/zonker/cluster/keving/walk
+
+INPUT
+f.*.Z	[1-200]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/walk5.param b/converter/ppm/ppmtompeg/examples/walk5.param
new file mode 100644
index 00000000..e792064f
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/walk5.param
@@ -0,0 +1,62 @@
+# speed test parameter file
+
+#PATTERN		IBBPBBPBBPBBPBB
+PATTERN		I
+OUTPUT		/n/picasso/users/keving/encode/output/walk5.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PNM
+YUV_SIZE	320x240
+
+INPUT_CONVERT	zcat *
+
+INPUT_DIR	/n/zonker/cluster/keving/walk
+
+INPUT
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+f.*.Z	[1-80]
+END_INPUT
+
+PARALLEL_TEST_FRAMES	10
+PARALLEL_TIME_CHUNKS	30
+
+
+IO_SERVER_CONVERT *
+SLAVE_CONVERT zcat *
+
+PARALLEL
+#charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+END_PARALLEL
+
+# quality parameters
+
+IQSCALE		1
+PQSCALE		6
+BQSCALE		10
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		7
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/walk50.param b/converter/ppm/ppmtompeg/examples/walk50.param
new file mode 100644
index 00000000..8f4d27e6
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/walk50.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/walk19.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	320x240
+
+INPUT_CONVERT	zcat *
+
+INPUT_DIR	/n/zonker/cluster/keving/walk
+
+INPUT
+f.*.Z	[1-200]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	EXHAUSTIVE
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/walk52.5.param b/converter/ppm/ppmtompeg/examples/walk52.5.param
new file mode 100644
index 00000000..f9d038f6
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/walk52.5.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/walk19.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	320x240
+
+INPUT_CONVERT	zcat *
+
+INPUT_DIR	/n/zonker/cluster/keving/walk
+
+INPUT
+f.*.Z	[1-16]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/walk52.param b/converter/ppm/ppmtompeg/examples/walk52.param
new file mode 100644
index 00000000..69626d2c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/walk52.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/walk19.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	320x240
+
+INPUT_CONVERT	zcat *
+
+INPUT_DIR	/n/zonker/cluster/keving/walk
+
+INPUT
+f.*.Z	[1-200]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/walk53.param b/converter/ppm/ppmtompeg/examples/walk53.param
new file mode 100644
index 00000000..72f57c7a
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/walk53.param
@@ -0,0 +1,35 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/walk19.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	320x240
+
+INPUT_CONVERT	zcat *
+
+INPUT_DIR	/n/zonker/cluster/keving/walk
+
+INPUT
+f.*.Z	[1-16]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	EXHAUSTIVE
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/examples/walk93.param b/converter/ppm/ppmtompeg/examples/walk93.param
new file mode 100644
index 00000000..243d6032
--- /dev/null
+++ b/converter/ppm/ppmtompeg/examples/walk93.param
@@ -0,0 +1,62 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+#PATTERN		I
+OUTPUT		/n/picasso/users/keving/encode/output/walk93.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	1
+
+BASE_FILE_FORMAT	PPM
+YUV_SIZE	320x240
+
+INPUT_CONVERT	zcat *
+
+INPUT_DIR	/n/zonker/cluster/keving/walk
+
+INPUT
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+title.ppm.Z
+f.*.Z	[1-2]
+END_INPUT
+
+PARALLEL_TEST_FRAMES	10
+PARALLEL_TIME_CHUNKS	30
+
+
+IO_SERVER_CONVERT *
+SLAVE_CONVERT zcat *
+
+PARALLEL
+#charlie-brown	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+#woodstock	keving	~keving/encode/bin/dec-alpha/mpeg_encode
+END_PARALLEL
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		7
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	DECODED
diff --git a/converter/ppm/ppmtompeg/file.c b/converter/ppm/ppmtompeg/file.c
new file mode 100644
index 00000000..ae741962
--- /dev/null
+++ b/converter/ppm/ppmtompeg/file.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /n/picasso/project/mm/mpeg/mpeg_dist/mpeg_encode/RCS/file.c,v 1.2 1993/06/30 20:06:09 keving Exp $
+ *  $Log: file.c,v $
+ * Revision 1.2  1993/06/30  20:06:09  keving
+ * nothing
+ *
+ * Revision 1.1  1993/06/03  21:08:08  keving
+ * nothing
+ *
+ */
+
+#include "tk.h"
+
+#include "all.h"
+
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <time.h>
+#include <string.h>
+#include <dirent.h>
+#include <strings.h>
+
+#define MAX_FILES   1000
+#define MAX_NAME_LEN	256
+#define MAX_STRING_LEN	MAX_NAME_LEN
+
+typedef int boolean;
+#define TRUE 1
+#define FALSE 0
+
+extern char currentPath[MAXPATHLEN];
+
+char	globString[1024];
+
+static DIR *dfd;
+
+void	ResetPath(void);
+int ListDirectory(ClientData nulldata, Tcl_Interp *interp, int argc,
+		  char **argv);
+int ChangeDirectory(ClientData nulldata, Tcl_Interp *interp, int argc,
+		  char **argv);
+void	SortFiles(int numStrings, char strings[MAX_FILES][MAX_NAME_LEN],
+		  boolean *dirList, int permute[]);
+
+static void	UpdatePath(Tcl_Interp *interp, char *directory);
+static boolean	MatchesGlob(char *string, char *glob);
+
+
+
+void	ResetPath()
+{
+    if ( getwd(currentPath) == 0 )
+    {
+	fprintf(stderr, "Error getting pathname!!!\n");
+	exit(1);
+    }
+
+    strcpy(&currentPath[strlen(currentPath)], "/");
+
+    dfd = opendir(currentPath);
+    if ( dfd == NULL )
+    {
+	fprintf(stderr, "can't open '%s'\n", currentPath);
+	exit(1);
+    }
+}
+
+
+static void	UpdatePath(Tcl_Interp *interp, char *directory)
+{
+    int length;
+    char *charPtr;
+
+    length = strlen(currentPath);
+
+    if ( strcmp(directory, "./") == 0 )
+	return /* nothing */ ;
+    else if ( strcmp(directory, "../") == 0 )
+    {
+	/* delete backwards up to '/' */
+
+	if ( length < 2 )
+	{
+	    fprintf(stderr, "Error:  backing up from root directory!!!\n");
+	    exit(1);
+	}
+
+	charPtr = &currentPath[length-2];
+	while ( (charPtr != currentPath) && (*charPtr != '/') )
+	    charPtr--;
+	charPtr++;	/* leave the '/' */
+	*charPtr = '\0';
+    }
+    else
+    {
+	strcpy(&currentPath[length], directory);
+    }
+}
+
+
+int ChangeDirectory(ClientData nulldata, Tcl_Interp *interp, int argc,
+		  char **argv)
+{
+    char *directory = argv[1];
+
+    UpdatePath(interp, directory);
+
+    fprintf(stdout, "Opening directory: '%s'\n", currentPath);
+
+    dfd = opendir(currentPath);
+    if ( dfd == NULL )
+    {
+	fprintf(stderr, "can't open '%s'\n", currentPath);
+	return TCL_OK;	/* shouldn't, really */
+    }
+
+    return TCL_OK;
+}
+
+
+int ListDirectory(ClientData nulldata, Tcl_Interp *interp, int argc,
+		  char **argv)
+{
+    struct dirent *dp;
+    struct stat stbuf;
+    char command[256];
+    char fileName[MAX_FILES][MAX_NAME_LEN];
+    boolean dirList[MAX_FILES];
+    int permute[MAX_FILES];
+    int	fileCount = 0;
+    register int index;
+    char fullName[MAXPATHLEN];
+    char    *restPtr;
+
+    sprintf(command, "ShowCurrentDirectory %s", currentPath);
+    Tcl_Eval(interp, command, 0, (char **) NULL);
+
+    if ( dfd == NULL )
+    {
+	fprintf(stderr, "TRIED TO LIST NULL DIRECTORY\n");
+
+	return TCL_OK;
+    }
+
+/* check if root directory */
+    if ( strlen(currentPath) != 1 )
+    {
+	sprintf(fileName[fileCount], "../");
+	dirList[fileCount] = TRUE;
+	fileCount++;
+    }
+
+    strcpy(fullName, currentPath);
+    restPtr = &fullName[strlen(fullName)];
+
+    while ( (dp = readdir(dfd)) != NULL )
+    {
+	strcpy(restPtr, dp->d_name);
+	stat(fullName, &stbuf);
+
+	if ( dp->d_name[0] != '.' )
+	{
+	    if ( S_ISDIR(stbuf.st_mode) )
+	    {
+		sprintf(fileName[fileCount], "%s/", dp->d_name);
+		dirList[fileCount] = TRUE;
+		fileCount++;
+	    }
+	    else
+	    {
+		if ( MatchesGlob(dp->d_name, globString) )
+		{
+		    strcpy(fileName[fileCount], dp->d_name);
+		    dirList[fileCount] = FALSE;
+		    fileCount++;
+		}
+	    }
+	}
+    }
+
+    SortFiles(fileCount, fileName, dirList, permute);
+
+    for ( index = 0; index < fileCount; index++ )
+    {
+	sprintf(command, "AddBrowseFile %s", fileName[permute[index]]);
+	Tcl_Eval(interp, command, 0, (char **) NULL);
+    }
+
+    closedir(dfd);
+
+    return TCL_OK;
+}
+
+
+void	SortFiles(int numStrings, char strings[MAX_FILES][MAX_NAME_LEN],
+		  boolean *dirList, int permute[])
+{
+    register int i, j;
+    int temp;
+    int	numDirs;
+    int	ptr;
+
+    for ( i = 0; i < numStrings; i++ )
+	permute[i] = i;
+
+    /* put all directories at front */
+    numDirs = 0;
+    ptr = numStrings-1;
+    while ( numDirs != ptr )
+    {
+	/* go past dirs */
+	while ( (numDirs < ptr) && (dirList[permute[numDirs]]) )
+	    numDirs++;
+
+	/* go past non-dirs */
+	while ( (numDirs < ptr) && (! dirList[permute[ptr]]) )
+	    ptr--;
+
+	if ( numDirs != ptr )
+	{
+	    temp = permute[numDirs];
+	    permute[numDirs] = ptr;
+	    permute[ptr] = temp;
+	}
+    }
+
+    if ( dirList[permute[numDirs]] )
+	numDirs++;
+
+    for ( i = 0; i < numDirs; i++ )
+	for ( j = i+1; j < numDirs; j++ )
+	{
+	    if ( strcmp(&strings[permute[j]][0], &strings[permute[i]][0]) < 0 )
+	    {
+		temp = permute[j];
+		permute[j] = permute[i];
+		permute[i] = temp;
+	    }
+	}
+
+    for ( i = numDirs; i < numStrings; i++ )
+	for ( j = i+1; j < numStrings; j++ )
+	{
+	    if ( strcmp(&strings[permute[j]][0], &strings[permute[i]][0]) < 0 )
+	    {
+		temp = permute[j];
+		permute[j] = permute[i];
+		permute[i] = temp;
+	    }
+	}
+}
+
+
+int SetBrowseGlob (ClientData nulldata, Tcl_Interp *interp,
+		   int argc, char **argv)
+{
+    if (argc == 2 )
+    {
+	strcpy(globString, argv[1]);
+
+	fprintf(stdout, "GLOB:  %s\n", globString);
+
+	return TCL_OK;
+    }
+
+	Tcl_AppendResult (interp, 
+            "wrong args: should be \"", argv[0]," string\"", (char *) NULL);
+	return TCL_ERROR;
+}
+
+
+static boolean	MatchesGlob(char *string, char *glob)
+{
+    char    *stringRight, *globRight;
+
+    while ( (*glob != '\0') && (*glob != '*') )	    /* match left side */
+    {
+	if ( (*string == '\0') || (*string != *glob) )
+	    return FALSE;
+	string++;
+	glob++;
+    }
+
+    if ( *glob == '\0' )	/* no star */
+	return TRUE;
+
+    /* now match right side */
+    stringRight = &string[strlen(string)-1];
+    globRight = &glob[strlen(glob)-1];
+
+    while ( *globRight != '*' )
+    {
+	if ( (stringRight < string) || (*stringRight != *globRight) )
+	    return FALSE;
+	globRight--;
+	stringRight--;
+    }
+
+    return TRUE;
+}
diff --git a/converter/ppm/ppmtompeg/frame.c b/converter/ppm/ppmtompeg/frame.c
new file mode 100644
index 00000000..09f46f66
--- /dev/null
+++ b/converter/ppm/ppmtompeg/frame.c
@@ -0,0 +1,836 @@
+/*===========================================================================*
+ * frame.c                                   *
+ *                                       *
+ *  basic frame procedures                           *
+ *                                       *
+ * EXPORTED PROCEDURES:                              *
+ *  Frame_Init                               *
+ *  Frame_Exit                               *
+ *  Frame_New                                *
+ *  Frame_Free                               *
+ *  Frame_AllocBlocks                            *
+ *  Frame_AllocYCC                               *
+ *  Frame_AllocDecoded                           *
+ *  Frame_AllocHalf                                  *
+ *  Frame_Resize                                     * 
+ *                                       *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+
+#include "mallocvar.h"
+
+#include "all.h"
+#include "mtypes.h"
+#include "frames.h"
+#include "frame.h"
+#include "fsize.h"
+#include "dct.h"
+
+/*===========*
+ * CONSTANTS *
+ *===========*/
+
+/* The maximum number of B-Frames allowed between reference frames. */
+#define  B_FRAME_RUN  16    
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+static MpegFrame * frameMemory[B_FRAME_RUN+2];
+static unsigned int numOfFrames;
+
+
+/*====================================================
+* Resize_Array_Width
+*    
+*   This function will resize any array width up
+* or down in size.  The algorithm is based on the
+* least common multiple approach more commonly
+* used in audio frequency adjustments.
+*=====================================================*/
+static void 
+Resize_Array_Width(uint8 ** const inarray,
+                   int      const in_x,
+                   int      const in_y,
+                   uint8 ** const outarray,
+                   int      const out_x) {
+
+    unsigned int i;
+    int in_total;
+    int out_total;
+    uint8 *inptr;
+    uint8 *outptr;
+    uint8 pointA,pointB;
+    /* double slope,diff; */
+    
+    for (i = 0; i < in_y; ++i) {     /* For each row */
+        unsigned int j;
+        inptr = &inarray[i][0];
+        outptr = &outarray[i][0];
+        in_total = 0;
+        out_total = 0;
+        for (j=0; j < out_x; ++j) {      /* For each output value */
+            if (in_total == out_total) {
+                *outptr = *inptr;
+                outptr++;
+                out_total=out_total+in_x;
+                while(in_total < out_total){
+                    in_total = in_total + out_x;
+                    ++inptr;
+                }
+                if (in_total > out_total) {
+                    in_total = in_total - out_x;
+                    --inptr;
+                }
+            } else {  
+                pointA = *inptr;
+                ++inptr;
+                pointB = *inptr;
+                --inptr;
+#if 0
+                /*Interpolative solution */
+                slope = ((double)(pointB -pointA))/((double)(out_x));
+                diff = (((double)(out_total - in_total)));
+                if (diff < (out_x/2)){
+                    *outptr = (pointA + (uint8)(slope*diff));
+                } else {
+                    *outptr = (pointB -
+                               (uint8)(slope*(((float)(out_x)) - diff)));
+                } 
+#endif
+                /* Non-Interpolative solution */
+                *outptr = *inptr;  
+
+                ++outptr;
+                out_total = out_total + in_x;
+                while(in_total < out_total) {
+                    in_total = in_total + out_x;
+                    ++inptr;
+                }
+                if (in_total > out_total) {
+                    in_total = in_total - out_x;
+                    --inptr;
+                }
+            }  /* end if */
+        }  /* end for each output value */
+    }  /* end for each row */
+}  /* end main */
+
+
+
+/*==============================
+* Resize_Array_Height
+*
+*    Resize any array height larger or smaller.
+* Same as Resize_array_Width except pointer
+* manipulation must change.
+*===============================*/
+static void 
+Resize_Array_Height(uint8 ** const inarray,
+                    int      const in_x,
+                    int      const in_y,
+                    uint8 ** const outarray,
+                    int      const out_y) {
+
+    unsigned int i;
+
+    for(i=0; i < in_x; ++i){    /* for each column */
+        int in_total;
+        int out_total;
+        uint8 pointA, pointB;
+        double slope, diff;
+        unsigned int j;
+        int k;
+
+        in_total = 0;
+        out_total = 0;
+        k = 0;
+        for(j=0; j < out_y; ++j){  /* for each output value */
+            if (in_total == out_total) {  
+                outarray[j][i] = inarray[k][i];
+                out_total=out_total+in_y;
+                while(in_total < out_total){
+                    in_total = in_total + out_y;
+                    ++k;
+                }
+                if (in_total > out_total) {
+                    in_total = in_total - out_y;
+                    --k;
+                }
+            } else {  
+                pointA = inarray[k][i];
+                if (k != (in_y -1)) {
+                    pointB = inarray[k+1][i];
+                } else
+                    pointB = pointA;
+                /* Interpolative case */
+                slope = ((double)(pointB -pointA))/(double)(out_y);
+                diff = (double)(out_total - in_total);
+                /*  outarray[j][i] = (inarray[k][i] + (uint8)(slope*diff)); */
+                /* Non-Interpolative case */
+                outarray[j][i] = inarray[k][i];
+                out_total = out_total + in_y;
+                while (in_total < out_total) {
+                    in_total = in_total + out_y;
+                    ++k;
+                }
+                if (in_total > out_total){
+                    in_total = in_total - out_y;
+                    --k;
+                }
+            } 
+        }
+    }
+}
+
+
+
+/*========================================================
+* Resize_Width
+*======================================================*/
+static void  
+Resize_Width(MpegFrame * const omfrw,
+             MpegFrame * const mfrw,
+             int         const in_x,
+             int         const in_y,
+             int         const out_x) {
+
+    int y;
+
+    omfrw->orig_y = NULL;
+    Fsize_x = out_x;
+
+    /* Allocate new frame memory */
+    MALLOCARRAY(omfrw->orig_y, Fsize_y);
+    ERRCHK(omfrw->orig_y, "malloc");
+    for (y = 0; y < Fsize_y; ++y) {
+        MALLOCARRAY(omfrw->orig_y[y], out_x);
+        ERRCHK(omfrw->orig_y[y], "malloc");
+    }
+
+    MALLOCARRAY(omfrw->orig_cr, Fsize_y / 2);
+    ERRCHK(omfrw->orig_cr, "malloc");
+    for (y = 0; y < Fsize_y / 2; ++y) {
+        MALLOCARRAY(omfrw->orig_cr[y], out_x / 2);
+        ERRCHK(omfrw->orig_cr[y], "malloc");
+    }
+
+    MALLOCARRAY(omfrw->orig_cb, Fsize_y / 2);
+    ERRCHK(omfrw->orig_cb, "malloc");
+    for (y = 0; y < Fsize_y / 2; ++y) {
+        MALLOCARRAY(omfrw->orig_cb[y], out_x / 2);
+        ERRCHK(omfrw->orig_cb[y], "malloc");
+    }
+
+    if (referenceFrame == ORIGINAL_FRAME) {
+        omfrw->ref_y = omfrw->orig_y;
+        omfrw->ref_cr = omfrw->orig_cr;
+        omfrw->ref_cb = omfrw->orig_cb;
+    }
+
+    /* resize each component array separately */
+    Resize_Array_Width(mfrw->orig_y, in_x, in_y, omfrw->orig_y, out_x);
+    Resize_Array_Width(mfrw->orig_cr, (in_x/2), (in_y/2), omfrw->orig_cr,
+                       (out_x/2));
+    Resize_Array_Width(mfrw->orig_cb, (in_x/2), (in_y/2), omfrw->orig_cb,
+                       (out_x/2));
+
+    /* Free old frame memory */
+    if (mfrw->orig_y) {
+        unsigned int i;
+        for (i = 0; i < in_y; ++i) {
+            free(mfrw->orig_y[i]);
+        }
+        free(mfrw->orig_y);
+        
+        for (i = 0; i < in_y / 2; ++i) {
+            free(mfrw->orig_cr[i]);
+        }
+        free(mfrw->orig_cr);
+        
+        for (i = 0; i < in_y / 2; ++i) {
+            free(mfrw->orig_cb[i]);
+        }
+        free(mfrw->orig_cb);
+    }
+}
+
+
+
+/*=======================================================
+* Resize_Height
+*
+*   Resize Frame height up or down
+*=======================================================*/
+static  void
+Resize_Height(MpegFrame * const omfrh,
+              MpegFrame * const mfrh,
+              int         const in_x,
+              int         const in_y,
+              int         const out_y) {
+    
+    unsigned int y; 
+    
+    Fsize_y = out_y;
+
+    /* Allocate new frame memory */
+    MALLOCARRAY(omfrh->orig_y, out_y);
+    ERRCHK(omfrh->orig_y, "malloc");
+    for (y = 0; y < out_y; ++y) {
+        MALLOCARRAY(omfrh->orig_y[y], Fsize_x);
+        ERRCHK(omfrh->orig_y[y], "malloc");
+    }
+
+    MALLOCARRAY(omfrh->orig_cr, out_y / 2);
+    ERRCHK(omfrh->orig_cr, "malloc");
+    for (y = 0; y < out_y / 2; ++y) {
+        MALLOCARRAY(omfrh->orig_cr[y], Fsize_x / 2);
+        ERRCHK(omfrh->orig_cr[y], "malloc");
+    }
+
+    MALLOCARRAY(omfrh->orig_cb, out_y / 2);
+    ERRCHK(omfrh->orig_cb, "malloc");
+    for (y = 0; y < out_y / 2; ++y) {
+        MALLOCARRAY(omfrh->orig_cb[y], Fsize_x / 2);
+        ERRCHK(omfrh->orig_cb[y], "malloc");
+    }
+
+    if (referenceFrame == ORIGINAL_FRAME) {
+        omfrh->ref_y = omfrh->orig_y;
+        omfrh->ref_cr = omfrh->orig_cr;
+        omfrh->ref_cb = omfrh->orig_cb;
+    }
+
+    /* resize component arrays separately */
+    Resize_Array_Height(mfrh->orig_y, in_x, in_y, omfrh->orig_y, out_y);
+    Resize_Array_Height(mfrh->orig_cr, (in_x/2), (in_y/2), omfrh->orig_cr,
+                        (out_y/2));
+    Resize_Array_Height(mfrh->orig_cb, (in_x/2), (in_y/2), omfrh->orig_cb,
+                        (out_y/2));
+
+    /* Free old frame memory */
+    if (mfrh->orig_y) {
+        unsigned int i;
+        for (i = 0; i < in_y; ++i) {
+            free(mfrh->orig_y[i]);
+        }
+        free(mfrh->orig_y);
+        
+    for (i = 0; i < in_y / 2; ++i) {
+        free(mfrh->orig_cr[i]);
+    }
+    free(mfrh->orig_cr);
+    
+    for (i = 0; i < in_y / 2; ++i) {
+        free(mfrh->orig_cb[i]);
+    }
+    free(mfrh->orig_cb);
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * Frame_Init
+ *
+ *  initializes the memory associated with all frames ever
+ *  If the input is not coming in from stdin, only 3 frames are needed ;
+ *      else, the program must create frames equal to the greatest distance
+ *      between two reference frames to hold the B frames while it is parsing
+ *      the input from stdin.
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    frameMemory, numOfFrames
+ *
+ *===========================================================================*/
+void
+Frame_Init(unsigned int const numOfFramesRequested) {
+    int idx;
+
+    numOfFrames = numOfFramesRequested;
+
+    for (idx = 0; idx < numOfFrames; ++idx) {
+        MALLOCVAR(frameMemory[idx]);
+        frameMemory[idx]->inUse = FALSE;
+        frameMemory[idx]->orig_y = NULL;    
+        frameMemory[idx]->y_blocks = NULL; 
+        frameMemory[idx]->decoded_y = NULL;
+        frameMemory[idx]->halfX = NULL;
+        frameMemory[idx]->next = NULL;
+    }
+}
+
+
+/*===========================================================================*
+ *
+ * FreeFrame
+ *
+ *  frees the memory associated with the given frame
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+FreeFrame(MpegFrame * const frameP) {
+
+    if (frameP) {
+        if (frameP->orig_y) {
+            unsigned int i;
+            for (i = 0; i < Fsize_y; ++i)
+                free(frameP->orig_y[i]);
+            free(frameP->orig_y);
+
+            for (i = 0; i < (Fsize_y / 2); ++i)
+                free(frameP->orig_cr[i]);
+            free(frameP->orig_cr);
+
+            for (i = 0; i < (Fsize_y / 2); ++i)
+                free(frameP->orig_cb[i]);
+            free(frameP->orig_cb);
+        }
+        if (frameP->decoded_y) {
+            unsigned int i;
+            for (i = 0; i < Fsize_y; ++i)
+                free(frameP->decoded_y[i]);
+            free(frameP->decoded_y);
+
+            for (i = 0; i < (Fsize_y / 2); ++i)
+                free(frameP->decoded_cr[i]);
+            free(frameP->decoded_cr);
+
+            for (i = 0; i < (Fsize_y / 2); ++i)
+                free(frameP->decoded_cb[i]);
+            free(frameP->decoded_cb);
+        }
+
+        if (frameP->y_blocks) {
+            unsigned int i;
+            for (i = 0; i < Fsize_y / DCTSIZE; ++i)
+                free(frameP->y_blocks[i]);
+            free(frameP->y_blocks);
+
+            for (i = 0; i < Fsize_y / (2 * DCTSIZE); ++i)
+                free(frameP->cr_blocks[i]);
+            free(frameP->cr_blocks);
+
+            for (i = 0; i < Fsize_y / (2 * DCTSIZE); ++i)
+                free(frameP->cb_blocks[i]);
+            free(frameP->cb_blocks);
+        }
+        if (frameP->halfX) {
+            unsigned int i;
+            for ( i = 0; i < Fsize_y; ++i )
+                free(frameP->halfX[i]);
+            free(frameP->halfX);
+            
+            for (i = 0; i < Fsize_y-1; ++i)
+                free(frameP->halfY[i]);
+            free(frameP->halfY);
+            
+            for (i = 0; i < Fsize_y-1; ++i)
+                free(frameP->halfBoth[i]);
+            free(frameP->halfBoth);
+        }
+        free(frameP);
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * Frame_Exit
+ *
+ *  frees the memory associated with frames
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    frameMemory
+ *
+ *===========================================================================*/
+void
+Frame_Exit(void) {
+
+    int idx;
+
+    for (idx = 0; idx < numOfFrames; ++idx) {
+        FreeFrame(frameMemory[idx]);
+    }
+}
+
+
+/*===========================================================================*
+ *
+ * Frame_Free
+ *
+ *  frees the given frame -- allows it to be re-used
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+Frame_Free(MpegFrame * const frameP) {
+    frameP->inUse = FALSE;
+}
+
+
+
+/*===========================================================================*
+ *
+ * GetUnusedFrame
+ *
+ *  return an unused frame
+ *
+ * RETURNS: the frame
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static MpegFrame *
+GetUnusedFrame() {
+    unsigned int idx;
+
+    for (idx = 0; idx < numOfFrames; ++idx) {
+        if (!frameMemory[idx]->inUse) {
+            frameMemory[idx]->inUse = TRUE;
+            break;
+        }
+    }
+    if (idx >= numOfFrames) {
+        fprintf(stderr, "ERROR:  No unused frames!!!\n");
+        fprintf(stderr, "        If you are using stdin for input, "
+                "it is likely that you have too many\n");
+        fprintf(stderr, "        B-frames between two reference frames.  "
+                "See the man page for help.\n");
+        exit(1);
+    }
+    return frameMemory[idx]; 
+}
+
+
+
+/*===========================================================================*
+ *
+ * ResetFrame
+ *
+ *  reset a frame to the given id and type
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+ResetFrame(int         const id,
+           int         const type,
+           MpegFrame * const frame) {
+
+    switch (type) {
+    case 'i':
+        frame->type = TYPE_IFRAME;
+        break;
+    case 'p':
+        frame->type = TYPE_PFRAME;
+        break;
+    case 'b':
+        frame->type = TYPE_BFRAME;
+        break;
+    default:
+        fprintf(stderr, "Invalid MPEG frame type %c\n", type);
+        exit(1);
+    }
+
+    frame->id = id;
+    frame->halfComputed = FALSE;
+    frame->next = NULL;
+}
+
+
+
+/*===========================================================================*
+ *
+ * Frame_New
+ *
+ *  finds a frame that isn't currently being used and resets it
+ *
+ * RETURNS: the frame
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+MpegFrame *
+Frame_New(int const id,
+          int const type) {
+
+    MpegFrame *frame;
+
+    frame = GetUnusedFrame();
+    ResetFrame(id, type, frame);
+
+    return frame;
+}
+
+
+
+/*===========================================================================*
+ *
+ * Frame_AllocBlocks
+ *
+ *  allocate memory for blocks for the given frame, if required
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+Frame_AllocBlocks(MpegFrame * const frameP) {
+
+    if (frameP->y_blocks != NULL) {
+        /* already allocated */
+    } else {
+        int const dctx = Fsize_x / DCTSIZE;
+        int const dcty = Fsize_y / DCTSIZE;
+
+        unsigned int i;
+
+        MALLOCARRAY(frameP->y_blocks, dcty);
+        ERRCHK(frameP->y_blocks, "malloc");
+        for (i = 0; i < dcty; ++i) {
+            MALLOCARRAY(frameP->y_blocks[i], dctx);
+            ERRCHK(frameP->y_blocks[i], "malloc");
+        }
+    
+        MALLOCARRAY(frameP->cr_blocks, dcty / 2);
+        ERRCHK(frameP->cr_blocks, "malloc");
+        MALLOCARRAY(frameP->cb_blocks, dcty / 2);
+        ERRCHK(frameP->cb_blocks, "malloc");
+        for (i = 0; i < (dcty / 2); ++i) {
+            MALLOCARRAY(frameP->cr_blocks[i], dctx / 2);
+            ERRCHK(frameP->cr_blocks[i], "malloc");
+            MALLOCARRAY(frameP->cb_blocks[i], dctx / 2);
+            ERRCHK(frameP->cb_blocks[i], "malloc");
+        }
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * Frame_AllocYCC
+ *
+ *  allocate memory for YCC info for the given frame, if required
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+Frame_AllocYCC(MpegFrame * const frameP) {
+
+    if (frameP->orig_y != NULL) {
+        /* already allocated */
+    } else {
+        unsigned int y;
+    
+        DBG_PRINT(("ycc_calc:\n"));
+        /*
+         * first, allocate tons of memory
+         */
+        MALLOCARRAY(frameP->orig_y, Fsize_y);
+        ERRCHK(frameP->orig_y, "malloc");
+        for (y = 0; y < Fsize_y; ++y) {
+            MALLOCARRAY(frameP->orig_y[y], Fsize_x);
+            ERRCHK(frameP->orig_y[y], "malloc");
+        }
+
+        MALLOCARRAY(frameP->orig_cr, Fsize_y / 2);
+        ERRCHK(frameP->orig_cr, "malloc");
+        for (y = 0; y < (Fsize_y / 2); ++y) {
+            MALLOCARRAY(frameP->orig_cr[y], Fsize_x / 2);
+            ERRCHK(frameP->orig_cr[y], "malloc");
+        }
+
+        MALLOCARRAY(frameP->orig_cb, Fsize_y / 2);
+        ERRCHK(frameP->orig_cb, "malloc");
+        for (y = 0; y < (Fsize_y / 2); ++y) {
+            MALLOCARRAY(frameP->orig_cb[y], Fsize_x / 2);
+            ERRCHK(frameP->orig_cb[y], "malloc");
+        }
+
+        if (referenceFrame == ORIGINAL_FRAME) {
+            frameP->ref_y  = frameP->orig_y;
+            frameP->ref_cr = frameP->orig_cr;
+            frameP->ref_cb = frameP->orig_cb;
+        }
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * Frame_AllocHalf
+ *
+ *  allocate memory for half-pixel values for the given frame, if required
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+Frame_AllocHalf(MpegFrame * const frameP) {
+
+    if (frameP->halfX != NULL) {
+    } else {
+        unsigned int y;
+
+        MALLOCARRAY(frameP->halfX, Fsize_y);
+        ERRCHK(frameP->halfX, "malloc");
+        for (y = 0; y < Fsize_y; ++y) {
+            MALLOCARRAY(frameP->halfX[y], Fsize_x - 1);
+            ERRCHK(frameP->halfX[y], "malloc");
+        }
+        MALLOCARRAY(frameP->halfY, Fsize_y - 1);
+        ERRCHK(frameP->halfY, "malloc");
+        for (y = 0; y < Fsize_y - 1; ++y) {
+            MALLOCARRAY(frameP->halfY[y], Fsize_x);
+            ERRCHK(frameP->halfY[y], "malloc");
+        }
+        MALLOCARRAY(frameP->halfBoth, Fsize_y - 1);
+        ERRCHK(frameP->halfBoth, "malloc");
+        for (y = 0; y < Fsize_y - 1; ++y) {
+            MALLOCARRAY(frameP->halfBoth[y], Fsize_x - 1);
+            ERRCHK(frameP->halfBoth[y], "malloc");
+        }
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * Frame_AllocDecoded
+ *
+ *  allocate memory for decoded frame for the given frame, if required
+ *  if makeReference == TRUE, then makes it reference frame
+ * 
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+Frame_AllocDecoded(MpegFrame * const frameP,
+                   boolean     const makeReference) {
+
+    if (frameP->decoded_y != NULL) {
+        /* already allocated */
+    } else {
+        unsigned int y;
+
+        /* allocate memory for decoded image */
+        /* can probably reuse original image memory, but may decide to use
+           it for some reason, so do it this way at least for now -- more
+           flexible
+        */
+        MALLOCARRAY(frameP->decoded_y, Fsize_y);
+        ERRCHK(frameP->decoded_y, "malloc");
+        for (y = 0; y < Fsize_y; ++y) {
+            MALLOCARRAY(frameP->decoded_y[y], Fsize_x);
+            ERRCHK(frameP->decoded_y[y], "malloc");
+        }
+        
+        MALLOCARRAY(frameP->decoded_cr, Fsize_y / 2);
+        ERRCHK(frameP->decoded_cr, "malloc");
+        for (y = 0; y < (Fsize_y / 2); ++y) {
+            MALLOCARRAY(frameP->decoded_cr[y], Fsize_x / 2);
+            ERRCHK(frameP->decoded_cr[y], "malloc");
+        }
+        
+        MALLOCARRAY(frameP->decoded_cb, Fsize_y / 2);
+        ERRCHK(frameP->decoded_cb, "malloc");
+        for (y = 0; y < (Fsize_y / 2); ++y) {
+            MALLOCARRAY(frameP->decoded_cb[y], Fsize_x / 2);
+            ERRCHK(frameP->decoded_cb[y], "malloc");
+        }
+
+        if (makeReference) {
+            frameP->ref_y  = frameP->decoded_y;
+            frameP->ref_cr = frameP->decoded_cr;
+            frameP->ref_cb = frameP->decoded_cb;
+        }
+    }
+}
+
+
+
+/*===============================================================
+ *
+ * Frame_Resize                  by James Boucher
+ *                Boston University Multimedia Communications Lab
+ *  
+ *     This function takes the mf input frame, read in READFrame(),
+ * and resizes all the input component arrays to the output
+ * dimensions specified in the parameter file as OUT_SIZE.
+ * The new frame is returned with the omf pointer.  As well,
+ * the values of Fsize_x and Fsize_y are adjusted.
+ ***************************************************************/
+void
+Frame_Resize(MpegFrame * const omf,
+             MpegFrame * const mf,
+             int         const insize_x,
+             int         const insize_y,
+             int         const outsize_x,
+             int         const outsize_y) {
+
+    MpegFrame * frameAP;  /* intermediate frame */
+
+    MALLOCVAR_NOFAIL(frameAP);
+    
+    if (insize_x != outsize_x && insize_y != outsize_y) {
+        Resize_Width(frameAP, mf, insize_x, insize_y, outsize_x);
+        Resize_Height(omf, frameAP, outsize_x, insize_y, outsize_y);
+    } else 
+        if (insize_x ==outsize_x && insize_y != outsize_y) {
+            Resize_Height(omf, mf, insize_x, insize_y, outsize_y);
+        } else
+            if (insize_x !=outsize_x && insize_y == outsize_y) {
+                Resize_Width(omf, mf, insize_x, insize_y, outsize_x);
+            } else
+                exit(1);
+
+    free(frameAP);
+    free(mf);
+}
diff --git a/converter/ppm/ppmtompeg/frametype.c b/converter/ppm/ppmtompeg/frametype.c
new file mode 100644
index 00000000..b7daacc9
--- /dev/null
+++ b/converter/ppm/ppmtompeg/frametype.c
@@ -0,0 +1,365 @@
+/*===========================================================================*
+ * frametype.c								     *
+ *									     *
+ *	procedures to keep track of frame types (I, P, B)		     *
+ *									     *
+ * EXPORTED PROCEDURES:							     *
+ *	FType_Type						             *
+ *	FType_FutureRef						             *
+ *	FType_PastRef						             *
+ *									     *
+ * SYNOPSIS								     *
+ *	FType_Type	returns the type of the given numbered frame	     *
+ *	FType_FutureRef	returns the number of the future reference frame     *
+ *	FType_PastRef	returns the number of the past reference frame	     *
+ *									     *
+ * 00.12.07 change malloc from frameTable to calloc to fix bug 
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "mallocvar.h"
+#include "all.h"
+#include "frames.h"
+#include "frame.h"
+#include "param.h"
+#include "specifics.h"
+#include "frametype.h"
+
+
+static FrameTable *frameTable=NULL;
+static boolean use_cache = FALSE;
+static int firstI = 0;
+static int numFrames;
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+extern int framePatternLen;
+extern char * framePattern;
+
+
+/*=====================*
+ * EXPORTED PROCEDURES *
+ *=====================*/
+
+/*===========================================================================*
+ *
+ * FType_Type
+ *
+ *	returns the type of the given numbered frame
+ *
+ * RETURNS:	the type
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+char
+FType_Type(unsigned int const frameNum) {
+
+    char const patternedType = framePattern[frameNum % framePatternLen];
+
+    char retval;
+
+    if (use_cache) 
+        return frameTable[frameNum].typ;
+  
+    if (frameNum+1 == numFrames) {
+        /* It's the last frame in the sequence.  If the pattern says it's
+           a B, we convert it to I because a B frame makes no sense as the
+           last frame of a sequence.
+        */
+        if (patternedType == 'b') 
+            retval = 'i';
+        else 
+            retval = patternedType;
+    } else {
+        if (specificsOn) {
+            static int lastI = -1;
+            int newtype;
+      
+            if (lastI > frameNum) 
+                lastI = -1;
+            newtype = SpecTypeLookup(frameNum);
+            switch (newtype) {
+            case 1:
+                lastI = frameNum;
+                retval = 'i';
+                break;
+            case 2:
+                retval = 'p';
+                break;
+            case 3:
+                retval = 'b';
+                break;
+            default:
+                if (lastI != -1) {
+                    unsigned int const pretendFrameNumber = 
+                        (frameNum - lastI + firstI) % framePatternLen;
+                    retval = framePattern[pretendFrameNumber];
+                } else 
+                    retval = patternedType;
+            }
+        } else 
+            retval = patternedType;
+    }
+    return retval;
+}
+
+
+
+unsigned int
+FType_FutureRef(unsigned int const currFrameNum) {
+/*----------------------------------------------------------------------------
+  Return the number of the future reference frame for the B frame
+  'currentFrameNum'.
+-----------------------------------------------------------------------------*/
+    unsigned int retval;
+
+    if (use_cache) {
+        retval = frameTable[currFrameNum].next->number;
+    } else {
+        int const index = currFrameNum % framePatternLen;
+        int const futureIndex = frameTable[index].next->number;
+        unsigned int const patternedFutureRef =
+            currFrameNum +
+            (((futureIndex-index)+framePatternLen) % framePatternLen);
+
+        retval = MIN(patternedFutureRef, numFrames-1);
+    }
+    return retval;
+}
+
+
+
+/*===========================================================================*
+ *
+ * FType_PastRef
+ *
+ *	returns the number of the past reference frame
+ *
+ * RETURNS:	the number
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+int
+FType_PastRef(currFrameNum)
+    int currFrameNum;
+{
+    int	    index;
+    int	    pastIndex;
+
+    if (use_cache) {
+      return frameTable[currFrameNum].prev->number;
+    } else {
+      index = currFrameNum % framePatternLen;
+      pastIndex = frameTable[index].prev->number;
+      
+      return currFrameNum -
+	(((index-pastIndex)+framePatternLen) % framePatternLen);
+    }
+}
+
+
+/*===========================================================================*
+ *
+ * SetFramePattern
+ *
+ *	set the IPB pattern; calls ComputeFrameTable to set up table
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    framePattern, framePatternLen, frameTable
+ *
+ *===========================================================================*/
+#define SIMPLE_ASCII_UPPER(x)  (((x)>='a') ? ((x)-'a'+'A') : (x))
+void
+SetFramePattern(const char * const pattern) {
+    unsigned int const len = strlen(pattern);
+
+    char *buf;
+    unsigned int index;
+
+    if (!pattern)
+        pm_error("INTERNAL ERROR: pattern cannot be NULL "
+            "in SetFramePattern");
+
+    if (SIMPLE_ASCII_UPPER(pattern[0]) != 'I') {
+        unsigned int index;
+        for (index = 0; index < len; ++index) {
+            if (SIMPLE_ASCII_UPPER(pattern[index]) == 'I') {
+                break;
+            } else if (SIMPLE_ASCII_UPPER(pattern[index]) == 'P')
+                pm_error("first reference frame must be 'i', not '%c'",
+                         pattern[index]);
+        }
+    }
+
+    buf = (char *)malloc(sizeof(char)*(len+1));
+    ERRCHK(buf, "malloc");
+
+    firstI = -1;
+    for (index = 0; index < len; index++) {
+        switch( SIMPLE_ASCII_UPPER(pattern[index]) ) {
+        case 'I':	
+            buf[index] = 'i';
+            if (firstI == -1) firstI = index;
+            break;
+        case 'P':	
+            buf[index] = 'p'; 
+            break;
+        case 'B':	
+            buf[index] = 'b';
+            break;
+        default:
+            pm_error("Invalid MPEG Frame type '%c'.", pattern[index]);
+        }
+    }
+    buf[len] = 0;
+
+    if (firstI == -1)
+        pm_error("Must have an I-frame in PATTERN");
+
+    framePattern = buf;
+    framePatternLen = len;
+    
+    /* Used to ComputeFrameTable(), but now must wait until param
+       parsed. (STDIN or not)
+    */
+}
+
+
+
+void
+ComputeFrameTable(unsigned int const numFramesArg) {
+/*----------------------------------------------------------------------------
+  Compute a table of I, P, B frames to help in determining dependencie
+
+  'numFrames' == 0 means number of frames is not known at this time.
+-----------------------------------------------------------------------------*/
+    int index;
+    FrameTable	*lastI, *lastIP, *firstB, *secondIP;
+    FrameTable	*ptr;
+    char typ;
+    int table_size;
+
+    numFrames = numFramesArg;
+
+    if (numFrames)
+        table_size = numFrames;
+    else
+        table_size = framePatternLen;
+
+    MALLOCARRAY_NOFAIL(frameTable, 1 + table_size);
+
+    lastI = NULL;
+    lastIP = NULL;
+    firstB = NULL;
+    secondIP = NULL;
+    for ( index = 0; index < table_size; index++ ) {
+        frameTable[index].number = index;
+        typ = FType_Type(index);
+        frameTable[index].typ = typ;
+        switch( typ ) {
+	    case 'i':
+            ptr = firstB;
+            while ( ptr != NULL ) {
+                ptr->next = &(frameTable[index]);
+                ptr = ptr->nextOutput;
+            }
+            frameTable[index].nextOutput = firstB;
+            frameTable[index].prev = lastIP;	/* for freeing */
+            if ( lastIP != NULL ) {
+                lastIP->next = &(frameTable[index]);
+                if ( secondIP == NULL ) {
+                    secondIP = &(frameTable[index]);
+                }
+            }
+            lastIP = &(frameTable[index]);
+            firstB = NULL;
+            break;
+	    case 'p':
+            ptr = firstB;
+            while ( ptr != NULL ) {
+                ptr->next = &(frameTable[index]);
+                ptr = ptr->nextOutput;
+            }
+            frameTable[index].nextOutput = firstB;
+            frameTable[index].prev = lastIP;
+            if ( lastIP != NULL ) {
+                lastIP->next = &(frameTable[index]);
+                if ( secondIP == NULL ) {
+                    secondIP = &(frameTable[index]);
+                }
+            }
+            lastIP = &(frameTable[index]);
+            firstB = NULL;
+            break;
+	    case 'b':
+            if ( (index+1 == framePatternLen) ||
+                 (FType_Type(index+1) != 'b') ) {
+                frameTable[index].nextOutput = NULL;
+            } else {
+                frameTable[index].nextOutput = &(frameTable[index+1]);
+            }
+            frameTable[index].prev = lastIP;
+            if ( firstB == NULL ) {
+                firstB = &(frameTable[index]);
+            }
+            break;
+	    default:
+	        fprintf(stderr, "Programmer Error in ComputeFrameTable (%d)\n",
+                    framePattern[index]);
+	        exit(1);
+	        break;
+        }
+    }
+    
+    /* why? SRS */
+    frameTable[table_size].number = framePatternLen;
+    ptr = firstB;
+    while ( ptr != NULL ) {
+        ptr->next = &(frameTable[table_size]);
+        ptr = ptr->nextOutput;
+    }
+    frameTable[table_size].nextOutput = firstB;
+    frameTable[table_size].prev = lastIP;
+    if ( secondIP == NULL )
+        frameTable[table_size].next = &(frameTable[0]);
+    else
+        frameTable[table_size].next = secondIP;
+
+    frameTable[0].prev = lastIP;
+    if ( lastIP != NULL ) {
+        lastIP->next = &(frameTable[table_size]);
+    }
+
+    if (numFrames)
+        use_cache = TRUE;
+}
diff --git a/converter/ppm/ppmtompeg/fsize.c b/converter/ppm/ppmtompeg/fsize.c
new file mode 100644
index 00000000..3808405c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/fsize.c
@@ -0,0 +1,134 @@
+/*===========================================================================*
+ * fsize.c								     *
+ *									     *
+ *	procedures to keep track of frame size				     *
+ *									     *
+ * EXPORTED PROCEDURES:							     *
+ *	Fsize_Reset							     *
+ *	Fsize_Note							     *
+ *	Fsize_Validate							     *
+ *									     *
+ * EXPORTED VARIABLES:							     *
+ *	Fsize_x								     *
+ *	Fsize_y								     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "all.h"
+#include "fsize.h"
+#include "dct.h"
+
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+int Fsize_x = 0;
+int Fsize_y = 0;
+
+
+/*=====================*
+ * EXPORTED PROCEDURES *
+ *=====================*/
+
+/*===========================================================================*
+ *
+ * Fsize_Reset
+ *
+ *	reset the frame size to 0
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    Fsize_x, Fsize_y
+ *
+ *===========================================================================*/
+void
+Fsize_Reset(void) {
+    Fsize_x = Fsize_y = 0;
+}
+
+
+
+/*===========================================================================*
+ *
+ * Fsize_Validate
+ *
+ *	make sure that the x, y values are 16-pixel aligned
+ *
+ * RETURNS:	modifies the x, y values to 16-pixel alignment
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+Fsize_Validate(int * const xP,
+               int * const yP) {
+
+    *xP &= ~(DCTSIZE * 2 - 1);  /* Zero the low-order bits */
+    *yP &= ~(DCTSIZE * 2 - 1);  /* Zero the low-order bits */
+}
+
+
+/*===========================================================================*
+ *
+ * Fsize_Note
+ *
+ *	note the given frame size and modify the global values as appropriate
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    Fsize_x, Fsize_y
+ *
+ *===========================================================================*/
+void
+Fsize_Note(int          const id,
+           unsigned int const width,
+           unsigned int const height) {
+
+    Fsize_x = width;
+    Fsize_y = height;
+    Fsize_Validate(&Fsize_x, &Fsize_y);
+
+    if ((Fsize_x == 0) || (Fsize_y == 0)) {
+        fprintf(stderr,"Frame %d:  size is less than the minimum: %d x %d!\n",
+                id, DCTSIZE*2, DCTSIZE*2);
+        exit(1);
+    }
+
+#ifdef BLEAH
+    if (Fsize_x == 0) {
+	Fsize_x = width;
+	Fsize_y = height;
+	Fsize_Validate(&Fsize_x, &Fsize_y);
+    } else if (width < Fsize_x || height < Fsize_y) {
+	fprintf(stderr, "Frame %d: wrong size: (%d,%d).  Should be greater or equal to: (%d,%d)\n",
+		id, width, height, Fsize_x, Fsize_y);
+	exit(1);
+    }
+#endif
+}
diff --git a/converter/ppm/ppmtompeg/gethostname.c b/converter/ppm/ppmtompeg/gethostname.c
new file mode 100644
index 00000000..014b42e8
--- /dev/null
+++ b/converter/ppm/ppmtompeg/gethostname.c
@@ -0,0 +1,26 @@
+#define _BSD_SOURCE   /* Make sure strdup() is in string.h */
+
+#include <string.h>
+#include <errno.h>
+#include <sys/utsname.h>
+
+#include "pm.h"
+
+#include "gethostname.h"
+
+const char *
+GetHostName(void) {
+/*----------------------------------------------------------------------------
+   Return the host name of this system.
+-----------------------------------------------------------------------------*/
+    struct utsname utsname;
+    int rc;
+
+    rc = uname(&utsname);
+
+    if (rc < 0)
+        pm_error("Unable to find out host name.  "
+                 "uname() failed with errno %d (%s)", errno, strerror(errno));
+
+    return strdup(utsname.nodename);
+}
diff --git a/converter/ppm/ppmtompeg/gethostname.h b/converter/ppm/ppmtompeg/gethostname.h
new file mode 100644
index 00000000..e5450175
--- /dev/null
+++ b/converter/ppm/ppmtompeg/gethostname.h
@@ -0,0 +1,7 @@
+#ifndef GETHOSTNAME_H_INCLUDED
+#define GETHOSTNAME_H_INCLUDED
+
+const char *
+GetHostName(void);
+
+#endif
diff --git a/converter/ppm/ppmtompeg/headers/all.h b/converter/ppm/ppmtompeg/headers/all.h
new file mode 100644
index 00000000..e350aab8
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/all.h
@@ -0,0 +1,95 @@
+/*===========================================================================*
+ * all.h								     *
+ *									     *
+ *	stuff included from ALL source files				     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /u/smoot/md/mpeg_encode/headers/RCS/all.h,v 1.9 1995/06/05 21:11:06 smoot Exp $
+ *  $Log: all.h,v $
+ * Revision 1.9  1995/06/05  21:11:06  smoot
+ * added little_endian force for irizx
+ *
+ * Revision 1.8  1995/02/02  22:02:18  smoot
+ * added ifdefs for compatability on stranger and stranger architectures...
+ *
+ * Revision 1.7  1995/02/02  07:26:45  eyhung
+ * added parens to all.h to remove compiler warning
+ *
+ * Revision 1.6  1995/02/02  01:47:11  eyhung
+ * added MAXINT
+ *
+ * Revision 1.5  1995/01/19  23:54:33  eyhung
+ * Changed copyrights
+ *
+ * Revision 1.4  1994/11/14  22:52:04  smoot
+ * Added linux #include for time.h
+ *
+ * Revision 1.3  1994/11/12  02:12:13  keving
+ * nothing
+ *
+ * Revision 1.2  1993/07/22  22:24:23  keving
+ * nothing
+ *
+ * Revision 1.1  1993/07/09  00:17:23  keving
+ * nothing
+ *
+ */
+
+
+#ifndef ENCODE_ALL_INCLUDED
+#define ENCODE_ALL_INCLUDED
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <memory.h>
+#include <limits.h>
+
+/* There's got to be a better way.... */
+#ifdef LINUX
+#include <time.h>
+#endif
+#ifdef MIPS
+#include <time.h>
+#endif
+#ifdef IRIX
+#define FORCE_LITTLE_ENDIAN
+#include <time.h>
+#endif
+
+#include "ansi.h"
+#include "general.h"
+
+/* some machines have #define index strchr; get rid of this nonsense */
+#ifdef index
+#undef index
+#endif /* index */
+
+#ifndef MAXINT
+#define MAXINT 0x7fffffff
+#endif
+
+#endif /* ENCODE_ALL_INCLUDED */
diff --git a/converter/ppm/ppmtompeg/headers/ansi.h b/converter/ppm/ppmtompeg/headers/ansi.h
new file mode 100644
index 00000000..b3c3ab17
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/ansi.h
@@ -0,0 +1,76 @@
+/*===========================================================================*
+ * ansi.h								     *
+ *									     *
+ *	macro for non-ansi compilers					     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/headers/RCS/ansi.h,v 1.6 1995/08/15 23:43:13 smoot Exp $
+ *  $Log: ansi.h,v $
+ *  Revision 1.6  1995/08/15 23:43:13  smoot
+ *  *** empty log message ***
+ *
+ *  Revision 1.5  1995/01/19 23:54:35  eyhung
+ *  Changed copyrights
+ *
+ * Revision 1.4  1994/11/12  02:12:13  keving
+ * nothing
+ *
+ * Revision 1.3  1993/07/22  22:24:23  keving
+ * nothing
+ *
+ * Revision 1.2  1993/07/09  00:17:23  keving
+ * nothing
+ *
+ * Revision 1.1  1993/06/14  22:50:22  keving
+ * nothing
+ *
+ */
+
+
+#ifndef ANSI_INCLUDED
+#define ANSI_INCLUDED
+
+
+/*  
+ *  _ANSI_ARGS_ macro stolen from Tcl6.5 by John Ousterhout
+ */
+#undef _ANSI_ARGS_
+#undef const
+#ifdef NON_ANSI_COMPILER
+#define _ANSI_ARGS_(x)       ()
+#define CONST
+#else
+#define _ANSI_ARGS_(x)   x
+#define CONST const
+#ifdef __cplusplus
+#define VARARGS (...)
+#else
+#define VARARGS ()
+#endif
+#endif
+
+
+#endif /* ANSI_INCLUDED */
diff --git a/converter/ppm/ppmtompeg/headers/bitio.h b/converter/ppm/ppmtompeg/headers/bitio.h
new file mode 100644
index 00000000..89e61fbb
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/bitio.h
@@ -0,0 +1,140 @@
+/*===========================================================================*
+ * bitio.h								     *
+ *									     *
+ *	bitwise input/output						     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /n/charlie-brown/project/mm/mpeg/mpeg_dist/mpeg_encode/headers/RCS/bitio.h,v 1.8 1995/01/19 23:54:37 eyhung Exp $
+ *  $Log: bitio.h,v $
+ * Revision 1.8  1995/01/19  23:54:37  eyhung
+ * Changed copyrights
+ *
+ * Revision 1.7  1994/11/12  02:12:14  keving
+ * nothing
+ *
+ * Revision 1.6  1993/07/22  22:24:23  keving
+ * nothing
+ *
+ * Revision 1.5  1993/07/09  00:17:23  keving
+ * nothing
+ *
+ * Revision 1.4  1993/06/03  21:08:53  keving
+ * nothing
+ *
+ * Revision 1.3  1993/01/18  10:20:02  dwallach
+ * *** empty log message ***
+ *
+ * Revision 1.2  1993/01/18  10:17:29  dwallach
+ * RCS headers installed, code indented uniformly
+ *
+ * Revision 1.2  1993/01/18  10:17:29  dwallach
+ * RCS headers installed, code indented uniformly
+ *
+ */
+
+
+#ifndef BIT_IO_INCLUDED
+#define BIT_IO_INCLUDED
+
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "general.h"
+#include "ansi.h"
+
+
+/*===========*
+ * CONSTANTS *
+ *===========*/
+
+#define WORDS_PER_BUCKET 128
+#define MAXBITS_PER_BUCKET	(WORDS_PER_BUCKET * 32)
+#define	MAX_BUCKETS	128
+#define MAX_BITS	MAX_BUCKETS*MAXBITS_PER_BUCKET
+
+
+/*=======================*
+ * STRUCTURE DEFINITIONS *
+ *=======================*/
+
+typedef struct bitBucket {
+    struct bitBucket *nextPtr;
+    uint32 bits[WORDS_PER_BUCKET];
+    int bitsleft, bitsleftcur, currword;
+} ActualBucket;
+
+typedef struct _BitBucket {
+    int totalbits;
+    int	cumulativeBits;
+    int	bitsWritten;
+    FILE    *filePtr;
+    ActualBucket *firstPtr;
+    ActualBucket *lastPtr;
+} BitBucket;
+
+
+/*========*
+ * MACROS *
+ *========*/
+
+#define	SET_ITH_BIT(bits, i)	(bits |= (1 << (i)))
+#define	GET_ITH_BIT(bits, i)	(bits & (1 << (i)))
+
+
+/*===============================*
+ * EXTERNAL PROCEDURE prototypes *
+ *===============================*/
+
+void	    
+Bitio_Free(BitBucket * const bbPtr);
+
+void
+Bitio_Write(BitBucket * const bbPtr, 
+            uint32      const bits, 
+            int         const nbits);
+
+void
+Bitio_BytePad(BitBucket * const bbPtr);
+
+BitBucket *
+Bitio_New(FILE * const filePtr);
+
+BitBucket *
+Bitio_New_Filename(const char * const fileName);
+
+void
+Bitio_Flush(BitBucket * const bbPtr);
+
+void
+Bitio_Close(BitBucket * const bbPtr);
+
+void
+Bitio_WriteToSocket(BitBucket * const bbPtr, 
+                    int         const socket);
+
+#endif /* BIT_IO_INCLUDED */
diff --git a/converter/ppm/ppmtompeg/headers/block.h b/converter/ppm/ppmtompeg/headers/block.h
new file mode 100644
index 00000000..46050492
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/block.h
@@ -0,0 +1,53 @@
+void
+ComputeDiffDCTs(MpegFrame * const current,
+                MpegFrame * const prev,
+                int         const by,
+                int         const bx,
+                vector      const m,
+                int *       const pattern);
+
+void
+ComputeDiffDCTBlock(Block           current,
+                    Block           dest,
+                    Block           motionBlock,
+                    boolean * const significantDifferenceP);
+
+void
+ComputeMotionBlock(uint8 ** const prev,
+                   int      const by,
+                   int      const bx,
+                   vector   const m,
+                   Block *  const motionBlockP);
+
+void
+ComputeMotionLumBlock(MpegFrame * const prevFrame,
+                      int         const by,
+                      int         const bx,
+                      vector      const m,
+                      LumBlock *  const motionBlockP);
+
+void
+BlockToData(uint8 ** const data,
+            Block          block,
+            int      const by,
+            int      const bx);
+
+void
+AddMotionBlock(Block          block,
+               uint8 ** const prev,
+               int      const by,
+               int      const bx,
+               vector   const m);
+
+void
+AddBMotionBlock(Block          block,
+                uint8 ** const prev,
+                uint8 ** const next,
+                int      const by,
+                int      const bx,
+                int      const mode,
+                motion   const motion);
+
+void
+BlockifyFrame(MpegFrame * const frameP);
+
diff --git a/converter/ppm/ppmtompeg/headers/byteorder.h b/converter/ppm/ppmtompeg/headers/byteorder.h
new file mode 100644
index 00000000..0070252a
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/byteorder.h
@@ -0,0 +1,77 @@
+/*===========================================================================*
+ * byteorder.h								     *
+ *									     *
+ *	stuff to handle different byte order				     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /u/smoot/md/mpeg_encode/headers/RCS/byteorder.h,v 1.3 1995/01/19 23:54:39 eyhung Exp $
+ *  $Log: byteorder.h,v $
+ * Revision 1.3  1995/01/19  23:54:39  eyhung
+ * Changed copyrights
+ *
+ * Revision 1.3  1995/01/19  23:54:39  eyhung
+ * Changed copyrights
+ *
+ * Revision 1.2  1994/11/12  02:12:15  keving
+ * nothing
+ *
+ * Revision 1.1  1993/07/22  22:24:23  keving
+ * nothing
+ *
+ */
+
+
+#include "general.h"
+
+
+#ifdef FORCE_BIG_ENDIAN
+    /* leave byte order as it is */
+#define htonl(x) (x)
+#define ntohl(x) (x)
+#define htons(x) (x)
+#define ntohs(x) (x)
+#else
+#ifdef FORCE_LITTLE_ENDIAN
+    /* need to reverse byte order */
+    /* note -- we assume here that htonl is called on a variable, not a
+     * constant; thus, this is not for general use, but works with bitio.c
+     */
+#define htonl(x)    \
+    ((((unsigned char *)(&x))[0] << 24) |	\
+     (((unsigned char *)(&x))[1] << 16) |	\
+     (((unsigned char *)(&x))[2] << 8) |	\
+     (((unsigned char *)(&x))[3]))
+#define ntohl(x)    htonl(x)
+#define htons(x)    \
+    ((((unsigned char *)(&x))[0] << 8) |	\
+     ((unsigned char *)(&x))[1])
+#define ntohs(x)    htons(x)
+#else
+    /* let in.h handle it, if possible */		   
+#include <sys/types.h>
+#include <netinet/in.h>
+#endif /* FORCE_LITTLE_ENDIAN */
+#endif /* FORCE_BIG_ENDIAN */
diff --git a/converter/ppm/ppmtompeg/headers/combine.h b/converter/ppm/ppmtompeg/headers/combine.h
new file mode 100644
index 00000000..e28c6dee
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/combine.h
@@ -0,0 +1,20 @@
+struct inputSource;
+
+void
+GOPsToMPEG(struct inputSource * const inputSourceP,
+           const char *         const outputFileName, 
+           FILE *               const outputFilePtr);
+
+typedef void (*fileAcquisitionFn)(void *       const handle,
+                                  unsigned int const frameNumber,
+                                  FILE **      const ifPP);
+
+
+typedef void (*fileDispositionFn)(void *       const handle,
+                                  unsigned int const frameNumber);
+
+void
+FramesToMPEG(FILE *               const outputFile, 
+             void *               const inputHandle,
+             fileAcquisitionFn          acquireInputFile,
+             fileDispositionFn          disposeInputFile);
diff --git a/converter/ppm/ppmtompeg/headers/dct.h b/converter/ppm/ppmtompeg/headers/dct.h
new file mode 100644
index 00000000..e024b6c1
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/dct.h
@@ -0,0 +1,79 @@
+/*===========================================================================*
+ * dct.h								     *
+ *									     *
+ *	DCT procedures							     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+
+#ifndef DCT_INCLUDED
+#define DCT_INCLUDED
+
+
+#include "ansi.h"
+
+
+
+#define DCTSIZE     8   /* you really don't want to change this */
+#define DCTSIZE_SQ 64   /* you really don't want to change this */
+
+#define DCTSIZE2    64
+typedef short DCTELEM;
+typedef DCTELEM DCTBLOCK[DCTSIZE2];
+typedef DCTELEM DCTBLOCK_2D[DCTSIZE][DCTSIZE];
+
+
+/*  
+ *  from mfwddct.c:
+ */
+extern void mp_fwd_dct_block2 _ANSI_ARGS_((DCTBLOCK_2D src, DCTBLOCK_2D dest));
+
+/* jrevdct.c */
+extern void init_pre_idct _ANSI_ARGS_((void ));
+extern void mpeg_jrevdct _ANSI_ARGS_((DCTBLOCK data ));
+
+
+/* We assume that right shift corresponds to signed division by 2 with
+ * rounding towards minus infinity.  This is correct for typical "arithmetic
+ * shift" instructions that shift in copies of the sign bit.  But some
+ * C compilers implement >> with an unsigned shift.  For these machines you
+ * must define RIGHT_SHIFT_IS_UNSIGNED.
+ * RIGHT_SHIFT provides a proper signed right shift of an int32 quantity.
+ * It is only applied with constant shift counts.  SHIFT_TEMPS must be
+ * included in the variables of any routine using RIGHT_SHIFT.
+ */
+
+#ifdef RIGHT_SHIFT_IS_UNSIGNED
+#define SHIFT_TEMPS     int32 shift_temp;
+#define RIGHT_SHIFT(x,shft)  \
+        ((shift_temp = (x)) < 0 ? \
+         (shift_temp >> (shft)) | ((~((int32) 0)) << (32-(shft))) : \
+         (shift_temp >> (shft)))
+#else
+#define SHIFT_TEMPS
+#define RIGHT_SHIFT(x,shft)     ((x) >> (shft))
+#endif
+
+
+#endif /* DCT_INCLUDED */
diff --git a/converter/ppm/ppmtompeg/headers/frame.h b/converter/ppm/ppmtompeg/headers/frame.h
new file mode 100644
index 00000000..1003ee15
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/frame.h
@@ -0,0 +1,147 @@
+/*===========================================================================*
+ * frame.h								     *
+ *									     *
+ *	basic frames procedures						     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+
+#ifndef FRAME_INCLUDED
+#define FRAME_INCLUDED
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "general.h"
+#include "ansi.h"
+#include "mtypes.h"
+
+/*===========*
+ * CONSTANTS *
+ *===========*/
+#define TYPE_IFRAME	2
+#define TYPE_PFRAME	3
+#define TYPE_BFRAME	4
+
+
+/*=======================*
+ * STRUCTURE DEFINITIONS *
+ *=======================*/
+
+typedef struct mpegFrame {
+    int type;
+    char    inputFileName[256];
+    int id;           /* the frame number -- starts at 0 */
+    boolean inUse;	/* TRUE iff this frame is currently being used */
+			/* FALSE means any data here can be thrashed */
+
+    /*  
+     *  now, the YCrCb data.  All pixel information is stored in unsigned
+     *  8-bit pieces.  We separate y, cr, and cb because cr and cb are
+     *  subsampled by a factor of 2.
+     *
+     *  if orig_y is NULL, then orig_cr, orig_cb are undefined
+     */
+    uint8 **orig_y, **orig_cr, **orig_cb;
+
+    /* now, the decoded data -- relevant only if
+     *	    referenceFrame == DECODED_FRAME
+     *
+     * if decoded_y is NULL, then decoded_cr, decoded_cb are undefined 
+     */
+    uint8 **decoded_y, **decoded_cr, **decoded_cb;
+
+    /* reference data */
+    uint8 **ref_y, **ref_cr, **ref_cb;
+
+    /*  
+     *  these are the Blocks which will ultimately compose MacroBlocks.
+     *  A Block is in a format that mp_fwddct() can crunch.
+     *  if y_blocks is NULL, then cr_blocks, cb_blocks are undefined
+     */
+    Block **y_blocks, **cr_blocks, **cb_blocks;
+
+    /*
+     *  this is the half-pixel luminance data (for reference frames)
+     */
+    uint8 **halfX, **halfY, **halfBoth;
+
+    boolean   halfComputed;        /* TRUE iff half-pixels already computed */
+
+    struct mpegFrame *next;  /* points to the next B-frame to be encoded, if
+		       * stdin is used as the input. 
+		       */
+} MpegFrame;
+
+
+void
+Frame_Init(unsigned int const numOfFramesRequested);
+
+void
+Frame_Exit(void);
+
+void
+Frame_Free(MpegFrame * const frameP);
+
+MpegFrame *
+Frame_New(int const id,
+          int const type);
+
+void
+Frame_AllocBlocks(MpegFrame * const frameP);
+
+void
+Frame_AllocYCC(MpegFrame * const frameP);
+
+void
+Frame_AllocHalf(MpegFrame * const frameP);
+
+void
+Frame_AllocDecoded(MpegFrame * const frameP,
+                   boolean     const makeReference);
+
+void
+Frame_Resize(MpegFrame * const omf,
+             MpegFrame * const mf,
+             int         const insize_x,
+             int         const insize_y,
+             int         const outsize_x,
+             int         const outsize_y);
+
+
+extern void	  Frame_Free _ANSI_ARGS_((MpegFrame *frame));
+extern void	  Frame_Exit _ANSI_ARGS_((void));
+extern void	  Frame_AllocPPM _ANSI_ARGS_((MpegFrame * frame));
+extern void	  Frame_AllocYCC _ANSI_ARGS_((MpegFrame * mf));
+extern void	  Frame_AllocDecoded _ANSI_ARGS_((MpegFrame *frame,
+						  boolean makeReference));
+extern void	  Frame_AllocHalf _ANSI_ARGS_((MpegFrame *frame));
+extern void	  Frame_AllocBlocks _ANSI_ARGS_((MpegFrame *mf));
+extern void	  Frame_Resize _ANSI_ARGS_((MpegFrame *omf, MpegFrame *mf,
+					    int insize_x, int insize_y,
+					    int outsize_x, int outsize_y));
+
+
+#endif /* FRAME_INCLUDED */
diff --git a/converter/ppm/ppmtompeg/headers/frames.h b/converter/ppm/ppmtompeg/headers/frames.h
new file mode 100644
index 00000000..3fefaea7
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/frames.h
@@ -0,0 +1,487 @@
+/*===========================================================================*
+ * frames.h                                  
+ *                                       
+ *  stuff dealing with frames                        
+ *                                       
+ *===========================================================================*/
+
+#ifndef FRAMES_INCLUDED
+#define FRAMES_INCLUDED
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "ansi.h"
+#include "mtypes.h"
+#include "mheaders.h"
+#include "frame.h"
+
+
+/*===========*
+ * CONSTANTS *
+ *===========*/
+
+#define I_FRAME 1
+#define P_FRAME 2
+#define B_FRAME 3
+
+#define LUM_BLOCK   0
+#define CHROM_BLOCK 1
+#define CR_BLOCK    2
+#define CB_BLOCK    3
+
+#define MOTION_FORWARD      0
+#define MOTION_BACKWARD     1
+#define MOTION_INTERPOLATE  2
+
+
+#define USE_HALF    0
+#define USE_FULL    1
+
+    /* motion vector stuff */
+#define FORW_F_CODE fCode       /* from picture header */
+#define BACK_F_CODE fCode
+#define FORW_F  (1 << (FORW_F_CODE - 1))
+#define BACK_F  (1 << (BACK_F_CODE - 1))
+#define RANGE_NEG   (-(1 << (3 + FORW_F_CODE)))
+#define RANGE_POS   ((1 << (3 + FORW_F_CODE))-1)
+#define MODULUS     (1 << (4 + FORW_F_CODE))
+
+#define ORIGINAL_FRAME  0
+#define DECODED_FRAME   1
+
+
+/*=======================*
+ * STRUCTURE DEFINITIONS *
+ *=======================*/
+
+typedef struct FrameTableStruct {
+    /* the following are all initted once and never changed */
+    /* (they depend only on the pattern */
+    char typ;
+    struct FrameTableStruct *next;
+    struct FrameTableStruct *prev;
+
+    /* nextOutput is a pointer to next frame table entry to output */
+    struct FrameTableStruct *nextOutput;
+
+    boolean freeNow;    /* TRUE iff no frames point back to this */
+
+    int number;
+
+    int bFrameNumber;       /* actual frame number, if a b-frame */
+    
+} FrameTable;
+
+
+/*==================*
+ * TYPE DEFINITIONS *
+ *==================*/
+
+typedef struct dct_data_tye_struct {
+  char useMotion;
+  char pattern, mode;
+  int fmotionX, fmotionY, bmotionX, bmotionY;
+} dct_data_type;
+
+void    EncodeYDC _ANSI_ARGS_((int32 dc_term, int32 *pred_term, BitBucket *bb));
+void
+EncodeCDC(int32       const dc_term,
+          int32     * const pred_term,
+          BitBucket * const bb);
+
+
+/*========*
+ * MACROS *
+ *========*/
+
+/* return ceiling(a/b) where a, b are ints, using temp value c */
+#define int_ceil_div(a,b,c)     ((b*(c = a/b) < a) ? (c+1) : c)
+#define int_floor_div(a,b,c)    ((b*(c = a/b) > a) ? (c-1) : c)
+
+/* assumes many things:
+ * block indices are (y,x)
+ * variables y_dc_pred, cr_dc_pred, and cb_dc_pred
+ * flat block fb exists
+ */
+#define GEN_I_BLOCK(frameType, frame, bb, mbAI, qscale) {                   \
+    boolean overflow, overflowChange=FALSE;                             \
+        int overflowValue = 0;                                              \
+        do {                                                                \
+      overflow =  Mpost_QuantZigBlock(dct[y][x], fb[0],                 \
+             qscale, TRUE)==MPOST_OVERFLOW;                     \
+          overflow |= Mpost_QuantZigBlock(dct[y][x+1], fb[1],               \
+                 qscale, TRUE)==MPOST_OVERFLOW;                     \
+      overflow |= Mpost_QuantZigBlock(dct[y+1][x], fb[2],               \
+                         qscale, TRUE)==MPOST_OVERFLOW;                     \
+      overflow |= Mpost_QuantZigBlock(dct[y+1][x+1], fb[3],             \
+                         qscale, TRUE)==MPOST_OVERFLOW;                     \
+      overflow |= Mpost_QuantZigBlock(dctb[y >> 1][x >> 1],             \
+                         fb[4], qscale, TRUE)==MPOST_OVERFLOW;              \
+      overflow |= Mpost_QuantZigBlock(dctr[y >> 1][x >> 1],             \
+             fb[5], qscale, TRUE)==MPOST_OVERFLOW;              \
+          if ((overflow) && (qscale!=31)) {                                 \
+           overflowChange = TRUE; overflowValue++;                          \
+       qscale++;                                                        \
+       } else overflow = FALSE;                                         \
+    } while (overflow);                                                 \
+        Mhead_GenMBHeader(bb,                           \
+            frameType /* pict_code_type */, mbAI /* addr_incr */,   \
+            qscale /* q_scale */,                               \
+            0 /* forw_f_code */, 0 /* back_f_code */,           \
+            0 /* horiz_forw_r */, 0 /* vert_forw_r */,          \
+            0 /* horiz_back_r */, 0 /* vert_back_r */,          \
+            0 /* motion_forw */, 0 /* m_horiz_forw */,          \
+            0 /* m_vert_forw */, 0 /* motion_back */,           \
+            0 /* m_horiz_back */, 0 /* m_vert_back */,          \
+            0 /* mb_pattern */, TRUE /* mb_intra */);           \
+                                        \
+    /* Y blocks */                              \
+        EncodeYDC(fb[0][0], &y_dc_pred, bb);                            \
+    Mpost_RLEHuffIBlock(fb[0], bb);                         \
+    EncodeYDC(fb[1][0], &y_dc_pred, bb);                        \
+        Mpost_RLEHuffIBlock(fb[1], bb);                             \
+    EncodeYDC(fb[2][0], &y_dc_pred, bb);                        \
+    Mpost_RLEHuffIBlock(fb[2], bb);                         \
+    EncodeYDC(fb[3][0], &y_dc_pred, bb);                        \
+    Mpost_RLEHuffIBlock(fb[3], bb);                         \
+                                        \
+    /* CB block */                              \
+    EncodeCDC(fb[4][0], &cb_dc_pred, bb);                   \
+    Mpost_RLEHuffIBlock(fb[4], bb);                     \
+                                        \
+    /* CR block */                              \
+    EncodeCDC(fb[5][0], &cr_dc_pred, bb);                   \
+    Mpost_RLEHuffIBlock(fb[5], bb);                     \
+    if (overflowChange) qscale -= overflowValue;                        \
+    }
+
+#define BLOCK_TO_FRAME_COORD(bx1, bx2, x1, x2) {    \
+    x1 = (bx1)*DCTSIZE;             \
+    x2 = (bx2)*DCTSIZE;             \
+    }
+
+static __inline__ void
+MotionToFrameCoord(int   const by,
+                   int   const bx,
+                   int   const my,
+                   int   const mx,
+                   int * const yP,
+                   int * const xP) {
+/*----------------------------------------------------------------------------
+   Given a DCT block location and a motion vector, return the pixel
+   coordinates to which the upper left corner of the block moves.
+
+   Return negative coordinates if it moves out of frame.
+-----------------------------------------------------------------------------*/
+    *yP = by * DCTSIZE + my;
+    *xP = bx * DCTSIZE + mx;
+}
+
+#define COORD_IN_FRAME(fy,fx, type)                 \
+    ((type == LUM_BLOCK) ?                      \
+     ((fy >= 0) && (fx >= 0) && (fy < Fsize_y) && (fx < Fsize_x)) : \
+     ((fy >= 0) && (fx >= 0) && (fy < (Fsize_y>>1)) && (fx < (Fsize_x>>1))))
+
+
+static __inline__ void
+encodeMotionVector(int      const x,
+                   int      const y,
+                   vector * const qP,
+                   vector * const rP,
+                   int      const f,
+                   int      const fCode) {
+
+    int tempX;
+    int tempY;
+    int tempC;
+
+    if (x < RANGE_NEG)
+        tempX = x + MODULUS;
+    else if (x > RANGE_POS)
+        tempX = x - MODULUS;
+    else
+        tempX = x;
+
+    if (y < RANGE_NEG)
+        tempY = y + MODULUS;
+    else if (y > RANGE_POS)
+        tempY = y - MODULUS;
+    else
+        tempY = y;
+
+    if (tempX >= 0) {
+        qP->x = int_ceil_div(tempX, f, tempC);
+        rP->x = f - 1 + tempX - qP->x*f;
+    } else {
+        qP->x = int_floor_div(tempX, f, tempC);
+        rP->x = f - 1 - tempX + qP->x*f;
+    }
+
+    if (tempY >= 0) {
+        qP->y = int_ceil_div(tempY, f, tempC);
+        rP->y = f - 1 + tempY - qP->y*f;
+    } else {
+        qP->y = int_floor_div(tempY, f, tempC);
+        rP->y = f - 1 - tempY + qP->y*f;
+    }
+}
+
+
+#define DoQuant(bit, src, dest)                                         \
+  if (pattern & bit) {                                                  \
+    switch (Mpost_QuantZigBlock(src, dest, QScale, FALSE)) {            \
+    case MPOST_NON_ZERO:                                                \
+      break;                                                            \
+    case MPOST_ZERO:                                                    \
+      pattern ^= bit;                                                   \
+      break;                                                            \
+    case MPOST_OVERFLOW:                                                \
+      if (QScale != 31) {                                               \
+    QScale++;                                                       \
+    overflowChange = TRUE;                                          \
+    overflowValue++;                                                \
+    goto calc_blocks;                                               \
+      }                                                                 \
+      break;                                                            \
+    }                                                                   \
+  }
+
+/*===============================*
+ * EXTERNAL PROCEDURE prototypes *
+ *===============================*/
+
+void
+ComputeBMotionLumBlock(MpegFrame * const prev,
+                       MpegFrame * const next,
+                       int         const by,
+                       int         const bx,
+                       int         const mode,
+                       motion      const motion,
+                       LumBlock *  const motionBlockP);
+
+int
+BMotionSearch(const LumBlock * const currentBlockP,
+              MpegFrame *      const prev,
+              MpegFrame *      const next,
+              int              const by,
+              int              const bx,
+              motion *         const motionP,
+              int              const oldMode);
+
+void GenIFrame (BitBucket * const bb,
+                MpegFrame * const mf);
+void GenPFrame (BitBucket * const bb,
+                MpegFrame * const current,
+                MpegFrame * const prev);
+void GenBFrame (BitBucket * const bb,
+                MpegFrame * const curr,
+                MpegFrame * const prev,
+                MpegFrame * const next);
+void    AllocDctBlocks _ANSI_ARGS_((void ));
+
+
+float
+IFrameTotalTime(void);
+
+float
+PFrameTotalTime(void);
+
+float
+BFrameTotalTime(void);
+
+void
+ShowIFrameSummary(unsigned int const inputFrameBits, 
+                  unsigned int const totalBits, 
+                  FILE *       const fpointer);
+
+void
+ShowPFrameSummary(unsigned int const inputFrameBits, 
+                  unsigned int const totalBits, 
+                  FILE *       const fpointer);
+
+void
+ShowBFrameSummary(unsigned int const inputFrameBits, 
+                  unsigned int const totalBits, 
+                  FILE *       const fpointer);
+
+/* DIFFERENCE FUNCTIONS */
+
+int32
+LumBlockMAD(const LumBlock * const currentBlockP,
+            const LumBlock * const motionBlockP,
+            int32            const bestSoFar);
+
+int32
+LumBlockMSE(const LumBlock * const currentBlockP,
+            const LumBlock * const motionBlockP,
+            int32            const bestSoFar);
+
+int32
+LumMotionError(const LumBlock * const currentBlockP,
+               MpegFrame *      const prev,
+               int              const by,
+               int              const bx,
+               vector           const m,
+               int32            const bestSoFar);
+
+int32
+LumAddMotionError(const LumBlock * const currentBlockP,
+                  const LumBlock * const blockSoFarP,
+                  MpegFrame *      const prev,
+                  int              const by,
+                  int              const bx,
+                  vector           const m,
+                  int32            const bestSoFar);
+
+int32
+LumMotionErrorA(const LumBlock * const currentBlockP,
+                MpegFrame *      const prevFrame,
+                int              const by,
+                int              const bx,
+                vector           const m,
+                int32            const bestSoFar);
+
+int32
+LumMotionErrorB(const LumBlock * const currentP,
+                MpegFrame *      const prevFrame,
+                int              const by,
+                int              const bx,
+                vector           const m,
+                int32            const bestSoFar);
+
+int32
+LumMotionErrorC(const LumBlock * const currentP,
+                MpegFrame *      const prevFrame,
+                int              const by,
+                int              const bx,
+                vector           const m,
+                int32            const bestSoFar);
+
+int32
+LumMotionErrorD(const LumBlock * const currentP,
+                MpegFrame *      const prevFrame,
+                int              const by,
+                int              const bx,
+                vector           const m,
+                int32            const bestSoFar);
+
+int32
+LumMotionErrorSubSampled(const LumBlock * const currentBlockP,
+                         MpegFrame *      const prevFrame,
+                         int              const by,
+                         int              const bx,
+                         vector           const m,
+                         int              const startY,
+                         int              const startX);
+
+void
+BlockComputeSNR(MpegFrame * const current,
+                float *     const snr,
+                float *     const psnr);
+
+int32
+time_elapsed(void);
+
+void
+AllocDctBlocks(void);
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+extern int pixelFullSearch;
+extern int searchRangeP,searchRangeB;  /* defined in psearch.c */
+extern int qscaleI;
+extern int gopSize;
+extern int slicesPerFrame;
+extern int blocksPerSlice;
+extern int referenceFrame;
+extern int specificsOn;
+extern int quietTime;       /* shut up for at least quietTime seconds;
+                 * negative means shut up forever
+                 */
+extern boolean realQuiet;   /* TRUE = no messages to stdout */
+
+extern boolean frameSummary;    /* TRUE = frame summaries should be printed */
+extern boolean  printSNR;
+extern boolean  printMSE;
+extern boolean  decodeRefFrames;    /* TRUE = should decode I and P frames */
+extern int  fCodeI,fCodeP,fCodeB;
+extern boolean    forceEncodeLast;
+extern int TIME_RATE;
+
+#endif /* FRAMES_INCLUDED */
+
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/headers/RCS/frames.h,v 1.13 1995/08/15 23:43:04 smoot Exp $
+ *  $Log: frames.h,v $
+ *  Revision 1.13  1995/08/15 23:43:04  smoot
+ *  *** empty log message ***
+ *
+ * Revision 1.12  1995/04/14  23:13:18  smoot
+ * Reorganized for better rate control.  Added overflow in DCT values
+ * handling.
+ *
+ * Revision 1.11  1995/01/19  23:54:46  smoot
+ * allow computediffdcts to un-assert parts of the pattern
+ *
+ * Revision 1.10  1995/01/16  07:43:10  eyhung
+ * Added realQuiet
+ *
+ * Revision 1.9  1995/01/10  23:15:28  smoot
+ * Fixed searchRange lack of def
+ *
+ * Revision 1.8  1994/11/15  00:55:36  smoot
+ * added printMSE
+ *
+ * Revision 1.7  1994/11/14  22:51:02  smoot
+ * added specifics flag.  Added BlockComputeSNR parameters
+ *
+ * Revision 1.6  1994/11/01  05:07:23  darryl
+ *  with rate control changes added
+ *
+ * Revision 1.1  1994/09/27  01:02:55  darryl
+ * Initial revision
+ *
+ * Revision 1.5  1993/07/22  22:24:23  keving
+ * nothing
+ *
+ * Revision 1.4  1993/07/09  00:17:23  keving
+ * nothing
+ *
+ * Revision 1.3  1993/06/03  21:08:53  keving
+ * nothing
+ *
+ * Revision 1.2  1993/03/02  19:00:27  keving
+ * nothing
+ *
+ * Revision 1.1  1993/02/19  20:15:51  keving
+ * nothing
+ *
+ */
+
+
diff --git a/converter/ppm/ppmtompeg/headers/frametype.h b/converter/ppm/ppmtompeg/headers/frametype.h
new file mode 100644
index 00000000..63bee964
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/frametype.h
@@ -0,0 +1,17 @@
+#ifndef FRAMETYPE_H_INCLUDED
+#define FRAMETYPE_H_INCLUDED
+
+char
+FType_Type(unsigned int const frameNum);
+
+unsigned int
+FType_FutureRef(unsigned int const currFrameNum);
+
+int	FType_PastRef _ANSI_ARGS_((int currFrameNum));
+
+void SetFramePattern(const char * const pattern);
+
+void
+ComputeFrameTable(unsigned int const numFrames);
+
+#endif
diff --git a/converter/ppm/ppmtompeg/headers/fsize.h b/converter/ppm/ppmtompeg/headers/fsize.h
new file mode 100644
index 00000000..6a1c910e
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/fsize.h
@@ -0,0 +1,49 @@
+/*===========================================================================*
+ * fsize.h								     *
+ *									     *
+ *	procedures to deal with frame size				     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+
+#ifndef FSIZE_H_INCLUDED
+#define FSIZE_H_INCLUDED
+
+extern int Fsize_x;
+extern int Fsize_y;
+
+
+void
+Fsize_Reset(void);
+
+void
+Fsize_Validate(int * const xP,
+               int * const yP);
+
+void
+Fsize_Note(int          const id,
+           unsigned int const width,
+           unsigned int const height);
+
+#endif
diff --git a/converter/ppm/ppmtompeg/headers/general.h b/converter/ppm/ppmtompeg/headers/general.h
new file mode 100644
index 00000000..f29c9445
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/general.h
@@ -0,0 +1,174 @@
+/*===========================================================================*
+ * general.h								     *
+ *									     *
+ *	very general stuff						     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/headers/RCS/general.h,v 1.7 1995/08/04 23:34:13 smoot Exp $
+ *  $Log: general.h,v $
+ *  Revision 1.7  1995/08/04 23:34:13  smoot
+ *  jpeg5 changed the silly HAVE_BOOLEAN define....
+ *
+ *  Revision 1.6  1995/01/19 23:54:49  eyhung
+ *  Changed copyrights
+ *
+ * Revision 1.5  1994/11/12  02:12:48  keving
+ * nothing
+ *
+ * Revision 1.4  1993/07/22  22:24:23  keving
+ * nothing
+ *
+ * Revision 1.3  1993/07/09  00:17:23  keving
+ * nothing
+ *
+ * Revision 1.2  1993/06/03  21:08:53  keving
+ * nothing
+ *
+ * Revision 1.1  1993/02/22  22:39:19  keving
+ * nothing
+ *
+ */
+
+
+#ifndef GENERAL_INCLUDED
+#define GENERAL_INCLUDED
+
+
+/* prototypes for library procedures
+ *
+ * if your /usr/include headers do not have these, then pass -DMISSING_PROTOS
+ * to your compiler
+ *
+ */ 
+#ifdef MISSING_PROTOS
+int fprintf();
+int fwrite();
+int fread();
+int fflush();
+int fclose();
+
+int sscanf();
+int bzero();
+int bcopy();
+int system();
+int time();
+int perror();
+
+int socket();
+int bind();
+int listen();
+int accept();
+int connect();
+int close();
+int read();
+int write();
+
+int pclose();
+
+#endif
+
+
+/*===========*
+ * CONSTANTS *
+ *===========*/
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+#define SPACE ' '
+#define TAB '\t'
+#define SEMICOLON ';'
+#define NULL_CHAR '\0'
+#define NEWLINE '\n'
+
+
+/*==================*
+ * TYPE DEFINITIONS *
+ *==================*/
+
+#ifndef HAVE_BOOLEAN
+typedef int boolean;
+#define HAVE_BOOLEAN
+/* JPEG library also defines boolean */
+#endif
+
+/* In the following, we need the "signed" in order to make these typedefs
+   match those in AIX system header files.  Otherwise, compile fails on 
+   AIX.  2000.09.11.
+*/
+typedef unsigned char uint8;
+typedef signed char int8;
+typedef unsigned short uint16;
+typedef signed short int16;
+
+/* The 32 bit integer types are probably obsolete.
+
+   parallel.c used to use them as buffers for socket I/O, because in the
+   protocol on the socket, an integer is represented by 4 octets.  But
+   the proper type for that is unsigned char[4], so we changed parallel.c
+   to that in September 2004.
+
+   A user of TRU64 4.0f in May 2000 used a -DLONG_32 option, but did
+   not indicate that it was really necessary.  After that, Configure
+   added -DLONG_32 for all TRU64 builds.  A user of TRU64 5.1A in July
+   2003 demonstrated that int is in fact 4 bytes on his machine (and
+   long is 8 bytes).  So we removed the -DLONG_32 from Configure.  This
+   whole issue may too be obsolete because of the fixing of parallel.c
+   mentioned above.
+*/
+
+    /* LONG_32 should only be defined iff
+     *	    1) long's are 32 bits and
+     *	    2) int's are not
+     */
+#ifdef LONG_32		
+typedef unsigned long uint32;
+typedef long int32;
+#else
+typedef unsigned int uint32;
+typedef signed int int32;
+#endif
+
+
+/*========*
+ * MACROS *
+ *========*/
+
+#undef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#undef min
+#define min(a,b) ((a) < (b) ? (a) : (b))
+#undef abs
+#define abs(a) ((a) >= 0 ? (a) : -(a))
+
+
+#endif
diff --git a/converter/ppm/ppmtompeg/headers/huff.h b/converter/ppm/ppmtompeg/headers/huff.h
new file mode 100644
index 00000000..47ffb843
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/huff.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /n/charlie-brown/project/mm/mpeg/mpeg_dist/mpeg_encode/headers/RCS/huff.h,v 1.3 1995/01/19 23:54:51 eyhung Exp $
+ */
+
+/*  
+ *  THIS FILE IS MACHINE GENERATED!  DO NOT EDIT!
+ */
+#define HUFF_MAXRUN	32
+#define HUFF_MAXLEVEL	41
+
+extern int huff_maxlevel[];
+extern uint32 *huff_table[];
+extern int *huff_bits[];
diff --git a/converter/ppm/ppmtompeg/headers/input.h b/converter/ppm/ppmtompeg/headers/input.h
new file mode 100644
index 00000000..75734237
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/input.h
@@ -0,0 +1,70 @@
+#ifndef INPUT_H_INCLUDED
+#define INPUT_H_INCLUDED
+
+#include "pm_c_util.h"
+#include "ppm.h"
+#include "frame.h"
+
+struct InputFileEntry;
+
+struct inputSource {
+/*----------------------------------------------------------------------------
+   This describes the source of data for the program.
+   Typically, the source data is a bunch of raw frames in PPM format.
+   But sometimes, it is a bunch of already encoded frames or GOPs.
+-----------------------------------------------------------------------------*/
+    bool stdinUsed;
+    int numInputFiles;
+        /* This is the maximum number of input files available.  If
+           we're reading from explicitly named files, it is exactly
+           the number available.  If we're reading from a stream, it's
+           infinity.  (At the moment, "reading from a stream" is
+           equivalent to "reading from Standard Input").
+        */
+
+    /* Members below here defined only if 'stdinUsed' is false */
+
+    struct InputFileEntry ** inputFileEntries;
+        /* Each element of this array describes a set of input files.
+           Valid elements are consecutive starting at index 0.
+        */
+    unsigned int             numInputFileEntries;
+        /* Number of valid entries in array inputFileEntries[] */
+    unsigned int             ifArraySize;
+        /* Number of allocated entries in the array inputFileEntries[] */
+};
+
+
+void
+GetNthInputFileName(struct inputSource * const inputSourceP,
+                    unsigned int         const n,
+                    const char **        const fileName);
+
+void
+ReadNthFrame(struct inputSource * const inputSourceP,
+             unsigned int         const frameNumber,
+             boolean              const remoteIO,
+             boolean              const childProcess,
+             boolean              const separateConversion,
+             const char *         const slaveConversion,
+             const char *         const inputConversion,
+             MpegFrame *          const frameP,
+             bool *               const endOfStreamP);
+
+void
+JM2JPEG(struct inputSource * const inputSourceP);
+
+void
+AddInputFiles(struct inputSource * const inputSourceP,
+              const char *         const input);
+
+void
+SetStdinInput(struct inputSource * const inputSourceP);
+
+void
+CreateInputSource(struct inputSource ** const inputSourcePP);
+
+void
+DestroyInputSource(struct inputSource * const inputSourceP);
+
+#endif
diff --git a/converter/ppm/ppmtompeg/headers/jpeg.h b/converter/ppm/ppmtompeg/headers/jpeg.h
new file mode 100644
index 00000000..62c6f930
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/jpeg.h
@@ -0,0 +1,12 @@
+#include "ansi.h"
+
+
+void
+JMovie2JPEG(const char * const infilename,
+            const char * const obase,
+            int          const start,
+            int          const end);
+
+void
+ReadJPEG(MpegFrame * const mf,
+         FILE *      const fp);
diff --git a/converter/ppm/ppmtompeg/headers/mheaders.h b/converter/ppm/ppmtompeg/headers/mheaders.h
new file mode 100644
index 00000000..21d43e3d
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/mheaders.h
@@ -0,0 +1,114 @@
+/*===========================================================================*
+ * mheaders.h								     *
+ *									     *
+ *	MPEG headers							     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /n/picasso/project/mm/mpeg/mpeg_dist/mpeg_encode/headers/RCS/mheaders.h,v 1.4 1995/03/27 19:29:24 smoot Exp $
+ *  $Log: mheaders.h,v $
+ * Revision 1.4  1995/03/27  19:29:24  smoot
+ * changed to remove mb_quant
+ *
+ * Revision 1.3  1995/01/19  23:54:56  eyhung
+ * Changed copyrights
+ *
+ * Revision 1.2  1994/11/12  02:12:51  keving
+ * nothing
+ *
+ * Revision 1.1  1993/07/22  22:24:23  keving
+ * nothing
+ *
+ *
+ */
+
+
+#ifndef MHEADERS_INCLUDED
+#define MHEADERS_INCLUDED
+
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "general.h"
+#include "ansi.h"
+#include "bitio.h"
+
+
+/*===============================*
+ * EXTERNAL PROCEDURE prototypes *
+ *===============================*/
+
+void	SetGOPStartTime _ANSI_ARGS_((int index));
+
+void
+Mhead_GenSequenceHeader(BitBucket *   const bbPtr, 
+                        uint32        const hsize, 
+                        uint32        const vsize,
+                        int32         const pratio, 
+                        int32         const pict_rate, 
+                        int32         const bit_rate_arg,
+                        int32         const buf_size_arg, 
+                        int32         const c_param_flag_arg, 
+                        const int32 * const iq_matrix, 
+                        const int32 * const niq_matrix,
+                        uint8 *       const ext_data, 
+                        int32         const ext_data_size, 
+                        uint8 *       const user_data,
+                        int32         const user_data_size);
+
+void	Mhead_GenSequenceEnder _ANSI_ARGS_((BitBucket *bbPtr));
+void	Mhead_GenGOPHeader _ANSI_ARGS_((BitBucket *bbPtr,
+	   int32 drop_frame_flag,
+           int32 tc_hrs, int32 tc_min,
+           int32 tc_sec, int32 tc_pict,
+           int32 closed_gop, int32 broken_link,
+           uint8 *ext_data, int32 ext_data_size,
+           uint8 *user_data, int32 user_data_size));
+void	Mhead_GenPictureHeader _ANSI_ARGS_((BitBucket *bbPtr, int frameType,
+					    int pictCount, int f_code));
+void	Mhead_GenSliceHeader _ANSI_ARGS_((BitBucket *bbPtr, uint32 slicenum,
+					  uint32 qscale, uint8 *extra_info,
+					  uint32 extra_info_size));
+void	Mhead_GenSliceEnder _ANSI_ARGS_((BitBucket *bbPtr));
+void	Mhead_GenMBHeader _ANSI_ARGS_((BitBucket *bbPtr,
+	  uint32 pict_code_type, uint32 addr_incr,
+          uint32 q_scale,
+          uint32 forw_f_code, uint32 back_f_code,
+          uint32 horiz_forw_r, uint32 vert_forw_r,
+          uint32 horiz_back_r, uint32 vert_back_r,
+          int32 motion_forw, int32 m_horiz_forw,
+          int32 m_vert_forw, int32 motion_back,
+          int32 m_horiz_back, int32 m_vert_back,
+          uint32 mb_pattern, uint32 mb_intra));
+
+
+#endif /* MHEADERS_INCLUDED */
+
+
+
+
+
diff --git a/converter/ppm/ppmtompeg/headers/motion_search.h b/converter/ppm/ppmtompeg/headers/motion_search.h
new file mode 100644
index 00000000..ab83cbca
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/motion_search.h
@@ -0,0 +1,154 @@
+/*===========================================================================*
+ * search.h                                  *
+ *                                       *
+ *  stuff dealing with the motion search                     *
+ *                                       *
+ *===========================================================================*/
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "ansi.h"
+
+
+/*===========*
+ * CONSTANTS *
+ *===========*/
+
+#define PSEARCH_SUBSAMPLE   0
+#define PSEARCH_EXHAUSTIVE  1
+#define PSEARCH_LOGARITHMIC 2
+#define PSEARCH_TWOLEVEL    3
+
+#define BSEARCH_EXHAUSTIVE  0
+#define BSEARCH_CROSS2      1
+#define BSEARCH_SIMPLE      2
+
+
+/*========*
+ * MACROS *
+ *========*/
+
+#define COMPUTE_MOTION_BOUNDARY(by,bx,stepSize,leftMY,leftMX,rightMY,rightMX)\
+leftMY = -2*DCTSIZE*by; /* these are valid motion vectors */         \
+leftMX = -2*DCTSIZE*bx;                          \
+/* these are invalid motion vectors */       \
+rightMY = 2*(Fsize_y - (by+2)*DCTSIZE + 1) - 1;              \
+rightMX = 2*(Fsize_x - (bx+2)*DCTSIZE + 1) - 1;              \
+\
+if ( stepSize == 2 ) { \
+    rightMY++;      \
+    rightMX++;      \
+    }
+    
+#define VALID_MOTION(m)   \
+(((m).y >= leftMY) && ((m).y < rightMY) &&   \
+ ((m).x >= leftMX) && ((m).x < rightMX) )
+
+
+/*===============================*
+ * EXTERNAL PROCEDURE prototypes *
+ *===============================*/
+
+void 
+SetPSearchAlg(const char * const alg);
+void 
+SetBSearchAlg(const char * const alg);
+const char *
+BSearchName(void);
+const char *
+PSearchName(void);
+
+int
+PLogarithmicSearch(const LumBlock * const currentBlockP,
+                   MpegFrame *      const prev,
+                   int              const by,
+                   int              const bx,
+                   vector *         const motionP,
+                   int              const searchRange);
+
+int
+PSubSampleSearch(const LumBlock * const currentBlockP,
+                 MpegFrame *      const prev,
+                 int              const by,
+                 int              const bx,
+                 vector *         const motionP,
+                 int              const searchRange);
+
+int
+PLocalSearch(const LumBlock * const currentBlockP,
+             MpegFrame *      const prev,
+             int              const by,
+             int              const bx,
+             vector *         const motionP,
+             int              const bestSoFar,
+             int              const searchRange);
+
+int
+PTwoLevelSearch(const LumBlock * const currentBlockP,
+                MpegFrame *      const prev,
+                int              const by,
+                int              const bx,
+                vector *         const motionP,
+                int              const bestSoFar,
+                int              const searchRange);
+void
+PMotionSearch(const LumBlock * const currentBlockP,
+              MpegFrame *      const prev, 
+              int              const by,
+              int              const bx, 
+              vector *         const motionP);
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+extern int psearchAlg;
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/headers/RCS/search.h,v 1.6 1995/08/15 23:43:36 smoot Exp $
+ *  $Log: search.h,v $
+ *  Revision 1.6  1995/08/15 23:43:36  smoot
+ *  *** empty log message ***
+ *
+ *  Revision 1.5  1995/01/19 23:55:20  eyhung
+ *  Changed copyrights
+ *
+ * Revision 1.4  1994/12/07  00:42:01  smoot
+ * Added seperate P and B search ranges
+ *
+ * Revision 1.3  1994/11/12  02:12:58  keving
+ * nothing
+ *
+ * Revision 1.2  1993/07/22  22:24:23  keving
+ * nothing
+ *
+ * Revision 1.1  1993/07/09  00:17:23  keving
+ * nothing
+ *
+ */
+
+
+
diff --git a/converter/ppm/ppmtompeg/headers/mpeg.h b/converter/ppm/ppmtompeg/headers/mpeg.h
new file mode 100644
index 00000000..d739dede
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/mpeg.h
@@ -0,0 +1,116 @@
+/*===========================================================================*
+ * mpeg.h								     *
+ *									     *
+ *	no comment							     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include <time.h>
+
+#include "pm_c_util.h"
+#include "ppm.h"
+#include "ansi.h"
+#include "mtypes.h"
+#include "frame.h"
+
+struct inputSource;
+
+/*===============================*
+ * EXTERNAL PROCEDURE prototypes *
+ *===============================*/
+
+enum frameContext {CONTEXT_WHOLESTREAM, CONTEXT_GOP, CONTEXT_JUSTFRAMES};
+
+void
+GenMPEGStream(struct inputSource * const inputSourceP,
+              enum frameContext    const context, 
+              unsigned int         const frameStart, 
+              unsigned int         const frameEnd, 
+              int32                const qtable[], 
+              int32                const niqtable[], 
+              bool                 const childProcess,
+              FILE *               const ofp, 
+              const char *         const outputFileName,
+              bool                 const wantVbvUnderflowWarning,
+              bool                 const wantVbvOverflowWarning,
+              unsigned int *       const inputFrameBitsP,
+              unsigned int *       const totalBitsP);
+
+void
+PrintStartStats(time_t               const startTime, 
+                bool                 const specificFrames,
+                unsigned int         const firstFrame, 
+                unsigned int         const lastFrame,
+                struct inputSource * const inputSourceP);
+
+void
+PrintEndStats(time_t       const startTime,
+              time_t       const endTime,
+              unsigned int const inputFrameBits, 
+              unsigned int const totalBits);
+
+void
+ComputeGOPFrames(int            const whichGOP, 
+                 unsigned int * const firstFrameP, 
+                 unsigned int * const lastFrameP, 
+                 unsigned int   const numFrames);
+
+extern void	IncrementTCTime _ANSI_ARGS_((void));
+void SetReferenceFrameType(const char * const type);
+
+boolean
+NonLocalRefFrame(int     const id);
+
+void
+ReadDecodedRefFrame(MpegFrame *  const frameP, 
+                    unsigned int const frameNumber);
+
+extern void	WriteDecodedFrame _ANSI_ARGS_((MpegFrame *frame));
+extern void	SetBitRateFileName _ANSI_ARGS_((char *fileName));
+extern void	SetFrameRate _ANSI_ARGS_((void));
+
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+extern MpegFrame *frameMemory[3];
+extern int32	  tc_hrs, tc_min, tc_sec, tc_pict, tc_extra;
+extern int	  totalFramesSent;
+extern int	  gopSize;
+extern char	 *framePattern;
+extern int	  framePatternLen;
+extern int32 qtable[];
+extern int32 niqtable[];
+extern int32 *customQtable;
+extern int32 *customNIQtable;
+extern int  aspectRatio;
+extern int  frameRate;
+extern int     frameRateRounded;
+extern boolean    frameRateInteger;
+
diff --git a/converter/ppm/ppmtompeg/headers/mproto.h b/converter/ppm/ppmtompeg/headers/mproto.h
new file mode 100644
index 00000000..c3b0f4b3
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/mproto.h
@@ -0,0 +1,132 @@
+/*===========================================================================*
+ * mproto.h								     *
+ *									     *
+ *	basically a lot of miscellaneous prototypes			     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /n/picasso/project/mm/mpeg/mpeg_dist/mpeg_encode/headers/RCS/mproto.h,v 1.12 1995/03/29 20:14:29 smoot Exp $
+ *  $Log: mproto.h,v $
+ * Revision 1.12  1995/03/29  20:14:29  smoot
+ * deleted unneeded dct prototype
+ *
+ * Revision 1.11  1995/01/19  23:55:02  eyhung
+ * Changed copyrights
+ *
+ * Revision 1.10  1995/01/16  06:20:10  eyhung
+ * Changed ReadYUV to ReadEYUV
+ *
+ * Revision 1.9  1993/07/22  22:24:23  keving
+ * nothing
+ *
+ * Revision 1.8  1993/07/09  00:17:23  keving
+ * nothing
+ *
+ * Revision 1.7  1993/06/03  21:08:53  keving
+ * nothing
+ *
+ * Revision 1.6  1993/02/24  19:13:33  keving
+ * nothing
+ *
+ * Revision 1.5  1993/02/17  23:18:20  dwallach
+ * checkin prior to keving's joining the project
+ *
+ * Revision 1.4  1993/01/18  10:20:02  dwallach
+ * *** empty log message ***
+ *
+ * Revision 1.3  1993/01/18  10:17:29  dwallach
+ * RCS headers installed, code indented uniformly
+ *
+ * Revision 1.3  1993/01/18  10:17:29  dwallach
+ * RCS headers installed, code indented uniformly
+ *
+ */
+
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "general.h"
+#include "ansi.h"
+#include "bitio.h"
+
+
+#define DCTSIZE2    DCTSIZE*DCTSIZE
+typedef short DCTELEM;
+typedef DCTELEM DCTBLOCK[DCTSIZE2];
+
+
+
+/*===============================*
+ * EXTERNAL PROCEDURE prototypes *
+ *===============================*/
+
+/*  
+ *  from mbasic.c:
+ */
+void mp_reset _ANSI_ARGS_((void));
+void mp_free _ANSI_ARGS_((MpegFrame *mf));
+MpegFrame *mp_new _ANSI_ARGS_((int fnumber, char type, MpegFrame *oldFrame));
+void mp_ycc_calc _ANSI_ARGS_((MpegFrame *mf));
+void mp_dct_blocks _ANSI_ARGS_((MpegFrame *mf));
+void	AllocDecoded _ANSI_ARGS_((MpegFrame *frame));
+
+/*  
+ *  from moutput.c:
+ */
+boolean mp_quant_zig_block _ANSI_ARGS_((Block in, FlatBlock out, int qscale, int iblock));
+void	UnQuantZig _ANSI_ARGS_((FlatBlock in, Block out, int qscale, boolean iblock));
+void mp_rle_huff_block _ANSI_ARGS_((FlatBlock in, BitBucket *out));
+void mp_rle_huff_pblock _ANSI_ARGS_((FlatBlock in, BitBucket *out));
+void mp_create_blocks _ANSI_ARGS_((MpegFrame *mf));
+
+
+
+
+void	ReadEYUV _ANSI_ARGS_((MpegFrame * mf, FILE *fpointer, int width,
+			    int height));
+boolean	ReadPPM _ANSI_ARGS_((MpegFrame *mf, FILE *fpointer));
+void PPMtoYCC _ANSI_ARGS_((MpegFrame * mf));
+
+void	MotionSearchPreComputation _ANSI_ARGS_((MpegFrame *frame));
+boolean	PMotionSearch _ANSI_ARGS_((LumBlock currentBlock, MpegFrame *prev,
+				   int by, int bx, int *motionY, int *motionX));
+void	ComputeHalfPixelData _ANSI_ARGS_((MpegFrame *frame));
+void mp_validate_size _ANSI_ARGS_((int *x, int *y));
+void AllocYCC _ANSI_ARGS_((MpegFrame * mf));
+
+
+/* jrevdct.c */
+void init_pre_idct _ANSI_ARGS_((void ));
+void j_rev_dct_sparse _ANSI_ARGS_((DCTBLOCK data , int pos ));
+void j_rev_dct _ANSI_ARGS_((DCTBLOCK data ));
+void j_rev_dct_sparse _ANSI_ARGS_((DCTBLOCK data , int pos ));
+void j_rev_dct _ANSI_ARGS_((DCTBLOCK data ));
+
+/* block.c */
+void	BlockToData _ANSI_ARGS_((uint8 **data, Block block, int by, int bx));
+void	AddMotionBlock _ANSI_ARGS_((Block block, uint8 **prev, int by, int bx,
+		       int my, int mx));
diff --git a/converter/ppm/ppmtompeg/headers/mtypes.h b/converter/ppm/ppmtompeg/headers/mtypes.h
new file mode 100644
index 00000000..0db5a330
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/mtypes.h
@@ -0,0 +1,109 @@
+/*===========================================================================*
+ * mtypes.h
+ *
+ *	MPEG data types
+ *
+ *===========================================================================*/
+
+/* Copyright information is at end of file */
+
+#ifndef MTYPES_INCLUDED
+#define MTYPES_INCLUDED
+
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "general.h"
+#include "dct.h"
+
+
+/*===========*
+ * CONSTANTS *
+ *===========*/
+
+#define TYPE_BOGUS	0   /* for the header of the circular list */
+#define TYPE_VIRGIN	1
+
+#define STATUS_EMPTY	0
+#define STATUS_LOADED	1
+#define STATUS_WRITTEN	2
+
+
+typedef struct vector {
+    int y;
+    int x;
+} vector;
+
+typedef struct motion {
+    vector fwd;
+    vector bwd;
+} motion;
+
+/*==================*
+ * TYPE DEFINITIONS *
+ *==================*/
+
+/*  
+ *  your basic Block type
+ */
+typedef int16 Block[DCTSIZE][DCTSIZE];
+typedef int16 FlatBlock[DCTSIZE_SQ];
+typedef	struct {
+    int32 l[2*DCTSIZE][2*DCTSIZE];
+} LumBlock;
+typedef	int32 ChromBlock[DCTSIZE][DCTSIZE];
+
+/*========*
+ * MACROS *
+ *========*/
+
+#ifdef ABS
+#undef ABS
+#endif
+
+#define ABS(x) (((x)<0)?-(x):(x))
+
+#ifdef HEINOUS_DEBUG_MODE
+#define DBG_PRINT(x) {printf x; fflush(stdout);}
+#else
+#define DBG_PRINT(x)
+#endif
+
+#define ERRCHK(bool, str) {if(!(bool)) {perror(str); exit(1);}}
+
+/* For Specifics */
+typedef struct detalmv_def {
+  int typ,fx,fy,bx,by;
+} BlockMV;
+#define TYP_SKIP 0
+#define TYP_FORW 1
+#define TYP_BACK 2
+#define TYP_BOTH 3
+
+
+#endif /* MTYPES_INCLUDED */
+
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
diff --git a/converter/ppm/ppmtompeg/headers/opts.h b/converter/ppm/ppmtompeg/headers/opts.h
new file mode 100644
index 00000000..5901a677
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/opts.h
@@ -0,0 +1,125 @@
+/*
+ * opts.h - set optional parameters
+ */
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/headers/RCS/opts.h,v 1.3 1995/08/15 23:43:43 smoot Exp $
+ *  $Log: opts.h,v $
+ *  Revision 1.3  1995/08/15 23:43:43  smoot
+ *  *** empty log message ***
+ *
+ * Revision 1.2  1995/05/02  22:00:51  smoot
+ * added TUNEing stuff
+ *
+ * Revision 1.1  1995/04/14  23:12:53  smoot
+ * Initial revision
+ *
+ */
+
+#include "general.h"
+#include "ansi.h"
+#include "mtypes.h"
+
+/*
+ TUNE b [limit] lower limit on how different a block must be to be DCT coded
+ TUNE c [file [color-diff]] Collect statistics on Quantization
+ TUNE d [RateScale DistortionScale] Do a DCT in the P search, not just DIFF
+ TUNE k [breakpt end [slope]] Squash small lum values
+ TUNE l Figure out Laplacian distrib and use them to dequantize and do snr calc
+ TUNE n Dont consider DC differenece in DCT searches
+ TUNE q Do MSE for distortion measure, not MAD
+ TUNE s [Max] | [LumMax ChromMax] Squash small differences in successive frames
+ TUNE u disallow skip blocks in B frames
+ TUNE w filename [c]  Write I block distortion numbers to file [with bit-rates]
+ TUNE z Zaps Intra blocks in P/B frames.
+
+ [ Note k and s make -snr numbers a lie, by playing with input ]
+ [ Note d n and q are contradictory (can only use one)         ]
+ [ Note c will not work on parallel encodings                  ]
+*/
+
+extern boolean tuneingOn;
+
+/* Smash to no-change a motion block DCT with MAD less than: */
+/* DETAL b value               */
+extern int block_bound;
+
+/* Collect info on quantization */
+extern boolean collect_quant;
+extern int collect_quant_detailed;
+extern FILE   *collect_quant_fp;
+
+/* Nuke dim areas */
+extern int kill_dim, kill_dim_break, kill_dim_end;
+extern float kill_dim_slope;
+
+
+/* Stuff to control MV search comparisons */
+#define DEFAULT_SEARCH 0
+#define LOCAL_DCT  1 /* Do DCT in search (SLOW!!!!) */
+#define NO_DC_SEARCH  2  /* Dont consider DC component in motion searches */
+#define DO_Mean_Squared_Distortion  3 /* Do Squared distortion, not ABS */
+
+/* Parameters for special searches */
+/* LOCAL_DCT */
+extern float LocalDCTRateScale, LocalDCTDistortScale;
+
+/* Search Type Variable */
+extern int SearchCompareMode;
+
+/* squash small differences */
+extern boolean squash_small_differences;
+extern int SquashMaxLum, SquashMaxChr;
+
+/* Disallows Intra blocks in P/B code */
+extern boolean IntraPBAllowed;
+
+/* Write out distortion numbers */
+extern boolean WriteDistortionNumbers;
+extern int collect_distortion_detailed;
+extern FILE *distortion_fp;
+extern FILE *fp_table_rate[31], *fp_table_dist[31];
+
+/* Laplacian Distrib */
+extern boolean DoLaplace;
+extern double **L1, **L2, **Lambdas;
+extern int LaplaceNum, LaplaceCnum;
+
+/* Turn on/off skipping in B frames */
+extern boolean BSkipBlocks;
+
+/* Procedures Prototypes */
+int	GetIQScale _ANSI_ARGS_((void));
+int	GetPQScale _ANSI_ARGS_((void));
+int	GetBQScale _ANSI_ARGS_((void));
+void	Tune_Init _ANSI_ARGS_((void));
+int     CalcRLEHuffLength _ANSI_ARGS_((FlatBlock in));
+void    ParseTuneParam(const char * const charPtr);
+int     mse _ANSI_ARGS_((Block blk1, Block blk2));
+void    Mpost_UnQuantZigBlockLaplace _ANSI_ARGS_((FlatBlock in, Block out, int qscale, boolean iblock));
+extern void CalcLambdas(void);
+
+
+
+
diff --git a/converter/ppm/ppmtompeg/headers/parallel.h b/converter/ppm/ppmtompeg/headers/parallel.h
new file mode 100644
index 00000000..e18d3f46
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/parallel.h
@@ -0,0 +1,135 @@
+/*===========================================================================*
+ * parallel.h          
+ *                     
+ *  parallel encoding  
+ *                     
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "ansi.h"
+#include "bitio.h"
+#include "frame.h"
+
+
+struct inputSource;
+
+/*===============================*
+ * EXTERNAL PROCEDURE prototypes *
+ *===============================*/
+
+void
+MasterServer(struct inputSource * const inputSourceP,
+             const char *         const paramFileName, 
+             const char *         const outputFileName);
+
+void
+NotifyMasterDone(const char * const hostName, 
+                 int          const portNum, 
+                 int          const machineNumber, 
+                 unsigned int const seconds, 
+                 boolean *    const moreWorkToDoP,
+                 int *        const frameStartP,
+                 int *        const frameEndP);
+
+void
+IoServer(struct inputSource * const inputSourceP,
+         const char *         const parallelHostName, 
+         int                  const portNum);
+
+void
+CombineServer(int          const numInputFiles, 
+              const char * const masterHostName, 
+              int          const masterPortNum,
+              const char*  const outputFileName);
+
+void
+DecodeServer(int          const numInputFiles, 
+             const char * const decodeFileName, 
+             const char * const parallelHostName, 
+             int          const portNum);
+
+void
+WaitForOutputFile(int number);
+
+void
+GetRemoteFrame(MpegFrame * const frameP,
+               int         const frameNumber);
+
+void
+SendRemoteFrame(int         const frameNumber,
+                BitBucket * const bbP);
+
+void
+NoteFrameDone(int frameStart, int frameEnd);
+
+void
+SetIOConvert(boolean separate);
+
+void
+SetRemoteShell(const char * const shell);
+
+void 
+NotifyDecodeServerReady(int const id);
+
+void 
+WaitForDecodedFrame(int id);
+
+void 
+SendDecodedFrame(MpegFrame * const frameP);
+
+void 
+GetRemoteDecodedRefFrame(MpegFrame * const frameP,
+                         int         const frameNumber);
+
+void 
+SetParallelPerfect(boolean val);
+
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+extern int parallelTestFrames;
+extern int parallelTimeChunks;
+
+extern const char *IOhostName;
+extern int ioPortNumber;
+extern int decodePortNumber;
+
+extern boolean  ioServer;
+extern boolean  niceProcesses;
+extern boolean  forceIalign;
+extern int    machineNumber;
+extern boolean remoteIO;
+extern boolean  separateConversion;
+
+
+
+
+
+
+
diff --git a/converter/ppm/ppmtompeg/headers/param.h b/converter/ppm/ppmtompeg/headers/param.h
new file mode 100644
index 00000000..31be61ee
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/param.h
@@ -0,0 +1,87 @@
+/*===========================================================================*
+ * param.h								     *
+ *									     *
+ *	reading the parameter file					     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "pm_c_util.h"
+#include "ansi.h"
+#include "input.h"
+
+
+/*===========*
+ * CONSTANTS *
+ *===========*/
+
+#define MAX_MACHINES	    256
+#ifndef MAXPATHLEN
+#define MAXPATHLEN  1024
+#endif
+
+#define	ENCODE_FRAMES	0
+#define COMBINE_GOPS	1
+#define COMBINE_FRAMES	2
+
+
+struct params {
+    struct inputSource * inputSourceP;
+    bool warnUnderflow;
+    bool warnOverflow;
+};
+
+
+void
+ReadParamFile(const char *    const fileName, 
+              int             const function,
+              struct params * const paramP);
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+/* All this stuff ought to be in a struct param instead */
+
+extern char	outputFileName[256];
+extern int	whichGOP;
+extern int numMachines;
+extern char	machineName[MAX_MACHINES][256];
+extern char	userName[MAX_MACHINES][256];
+extern char	executable[MAX_MACHINES][1024];
+extern char	remoteParamFile[MAX_MACHINES][1024];
+extern boolean	remote[MAX_MACHINES];
+extern char	currentPath[MAXPATHLEN];
+extern char	currentFramePath[MAXPATHLEN];
+extern char	currentGOPPath[MAXPATHLEN];
+extern char inputConversion[1024];
+extern char yuvConversion[256];
+extern int  yuvWidth, yuvHeight;
+extern int  realWidth, realHeight;
+extern char ioConversion[1024];
+extern char slaveConversion[1024];
+extern FILE *bitRateFile;
+extern boolean showBitRatePerFrame;
+extern boolean computeMVHist;
+extern const double VidRateNum[9];
+extern boolean keepTempFiles;
diff --git a/converter/ppm/ppmtompeg/headers/postdct.h b/converter/ppm/ppmtompeg/headers/postdct.h
new file mode 100644
index 00000000..3f3b51fe
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/postdct.h
@@ -0,0 +1,40 @@
+/*===========================================================================*
+ * postdct.h								     *
+ *									     *
+ *	MPEG post-DCT processing					     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "bitio.h"
+
+
+int     Mpost_QuantZigBlock (Block in, FlatBlock out, int qscale, int iblock);
+void	Mpost_UnQuantZigBlock (FlatBlock in, Block out,
+                               int qscale, boolean iblock);
+void	Mpost_RLEHuffIBlock (FlatBlock in, BitBucket *out);
+void	Mpost_RLEHuffPBlock (FlatBlock in, BitBucket *out);
+
+#define MPOST_ZERO 0
+#define MPOST_NON_ZERO 1
+#define MPOST_OVERFLOW (-1)
diff --git a/converter/ppm/ppmtompeg/headers/prototypes.h b/converter/ppm/ppmtompeg/headers/prototypes.h
new file mode 100644
index 00000000..d729aafa
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/prototypes.h
@@ -0,0 +1,78 @@
+/*===========================================================================*
+ * prototypes.h                                  *
+ *                                       *
+ *  miscellaneous prototypes                         *
+ *                                       *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "general.h"
+#include "ansi.h"
+#include "frame.h"
+
+
+/*===============================*
+ * EXTERNAL PROCEDURE prototypes *
+ *===============================*/
+
+int GetBQScale _ANSI_ARGS_((void));
+int GetPQScale _ANSI_ARGS_((void));
+void    ResetBFrameStats _ANSI_ARGS_((void));
+void    ResetPFrameStats _ANSI_ARGS_((void));
+void SetSearchRange (int const pixelsP,
+                     int const pixelsB);
+void    ResetIFrameStats _ANSI_ARGS_((void));
+void
+SetPixelSearch(const char * const searchType);
+void    SetIQScale _ANSI_ARGS_((int qI));
+void    SetPQScale _ANSI_ARGS_((int qP));
+void    SetBQScale _ANSI_ARGS_((int qB));
+float   EstimateSecondsPerIFrame _ANSI_ARGS_((void));
+float   EstimateSecondsPerPFrame _ANSI_ARGS_((void));
+float   EstimateSecondsPerBFrame _ANSI_ARGS_((void));
+void    SetGOPSize _ANSI_ARGS_((int size));
+void
+SetStatFileName(const char * const fileName);
+void    SetSlicesPerFrame _ANSI_ARGS_((int number));
+void    SetBlocksPerSlice _ANSI_ARGS_((void));
+
+
+void DCTFrame _ANSI_ARGS_((MpegFrame * mf));
+
+void PPMtoYCC _ANSI_ARGS_((MpegFrame * mf));
+
+void    MotionSearchPreComputation _ANSI_ARGS_((MpegFrame *frame));
+
+void    ComputeHalfPixelData _ANSI_ARGS_((MpegFrame *frame));
+void mp_validate_size _ANSI_ARGS_((int *x, int *y));
+
+extern void SetFCode _ANSI_ARGS_((void));
+
+
+/* psearch.c */
+void    ShowPMVHistogram _ANSI_ARGS_((FILE *fpointer));
+void    ShowBBMVHistogram _ANSI_ARGS_((FILE *fpointer));
+void    ShowBFMVHistogram _ANSI_ARGS_((FILE *fpointer));
diff --git a/converter/ppm/ppmtompeg/headers/psocket.h b/converter/ppm/ppmtompeg/headers/psocket.h
new file mode 100644
index 00000000..214f5dce
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/psocket.h
@@ -0,0 +1,41 @@
+#ifndef PSOCKET_H_INCLUDED
+#define PSOCKET_H_INCLUDED
+
+#include <netdb.h>
+
+void
+ReadInt(int   const socketFd,
+        int * const valueP);
+
+void
+ReadBytes(int             const fd,
+          unsigned char * const buf,
+          unsigned int    const nbyte);
+
+void
+WriteInt(int const socketFd,
+         int const value);
+
+void
+WriteBytes(int             const fd,
+           unsigned char * const buf,
+           unsigned int    const nbyte);
+
+void
+ConnectToSocket(const char *      const machineName, 
+                int               const portNum, 
+                struct hostent ** const hostEnt,
+                int *             const socketFdP,
+                const char **     const errorP);
+
+void
+CreateListeningSocket(int *         const socketP,
+                      int *         const portNumP,
+                      const char ** const errorP);
+
+void
+AcceptConnection(int           const listenSocketFd,
+                 int *         const connectSocketFdP,
+                 const char ** const errorP);
+
+#endif
diff --git a/converter/ppm/ppmtompeg/headers/rate.h b/converter/ppm/ppmtompeg/headers/rate.h
new file mode 100644
index 00000000..df5ca1cc
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/rate.h
@@ -0,0 +1,204 @@
+/*===========================================================================*
+ * rate.h								     *
+ *									     *
+ *	Procedures concerned with rate control
+ *									     *
+ * EXPORTED PROCEDURES:							     *
+ *  getRateMode()
+ *  setBitRate()
+ *  getBitRate()
+ *  setBufferSize()
+ *  getBufferSize()
+ *	initRateControl()
+ *	targetRateControl()
+ * 	updateRateControl()
+ *	MB_RateOut()
+ *									     *
+ *===========================================================================*/
+
+/* 	COPYRIGHT INFO HERE	*/
+
+#define VARIABLE_RATE 0
+#define FIXED_RATE 1
+
+
+/*==================*
+ * Exported VARIABLES *
+ *==================*/
+
+
+extern int rc_bitsThisMB;
+extern int rc_numBlocks;
+extern int rc_totalQuant;
+extern int rc_quantOverride;
+
+
+/*=====================*
+ * EXPORTED PROCEDURES *
+ *=====================*/
+
+/*===========================================================================*
+ *
+ * initRateControl
+ *
+ *	initialize the allocation parameters.
+ *===========================================================================*/
+int
+initRateControl(bool const wantUnderflowWarning,
+                bool const wantOverflowWarning);
+
+/*===========================================================================*
+ *
+ * targetRateControl
+ *
+ *      Determine the target allocation for given picture type.
+ *
+ * RETURNS:     target size in bits
+ *===========================================================================*/
+void
+targetRateControl(MpegFrame * const frameP);
+
+
+/*===========================================================================*
+ *
+ * MB_RateOut
+ *
+ *      Prints out sampling of MB rate control data.  Every "nth" block
+ *	stats are printed, with "n" controled by global RC_MB_SAMPLE_RATE
+ *
+ * RETURNS:     nothing
+ *===========================================================================*/
+extern void MB_RateOut _ANSI_ARGS_((int type));
+
+
+/*===========================================================================*
+ *
+ * updateRateControl
+ *
+ *      Update the statistics kept, after end of frame
+ *
+ * RETURNS:     nothing
+ *
+ * SIDE EFFECTS:   many global variables
+ *===========================================================================*/
+void
+updateRateControl(int const type);
+
+
+/*===========================================================================*
+ *
+ * needQScaleChange(current Q scale, 4 luminance blocks)
+ *
+ *
+ * RETURNS:     new Qscale
+ *===========================================================================*/
+extern int needQScaleChange _ANSI_ARGS_((int oldQScale,  Block blk0, Block blk1, Block blk2, Block blk3));
+
+/*===========================================================================*
+ *
+ * incNumBlocks()
+ *
+ *
+ * RETURNS:   nothing
+ *===========================================================================*/
+extern void incNumBlocks _ANSI_ARGS_((int num));
+
+
+/*===========================================================================*
+ *
+ * incMacroBlockBits()
+ *
+ *  Increments the number of Macro Block bits and the total of Frame
+ *  bits by the number passed.
+ *
+ * RETURNS:   nothing
+ *===========================================================================*/
+extern void incMacroBlockBits _ANSI_ARGS_((int num));
+
+
+/*===========================================================================*
+ *
+ * SetRateControl ()
+ *
+ *      Checks the string parsed from the parameter file.  Verifies
+ *  number and sets global values.
+ *
+ * RETURNS:     nothing
+ *===========================================================================*/
+extern void SetRateControl _ANSI_ARGS_((char *charPtr));
+
+
+/*===========================================================================*
+ *
+ * setBufferSize ()
+ *
+ *      Checks the string parsed from the parameter file.  Verifies
+ *  number and sets global values.
+ *
+ * RETURNS:     nothing
+ *===========================================================================*/
+extern void 
+setBufferSize(const char * const charPtr);
+
+
+/*===========================================================================*
+ *
+ * getBufferSize ()
+ *
+ *      returns the buffer size read from the parameter file.  Size is
+ *  in bits- not in units of 16k as written to the sequence header.
+ *
+ * RETURNS:     int (or -1 if invalid)
+ *===========================================================================*/
+extern int getBufferSize _ANSI_ARGS_((void));
+
+
+/*===========================================================================*
+ *
+ * setBitRate ()
+ *
+ *      Checks the string parsed from the parameter file.  Verifies
+ *  number and sets global values.
+ *
+ * RETURNS:     nothing
+ *
+ * SIDE EFFECTS:   global variables
+ *===========================================================================*/
+extern void 
+setBitRate(const char * const charPtr);
+
+
+/*===========================================================================*
+ *
+ * getBitRate ()
+ *
+ *      Returns the bit rate read from the parameter file.  This is the
+ *  real rate in bits per second, not in 400 bit units as is written to
+ *  the sequence header.
+ *
+ * RETURNS:     int (-1 if Variable mode operation)
+ *===========================================================================*/
+extern int getBitRate _ANSI_ARGS_((void));
+
+
+/*===========================================================================*
+ *
+ * getRateMode ()
+ *
+ *      Returns the rate mode- interpreted waa either Fixed or Variable
+ *
+ * RETURNS:     integer
+ *===========================================================================*/
+extern int getRateMode _ANSI_ARGS_((void));
+
+
+/*===========================================================================*
+ *
+ * incQuantOverride()
+ *
+ *  counter of override of quantization
+ *
+ * RETURNS:   nothing
+ *===========================================================================*/
+extern void incQuantOverride  _ANSI_ARGS_((int num));
+
diff --git a/converter/ppm/ppmtompeg/headers/readframe.h b/converter/ppm/ppmtompeg/headers/readframe.h
new file mode 100644
index 00000000..3a6876b1
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/readframe.h
@@ -0,0 +1,69 @@
+/*===========================================================================*
+ * readframe.h								     *
+ *									     *
+ *	stuff dealing with reading frames				     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "pm_c_util.h"
+
+/*===========*
+ * CONSTANTS *
+ *===========*/
+
+#define	PPM_FILE_TYPE	    0
+#define YUV_FILE_TYPE	    2
+#define ANY_FILE_TYPE	    3
+#define BASE_FILE_TYPE	    4
+#define PNM_FILE_TYPE	    5
+#define SUB4_FILE_TYPE	    6
+#define JPEG_FILE_TYPE	    7
+#define JMOVIE_FILE_TYPE    8
+#define Y_FILE_TYPE	    9
+
+
+struct inputSource;
+
+void
+ReadFrameFile(MpegFrame *  const frameP,
+              FILE *       const ifP,
+              const char * const conversion,
+              bool *       const eofP);
+
+void
+ReadFrame(MpegFrame *          const frameP, 
+          struct inputSource * const inputSourceP,
+          unsigned int         const frameNumber,
+          const char *         const conversion,
+          bool *               const endOfStreamP);
+
+FILE *
+ReadIOConvert(struct inputSource * const inputSourceP,
+              unsigned int         const frameNumber);
+
+extern void	SetFileType(const char * const conversion);
+extern void	SetFileFormat(const char * const format);
+extern void	SetResize(bool const set);
+
+extern int	baseFormat;
diff --git a/converter/ppm/ppmtompeg/headers/rgbtoycc.h b/converter/ppm/ppmtompeg/headers/rgbtoycc.h
new file mode 100644
index 00000000..52159963
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/rgbtoycc.h
@@ -0,0 +1,39 @@
+/*===========================================================================*
+ * rgbtoycc.h								     *
+ *									     *
+ *	Procedures to convert from RGB space to YUV space		     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+
+
+#include "frame.h"
+#include "pnm.h"
+
+void
+PNMtoYUV(MpegFrame *  const frame,
+         xel **       const xels,
+         unsigned int const cols,
+         unsigned int const rows,
+         xelval       const maxval);
diff --git a/converter/ppm/ppmtompeg/headers/specifics.h b/converter/ppm/ppmtompeg/headers/specifics.h
new file mode 100644
index 00000000..7bcf4ace
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/specifics.h
@@ -0,0 +1,36 @@
+#include "ansi.h"
+
+
+/*===========*
+ * TYPES     *
+ *===========*/
+
+typedef struct bs_def {
+  int num;
+  boolean relative;
+  char qscale;
+  BlockMV *mv;  /* defined in mtypes.h */
+  struct bs_def *next;
+} Block_Specifics;
+
+typedef struct detail_def {
+  int num;
+  char qscale;
+  struct detail_def *next;
+}  Slice_Specifics;
+
+typedef struct fsl_def {
+  int framenum; 
+  int frametype;
+  char qscale;
+  Slice_Specifics *slc;
+  Block_Specifics *bs;
+  struct fsl_def *next;
+} FrameSpecList;
+
+
+void	Specifics_Init _ANSI_ARGS_((void));
+int     SpecLookup _ANSI_ARGS_((int fn, int typ, int num, 
+				BlockMV **info, int start_qs));
+int SpecTypeLookup _ANSI_ARGS_((int fn));
+
diff --git a/converter/ppm/ppmtompeg/huff.c b/converter/ppm/ppmtompeg/huff.c
new file mode 100644
index 00000000..821daca1
--- /dev/null
+++ b/converter/ppm/ppmtompeg/huff.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /n/charlie-brown/project/mm/mpeg/mpeg_dist/mpeg_encode/RCS/huff.c,v 1.6 1995/01/19 23:07:39 eyhung Exp $
+ */
+
+/*  
+ *  THIS FILE IS MACHINE GENERATED!  DO NOT EDIT!
+ */
+#include "mtypes.h"
+#include "huff.h"
+
+int huff_maxlevel[32] = { 41, 19, 6, 5, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 };
+
+uint32 huff_table0[41] = { 0x0, 0x6, 0x8, 0xa, 0xc, 0x4c, 0x42, 0x14, 0x3a, 0x30, 0x26, 0x20, 0x34, 0x32, 0x30, 0x2e, 0x3e, 0x3c, 0x3a, 0x38, 0x36, 0x34, 0x32, 0x30, 0x2e, 0x2c, 0x2a, 0x28, 0x26, 0x24, 0x22, 0x20, 0x30, 0x2e, 0x2c, 0x2a, 0x28, 0x26, 0x24, 0x22, 0x20 };
+int huff_bits0[41] = { 0, 3, 5, 6, 8, 9, 9, 11, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16 };
+
+uint32 huff_table1[19] = { 0x0, 0x6, 0xc, 0x4a, 0x18, 0x36, 0x2c, 0x2a, 0x3e, 0x3c, 0x3a, 0x38, 0x36, 0x34, 0x32, 0x26, 0x24, 0x22, 0x20 };
+int huff_bits1[19] = { 0, 4, 7, 9, 11, 13, 14, 14, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17 };
+
+uint32 huff_table2[6] = { 0x0, 0xa, 0x8, 0x16, 0x28, 0x28 };
+int huff_bits2[6] = { 0, 5, 8, 11, 13, 14 };
+
+uint32 huff_table3[5] = { 0x0, 0xe, 0x48, 0x38, 0x26 };
+int huff_bits3[5] = { 0, 6, 9, 13, 14 };
+
+uint32 huff_table4[4] = { 0x0, 0xc, 0x1e, 0x24 };
+int huff_bits4[4] = { 0, 6, 11, 13 };
+
+uint32 huff_table5[4] = { 0x0, 0xe, 0x12, 0x24 };
+int huff_bits5[4] = { 0, 7, 11, 14 };
+
+uint32 huff_table6[4] = { 0x0, 0xa, 0x3c, 0x28 };
+int huff_bits6[4] = { 0, 7, 13, 17 };
+
+uint32 huff_table7[3] = { 0x0, 0x8, 0x2a };
+int huff_bits7[3] = { 0, 7, 13 };
+
+uint32 huff_table8[3] = { 0x0, 0xe, 0x22 };
+int huff_bits8[3] = { 0, 8, 13 };
+
+uint32 huff_table9[3] = { 0x0, 0xa, 0x22 };
+int huff_bits9[3] = { 0, 8, 14 };
+
+uint32 huff_table10[3] = { 0x0, 0x4e, 0x20 };
+int huff_bits10[3] = { 0, 9, 14 };
+
+uint32 huff_table11[3] = { 0x0, 0x46, 0x34 };
+int huff_bits11[3] = { 0, 9, 17 };
+
+uint32 huff_table12[3] = { 0x0, 0x44, 0x32 };
+int huff_bits12[3] = { 0, 9, 17 };
+
+uint32 huff_table13[3] = { 0x0, 0x40, 0x30 };
+int huff_bits13[3] = { 0, 9, 17 };
+
+uint32 huff_table14[3] = { 0x0, 0x1c, 0x2e };
+int huff_bits14[3] = { 0, 11, 17 };
+
+uint32 huff_table15[3] = { 0x0, 0x1a, 0x2c };
+int huff_bits15[3] = { 0, 11, 17 };
+
+uint32 huff_table16[3] = { 0x0, 0x10, 0x2a };
+int huff_bits16[3] = { 0, 11, 17 };
+
+uint32 huff_table17[2] = { 0x0, 0x3e };
+int huff_bits17[2] = { 0, 13 };
+
+uint32 huff_table18[2] = { 0x0, 0x34 };
+int huff_bits18[2] = { 0, 13 };
+
+uint32 huff_table19[2] = { 0x0, 0x32 };
+int huff_bits19[2] = { 0, 13 };
+
+uint32 huff_table20[2] = { 0x0, 0x2e };
+int huff_bits20[2] = { 0, 13 };
+
+uint32 huff_table21[2] = { 0x0, 0x2c };
+int huff_bits21[2] = { 0, 13 };
+
+uint32 huff_table22[2] = { 0x0, 0x3e };
+int huff_bits22[2] = { 0, 14 };
+
+uint32 huff_table23[2] = { 0x0, 0x3c };
+int huff_bits23[2] = { 0, 14 };
+
+uint32 huff_table24[2] = { 0x0, 0x3a };
+int huff_bits24[2] = { 0, 14 };
+
+uint32 huff_table25[2] = { 0x0, 0x38 };
+int huff_bits25[2] = { 0, 14 };
+
+uint32 huff_table26[2] = { 0x0, 0x36 };
+int huff_bits26[2] = { 0, 14 };
+
+uint32 huff_table27[2] = { 0x0, 0x3e };
+int huff_bits27[2] = { 0, 17 };
+
+uint32 huff_table28[2] = { 0x0, 0x3c };
+int huff_bits28[2] = { 0, 17 };
+
+uint32 huff_table29[2] = { 0x0, 0x3a };
+int huff_bits29[2] = { 0, 17 };
+
+uint32 huff_table30[2] = { 0x0, 0x38 };
+int huff_bits30[2] = { 0, 17 };
+
+uint32 huff_table31[2] = { 0x0, 0x36 };
+int huff_bits31[2] = { 0, 17 };
+
+uint32 *huff_table[32] = { huff_table0, huff_table1, huff_table2, huff_table3, huff_table4, huff_table5, huff_table6, huff_table7, huff_table8, huff_table9, huff_table10, huff_table11, huff_table12, huff_table13, huff_table14, huff_table15, huff_table16, huff_table17, huff_table18, huff_table19, huff_table20, huff_table21, huff_table22, huff_table23, huff_table24, huff_table25, huff_table26, huff_table27, huff_table28, huff_table29, huff_table30, huff_table31 };
+int *huff_bits[32] = { huff_bits0, huff_bits1, huff_bits2, huff_bits3, huff_bits4, huff_bits5, huff_bits6, huff_bits7, huff_bits8, huff_bits9, huff_bits10, huff_bits11, huff_bits12, huff_bits13, huff_bits14, huff_bits15, huff_bits16, huff_bits17, huff_bits18, huff_bits19, huff_bits20, huff_bits21, huff_bits22, huff_bits23, huff_bits24, huff_bits25, huff_bits26, huff_bits27, huff_bits28, huff_bits29, huff_bits30, huff_bits31 };
diff --git a/converter/ppm/ppmtompeg/huff.h b/converter/ppm/ppmtompeg/huff.h
new file mode 100644
index 00000000..4d0b8840
--- /dev/null
+++ b/converter/ppm/ppmtompeg/huff.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /n/charlie-brown/project/mm/mpeg/mpeg_dist/mpeg_encode/RCS/huff.h,v 1.2 1995/01/19 23:08:28 eyhung Exp $
+ */
+
+/*  
+ *  THIS FILE IS MACHINE GENERATED!  DO NOT EDIT!
+ */
+#define HUFF_MAXRUN	32
+#define HUFF_MAXLEVEL	41
+
+extern int huff_maxlevel[];
+extern uint32 *huff_table[];
+extern int *huff_bits[];
diff --git a/converter/ppm/ppmtompeg/huff.table b/converter/ppm/ppmtompeg/huff.table
new file mode 100644
index 00000000..4f01d325
--- /dev/null
+++ b/converter/ppm/ppmtompeg/huff.table
@@ -0,0 +1,172 @@
+# 
+# Copyright (c) 1993 The Regents of the University of California.
+# All rights reserved.
+# 
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose, without fee, and without written agreement is
+# hereby granted, provided that the above copyright notice and the following
+# two paragraphs appear in all copies of this software.
+# 
+# IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+# OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+# CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# 
+# THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+# ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+# PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+# 
+
+#  
+#  $Header: /n/picasso/users/dwallach/vid2/mpeg_encode/RCS/huff.table,v 1.3 1993/02/17 23:21:58 dwallach Exp $
+#  $Log: huff.table,v $
+# Revision 1.3  1993/02/17  23:21:58  dwallach
+# checkin prior to keving's joining the project
+#
+# Revision 1.2  1993/01/18  10:20:02  dwallach
+# *** empty log message ***
+#
+#
+
+# this files is the raw Huffman encoding tables, from
+# the MPEG draft standard, P. D-41 - D-43 (Table D-2.15)
+
+# Format:
+# Run	Level	VLC Code
+
+# Run 0, Level 1 is special -- this table has the "NOT FIRST COEFF" entry
+# The "IF FIRST COEFF" would be: 0	1	1s
+
+0	1	11s
+
+0	2	0100 s
+0	3	0010 1s
+0	4	0000 110s
+0	5	0010 0110 s
+0	6	0010 0001 s
+0	7	0000 0010 10s
+0	8	0000 0001 1101 s
+0	9	0000 0001 1000 s
+0	10	0000 0001 0011 s
+0	11	0000 0001 0000 s
+0	12	0000 0000 1101 0s
+0	13	0000 0000 1100 1s
+0	14	0000 0000 1100 0s
+0	15	0000 0000 1011 1s
+0	16	0000 0000 0111 11s
+0	17	0000 0000 0111 10s
+0	18	0000 0000 0111 01s
+0	19	0000 0000 0111 00s
+0	20	0000 0000 0110 11s
+0	21	0000 0000 0110 10s
+0	22	0000 0000 0110 01s
+0	23	0000 0000 0110 00s
+0	24	0000 0000 0101 11s
+0	25	0000 0000 0101 10s
+0	26	0000 0000 0101 01s
+0	27	0000 0000 0101 00s
+0	28	0000 0000 0100 11s
+0	29	0000 0000 0100 10s
+0	30	0000 0000 0100 01s
+0	31	0000 0000 0100 00s
+0	32	0000 0000 0011 000s
+0	33	0000 0000 0010 111s
+0	34	0000 0000 0010 110s
+0	35	0000 0000 0010 101s
+0	36	0000 0000 0010 100s
+0	37	0000 0000 0010 011s
+0	38	0000 0000 0010 010s
+0	39	0000 0000 0010 001s
+0	40	0000 0000 0010 000s
+
+1	1	011s
+1	2	0001 10s
+1	3	0010 0101 s
+1	4	0000 0011 00s
+1	5	0000 0001 1011 s
+1	6	0000 0000 1011 0s
+1	7	0000 0000 1010 1s
+1	8	0000 0000 0011 111s
+1	9	0000 0000 0011 110s
+1	10	0000 0000 0011 101s
+1	11	0000 0000 0011 100s
+1	12	0000 0000 0011 011s
+1	13	0000 0000 0011 010s
+1	14	0000 0000 0011 001s
+1	15	0000 0000 0001 0011 s
+1	16	0000 0000 0001 0010 s
+1	17	0000 0000 0001 0001 s
+1	18	0000 0000 0001 0000 s
+
+2	1	0101 s
+2	2	0000 100s
+2	3	0000 0010 11s
+2	4	0000 0001 0100 s
+2	5	0000 0000 1010 0s
+
+3	1	0011 1s
+3	2	0010 0100 s
+3	3	0000 0001 1100 s
+3	4	0000 0000 1001 1s
+
+4	1	0011 0s
+4	2	0000 0011 11s
+4	3	0000 0001 0010 s
+
+5	1	0001 11s
+5	2	0000 0010 01s
+5	3	0000 0000 1001 0s
+
+6	1	0001 01s
+6	2	0000 0001 1110 s
+6	3	0000 0000 0001 0100 s
+
+7	1	0001 00s
+7	2	0000 0001 0101 s
+
+8	1	0000 111s
+8	2	0000 0001 0001 s
+
+9	1	0000 101s
+9	2	0000 0000 1000 1s
+
+10	1	0010 0111 s
+10	2	0000 0000 1000 0s
+
+11	1	0010 0011 s
+11	2	0000 0000 0001 1010 s
+
+12	1	0010 0010 s
+12	2	0000 0000 0001 1001 s
+
+13	1	0010 0000 s
+13	2	0000 0000 0001 1000 s
+
+14	1	0000 0011 10s
+14	2	0000 0000 0001 0111 s
+
+15	1	0000 0011 01s
+15	2	0000 0000 0001 0110 s
+
+16	1	0000 0010 00s
+16	2	0000 0000 0001 0101 s
+
+17	1	0000 0001 1111 s
+18	1	0000 0001 1010 s
+19	1	0000 0001 1001 s
+20	1	0000 0001 0111 s
+21	1	0000 0001 0110 s
+
+22	1	0000 0000 1111 1s
+23	1	0000 0000 1111 0s
+24	1	0000 0000 1110 1s
+25	1	0000 0000 1110 0s
+26	1	0000 0000 1101 1s
+
+27	1	0000 0000 0001 1111 s
+28	1	0000 0000 0001 1110 s
+29	1	0000 0000 0001 1101 s
+30	1	0000 0000 0001 1100 s
+31	1	0000 0000 0001 1011 s
diff --git a/converter/ppm/ppmtompeg/iframe.c b/converter/ppm/ppmtompeg/iframe.c
new file mode 100644
index 00000000..cb6d4683
--- /dev/null
+++ b/converter/ppm/ppmtompeg/iframe.c
@@ -0,0 +1,1062 @@
+/*===========================================================================*
+ * iframe.c                                  *
+ *                                       *
+ *  Procedures concerned with the I-frame encoding               *
+ *                                       *
+ * EXPORTED PROCEDURES:                              *
+ *  GenIFrame                                *
+ *  SetSlicesPerFrame                            *
+ *  SetBlocksPerSlice                            *
+ *  SetIQScale                               *
+ *  GetIQScale                               *
+ *  ResetIFrameStats                             *
+ *  ShowIFrameSummary                            *
+ *  EstimateSecondsPerIFrame                         *
+ *  EncodeYDC                                *
+ *  EncodeCDC                                *
+ *      time_elapsed                                                         *
+ *                                       *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+
+#ifdef CLOCKS_PER_SEC
+#include <times.h>
+#else
+#include <sys/times.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include "all.h"
+#include "mtypes.h"
+#include "frames.h"
+#include "prototypes.h"
+#include "block.h"
+#include "mpeg.h"
+#include "param.h"
+#include "mheaders.h"
+#include "fsize.h"
+#include "parallel.h"
+#include "postdct.h"
+#include "rate.h"
+#include "specifics.h"
+#include "opts.h"
+
+/*==================*
+ * STATIC VARIABLES *
+ *==================*/
+
+static  int lastNumBits = 0;
+static  int lastIFrame = 0;
+static int numBlocks = 0;
+static int numBits;
+static int numFrames = 0;
+static int numFrameBits = 0;
+static int32 totalTime = 0;
+static float    totalSNR = 0.0;
+static float    totalPSNR = 0.0;
+
+static int lengths[256] = {
+    0, 1, 2, 2, 3, 3, 3, 3,     /* 0 - 7 */
+    4, 4, 4, 4, 4, 4, 4, 4,     /* 8 - 15 */
+    5, 5, 5, 5, 5, 5, 5, 5,     /* 16 - 31 */
+    5, 5, 5, 5, 5, 5, 5, 5,
+    6, 6, 6, 6, 6, 6, 6, 6,     /* 32 - 63 */
+    6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6,
+    7, 7, 7, 7, 7, 7, 7, 7,     /* 64 - 127 */
+    7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7,
+    7, 7, 7, 7, 7, 7, 7, 7,
+    8, 8, 8, 8, 8, 8, 8, 8,
+    8, 8, 8, 8, 8, 8, 8, 8,
+    8, 8, 8, 8, 8, 8, 8, 8,
+    8, 8, 8, 8, 8, 8, 8, 8,
+    8, 8, 8, 8, 8, 8, 8, 8,
+    8, 8, 8, 8, 8, 8, 8, 8,
+    8, 8, 8, 8, 8, 8, 8, 8,
+    8, 8, 8, 8, 8, 8, 8, 8,
+    8, 8, 8, 8, 8, 8, 8, 8,
+    8, 8, 8, 8, 8, 8, 8, 8,
+    8, 8, 8, 8, 8, 8, 8, 8,
+    8, 8, 8, 8, 8, 8, 8, 8,
+    8, 8, 8, 8, 8, 8, 8, 8,
+    8, 8, 8, 8, 8, 8, 8, 8,
+    8, 8, 8, 8, 8, 8, 8, 8,
+    8, 8, 8, 8, 8, 8, 8, 8
+};
+
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+int qscaleI;
+int slicesPerFrame;
+int blocksPerSlice;
+int fCodeI, fCodeP, fCodeB;
+boolean printSNR = FALSE;
+boolean printMSE = FALSE;
+boolean decodeRefFrames = FALSE;
+Block **dct=NULL, **dctr=NULL, **dctb=NULL;
+dct_data_type   **dct_data; /* used in p/bframe.c */
+int  TIME_RATE;
+
+
+/*=====================*
+ * EXPORTED PROCEDURES *
+ *=====================*/
+extern void PrintItoIBitRate _ANSI_ARGS_((int numBits, int frameNum));
+
+/*===============================*
+ * INTERNAL PROCEDURE prototypes *
+ *===============================*/
+void AllocDctBlocks(void );
+int SetFCodeHelper (int sr);
+void CalcDistortion (MpegFrame *current, int y, int x);
+
+int
+SetFCodeHelper(int const SR) {
+
+    int     range,fCode;
+
+    if ( pixelFullSearch ) {
+        range = SR;
+    } else {
+        range = SR*2;
+    }
+
+    if ( range < 256 ) {
+        if ( range < 64 ) {
+            if ( range < 32 ) {
+                fCode = 1;
+            } else {
+                fCode = 2;
+            }
+        } else {
+            if ( range < 128 ) {
+                fCode = 3;
+            } else {
+                fCode = 4;
+            }
+        }
+    } else {
+        if ( range < 1024 ) {
+            if ( range < 512 ) {
+                fCode = 5;
+            } else {
+                fCode = 6;
+            }
+        } else {
+            if ( range < 2048 ) {
+                fCode = 7;
+            } else {
+                fprintf(stderr, "ERROR:  INVALID SEARCH RANGE!!!\n");
+                exit(1);
+            }
+        }
+    }
+    return fCode;
+}
+
+
+
+/*===========================================================================*
+ *
+ * SetFCode
+ *
+ *  set the forward_f_code and backward_f_code according to the search
+ *  range.  Must be called AFTER pixelFullSearch and searchRange have
+ *  been initialized.  Irrelevant for I-frames, but computation is
+ *  negligible (done only once, as well)
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    fCodeI,fCodeP,fCodeB
+ *
+ *===========================================================================*/
+void
+SetFCode(void) {
+    fCodeI = SetFCodeHelper(1); /* GenIFrame ignores value */
+    fCodeP = SetFCodeHelper(searchRangeP);
+    fCodeB = SetFCodeHelper(searchRangeB);
+}
+
+
+
+/*===========================================================================*
+ *
+ * SetSlicesPerFrame
+ *
+ *  set the number of slices per frame
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    slicesPerFrame
+ *
+ *===========================================================================*/
+void
+SetSlicesPerFrame(int const number) {
+
+    slicesPerFrame = number;
+}
+
+
+
+/*===========================================================================*
+ *
+ * SetBlocksPerSlice
+ *
+ *  set the number of blocks per slice, based on slicesPerFrame
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    blocksPerSlice
+ *
+ *===========================================================================*/
+void
+SetBlocksPerSlice(void) {
+
+    int     totalBlocks;
+    
+    totalBlocks = (Fsize_y>>4)*(Fsize_x>>4);
+
+    if ( slicesPerFrame > totalBlocks ) {
+        blocksPerSlice = 1;
+    } else {
+        blocksPerSlice = totalBlocks/slicesPerFrame;
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * SetIQScale
+ *
+ *  set the I-frame Q-scale
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    qscaleI
+ *
+ *===========================================================================*/
+void
+SetIQScale(int const qI) {
+    qscaleI = qI;
+}
+
+
+
+/*===========================================================================*
+ *
+ * GetIQScale
+ *
+ *  Get the I-frame Q-scale
+ *
+ * RETURNS: the Iframe Qscale
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+int
+GetIQScale(void) {
+    return qscaleI;
+}
+
+
+
+/*===========================================================================*
+ *
+ * GenIFrame
+ *
+ *  generate an I-frame; appends result to bb
+ *
+ * RETURNS: I-frame appended to bb
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+GenIFrame(BitBucket * const bb, 
+          MpegFrame * const current) {
+
+    int x, y;
+    int index;
+    FlatBlock    fb[6];
+    Block    dec[6];
+    int32 y_dc_pred, cr_dc_pred, cb_dc_pred;
+    int          totalBits;
+    int          totalFrameBits;
+    int32        startTime, endTime;
+    float        snr[3], psnr[3];
+    int          mbAddress;
+    int          QScale;
+    BlockMV      *info; /* Not used in Iframes, but nice to pass in anyway */
+    int          bitstreamMode, newQScale;
+    int          rc_blockStart=0;
+
+    if (dct==NULL) AllocDctBlocks();
+    if (collect_quant) {fprintf(collect_quant_fp, "# I\n");}
+
+    /* set-up for statistics */
+    numFrames++;
+    totalFrameBits = bb->cumulativeBits;
+    if ( showBitRatePerFrame ) {
+        if ( lastNumBits == 0 ) {
+            lastNumBits = bb->cumulativeBits;
+            lastIFrame = current->id;
+        } else {
+            /* ASSUMES 30 FRAMES PER SECOND */
+    
+            if (! realQuiet) {
+                fprintf(stdout, "I-to-I (frames %5d to %5d) bitrate:  %8d\n",
+                        lastIFrame, current->id-1,
+                        ((bb->cumulativeBits-lastNumBits)*30)/
+                        (current->id-lastIFrame));
+            }
+    
+            fprintf(bitRateFile, "I-to-I (frames %5d to %5d) bitrate:  %8d\n",
+                    lastIFrame, current->id-1,
+                    ((bb->cumulativeBits-lastNumBits)*30)/
+                    (current->id-lastIFrame));
+            lastNumBits = bb->cumulativeBits;       
+            lastIFrame = current->id;
+        }
+    }
+    
+    startTime = time_elapsed();
+    
+    Frame_AllocBlocks(current);
+    BlockifyFrame(current);
+
+    DBG_PRINT(("Generating iframe\n"));
+    QScale = GetIQScale();
+    /*   Allocate bits for this frame for rate control purposes */
+    bitstreamMode = getRateMode();
+    if (bitstreamMode == FIXED_RATE) {
+        targetRateControl(current);
+    }
+
+    Mhead_GenPictureHeader(bb, I_FRAME, current->id, fCodeI);
+    /* Check for Qscale change */
+    if (specificsOn) {
+        newQScale = SpecLookup(current->id, 0, 0 /* junk */, &info, QScale);
+        if (newQScale != -1) {
+            QScale = newQScale;
+        }
+        /* check for slice */
+        newQScale = SpecLookup(current->id, 1, 1, &info, QScale);
+        if (newQScale != -1) {
+            QScale = newQScale;
+        }
+    }
+    Mhead_GenSliceHeader(bb, 1, QScale, NULL, 0);
+    
+    if ( referenceFrame == DECODED_FRAME ) {
+        Frame_AllocDecoded(current, TRUE);
+    } else if ( printSNR ) {
+        Frame_AllocDecoded(current, FALSE);
+    }
+    
+    y_dc_pred = cr_dc_pred = cb_dc_pred = 128;
+    totalBits = bb->cumulativeBits;
+    mbAddress = 0;
+
+    /* DCT the macroblocks */
+    for (y = 0;  y < (Fsize_y >> 3);  y += 2) {
+        for (x = 0;  x < (Fsize_x >> 3);  x += 2) {
+            if (collect_quant && (collect_quant_detailed & 1)) 
+                fprintf(collect_quant_fp, "l\n");
+            if (DoLaplace) {LaplaceCnum = 0;}
+            mp_fwd_dct_block2(current->y_blocks[y][x], dct[y][x]);
+            mp_fwd_dct_block2(current->y_blocks[y][x+1], dct[y][x+1]);
+            mp_fwd_dct_block2(current->y_blocks[y+1][x], dct[y+1][x]);
+            mp_fwd_dct_block2(current->y_blocks[y+1][x+1], dct[y+1][x+1]);
+            if (collect_quant && (collect_quant_detailed & 1)) 
+                fprintf(collect_quant_fp, "c\n");
+            if (DoLaplace) {LaplaceCnum = 1;}
+            mp_fwd_dct_block2(current->cb_blocks[y>>1][x>>1], 
+                              dctb[y>>1][x>>1]);
+            if (DoLaplace) {LaplaceCnum = 2;}
+            mp_fwd_dct_block2(current->cr_blocks[y>>1][x>>1], 
+                              dctr[y>>1][x>>1]);
+        }
+    }
+    
+    if (DoLaplace)
+        CalcLambdas();
+
+    for (y = 0;  y < (Fsize_y >> 3);  y += 2) {
+        for (x = 0;  x < (Fsize_x >> 3);  x += 2) {
+            /* Check for Qscale change */
+            if (specificsOn) {
+                newQScale = 
+                    SpecLookup(current->id, 2, mbAddress, &info, QScale);
+                if (newQScale != -1) {
+                    QScale = newQScale;
+                }
+            }
+    
+            /*  Determine if new Qscale needed for Rate Control purposes  */
+            if (bitstreamMode == FIXED_RATE) {
+                rc_blockStart = bb->cumulativeBits;
+                newQScale = needQScaleChange(qscaleI,
+                                             current->y_blocks[y][x],
+                                             current->y_blocks[y][x+1],
+                                             current->y_blocks[y+1][x],
+                                             current->y_blocks[y+1][x+1]);
+                if (newQScale > 0) {
+                    QScale = newQScale;
+                }
+            }
+    
+            if ( (mbAddress % blocksPerSlice == 0) && (mbAddress != 0) ) {
+                /* create a new slice */
+                if (specificsOn) {
+                    /* Make sure no slice Qscale change */
+                    newQScale = SpecLookup(current->id, 1,
+                                           mbAddress/blocksPerSlice, &info, 
+                                           QScale);
+                    if (newQScale != -1) QScale = newQScale;
+                }
+                Mhead_GenSliceEnder(bb);
+                Mhead_GenSliceHeader(bb, 1+(y>>1), QScale, NULL, 0);
+                y_dc_pred = cr_dc_pred = cb_dc_pred = 128;
+      
+                GEN_I_BLOCK(I_FRAME, current, bb, 1+(x>>1), QScale);
+            } else {
+                GEN_I_BLOCK(I_FRAME, current, bb, 1, QScale);
+            }
+
+            if (WriteDistortionNumbers) {
+                CalcDistortion(current, y, x);
+            }
+    
+            if ( decodeRefFrames ) {
+                /* now, reverse the DCT transform */
+                LaplaceCnum = 0;
+                for ( index = 0; index < 6; index++ ) {
+                    if (!DoLaplace) {
+                        Mpost_UnQuantZigBlock(fb[index], dec[index], QScale, 
+                                              TRUE);
+                    } else {
+                        if (index == 4) {LaplaceCnum = 1;}
+                        if (index == 5) {LaplaceCnum = 2;}
+                        Mpost_UnQuantZigBlockLaplace(fb[index], dec[index], 
+                                                     QScale, TRUE);
+                    }
+                    mpeg_jrevdct((int16 *)dec[index]);      
+                }
+      
+                /* now, unblockify */
+                BlockToData(current->decoded_y, dec[0], y, x);
+                BlockToData(current->decoded_y, dec[1], y, x+1);
+                BlockToData(current->decoded_y, dec[2], y+1, x);
+                BlockToData(current->decoded_y, dec[3], y+1, x+1);
+                BlockToData(current->decoded_cb, dec[4], y>>1, x>>1);
+                BlockToData(current->decoded_cr, dec[5], y>>1, x>>1);
+            }
+    
+            numBlocks++;
+            mbAddress++;
+            /*   Rate Control */
+            if (bitstreamMode == FIXED_RATE) {
+                incMacroBlockBits(bb->cumulativeBits - rc_blockStart);
+                rc_blockStart = bb->cumulativeBits;
+                MB_RateOut(TYPE_IFRAME);
+            }
+        }
+    }
+    
+    if ( printSNR ) {
+        BlockComputeSNR(current,snr,psnr);
+        totalSNR += snr[0];
+        totalPSNR += psnr[0];
+    }
+    
+    numBits += (bb->cumulativeBits-totalBits);
+    
+    DBG_PRINT(("End of frame\n"));
+    
+    Mhead_GenSliceEnder(bb);
+    /*   Rate Control  */
+    if (bitstreamMode == FIXED_RATE) {
+        updateRateControl(TYPE_IFRAME);
+    }
+    
+    endTime = time_elapsed();
+    totalTime += (endTime-startTime);
+    
+    numFrameBits += (bb->cumulativeBits-totalFrameBits);
+    
+    if ( showBitRatePerFrame ) {
+        /* ASSUMES 30 FRAMES PER SECOND */
+        fprintf(bitRateFile, "%5d\t%8d\n", current->id,
+                30*(bb->cumulativeBits-totalFrameBits));
+    }
+    
+    if ( frameSummary && !realQuiet ) {
+        
+        /* ASSUMES 30 FRAMES PER SECOND */
+        fprintf(stdout, 
+                "FRAME %d (I):  %ld seconds  (%d bits/s output)\n", 
+                current->id, (long)((endTime-startTime)/TIME_RATE),
+                30*(bb->cumulativeBits-totalFrameBits));
+        if ( printSNR ) {
+            fprintf(stdout, 
+                    "FRAME %d:  SNR:  %.1f\t%.1f\t%.1f\t"
+                    "PSNR:  %.1f\t%.1f\t%.1f\n",
+                        current->id, snr[0], snr[1], snr[2],
+                    psnr[0], psnr[1], psnr[2]);
+        }
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * ResetIFrameStats
+ *
+ *  reset the I-frame statistics
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+ResetIFrameStats(void) {
+    numBlocks = 0;
+    numBits = 0;
+    numFrames = 0;
+    numFrameBits = 0;
+    totalTime = 0;
+}
+
+
+
+float
+IFrameTotalTime(void) {
+    return (float)totalTime/(float)TIME_RATE;
+}
+
+
+
+void
+ShowIFrameSummary(unsigned int const inputFrameBits, 
+                  unsigned int const totalBits, 
+                  FILE *       const fpointer) {
+/*----------------------------------------------------------------------------
+   Print out statistics on all I frames.
+-----------------------------------------------------------------------------*/
+    if (numFrames > 0) {
+        fprintf(fpointer, "-------------------------\n");
+        fprintf(fpointer, "*****I FRAME SUMMARY*****\n");
+        fprintf(fpointer, "-------------------------\n");
+
+        fprintf(fpointer, "  Blocks:    %5d     (%6d bits)     (%5d bpb)\n",
+                numBlocks, numBits, numBits/numBlocks);
+        fprintf(fpointer, "  Frames:    %5d     (%6d bits)     (%5d bpf)"
+                "(%2.1f%% of total)\n",
+                numFrames, numFrameBits, numFrameBits/numFrames,
+                100.0*(float)numFrameBits/(float)totalBits);
+        fprintf(fpointer, "  Compression:  %3d:1     (%9.4f bpp)\n",
+                numFrames*inputFrameBits/numFrameBits,
+                24.0*(float)numFrameBits/(float)(numFrames*inputFrameBits));
+        if ( printSNR )
+            fprintf(fpointer, "  Avg Y SNR/PSNR:  %.1f     %.1f\n",
+                    totalSNR/(float)numFrames, totalPSNR/(float)numFrames);
+        if ( totalTime == 0 ) {
+            fprintf(fpointer, "  Seconds:  NONE\n");
+        } else {
+            fprintf(fpointer, "  Seconds:  %9ld     (%9.4f fps)  (%9ld pps)  "
+                    "(%9ld mps)\n",
+                    (long)(totalTime/TIME_RATE),
+                    (float)((float)(TIME_RATE*numFrames)/(float)totalTime),
+                    (long)((float)TIME_RATE * (float)numFrames *
+                           (float)inputFrameBits/(24.0*(float)totalTime)),
+                    (long)((float)TIME_RATE*(float)numFrames *
+                           (float)inputFrameBits/(256.0*24.0 *
+                                                  (float)totalTime)));
+        }
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * EstimateSecondsPerIFrame
+ *
+ *  estimates the number of seconds required per I-frame
+ *
+ * RETURNS: seconds (floating point value)
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+float
+EstimateSecondsPerIFrame()
+{
+    return (float)totalTime/((float)TIME_RATE*(float)numFrames);
+}
+
+
+/*===========================================================================*
+ *
+ * EncodeYDC
+ *
+ *  Encode the DC portion of a DCT of a luminance block
+ *
+ * RETURNS: result appended to bb
+ *
+ * SIDE EFFECTS:    updates pred_term
+ *
+ *===========================================================================*/
+void
+EncodeYDC(int32       const dc_term,
+          int32 *     const pred_term,
+          BitBucket * const bb) {
+
+    /* see Table B.5a -- MPEG-I doc */
+    static int codes[9] = {
+    0x4, 0x0, 0x1, 0x5, 0x6, 0xe, 0x1e, 0x3e, 0x7e
+    };
+    static int codeLengths[9] = {
+    3,   2,   2,   3,   3,   4,   5,    6,    7
+    };
+    int ydiff, ydiff_abs;
+    int length;
+
+    ydiff = (dc_term - (*pred_term));
+    if (ydiff > 255) {
+#ifdef BLEAH 
+      fprintf(stdout, "TRUNCATED\n");
+#endif
+    ydiff = 255;
+    } else if (ydiff < -255) {
+#ifdef BLEAH 
+      fprintf(stdout, "TRUNCATED\n");
+#endif
+    ydiff = -255;
+    }
+
+    ydiff_abs = ABS(ydiff);
+    length = lengths[ydiff_abs];
+    Bitio_Write(bb, codes[length], codeLengths[length]);
+    if ( length != 0 ) {
+    if ( ydiff > 0 ) {
+        Bitio_Write(bb, ydiff_abs, length);
+    } else {
+        Bitio_Write(bb, ~ydiff_abs, length);
+    }
+    }
+
+    (*pred_term) += ydiff;
+}
+
+
+
+/*===========================================================================*
+ *
+ * EncodeCDC
+ *
+ *  Encode the DC portion of a DCT of a chrominance block
+ *
+ * RETURNS: result appended to bb
+ *
+ * SIDE EFFECTS:    updates pred_term
+ *
+ *===========================================================================*/
+void
+EncodeCDC(int32       const dc_term,
+          int32     * const pred_term,
+          BitBucket * const bb) {
+
+    /* see Table B.5b -- MPEG-I doc */
+    static int codes[9] = {
+        0x0, 0x1, 0x2, 0x6, 0xe, 0x1e, 0x3e, 0x7e, 0xfe
+    };
+    static int codeLengths[9] = {
+        2,   2,   2,   3,   4,   5,    6,    7,    8
+    };
+    int cdiff, cdiff_abs;
+    int length;
+
+    cdiff = (dc_term - (*pred_term));
+    if (cdiff > 255) {
+#ifdef BLEAH
+        fprintf(stdout, "TRUNCATED\n"); 
+#endif
+        cdiff = 255;
+    } else if (cdiff < -255) {
+#ifdef BLEAH
+        fprintf(stdout, "TRUNCATED\n"); 
+#endif
+        cdiff = -255;
+    }
+
+    cdiff_abs = ABS(cdiff);
+    length = lengths[cdiff_abs];
+    Bitio_Write(bb, codes[length], codeLengths[length]);
+    if ( length != 0 ) {
+        if ( cdiff > 0 ) {
+            Bitio_Write(bb, cdiff_abs, length);
+        } else {
+            Bitio_Write(bb, ~cdiff_abs, length);
+        }
+    }
+
+    (*pred_term) += cdiff;
+}
+
+
+
+void
+BlockComputeSNR(MpegFrame * const current,
+                float *     const snr,
+                float *     const psnr) {
+
+    int32    tempInt;
+    int y, x;
+    int32 varDiff[3];
+    double    ratio[3];
+    double    total[3];
+    uint8 **origY=current->orig_y, **origCr=current->orig_cr, 
+        **origCb=current->orig_cb;
+    uint8 **newY=current->decoded_y, **newCr=current->decoded_cr, 
+        **newCb=current->decoded_cb;
+    static int32       **SignalY,  **NoiseY;
+    static int32       **SignalCb, **NoiseCb;
+    static int32       **SignalCr, **NoiseCr;
+    static short   ySize[3], xSize[3];
+    static boolean needs_init=TRUE;
+  
+    /* Init */
+    if (needs_init) {
+        int ysz = (Fsize_y>>3) * sizeof(int32 *);
+        int xsz = (Fsize_x>>3);
+    
+        needs_init = FALSE;
+        for (y=0; y<3; y++) {
+            varDiff[y] = ratio[y] = total[y] = 0.0;
+        }
+        ySize[0]=Fsize_y;     xSize[0]=Fsize_x;
+        ySize[1]=Fsize_y>>1;  xSize[1]=Fsize_x>>1;
+        ySize[2]=Fsize_y>>1;  xSize[2]=Fsize_x>>1;
+        SignalY  = (int32 **) malloc(ysz);
+        NoiseY   = (int32 **) malloc(ysz);
+        SignalCb = (int32 **) malloc(ysz);
+        NoiseCb  = (int32 **) malloc(ysz);
+        SignalCr = (int32 **) malloc(ysz);
+        NoiseCr  = (int32 **) malloc(ysz);
+        if (SignalY == NULL || NoiseY == NULL || SignalCr == NULL || 
+            NoiseCb == NULL || SignalCb == NULL || NoiseCr == NULL) {
+            fprintf(stderr, "Out of memory in BlockComputeSNR\n");
+            exit(-1);
+        }
+        for (y = 0; y < ySize[0]>>3; y++) {
+            SignalY[y]  = (int32 *) calloc(xsz,4);
+            SignalCr[y]  = (int32 *) calloc(xsz,4);
+            SignalCb[y]  = (int32 *) calloc(xsz,4);
+            NoiseY[y]  = (int32 *) calloc(xsz,4);
+            NoiseCr[y]  = (int32 *) calloc(xsz,4);
+            NoiseCb[y]  = (int32 *) calloc(xsz,4);
+        }
+    } else {
+        for (y = 0; y < ySize[0]>>3; y++) {
+            memset((char *) &NoiseY[y][0], 0, (xSize[0]>>3) * 4);
+            memset((char *) &SignalY[y][0], 0, (xSize[0]>>3) * 4);
+            memset((char *) &NoiseCb[y][0], 0, (xSize[0]>>3) * 4);
+            memset((char *) &NoiseCr[y][0], 0, (xSize[0]>>3) * 4);
+            memset((char *) &SignalCb[y][0], 0, (xSize[0]>>3) * 4);
+            memset((char *) &SignalCr[y][0], 0, (xSize[0]>>3) * 4);
+        }
+    }
+  
+    /* find all the signal and noise */
+    for (y = 0; y < ySize[0]; y++) {
+        for (x = 0; x < xSize[0]; x++) {
+            tempInt = (origY[y][x] - newY[y][x]);
+            NoiseY[y>>4][x>>4] += tempInt*tempInt;
+            total[0] += (double)abs(tempInt);
+            tempInt = origY[y][x];
+            SignalY[y>>4][x>>4] += tempInt*tempInt;
+        }}
+    for (y = 0; y < ySize[1]; y++) {
+        for (x = 0; x < xSize[1]; x ++) {
+            tempInt = (origCb[y][x] - newCb[y][x]);
+            NoiseCb[y>>3][x>>3] += tempInt*tempInt;
+            total[1] += (double)abs(tempInt);
+            tempInt = origCb[y][x];
+            SignalCb[y>>3][x>>3] += tempInt*tempInt;
+            tempInt = (origCr[y][x]-newCr[y][x]);
+            NoiseCr[y>>3][x>>3] += tempInt*tempInt;
+            total[2] += (double)abs(tempInt);
+            tempInt = origCr[y][x];
+            SignalCr[y>>3][x>>3] += tempInt*tempInt;
+        }}
+  
+    /* Now sum up that noise */
+    for(y=0; y<Fsize_y>>4; y++){
+        for(x=0; x<Fsize_x>>4; x++){
+            varDiff[0] += NoiseY[y][x];
+            varDiff[1] += NoiseCb[y][x];
+            varDiff[2] += NoiseCr[y][x];
+            if (printMSE) printf("%4d ",(int)(NoiseY[y][x]/256.0));
+        }
+        if (printMSE) puts("");
+    }
+  
+    /* Now look at those ratios! */
+    for(y=0; y<Fsize_y>>4; y++){
+        for(x=0; x<Fsize_x>>4; x++){
+            ratio[0] += (double)SignalY[y][x]/(double)varDiff[0];
+            ratio[1] += (double)SignalCb[y][x]/(double)varDiff[1];
+            ratio[2] += (double)SignalCr[y][x]/(double)varDiff[2];
+        }}
+  
+    for (x=0; x<3; x++) {
+        snr[x] = 10.0 * log10(ratio[x]);
+        psnr[x] = 20.0 * log10(255.0/sqrt((double)varDiff[x]/
+                                          (double)(ySize[x]*xSize[x])));
+
+        if (! realQuiet) {
+            fprintf(stdout, "Mean error[%1d]:  %f\n",
+                    x, total[x] / (double)(xSize[x] * ySize[x]));
+        }
+
+    }
+}
+
+
+
+void
+WriteDecodedFrame(MpegFrame * const frame) {
+
+    FILE * fpointer;
+    char   fileName[256];
+    int    width, height;
+    int    y;
+
+    /* need to save decoded frame to disk because it might be accessed
+       by another process */
+
+    width = Fsize_x;
+    height = Fsize_y;
+
+    sprintf(fileName, "%s.decoded.%d", outputFileName, frame->id);
+
+    if (!realQuiet) {
+        fprintf(stdout, "Outputting to %s\n", fileName);
+        fflush(stdout);
+    }
+
+    fpointer = fopen(fileName, "wb");
+
+    for ( y = 0; y < height; y++ ) {
+        fwrite(frame->decoded_y[y], 1, width, fpointer);
+    }
+
+    for (y = 0; y < (height >> 1); y++) {           /* U */
+        fwrite(frame->decoded_cb[y], 1, width >> 1, fpointer);
+    }
+
+    for (y = 0; y < (height >> 1); y++) {           /* V */
+        fwrite(frame->decoded_cr[y], 1, width >> 1, fpointer);
+    }
+    fflush(fpointer);
+    fclose(fpointer);
+}
+
+
+
+void
+PrintItoIBitRate(int const numBits,
+                 int const frameNum) {
+
+    if ( showBitRatePerFrame ) {
+        /* ASSUMES 30 FRAMES PER SECOND */
+
+        if (! realQuiet) {
+            fprintf(stdout, "I-to-I (frames %5d to %5d) bitrate:  %8d\n",
+                    lastIFrame, frameNum-1,
+                    ((numBits-lastNumBits)*30)/
+                    (frameNum-lastIFrame));
+        }
+
+        fprintf(bitRateFile, "I-to-I (frames %5d to %5d) bitrate:  %8d\n",
+                lastIFrame, frameNum-1,
+                ((numBits-lastNumBits)*30)/
+                (frameNum-lastIFrame));
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * AllocDctBlocks
+ *
+ *  allocate memory for dct blocks
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    creates dct, dctr, dctb
+ *
+ *===========================================================================*/
+void
+AllocDctBlocks(void) {
+    int dctx, dcty;
+    int i;
+
+    dctx = Fsize_x / DCTSIZE;
+    dcty = Fsize_y / DCTSIZE;
+
+    dct = (Block **) malloc(sizeof(Block *) * dcty);
+    ERRCHK(dct, "malloc");
+    for (i = 0; i < dcty; i++) {
+        dct[i] = (Block *) malloc(sizeof(Block) * dctx);
+        ERRCHK(dct[i], "malloc");
+    }
+
+    dct_data = (dct_data_type **) malloc(sizeof(dct_data_type *) * dcty);
+    ERRCHK(dct_data, "malloc");
+    for (i = 0; i < dcty; i++) {
+        dct_data[i] = (dct_data_type *) malloc(sizeof(dct_data_type) * dctx);
+        ERRCHK(dct[i], "malloc");
+    }
+
+    dctr = (Block **) malloc(sizeof(Block *) * (dcty >> 1));
+    dctb = (Block **) malloc(sizeof(Block *) * (dcty >> 1));
+    ERRCHK(dctr, "malloc");
+    ERRCHK(dctb, "malloc");
+    for (i = 0; i < (dcty >> 1); i++) {
+        dctr[i] = (Block *) malloc(sizeof(Block) * (dctx >> 1));
+        dctb[i] = (Block *) malloc(sizeof(Block) * (dctx >> 1));
+        ERRCHK(dctr[i], "malloc");
+        ERRCHK(dctb[i], "malloc");
+    }
+}
+
+
+
+/*======================================================================*
+ *
+ * time_elapsed
+ *
+ *     Handle different time systems on different machines
+ *
+ *  RETURNS number of seconds process time used
+ *
+ *======================================================================*/
+int32 time_elapsed(void) {
+#ifdef CLOCKS_PER_SEC
+    /* ANSI C */
+    TIME_RATE = CLOCKS_PER_SEC;
+    return (int32) clock();
+#else
+    struct tms   timeBuffer;
+    TIME_RATE = 60;
+    times(&timeBuffer);
+    return timeBuffer.tms_utime + timeBuffer.tms_stime;
+#endif
+}
+
+
+
+void
+CalcDistortion(MpegFrame * const current,
+               int         const y,
+               int         const x) {
+
+    int qscale, distort=0;
+    Block decblk;
+    FlatBlock fblk;
+    int datarate = 0;
+  
+    for (qscale = 1; qscale < 32; qscale ++) {
+        distort = 0;
+        datarate = 0;
+        Mpost_QuantZigBlock(dct[y][x], fblk, qscale, TRUE);
+        Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE);
+        if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk);
+        mpeg_jrevdct((int16 *)decblk);
+        distort += mse(current->y_blocks[y][x], decblk);
+
+        Mpost_QuantZigBlock(dct[y][x+1], fblk, qscale, TRUE);
+        Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE);
+        if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk);
+        mpeg_jrevdct((int16 *)decblk);
+        distort += mse(current->y_blocks[y][x+1], decblk);
+
+        Mpost_QuantZigBlock(dct[y+1][x], fblk, qscale, TRUE);
+        Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE);
+        if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk);
+        mpeg_jrevdct((int16 *)decblk);
+        distort += mse(current->y_blocks[y+1][x], decblk);
+
+        Mpost_QuantZigBlock(dct[y+1][x+1], fblk, qscale, TRUE);
+        Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE);
+        if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk);
+        mpeg_jrevdct((int16 *)decblk);
+        distort += mse(current->y_blocks[y+1][x+1], decblk);
+
+        Mpost_QuantZigBlock(dctb[y >> 1][x >> 1], fblk, qscale, TRUE);
+        Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE);
+        if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk);
+        mpeg_jrevdct((int16 *)decblk);
+        distort += mse(current->cb_blocks[y>>1][x>>1], decblk);
+
+        Mpost_QuantZigBlock(dctr[y >> 1][x >> 1], fblk, qscale, TRUE);
+        Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE);
+        if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk);
+        mpeg_jrevdct((int16 *)decblk);
+        distort += mse(current->cr_blocks[y >> 1][x >> 1], decblk);
+
+        if (!collect_distortion_detailed) {
+            fprintf(distortion_fp, "\t%d\n", distort);
+        } else if (collect_distortion_detailed == 1) {
+            fprintf(distortion_fp, "\t%d\t%d\n", distort, datarate);
+        } else {
+            fprintf(fp_table_rate[qscale-1], "%d\n", datarate);
+            fprintf(fp_table_dist[qscale-1], "%d\n", distort);
+        }
+    }
+}
+
+
+
+
diff --git a/converter/ppm/ppmtompeg/input.c b/converter/ppm/ppmtompeg/input.c
new file mode 100644
index 00000000..2b87afa7
--- /dev/null
+++ b/converter/ppm/ppmtompeg/input.c
@@ -0,0 +1,515 @@
+/*===========================================================================*
+ * input.c              
+ *                      
+ *  Stuff for getting the raw frame input for the program
+ *                                    
+ *===========================================================================*/
+
+/* COPYRIGHT INFORMATION IS AT THE END OF THIS FILE */
+
+#define _XOPEN_SOURCE 1
+    /* This makes sure popen() is in stdio.h.  In GNU libc 2.1.3, 
+     _POSIX_C_SOURCE = 2 is sufficient, but on AIX 4.3, the higher level
+     _XOPEN_SOURCE is required.  2000.09.09 
+    */
+
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+
+#include "nstring.h"
+#include "mallocvar.h"
+#include "parallel.h"
+#include "readframe.h"
+#include "param.h"
+#include "jpeg.h"
+#include "input.h"
+
+extern boolean realQuiet;	/* TRUE = no messages to stdout */
+
+struct InputFileEntry {
+    char    left[256];
+    char    right[256];
+    bool    glob;       /* if FALSE, left is complete name */
+    int     startID;
+    int     endID;
+    int     skip;
+    int     numPadding;     /* -1 if there is none */
+    int     numFiles;
+    bool    repeat;
+};
+
+
+struct inputSource inputSource;
+
+
+
+void
+GetNthInputFileName(struct inputSource * const inputSourceP,
+                    unsigned int         const n,
+                    const char **        const fileNameP) {
+/*----------------------------------------------------------------------------
+   Return the file name of the Nth input file.
+-----------------------------------------------------------------------------*/
+    static int	lastN = 0, lastMapN = 0, lastSoFar = 0;
+    int	    mapN;
+    int     index;
+    int	    soFar;
+    int	    numPadding;
+    struct InputFileEntry * mapNEntryP;
+
+    assert(!inputSourceP->stdinUsed);
+    assert(n < inputSourceP->numInputFiles);
+
+    if (n >= lastN) {
+        soFar = lastSoFar;
+        index = lastMapN;
+    } else {
+        soFar = 0;
+        index = 0;
+    }
+
+    assert(index < inputSourceP->numInputFileEntries);
+
+    while (soFar + inputSourceP->inputFileEntries[index]->numFiles <= n) {
+        soFar +=  inputSourceP->inputFileEntries[index]->numFiles;
+        ++index;
+        assert(index < inputSourceP->numInputFileEntries);
+    }
+
+    mapN = index;
+    
+    mapNEntryP = inputSourceP->inputFileEntries[mapN];
+
+    index = mapNEntryP->startID + mapNEntryP->skip*(n - soFar);
+
+    numPadding = mapNEntryP->numPadding;
+
+    if (numPadding != -1) {
+        char    numBuffer[33];
+        int	    loop;
+
+        sprintf(numBuffer, "%32d", index);
+        for (loop = 32-numPadding; loop < 32; ++loop) {
+            if (numBuffer[loop] != ' ')
+                break;
+            else
+                numBuffer[loop] = '0';
+        }
+
+        if (mapNEntryP->repeat != TRUE)
+            asprintfN(fileNameP, "%s%s%s",
+                      mapNEntryP->left, &numBuffer[32-numPadding],
+                      mapNEntryP->right);
+        else
+            asprintfN(fileNameP, "%s", mapNEntryP->left);
+    } else {
+        if (mapNEntryP->repeat != TRUE)
+            asprintfN(fileNameP, "%s%d%s",
+                      mapNEntryP->left, index, mapNEntryP->right);
+        else
+            asprintfN(fileNameP, "%s", mapNEntryP->left);
+    }
+
+    lastN = n;
+    lastMapN = mapN;
+    lastSoFar = soFar;
+}
+
+
+
+void
+ReadNthFrame(struct inputSource * const inputSourceP,
+             unsigned int         const frameNumber,
+             boolean              const remoteIO,
+             boolean              const childProcess,
+             boolean              const separateConversion,
+             const char *         const slaveConversion,
+             const char *         const inputConversion,
+             MpegFrame *          const frameP,
+             bool *               const endOfStreamP) {
+
+    if (remoteIO)
+        /* Get the frame from the remote I/O server */
+        GetRemoteFrame(frameP, frameNumber);
+    else {
+        /* Get the frame out of the file in which it lives */
+
+        const char * conversion;
+
+        if (childProcess && separateConversion)
+            conversion = slaveConversion;
+        else
+            conversion = inputConversion;
+
+        ReadFrame(frameP, inputSourceP, frameNumber, conversion, endOfStreamP);
+    }
+}
+
+
+
+/* Jim Boucher's code */
+
+void
+JM2JPEG(struct inputSource * const inputSourceP) {
+    char full_path[1024];
+    char inter_file[1024]; 
+    int ci;
+    
+    for(ci = 0; ci < inputSourceP->numInputFileEntries; ci++) {
+        inter_file[0] = '\0';
+        full_path[0] = '\0';
+        strcpy(full_path, currentPath);
+    
+        if (!inputSource.stdinUsed) {
+            strcat(full_path, "/");
+            strcat(full_path, inputSourceP->inputFileEntries[ci]->left);
+            strcpy(inter_file,full_path);
+    
+            if (!realQuiet)
+                fprintf(stdout, "Extracting JPEG's in the JMOVIE from %s\n",
+                        full_path);
+    
+            JMovie2JPEG(full_path,
+                        inter_file,
+                        inputSourceP->inputFileEntries[ci]->startID, 
+                        inputSourceP->inputFileEntries[ci]->endID);
+        } else
+            pm_error("ERROR: Cannot use JMovie format on Standard Input");
+    }
+}
+
+
+
+static const char *
+SkipSpacesTabs(const char * const start) {
+
+    const char * p;
+
+    for (p = start; *p == ' ' || *p == '\t'; ++p);
+
+    return p;
+}
+
+
+
+static void
+processGlob(const char *            const input, 
+            struct InputFileEntry * const inputFileEntryP) {
+
+    const char *globPtr;
+    char *  charPtr;
+    char    left[256], right[256];
+    char    leftNumText[256], rightNumText[256];
+    char    skipNumText[256];
+    int	    leftNum, rightNum;
+    int	    skipNum;
+    boolean padding;
+    int	    numPadding = 0;
+
+    inputFileEntryP->glob = TRUE;
+    inputFileEntryP->repeat = FALSE;
+
+    /* star expand */
+
+    globPtr = input;
+    charPtr = left;
+    /* copy left of '*' */
+    while ( (*globPtr != '\0') && (*globPtr != '*') ) {
+        *charPtr = *globPtr;
+        charPtr++;
+        globPtr++;
+    }
+    *charPtr = '\0';
+
+    if (*globPtr == '\0') {
+        fprintf(stderr, 
+                "WARNING: expanding non-star regular expression\n");
+        inputFileEntryP->repeat = TRUE;
+        globPtr = input;
+        charPtr = left;
+        /* recopy left of whitespace */
+        while ( (*globPtr != '\0') && (*globPtr != '*') && 
+                (*globPtr != ' ')  && (*globPtr != '\t')) {
+            *charPtr = *globPtr;
+            charPtr++;
+            globPtr++;
+        }
+        *charPtr = '\0';
+        *right = '\0';
+    } else {
+
+        globPtr++;
+        charPtr = right;
+        /* copy right of '*' */
+        while ( (*globPtr != '\0') && (*globPtr != ' ') &&
+                (*globPtr != '\t') ) {
+            *charPtr = *globPtr;
+            charPtr++;
+            globPtr++;
+        }
+        *charPtr = '\0';
+    }
+      
+    globPtr = SkipSpacesTabs(globPtr);
+
+    if ( *globPtr != '[' ) {
+        fprintf(stderr, 
+                "ERROR:  "
+                "Invalid input file expansion expression (no '[')\n");
+        exit(1);
+    }
+
+    globPtr++;
+    charPtr = leftNumText;
+    /* copy left number */
+    while ( ISDIGIT(*globPtr) ) {
+        *charPtr = *globPtr;
+        charPtr++;
+        globPtr++;
+    }
+    *charPtr = '\0';
+
+    if ( *globPtr != '-' ) {
+        fprintf(stderr, 
+                "ERROR:  "
+                "Invalid input file expansion expression (no '-')\n");
+        exit(1);
+    }
+
+    globPtr++;
+    charPtr = rightNumText;
+    /* copy right number */
+    while ( ISDIGIT(*globPtr) ) {
+        *charPtr = *globPtr;
+        charPtr++;
+        globPtr++;
+    }
+    *charPtr = '\0';
+    if ( atoi(rightNumText) < atoi(leftNumText) ) {
+        fprintf(stderr, 
+                "ERROR:  "
+                "Beginning of input range is higher than end.\n");
+        exit(1);
+    }
+
+
+    if ( *globPtr != ']' ) {
+        if ( *globPtr != '+' ) {
+            fprintf(stderr, 
+                    "ERROR:  "
+                    "Invalid input file expansion expression "
+                    "(no ']')\n");
+            exit(1);
+        }
+
+        globPtr++;
+        charPtr = skipNumText;
+        /* copy skip number */
+        while ( ISDIGIT(*globPtr) ) {
+            *charPtr = *globPtr;
+            charPtr++;
+            globPtr++;
+        }
+        *charPtr = '\0';
+
+        if ( *globPtr != ']' ) {
+            fprintf(stderr, 
+                    "ERROR:  Invalid input file expansion expression "
+                    "(no ']')\n");
+            exit(1);
+        }
+
+        skipNum = atoi(skipNumText);
+    } else {
+        skipNum = 1;
+    }
+
+    leftNum = atoi(leftNumText);
+    rightNum = atoi(rightNumText);
+
+    if ( (leftNumText[0] == '0') && (leftNumText[1] != '\0') ) {
+        padding = TRUE;
+        numPadding = strlen(leftNumText);
+    } else {
+        padding = FALSE;
+    }
+
+    inputFileEntryP->startID = leftNum;
+    inputFileEntryP->endID = rightNum;
+    inputFileEntryP->skip = skipNum;
+    inputFileEntryP->numFiles = 
+        (rightNum-leftNum+1)/skipNum;
+    strcpy(inputFileEntryP->left, left);
+    strcpy(inputFileEntryP->right, right);
+    if ( padding ) {
+        inputFileEntryP->numPadding = numPadding;
+    } else {
+        inputFileEntryP->numPadding = -1;
+    }
+}
+
+
+
+static void processJmovie(const char *            const input, 
+                          struct InputFileEntry * const inputFileEntryP) {
+    FILE    *jmovie;
+    char    full_path[1024];
+
+    inputFileEntryP->glob = TRUE;
+    full_path[0] = '\0';
+    strcpy(full_path, currentPath);
+    
+    strcat(full_path, "/");
+    strcat(full_path, input);
+    jmovie = fopen(input, "rb"); 
+    
+    if (jmovie == NULL) {
+        perror (input); 
+        exit (1);
+    }
+    
+    fseek (jmovie, (8*sizeof(char)), 0);
+    fseek (jmovie, (2*sizeof(int)), 1);
+    
+    if (fread(&(inputFileEntryP->numFiles),
+              sizeof(int), 1, jmovie) != 1) {
+        perror ("Error in reading number of frames in JMOVIE");
+        exit(1);
+    }
+    fclose (jmovie);
+
+    strcpy(inputFileEntryP->right,".jpg");
+    inputFileEntryP->numPadding = -1;
+    inputFileEntryP->startID = 1;
+    inputFileEntryP->endID = (inputFileEntryP->numFiles-1);
+    inputFileEntryP->skip = 1;
+    if (! realQuiet) {
+        fprintf (stdout, 
+                 "Encoding all %d frames from JMOVIE.\n", 
+                 inputFileEntryP->endID);
+    }
+} 
+
+
+
+static void
+processSimpleFileName(const char *            const input,
+                      struct InputFileEntry * const inputFileEntryP) {
+
+    inputFileEntryP->glob = FALSE;
+    inputFileEntryP->numFiles = 1;
+    /* fixes a bug from version 1.3: */
+    inputFileEntryP->numPadding = 0;
+    /* fixes a bug from version 1.4 */
+    strcpy(inputFileEntryP->right,"\0");
+    inputFileEntryP->startID = 0;
+    inputFileEntryP->endID = 0;
+    inputFileEntryP->skip = 0;
+}
+
+
+#define INPUT_ENTRY_BLOCK_SIZE   128
+
+void
+AddInputFiles(struct inputSource * const inputSourceP,
+              const char *         const input) {
+
+    unsigned int const currentIndex = inputSourceP->numInputFileEntries;
+
+    struct InputFileEntry * currentEntryP;
+
+    if (currentIndex >= inputSourceP->ifArraySize) {
+        /* Get more space */
+        inputSourceP->ifArraySize += INPUT_ENTRY_BLOCK_SIZE;
+        REALLOCARRAY_NOFAIL(inputSourceP->inputFileEntries, 
+                            inputSourceP->ifArraySize);
+    }
+    MALLOCVAR_NOFAIL(inputSourceP->inputFileEntries[currentIndex]);
+    currentEntryP = inputSourceP->inputFileEntries[currentIndex];
+
+    if (input[strlen(input)-1] == ']') 
+        processGlob(input, currentEntryP);
+    else {
+        strcpy(currentEntryP->left, input);
+        if (baseFormat == JMOVIE_FILE_TYPE) 
+            processJmovie(input, currentEntryP);
+        else 
+            processSimpleFileName(input, currentEntryP);
+    }
+    inputSourceP->numInputFiles += currentEntryP->numFiles;
+    ++inputSourceP->numInputFileEntries;
+}
+
+
+
+void
+SetStdinInput(struct inputSource * const inputSourceP) {
+
+    assert(inputSourceP->numInputFileEntries == 0);
+
+    inputSourceP->stdinUsed = TRUE;
+    inputSourceP->numInputFiles = INT_MAX;
+}
+
+
+
+void
+CreateInputSource(struct inputSource ** const inputSourcePP) {
+
+    struct inputSource * inputSourceP;
+
+    MALLOCVAR_NOFAIL(inputSourceP);
+
+    inputSourceP->stdinUsed = FALSE;
+    inputSourceP->numInputFileEntries = 0;
+    inputSourceP->ifArraySize = 1;
+    inputSourceP->numInputFiles = 0;
+    MALLOCARRAY_NOFAIL(inputSourceP->inputFileEntries, 1);
+    
+    *inputSourcePP = inputSourceP;
+}
+
+
+
+void
+DestroyInputSource(struct inputSource * const inputSourceP) {
+
+    unsigned int i;
+
+    for (i = 0; i < inputSourceP->numInputFileEntries; ++i)
+        free(inputSourceP->inputFileEntries[i]);
+
+    free(inputSourceP->inputFileEntries);
+
+    free(inputSourceP);
+}
+
+
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
diff --git a/converter/ppm/ppmtompeg/jpeg.c b/converter/ppm/ppmtompeg/jpeg.c
new file mode 100644
index 00000000..3aad6364
--- /dev/null
+++ b/converter/ppm/ppmtompeg/jpeg.c
@@ -0,0 +1,634 @@
+/*===========================================================================*
+ * jpeg.c                              
+ *                                     
+ *  procedures to deal with JPEG files 
+ *                                     
+ * EXPORTED PROCEDURES:                
+ *  JMovie2JPEG                        
+ *      ReadJPEG                      
+ *                                    
+ *===========================================================================*/
+
+/* COPYRIGHT INFORMATION IS AT THE END OF THIS FILE */
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+#define _XOPEN_SOURCE    /* Make sure stdio.h contains fileno() */
+#include <stdio.h>
+#include "all.h"
+/* With the lossless jpeg patch applied to the Jpeg library
+    (ftp://ftp.wizards.dupont.com/pub/ImageMagick/delegates/ljpeg-6b.tar.gz),
+    the name of min_DCT_scaled_size changes to min_codec_data_unit,
+    for some reason.  With this macro, we change it back.  
+*/
+#define min_codec_data_unit min_DCT_scaled_size
+#include <jpeglib.h>
+#undef min_codec_data_unit
+#include "mtypes.h"
+#include "frames.h"
+#include "prototypes.h"
+#include "param.h"
+#include "readframe.h"
+#include "fsize.h"
+#include "rgbtoycc.h"
+#include "jpeg.h"
+
+#include "mallocvar.h"
+
+/* make it happier.... */
+#undef DCTSIZE2
+
+/* jcopy_sample_rows() is an internal routine in the JPEG library, not 
+   meant for use by us.  We should figure out what the official interface
+   for this is and use it.  The following is copied out of jpegint.h, which
+   is part of the JPEG library source code.
+   */
+extern void jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row,
+                    JSAMPARRAY output_array, int dest_row,
+                    int num_rows, JDIMENSION num_cols));
+
+
+#define HEADER_SIZE 607   /*JFIF header size used on output images*/
+
+
+
+/*=======================================================================*
+ *                                                                       *
+ * JMovie2JPEG                                                           *
+ *                                                                       *
+ *      Splits up a Parallax J_Movie into a set of JFIF image files      *
+ *                                                                       *
+ * RETURNS:     nothing                                                  *
+ *                                                                       *
+ * SIDE EFFECTS:    none                                                 *
+ *                                                                       *
+ *   Contributed By James Boucher(jboucher@flash.bu.edu)                 *
+ *               Boston University Multimedia Communications Lab         *
+ * This code was adapted from the Berkeley Playone.c and Brian Smith's   *
+ * JGetFrame code after being modified on 10-7-93 by Dinesh Venkatesh    *
+ * of BU.                                                                *
+ *       This code converts a version 2 Parallax J_Movie into a          *
+ * set of JFIF compatible JPEG images. It works for all image            *
+ * sizes and qualities.                                                  *
+ ************************************************************************/
+void
+JMovie2JPEG(const char * const infilename,
+                /* input filename string */
+            const char * const obase,
+                /* output filename base string=>obase##.jpg */ 
+            int          const start,
+                /* first frame to be extracted */
+            int          const end
+                /* last frame to be extracted */
+    ) {
+
+    FILE *inFile;         /* Jmovie file pointer */
+    FILE *outFile;        /* JPEG file pointer for output file */
+    int fd, i;            /* input file descriptor and a counting variable*/
+    char ofname[256];     /* output filename string */
+    int Temp = 0, temp = 0;   /* dummy variables */
+    int image_offset = 0;     /* counting variable */
+    /* J_Movie header infomation */
+    int ver_no;           /* version number - expected to be 2 */
+    int fps;              /* frame rate - frames per second */
+    int no_frames;        /* total number of frames in jmovie */
+    int bandwidth;        /* bandwidth required for normal playback*/
+    int qfactor;          /* quality factor used to scale Q matrix */
+    int mapsize;          /* number of color map entries - 2^24 */
+    int audio_tracks;     /* number of audio tracks ==1    */
+    int audiosize;        /*number of bytes in audio tracks */
+    int *inoffsets;       /* input frame offsets from start of jmovie*/
+    int width;            /* image width */
+    int height;           /* image height */
+    int size;             /* total image size in bytes */
+    char op_code;         /* jmovie op_code */
+    char jpeg_size[4];        /* jpeg data size */
+    static char junk[1000];   /* data sink for audio data */
+    int last;             /* Number of last frame we will process */
+
+    /* The next array represents the default JFIF header for
+       quality = 100 and size = 320x240. The values are
+       adjusted as the J_Movie header is read.  The default
+       size of this array is set large so as to make room
+       for the appending of the jpeg bitstream. It can be
+       made smaller if you have a better idea of its expected size
+    */
+    static unsigned char inbuffer[300000] = {    
+        0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46,  
+        0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01,
+        0x00, 0x01, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x11,  
+        0x08, 0x00, 0xF0, 0x01, 0x40, 0x03, 0x01, 0x21,
+        0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF,  
+        0xDB, 0x00, 0x84, 0x00, 0x10, 0x0B, 0x0C, 0x0E,
+        0x0C, 0x0A, 0x10, 0x0E, 0x0D, 0x0E, 0x12,  
+        0x11, 0x10, 0x13, 0x18, 0x28, 0x1A, 0x18, 0x16,
+        0x16, 0x18, 0x31, 0x23, 0x25, 0x1D, 0x28, 0x3A,  
+        0x33, 0x3D, 0x3C, 0x39, 0x33, 0x38, 0x37, 0x40,
+        0x48, 0x5C, 0x4E, 0x40, 0x44, 0x57, 0x45, 0x37,  
+        0x38, 0x50, 0x6D, 0x51, 0x57, 0x5F, 0x62, 0x67,
+        0x68, 0x67, 0x3E, 0x4D, 0x71, 0x79, 0x70, 0x64,  
+        0x78, 0x5C, 0x65, 0x67, 0x63, 0x01, 0x11, 0x12,
+        0x12, 0x18, 0x15, 0x18, 0x2F, 0x1A, 0x1A, 0x2F,  
+        0x63, 0x42, 0x38, 0x42, 0x63, 0x63, 0x63, 0x63,
+        0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,  
+        0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,  
+        0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,  
+        0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,  
+        0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,  
+        0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0xFF, 0xC4,
+        0x01, 0xA2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01,  
+        0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,  
+        0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+        0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04,  
+        0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01,
+        0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05,  
+        0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
+        0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1,  
+        0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1,
+        0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09,   
+        0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26,
+        0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37,  
+        0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47,
+        0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57,  
+        0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67,
+        0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77,  
+        0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87,
+        0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96,  
+        0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5,
+        0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4,  
+        0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3,
+        0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2,  
+        0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA,
+        0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8,  
+        0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
+        0xF7, 0xF8, 0xF9, 0xFA, 0x01, 0x00, 0x03, 0x01,  
+        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,  
+        0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 
+        0x0A, 0x0B, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04,  
+        0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00,
+        0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11,  
+        0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51,
+        0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08,  
+        0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23,
+        0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1,   
+        0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17,
+        0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A,  
+        0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
+        0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54,  
+        0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64,
+        0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74,  
+        0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83,
+        0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92,  
+        0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A,
+        0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9,  
+        0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8,
+        0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,  
+        0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
+        0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5,  
+        0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4,
+        0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDA,  
+        0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03,
+        0x11, 0x00, 0x3F, 0x00  
+
+    };
+    
+    if (start > end) {
+        fprintf(stderr,"bad frame numbers\n");
+        exit(1);
+    }
+    
+    /* open J_Movie */
+    inFile = fopen(infilename, "rb");
+    if (inFile == NULL) {
+        perror (infilename);
+        exit(1);
+    }
+    
+    /* get file descriptor */    
+    fd = fileno(inFile);
+    
+    /* The following lines parse the jpeg_movie header and recover the */
+    /* relavant information */
+
+    fseek(inFile, 8 * sizeof(char), 0);
+    
+    if (fread(&ver_no, sizeof(int), 1, inFile) != 1) {
+        perror("Error in reading version");
+        exit(1);
+    }  
+    if (ver_no != 2) {
+        perror("Unrecognized version - Quantization tables may be wrong\n");
+    }
+    if (fread(&fps, sizeof(int), 1, inFile) != 1) {
+        perror("Error in reading fps");
+        exit(1);
+    }  
+    if (fread (&no_frames, sizeof(int), 1, inFile) != 1) {
+        perror("Error in reading no_frames");
+        exit(1);
+    }  
+
+    MALLOCARRAY(inoffsets, no_frames);
+    
+    if (fread(&width, sizeof(int), 1, inFile) != 1) {
+        perror("Error in reading width");
+        exit(1);
+    }  
+    /* set image width in JFIF header */
+    inbuffer[27] = (char)(0xFF & (width >> 8));
+    inbuffer[28] = (char)(0xFF & width);
+ 
+    if (fread(&height, sizeof(int), 1, inFile) != 1) {
+        perror("Error in reading height");
+        exit(1);
+    }  
+    /* set image height in JFIF header */
+    inbuffer[25] = (char)(0xFF & (height >> 8));
+    inbuffer[26] = (char)(0xFF & height);
+    
+    if (fread(&bandwidth, sizeof(int), 1, inFile) != 1) {
+        perror("Error in reading bandwidth");
+        exit(1);
+    }  
+    
+    if (fread(&qfactor, sizeof(int), 1, inFile) != 1) {
+        perror("Error in reading qfactor");
+        exit(1);
+    }  
+    /* The default quality factor = 100, therefore, if
+       our quality factor does not equal 100 we must
+       scale the quantization matrices in the JFIF header
+    */    
+    /* Note values are clipped to a max of 255 */
+    if (qfactor != 100) {
+        for (Temp = 44; Temp < 108; ++Temp) {
+            temp= (inbuffer[Temp]*qfactor)/100;
+            inbuffer[Temp] = (char)((temp<255) ? temp : 255);
+        }
+        for (Temp = 109; Temp < 173; ++Temp) {
+            temp = (inbuffer[Temp]*qfactor)/100;
+            inbuffer[Temp] = (char)((temp<255) ? temp : 255);
+        }    
+    }
+  
+    if (fread(&mapsize, sizeof(int), 1, inFile) != 1) {
+        perror("Error in reading mapsize");
+        exit(1);
+    }  
+    if (fread (&image_offset, sizeof(int), 1, inFile) != 1) {
+        perror("Error in reading image offset");
+        exit(1);
+    }
+    if (fread (&audio_tracks, sizeof(int), 1, inFile) != 1) {
+        perror("Error in reading audio tracks");
+        exit(1);
+    }
+    
+    fread(junk,sizeof(int),1,inFile);
+    
+    if (fread (&audiosize, sizeof(int), 1, inFile) != 1) {
+        perror("Error in reading audiosize");
+        exit(1);
+    }
+    
+    fseek (inFile, image_offset, 0);
+    
+    last = MIN(end, no_frames-1);
+
+    for (i = 0; i < no_frames; ++i)  {
+        fread(&(inoffsets[i]), sizeof(int), 1, inFile);
+    } /* Reads in the frame sizes into the array */
+    
+    rewind(inFile);
+
+    /* Extract JFIF files from J_Movie */    
+    for (i = start; i <= last; ++i) {
+        size = inoffsets[i] - inoffsets[i-1]- 5;
+        lseek(fd, inoffsets[i-1], 0); 
+        read(fd, &(op_code), 1);
+        while (op_code !=  0xffffffec) {
+            read(fd,junk,audiosize);
+            read(fd, &(op_code), 1);  
+            size = size - audiosize ;
+        } /* To skip the audio bytes in each frame */
+        read(fd, jpeg_size, 4);
+        read(fd, &(inbuffer[607]), size);
+        sprintf(ofname, "%s%d.jpg", obase, i);
+        outFile = fopen(ofname, "wb");
+        fwrite(inbuffer, (size+607), sizeof(char), outFile);
+        fclose(outFile);        
+    }
+    free(inoffsets);
+    fclose(inFile);
+}
+
+
+
+
+/*===========================================================================*
+ *
+ * ReadJPEG  contributed by James Arthur Boucher of Boston University's
+ *                                Multimedia Communications Lab
+ *
+ *      read a JPEG file and copy data into frame original data arrays
+ *
+ * RETURNS:     mf modified
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+/*************************JPEG LIBRARY INTERFACE*********************/
+/*
+ * THE BIG PICTURE:
+ *
+ * The rough outline this JPEG decompression operation is:
+ *
+ *      allocate and initialize JPEG decompression object
+ *      specify data source (eg, a file)
+ *      jpeg_read_header();     // obtain image dimensions and other parameters
+ *      set parameters for decompression
+ *      jpeg_start_decompress();
+ *      while (scan lines remain to be read)
+ *              jpeg_read_scanlines(...);
+ *      jpeg_finish_decompress();
+ *      release JPEG decompression object
+ *
+ */
+void
+ReadJPEG(MpegFrame * const mf,
+         FILE *      const fp) {
+
+    static struct jpeg_decompress_struct cinfo;
+        /* The JPEG decompression parameters and pointers to
+           working data (which is allocated as needed by the JPEG library).
+        */
+    struct jpeg_error_mgr jerr;
+    JSAMPARRAY scanarray[3];
+    int ci,cd,cp;
+    JDIMENSION ncols[3];
+    JDIMENSION nrows[3];
+    jpeg_component_info *compptr;
+    int buffer_height;
+    int current_row[3];
+    uint8 **orig[3];
+    int h_samp[3],v_samp[3];
+    int max_h_samp,max_v_samp;
+    int temp_h, temp_v;
+    int temp;
+
+    /* Allocate and initialize JPEG decompression object */
+    cinfo.err = jpeg_std_error(&jerr);
+
+    /*
+    ** If we're reading from stdin we want to create the cinfo struct
+    ** ONCE (during the first read).  This is because when reading jpeg
+    ** from stdin we will not release the cinfo struct, because doing
+    ** so would totally screw up the read buffer and make it impossible
+    ** to read jpegs from stdin.
+    ** Dave Scott (dhs), UofO, 7/19/95
+    */
+    {
+        static int first_stdin;
+        first_stdin = TRUE;  /* initial value */
+        if ((fp != stdin) || first_stdin) {
+            first_stdin = FALSE;
+            /* Now we can initialize the JPEG decompression object. */
+            jpeg_create_decompress(&cinfo);
+            /* specify data source (eg, a file) */
+            jpeg_stdio_src(&cinfo, fp);
+        }
+    }
+  
+    /* specify data source (eg, a file) */
+  
+    jpeg_stdio_src(&cinfo, fp);
+  
+    /* read file parameters with jpeg_read_header() */
+  
+  
+    (void) jpeg_read_header(&cinfo, TRUE);
+    /* We can ignore the return value from jpeg_read_header since
+     *   (a) suspension is not possible with the stdio data source, and
+     *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
+     */
+  
+    /* set parameters for decompression */
+#ifdef JPEG4
+    cinfo.want_raw_output = TRUE;
+#else
+    cinfo.raw_data_out = TRUE;
+#endif
+    cinfo.out_color_space = JCS_YCbCr;
+  
+    /* calculate image output dimensions */
+    jpeg_calc_output_dimensions(&cinfo);
+    /* the above calculation will set these soon */
+    /* for now we'll set them ourselves */
+    
+    /* tell mpeg_encode the size of the JPEG Image*/
+    Fsize_Note(mf->id,(int)(cinfo.image_width),(int)(cinfo.image_height));
+  
+    /* Allocate memory for the raw YCbCr data to occupy*/
+    Frame_AllocYCC(mf);      /*allocate space for mpeg frame*/
+  
+    /* copy pointers to array structure- this make the following
+       code more compact
+    */
+    orig[0] = mf->orig_y;
+    orig[1] = mf->orig_cb;
+    orig[2] = mf->orig_cr;
+  
+    /* Note that we can use the info obtained from jpeg_read_header.
+     */
+  
+    /* Start decompressor */
+  
+    jpeg_start_decompress(&cinfo);
+  
+  
+    /* JSAMPLEs per row in output buffer  */
+    /* collect component subsample values*/
+    for (cp=0, compptr = cinfo.comp_info;
+         cp < cinfo.num_components;
+         cp++, compptr++) {
+        h_samp[cp] = compptr->h_samp_factor;
+        v_samp[cp] = compptr->v_samp_factor;
+    }
+    /* calculate max subsample values*/
+    temp_h = (h_samp[0]<h_samp[1]) ? h_samp[1] : h_samp[0];
+    max_h_samp = (temp_h<h_samp[2]) ? h_samp[2]:temp_h;
+    temp_v = (v_samp[0]<v_samp[1]) ? v_samp[1] : v_samp[0];
+    max_v_samp = (temp_v<v_samp[2]) ? v_samp[2]:temp_v;
+  
+    /* Make an 8-row-high sample array that will go away when done
+       with image
+    */
+#ifdef JPEG4
+    buffer_height = 8;  /* could be 2, 4,8 rows high */
+#else
+    buffer_height = cinfo.max_v_samp_factor * cinfo.min_DCT_scaled_size;
+#endif
+  
+    for(cp=0,compptr = cinfo.comp_info;cp<cinfo.num_components;
+        cp++,compptr++) {
+        ncols[cp] = (JDIMENSION)((cinfo.image_width*compptr->h_samp_factor)/
+                                 max_h_samp);
+    
+        nrows[cp] = (JDIMENSION)((buffer_height*compptr->v_samp_factor)/
+                                 max_v_samp);
+    
+        scanarray[cp] = (*cinfo.mem->alloc_sarray)
+            ((j_common_ptr) &cinfo, JPOOL_IMAGE, ncols[cp], nrows[cp]);
+    }
+  
+    /*  while (scan lines remain to be read)
+           jpeg_read_scanlines(...);
+    */
+  
+    /* Here we use the library's state variable cinfo.output_scanline as the
+     * loop counter, so that we don't have to keep track ourselves.
+     */
+
+    while (cinfo.output_scanline < cinfo.output_height) {
+
+#ifdef JPEG4
+        (void) jpeg_read_raw_scanlines(&cinfo, scanarray, buffer_height);
+#else
+        (void) jpeg_read_raw_data(&cinfo, scanarray, buffer_height);
+#endif
+
+        /* alter subsample ratio's if neccessary */
+        if ((h_samp[0]==2) && (h_samp[1]==1) && (h_samp[2]==1) &&
+            (v_samp[0]==2) && (v_samp[1]==1) && (v_samp[2]==1)) {
+            /* we are 4:1:1 as expected by the encoder*/
+        } else if((h_samp[0] == 2) && (h_samp[1] == 1) && (h_samp[2]==1) &&
+                  (v_samp[0] == 1) && (v_samp[1] == 1) && (v_samp[2]==1)) {
+            /* must subsample 2:1 vertically and adjust params*/
+            for (ci = 1; ci < 3; ++ci) {
+                for (cp = 0; cp < (buffer_height/2); cp = cp+1) {
+                    for (cd = 0; cd < ncols[ci]; ++cd) {
+                        temp = ((scanarray[ci][cp*2][cd] +
+                                 scanarray[ci][(cp*2)+1][cd]) / 2);
+                        scanarray[ci][cp][cd] = (JSAMPLE)(temp);
+                    }
+                }
+            }
+            /* only reset values the first time through*/
+            if (cinfo.output_scanline == buffer_height) {
+                nrows[1] = nrows[1]/2;
+                nrows[2] = nrows[2]/2;
+                max_v_samp = 2;
+                v_samp[0] = 2;
+            }
+        } else
+            pm_error("Invalid subsampling ratio");
+    
+        /* transfer data from jpeg buffer to MPEG frame */
+        /* calculate the row we wish to output into */
+        for (ci = 0, compptr = cinfo.comp_info;
+             ci < cinfo.num_components;
+             ++ci, ++compptr) {
+            current_row[ci] =((cinfo.output_scanline - buffer_height)*
+                              (v_samp[ci])/max_v_samp);  
+      
+            jcopy_sample_rows(scanarray[ci],0,(JSAMPARRAY)(orig[ci]),
+                              current_row[ci],nrows[ci],ncols[ci]);
+        }
+    }  
+  
+    /* Step 7: Finish decompression */
+  
+    (void) jpeg_finish_decompress(&cinfo);
+    /* We can ignore the return value since suspension is not possible
+     * with the stdio data source.
+     */
+  
+    /* Step 8: Release JPEG decompression object */
+  
+    /*
+    ** DO NOT release the cinfo struct if we are reading from stdin, this
+    ** is because the cinfo struct contains the read buffer, and the read
+    ** buffer may (and almost always does) contain the end of one image and
+    ** the beginning of another.  If we throw away the read buffer then
+    ** we loose the beginning of the next image, and we're screwed.
+    ** Dave Scott (dhs), UofO, 7/19/95
+    */
+    if (fp == stdin) {
+        static int no_from_stdin = 0;
+        ++no_from_stdin;
+    } else {
+        /* This is an important step since it will release a good deal
+           of memory.
+        */
+        jpeg_destroy_decompress(&cinfo);
+    }
+
+    /* After finish_decompress, we can close the input file.  Here we
+       postpone it until after no more JPEG errors are possible, so as
+       to simplify the setjmp error logic above.  (Actually, I don't
+       think that jpeg_destroy can do an error exit, but why assume
+       anything...)
+    */
+  
+    /* At this point you may want to check to see whether any corrupt-data
+     * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
+     * If you prefer to treat corrupt data as a fatal error, override the
+     * error handler's emit_message method to call error_exit on a warning.
+     */
+}
+
+
+
+/*
+ * SOME FINE POINTS:
+ *
+ * In the above loop, we ignored the return value of jpeg_read_scanlines,
+ * which is the number of scanlines actually read.  We could get away with
+ * this for the same reasons discussed in the compression example.  Actually
+ * there is one perfectly normal situation in which jpeg_read_scanlines may
+ * return fewer lines than you asked for: at the bottom of the image.  But the
+ * loop above can't ask for more lines than there are in the image since it
+ * reads only one line at a time.
+ *
+ * In some high-speed operating modes, some data copying can be saved by
+ * making the buffer passed to jpeg_read_scanlines be cinfo.rec_outbuf_height
+ * lines high (or a multiple thereof).  This will usually be 1, 2, or 4 lines.
+ *
+ * To decompress multiple images, you can repeat the whole sequence, or you
+ * can keep the JPEG object around and just repeat steps 2-7.  This will
+ * save a little bit of startup/shutdown time.
+ *
+ * As with compression, some operating modes may require temporary files.
+ * On some systems you may need to set up a signal handler to ensure that
+ * temporary files are deleted if the program is interrupted.
+ *
+ * Scanlines are returned in the same order as they appear in the JPEG file,
+ * which is standardly top-to-bottom.  If you must have data supplied
+ * bottom-to-top, you can use one of the virtual arrays provided by the
+ * JPEG memory manager to invert the data.  See wrrle.c for an example.
+ */
+
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
diff --git a/converter/ppm/ppmtompeg/jrevdct.c b/converter/ppm/ppmtompeg/jrevdct.c
new file mode 100644
index 00000000..2e99a67a
--- /dev/null
+++ b/converter/ppm/ppmtompeg/jrevdct.c
@@ -0,0 +1,1278 @@
+/*
+ * jrevdct.c
+ *
+ * Copyright (C) 1991, 1992, Thomas G. Lane.
+ * This file is part of the Independent JPEG Group's software.
+ * For conditions of distribution and use, see the accompanying README file.
+ *
+ * This file contains the basic inverse-DCT transformation subroutine.
+ *
+ * This implementation is based on an algorithm described in
+ *   C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT
+ *   Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics,
+ *   Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991.
+ * The primary algorithm described there uses 11 multiplies and 29 adds.
+ * We use their alternate method with 12 multiplies and 32 adds.
+ * The advantage of this method is that no data path contains more than one
+ * multiplication; this allows a very simple and accurate implementation in
+ * scaled fixed-point arithmetic, with a minimal number of shifts.
+ * 
+ * I've made lots of modifications to attempt to take advantage of the
+ * sparse nature of the DCT matrices we're getting.  Although the logic
+ * is cumbersome, it's straightforward and the resulting code is much
+ * faster.
+ *
+ * A better way to do this would be to pass in the DCT block as a sparse
+ * matrix, perhaps with the difference cases encoded.
+ */
+
+#include <memory.h>
+#include "all.h"
+#include "ansi.h"
+#include "dct.h"
+
+
+#define CONST_BITS 13
+
+/*
+ * This routine is specialized to the case DCTSIZE = 8.
+ */
+
+#if DCTSIZE != 8
+  Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */
+#endif
+
+
+/*
+ * A 2-D IDCT can be done by 1-D IDCT on each row followed by 1-D IDCT
+ * on each column.  Direct algorithms are also available, but they are
+ * much more complex and seem not to be any faster when reduced to code.
+ *
+ * The poop on this scaling stuff is as follows:
+ *
+ * Each 1-D IDCT step produces outputs which are a factor of sqrt(N)
+ * larger than the true IDCT outputs.  The final outputs are therefore
+ * a factor of N larger than desired; since N=8 this can be cured by
+ * a simple right shift at the end of the algorithm.  The advantage of
+ * this arrangement is that we save two multiplications per 1-D IDCT,
+ * because the y0 and y4 inputs need not be divided by sqrt(N).
+ *
+ * We have to do addition and subtraction of the integer inputs, which
+ * is no problem, and multiplication by fractional constants, which is
+ * a problem to do in integer arithmetic.  We multiply all the constants
+ * by CONST_SCALE and convert them to integer constants (thus retaining
+ * CONST_BITS bits of precision in the constants).  After doing a
+ * multiplication we have to divide the product by CONST_SCALE, with proper
+ * rounding, to produce the correct output.  This division can be done
+ * cheaply as a right shift of CONST_BITS bits.  We postpone shifting
+ * as long as possible so that partial sums can be added together with
+ * full fractional precision.
+ *
+ * The outputs of the first pass are scaled up by PASS1_BITS bits so that
+ * they are represented to better-than-integral precision.  These outputs
+ * require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word
+ * with the recommended scaling.  (To scale up 12-bit sample data further, an
+ * intermediate int32 array would be needed.)
+ *
+ * To avoid overflow of the 32-bit intermediate results in pass 2, we must
+ * have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26.  Error analysis
+ * shows that the values given below are the most effective.
+ */
+
+#ifdef EIGHT_BIT_SAMPLES
+#define PASS1_BITS  2
+#else
+#define PASS1_BITS  1		/* lose a little precision to avoid overflow */
+#endif
+
+#define ONE	((int32) 1)
+
+#define CONST_SCALE (ONE << CONST_BITS)
+
+/* Convert a positive real constant to an integer scaled by CONST_SCALE.
+ * IMPORTANT: if your compiler doesn't do this arithmetic at compile time,
+ * you will pay a significant penalty in run time.  In that case, figure
+ * the correct integer constant values and insert them by hand.
+ */
+
+/* Actually FIX is no longer used, we precomputed them all */
+#define FIX(x)	((int32) ((x) * CONST_SCALE + 0.5)) 
+
+/* Descale and correctly round an int32 value that's scaled by N bits.
+ * We assume RIGHT_SHIFT rounds towards minus infinity, so adding
+ * the fudge factor is correct for either sign of X.
+ */
+
+#define DESCALE(x,n)  RIGHT_SHIFT((x) + (ONE << ((n)-1)), n)
+
+/* Multiply an int32 variable by an int32 constant to yield an int32 result.
+ * For 8-bit samples with the recommended scaling, all the variable
+ * and constant values involved are no more than 16 bits wide, so a
+ * 16x16->32 bit multiply can be used instead of a full 32x32 multiply;
+ * this provides a useful speedup on many machines.
+ * There is no way to specify a 16x16->32 multiply in portable C, but
+ * some C compilers will do the right thing if you provide the correct
+ * combination of casts.
+ * NB: for 12-bit samples, a full 32-bit multiplication will be needed.
+ */
+
+#ifdef EIGHT_BIT_SAMPLES
+#ifdef SHORTxSHORT_32		/* may work if 'int' is 32 bits */
+#define MULTIPLY(var,const)  (((INT16) (var)) * ((INT16) (const)))
+#endif
+#ifdef SHORTxLCONST_32		/* known to work with Microsoft C 6.0 */
+#define MULTIPLY(var,const)  (((INT16) (var)) * ((int32) (const)))
+#endif
+#endif
+
+#ifndef MULTIPLY		/* default definition */
+#define MULTIPLY(var,const)  ((var) * (const))
+#endif
+
+
+/* 
+  Unlike our decoder where we approximate the FIXes, we need to use exact
+ones here or successive P-frames will drift too much with Reference frame coding 
+*/
+#define FIX_0_211164243 1730
+#define FIX_0_275899380 2260
+#define FIX_0_298631336 2446
+#define FIX_0_390180644 3196
+#define FIX_0_509795579 4176
+#define FIX_0_541196100 4433
+#define FIX_0_601344887 4926
+#define FIX_0_765366865 6270
+#define FIX_0_785694958 6436
+#define FIX_0_899976223 7373
+#define FIX_1_061594337 8697
+#define FIX_1_111140466 9102
+#define FIX_1_175875602 9633
+#define FIX_1_306562965 10703
+#define FIX_1_387039845 11363
+#define FIX_1_451774981 11893
+#define FIX_1_501321110 12299
+#define FIX_1_662939225 13623
+#define FIX_1_847759065 15137
+#define FIX_1_961570560 16069
+#define FIX_2_053119869 16819
+#define FIX_2_172734803 17799
+#define FIX_2_562915447 20995
+#define FIX_3_072711026 25172
+
+/*
+  Switch on reverse_dct choices
+*/
+void reference_rev_dct _ANSI_ARGS_((int16 *block));
+void mpeg_jrevdct_quick _ANSI_ARGS_((int16 *block));
+void init_idctref _ANSI_ARGS_((void));
+
+extern boolean pureDCT;
+
+void
+mpeg_jrevdct(data)
+    DCTBLOCK data;
+{
+  if (pureDCT) reference_rev_dct(data);
+  else mpeg_jrevdct_quick(data);
+}
+
+/*
+ * Perform the inverse DCT on one block of coefficients.
+ */
+
+void
+mpeg_jrevdct_quick(data)
+    DCTBLOCK data;
+{
+  int32 tmp0, tmp1, tmp2, tmp3;
+  int32 tmp10, tmp11, tmp12, tmp13;
+  int32 z1, z2, z3, z4, z5;
+  int32 d0, d1, d2, d3, d4, d5, d6, d7;
+  register DCTELEM *dataptr;
+  int rowctr;
+  SHIFT_TEMPS
+   
+  /* Pass 1: process rows. */
+  /* Note results are scaled up by sqrt(8) compared to a true IDCT; */
+  /* furthermore, we scale the results by 2**PASS1_BITS. */
+
+  dataptr = data;
+
+  for (rowctr = DCTSIZE-1; rowctr >= 0; rowctr--) {
+    /* Due to quantization, we will usually find that many of the input
+     * coefficients are zero, especially the AC terms.  We can exploit this
+     * by short-circuiting the IDCT calculation for any row in which all
+     * the AC terms are zero.  In that case each output is equal to the
+     * DC coefficient (with scale factor as needed).
+     * With typical images and quantization tables, half or more of the
+     * row DCT calculations can be simplified this way.
+     */
+
+    register int *idataptr = (int*)dataptr;
+    d0 = dataptr[0];
+    d1 = dataptr[1];
+    if ((d1 == 0) && (idataptr[1] | idataptr[2] | idataptr[3]) == 0) {
+      /* AC terms all zero */
+      if (d0) {
+	  /* Compute a 32 bit value to assign. */
+	  DCTELEM dcval = (DCTELEM) (d0 << PASS1_BITS);
+	  register int v = (dcval & 0xffff) | ((dcval << 16) & 0xffff0000);
+	  
+	  idataptr[0] = v;
+	  idataptr[1] = v;
+	  idataptr[2] = v;
+	  idataptr[3] = v;
+      }
+      
+      dataptr += DCTSIZE;	/* advance pointer to next row */
+      continue;
+    }
+    d2 = dataptr[2];
+    d3 = dataptr[3];
+    d4 = dataptr[4];
+    d5 = dataptr[5];
+    d6 = dataptr[6];
+    d7 = dataptr[7];
+
+    /* Even part: reverse the even part of the forward DCT. */
+    /* The rotator is sqrt(2)*c(-6). */
+{
+    if (d6) {
+	if (d4) {
+	    if (d2) {
+		if (d0) {
+		    /* d0 != 0, d2 != 0, d4 != 0, d6 != 0 */
+		    z1 = MULTIPLY(d2 + d6, FIX_0_541196100);
+		    tmp2 = z1 + MULTIPLY(-d6, FIX_1_847759065);
+		    tmp3 = z1 + MULTIPLY(d2, FIX_0_765366865);
+
+		    tmp0 = (d0 + d4) << CONST_BITS;
+		    tmp1 = (d0 - d4) << CONST_BITS;
+
+		    tmp10 = tmp0 + tmp3;
+		    tmp13 = tmp0 - tmp3;
+		    tmp11 = tmp1 + tmp2;
+		    tmp12 = tmp1 - tmp2;
+		} else {
+		    /* d0 == 0, d2 != 0, d4 != 0, d6 != 0 */
+		    z1 = MULTIPLY(d2 + d6, FIX_0_541196100);
+		    tmp2 = z1 + MULTIPLY(-d6, FIX_1_847759065);
+		    tmp3 = z1 + MULTIPLY(d2, FIX_0_765366865);
+
+		    tmp0 = d4 << CONST_BITS;
+
+		    tmp10 = tmp0 + tmp3;
+		    tmp13 = tmp0 - tmp3;
+		    tmp11 = tmp2 - tmp0;
+		    tmp12 = -(tmp0 + tmp2);
+		}
+	    } else {
+		if (d0) {
+		    /* d0 != 0, d2 == 0, d4 != 0, d6 != 0 */
+		    tmp2 = MULTIPLY(-d6, FIX_1_306562965);
+		    tmp3 = MULTIPLY(d6, FIX_0_541196100);
+
+		    tmp0 = (d0 + d4) << CONST_BITS;
+		    tmp1 = (d0 - d4) << CONST_BITS;
+
+		    tmp10 = tmp0 + tmp3;
+		    tmp13 = tmp0 - tmp3;
+		    tmp11 = tmp1 + tmp2;
+		    tmp12 = tmp1 - tmp2;
+		} else {
+		    /* d0 == 0, d2 == 0, d4 != 0, d6 != 0 */
+		    tmp2 = MULTIPLY(-d6, FIX_1_306562965);
+		    tmp3 = MULTIPLY(d6, FIX_0_541196100);
+
+		    tmp0 = d4 << CONST_BITS;
+
+		    tmp10 = tmp0 + tmp3;
+		    tmp13 = tmp0 - tmp3;
+		    tmp11 = tmp2 - tmp0;
+		    tmp12 = -(tmp0 + tmp2);
+		}
+	    }
+	} else {
+	    if (d2) {
+		if (d0) {
+		    /* d0 != 0, d2 != 0, d4 == 0, d6 != 0 */
+		    z1 = MULTIPLY(d2 + d6, FIX_0_541196100);
+		    tmp2 = z1 + MULTIPLY(-d6, FIX_1_847759065);
+		    tmp3 = z1 + MULTIPLY(d2, FIX_0_765366865);
+
+		    tmp0 = d0 << CONST_BITS;
+
+		    tmp10 = tmp0 + tmp3;
+		    tmp13 = tmp0 - tmp3;
+		    tmp11 = tmp0 + tmp2;
+		    tmp12 = tmp0 - tmp2;
+		} else {
+		    /* d0 == 0, d2 != 0, d4 == 0, d6 != 0 */
+		    z1 = MULTIPLY(d2 + d6, FIX_0_541196100);
+		    tmp2 = z1 + MULTIPLY(-d6, FIX_1_847759065);
+		    tmp3 = z1 + MULTIPLY(d2, FIX_0_765366865);
+
+		    tmp10 = tmp3;
+		    tmp13 = -tmp3;
+		    tmp11 = tmp2;
+		    tmp12 = -tmp2;
+		}
+	    } else {
+		if (d0) {
+		    /* d0 != 0, d2 == 0, d4 == 0, d6 != 0 */
+		    tmp2 = MULTIPLY(-d6, FIX_1_306562965);
+		    tmp3 = MULTIPLY(d6, FIX_0_541196100);
+
+		    tmp0 = d0 << CONST_BITS;
+
+		    tmp10 = tmp0 + tmp3;
+		    tmp13 = tmp0 - tmp3;
+		    tmp11 = tmp0 + tmp2;
+		    tmp12 = tmp0 - tmp2;
+		} else {
+		    /* d0 == 0, d2 == 0, d4 == 0, d6 != 0 */
+		    tmp2 = MULTIPLY(-d6, FIX_1_306562965);
+		    tmp3 = MULTIPLY(d6, FIX_0_541196100);
+
+		    tmp10 = tmp3;
+		    tmp13 = -tmp3;
+		    tmp11 = tmp2;
+		    tmp12 = -tmp2;
+		}
+	    }
+	}
+    } else {
+	if (d4) {
+	    if (d2) {
+		if (d0) {
+		    /* d0 != 0, d2 != 0, d4 != 0, d6 == 0 */
+		    tmp2 = MULTIPLY(d2, FIX_0_541196100);
+		    tmp3 = MULTIPLY(d2, FIX_1_306562965);
+
+		    tmp0 = (d0 + d4) << CONST_BITS;
+		    tmp1 = (d0 - d4) << CONST_BITS;
+
+		    tmp10 = tmp0 + tmp3;
+		    tmp13 = tmp0 - tmp3;
+		    tmp11 = tmp1 + tmp2;
+		    tmp12 = tmp1 - tmp2;
+		} else {
+		    /* d0 == 0, d2 != 0, d4 != 0, d6 == 0 */
+		    tmp2 = MULTIPLY(d2, FIX_0_541196100);
+		    tmp3 = MULTIPLY(d2, FIX_1_306562965);
+
+		    tmp0 = d4 << CONST_BITS;
+
+		    tmp10 = tmp0 + tmp3;
+		    tmp13 = tmp0 - tmp3;
+		    tmp11 = tmp2 - tmp0;
+		    tmp12 = -(tmp0 + tmp2);
+		}
+	    } else {
+		if (d0) {
+		    /* d0 != 0, d2 == 0, d4 != 0, d6 == 0 */
+		    tmp10 = tmp13 = (d0 + d4) << CONST_BITS;
+		    tmp11 = tmp12 = (d0 - d4) << CONST_BITS;
+		} else {
+		    /* d0 == 0, d2 == 0, d4 != 0, d6 == 0 */
+		    tmp10 = tmp13 = d4 << CONST_BITS;
+		    tmp11 = tmp12 = -tmp10;
+		}
+	    }
+	} else {
+	    if (d2) {
+		if (d0) {
+		    /* d0 != 0, d2 != 0, d4 == 0, d6 == 0 */
+		    tmp2 = MULTIPLY(d2, FIX_0_541196100);
+		    tmp3 = MULTIPLY(d2, FIX_1_306562965);
+
+		    tmp0 = d0 << CONST_BITS;
+
+		    tmp10 = tmp0 + tmp3;
+		    tmp13 = tmp0 - tmp3;
+		    tmp11 = tmp0 + tmp2;
+		    tmp12 = tmp0 - tmp2;
+		} else {
+		    /* d0 == 0, d2 != 0, d4 == 0, d6 == 0 */
+		    tmp2 = MULTIPLY(d2, FIX_0_541196100);
+		    tmp3 = MULTIPLY(d2, FIX_1_306562965);
+
+		    tmp10 = tmp3;
+		    tmp13 = -tmp3;
+		    tmp11 = tmp2;
+		    tmp12 = -tmp2;
+		}
+	    } else {
+		if (d0) {
+		    /* d0 != 0, d2 == 0, d4 == 0, d6 == 0 */
+		    tmp10 = tmp13 = tmp11 = tmp12 = d0 << CONST_BITS;
+		} else {
+		    /* d0 == 0, d2 == 0, d4 == 0, d6 == 0 */
+		    tmp10 = tmp13 = tmp11 = tmp12 = 0;
+		}
+	    }
+	}
+      }
+
+    /* Odd part per figure 8; the matrix is unitary and hence its
+     * transpose is its inverse.  i0..i3 are y7,y5,y3,y1 respectively.
+     */
+
+    if (d7) {
+	if (d5) {
+	    if (d3) {
+		if (d1) {
+		    /* d1 != 0, d3 != 0, d5 != 0, d7 != 0 */
+		    z1 = d7 + d1;
+		    z2 = d5 + d3;
+		    z3 = d7 + d3;
+		    z4 = d5 + d1;
+		    z5 = MULTIPLY(z3 + z4, FIX_1_175875602);
+		    
+		    tmp0 = MULTIPLY(d7, FIX_0_298631336); 
+		    tmp1 = MULTIPLY(d5, FIX_2_053119869);
+		    tmp2 = MULTIPLY(d3, FIX_3_072711026);
+		    tmp3 = MULTIPLY(d1, FIX_1_501321110);
+		    z1 = MULTIPLY(-z1, FIX_0_899976223);
+		    z2 = MULTIPLY(-z2, FIX_2_562915447);
+		    z3 = MULTIPLY(-z3, FIX_1_961570560);
+		    z4 = MULTIPLY(-z4, FIX_0_390180644);
+		    
+		    z3 += z5;
+		    z4 += z5;
+		    
+		    tmp0 += z1 + z3;
+		    tmp1 += z2 + z4;
+		    tmp2 += z2 + z3;
+		    tmp3 += z1 + z4;
+		} else {
+		    /* d1 == 0, d3 != 0, d5 != 0, d7 != 0 */
+		    z2 = d5 + d3;
+		    z3 = d7 + d3;
+		    z5 = MULTIPLY(z3 + d5, FIX_1_175875602);
+		    
+		    tmp0 = MULTIPLY(d7, FIX_0_298631336); 
+		    tmp1 = MULTIPLY(d5, FIX_2_053119869);
+		    tmp2 = MULTIPLY(d3, FIX_3_072711026);
+		    z1 = MULTIPLY(-d7, FIX_0_899976223);
+		    z2 = MULTIPLY(-z2, FIX_2_562915447);
+		    z3 = MULTIPLY(-z3, FIX_1_961570560);
+		    z4 = MULTIPLY(-d5, FIX_0_390180644);
+		    
+		    z3 += z5;
+		    z4 += z5;
+		    
+		    tmp0 += z1 + z3;
+		    tmp1 += z2 + z4;
+		    tmp2 += z2 + z3;
+		    tmp3 = z1 + z4;
+		}
+	    } else {
+		if (d1) {
+		    /* d1 != 0, d3 == 0, d5 != 0, d7 != 0 */
+		    z1 = d7 + d1;
+		    z4 = d5 + d1;
+		    z5 = MULTIPLY(d7 + z4, FIX_1_175875602);
+		    
+		    tmp0 = MULTIPLY(d7, FIX_0_298631336); 
+		    tmp1 = MULTIPLY(d5, FIX_2_053119869);
+		    tmp3 = MULTIPLY(d1, FIX_1_501321110);
+		    z1 = MULTIPLY(-z1, FIX_0_899976223);
+		    z2 = MULTIPLY(-d5, FIX_2_562915447);
+		    z3 = MULTIPLY(-d7, FIX_1_961570560);
+		    z4 = MULTIPLY(-z4, FIX_0_390180644);
+		    
+		    z3 += z5;
+		    z4 += z5;
+		    
+		    tmp0 += z1 + z3;
+		    tmp1 += z2 + z4;
+		    tmp2 = z2 + z3;
+		    tmp3 += z1 + z4;
+		} else {
+		    /* d1 == 0, d3 == 0, d5 != 0, d7 != 0 */
+		    tmp0 = MULTIPLY(-d7, FIX_0_601344887); 
+		    z1 = MULTIPLY(-d7, FIX_0_899976223);
+		    z3 = MULTIPLY(-d7, FIX_1_961570560);
+		    tmp1 = MULTIPLY(-d5, FIX_0_509795579);
+		    z2 = MULTIPLY(-d5, FIX_2_562915447);
+		    z4 = MULTIPLY(-d5, FIX_0_390180644);
+		    z5 = MULTIPLY(d5 + d7, FIX_1_175875602);
+		    
+		    z3 += z5;
+		    z4 += z5;
+		    
+		    tmp0 += z3;
+		    tmp1 += z4;
+		    tmp2 = z2 + z3;
+		    tmp3 = z1 + z4;
+		}
+	    }
+	} else {
+	    if (d3) {
+		if (d1) {
+		    /* d1 != 0, d3 != 0, d5 == 0, d7 != 0 */
+		    z1 = d7 + d1;
+		    z3 = d7 + d3;
+		    z5 = MULTIPLY(z3 + d1, FIX_1_175875602);
+		    
+		    tmp0 = MULTIPLY(d7, FIX_0_298631336); 
+		    tmp2 = MULTIPLY(d3, FIX_3_072711026);
+		    tmp3 = MULTIPLY(d1, FIX_1_501321110);
+		    z1 = MULTIPLY(-z1, FIX_0_899976223);
+		    z2 = MULTIPLY(-d3, FIX_2_562915447);
+		    z3 = MULTIPLY(-z3, FIX_1_961570560);
+		    z4 = MULTIPLY(-d1, FIX_0_390180644);
+		    
+		    z3 += z5;
+		    z4 += z5;
+		    
+		    tmp0 += z1 + z3;
+		    tmp1 = z2 + z4;
+		    tmp2 += z2 + z3;
+		    tmp3 += z1 + z4;
+		} else {
+		    /* d1 == 0, d3 != 0, d5 == 0, d7 != 0 */
+		    z3 = d7 + d3;
+		    
+		    tmp0 = MULTIPLY(-d7, FIX_0_601344887); 
+		    z1 = MULTIPLY(-d7, FIX_0_899976223);
+		    tmp2 = MULTIPLY(d3, FIX_0_509795579);
+		    z2 = MULTIPLY(-d3, FIX_2_562915447);
+		    z5 = MULTIPLY(z3, FIX_1_175875602);
+		    z3 = MULTIPLY(-z3, FIX_0_785694958);
+		    
+		    tmp0 += z3;
+		    tmp1 = z2 + z5;
+		    tmp2 += z3;
+		    tmp3 = z1 + z5;
+		}
+	    } else {
+		if (d1) {
+		    /* d1 != 0, d3 == 0, d5 == 0, d7 != 0 */
+		    z1 = d7 + d1;
+		    z5 = MULTIPLY(z1, FIX_1_175875602);
+
+		    z1 = MULTIPLY(z1, FIX_0_275899380);
+		    z3 = MULTIPLY(-d7, FIX_1_961570560);
+		    tmp0 = MULTIPLY(-d7, FIX_1_662939225); 
+		    z4 = MULTIPLY(-d1, FIX_0_390180644);
+		    tmp3 = MULTIPLY(d1, FIX_1_111140466);
+
+		    tmp0 += z1;
+		    tmp1 = z4 + z5;
+		    tmp2 = z3 + z5;
+		    tmp3 += z1;
+		} else {
+		    /* d1 == 0, d3 == 0, d5 == 0, d7 != 0 */
+		    tmp0 = MULTIPLY(-d7, FIX_1_387039845);
+		    tmp1 = MULTIPLY(d7, FIX_1_175875602);
+		    tmp2 = MULTIPLY(-d7, FIX_0_785694958);
+		    tmp3 = MULTIPLY(d7, FIX_0_275899380);
+		}
+	    }
+	}
+    } else {
+	if (d5) {
+	    if (d3) {
+		if (d1) {
+		    /* d1 != 0, d3 != 0, d5 != 0, d7 == 0 */
+		    z2 = d5 + d3;
+		    z4 = d5 + d1;
+		    z5 = MULTIPLY(d3 + z4, FIX_1_175875602);
+		    
+		    tmp1 = MULTIPLY(d5, FIX_2_053119869);
+		    tmp2 = MULTIPLY(d3, FIX_3_072711026);
+		    tmp3 = MULTIPLY(d1, FIX_1_501321110);
+		    z1 = MULTIPLY(-d1, FIX_0_899976223);
+		    z2 = MULTIPLY(-z2, FIX_2_562915447);
+		    z3 = MULTIPLY(-d3, FIX_1_961570560);
+		    z4 = MULTIPLY(-z4, FIX_0_390180644);
+		    
+		    z3 += z5;
+		    z4 += z5;
+		    
+		    tmp0 = z1 + z3;
+		    tmp1 += z2 + z4;
+		    tmp2 += z2 + z3;
+		    tmp3 += z1 + z4;
+		} else {
+		    /* d1 == 0, d3 != 0, d5 != 0, d7 == 0 */
+		    z2 = d5 + d3;
+		    
+		    z5 = MULTIPLY(z2, FIX_1_175875602);
+		    tmp1 = MULTIPLY(d5, FIX_1_662939225);
+		    z4 = MULTIPLY(-d5, FIX_0_390180644);
+		    z2 = MULTIPLY(-z2, FIX_1_387039845);
+		    tmp2 = MULTIPLY(d3, FIX_1_111140466);
+		    z3 = MULTIPLY(-d3, FIX_1_961570560);
+		    
+		    tmp0 = z3 + z5;
+		    tmp1 += z2;
+		    tmp2 += z2;
+		    tmp3 = z4 + z5;
+		}
+	    } else {
+		if (d1) {
+		    /* d1 != 0, d3 == 0, d5 != 0, d7 == 0 */
+		    z4 = d5 + d1;
+		    
+		    z5 = MULTIPLY(z4, FIX_1_175875602);
+		    z1 = MULTIPLY(-d1, FIX_0_899976223);
+		    tmp3 = MULTIPLY(d1, FIX_0_601344887);
+		    tmp1 = MULTIPLY(-d5, FIX_0_509795579);
+		    z2 = MULTIPLY(-d5, FIX_2_562915447);
+		    z4 = MULTIPLY(z4, FIX_0_785694958);
+		    
+		    tmp0 = z1 + z5;
+		    tmp1 += z4;
+		    tmp2 = z2 + z5;
+		    tmp3 += z4;
+		} else {
+		    /* d1 == 0, d3 == 0, d5 != 0, d7 == 0 */
+		    tmp0 = MULTIPLY(d5, FIX_1_175875602);
+		    tmp1 = MULTIPLY(d5, FIX_0_275899380);
+		    tmp2 = MULTIPLY(-d5, FIX_1_387039845);
+		    tmp3 = MULTIPLY(d5, FIX_0_785694958);
+		}
+	    }
+	} else {
+	    if (d3) {
+		if (d1) {
+		    /* d1 != 0, d3 != 0, d5 == 0, d7 == 0 */
+		    z5 = d1 + d3;
+		    tmp3 = MULTIPLY(d1, FIX_0_211164243);
+		    tmp2 = MULTIPLY(-d3, FIX_1_451774981);
+		    z1 = MULTIPLY(d1, FIX_1_061594337);
+		    z2 = MULTIPLY(-d3, FIX_2_172734803);
+		    z4 = MULTIPLY(z5, FIX_0_785694958);
+		    z5 = MULTIPLY(z5, FIX_1_175875602);
+		    
+		    tmp0 = z1 - z4;
+		    tmp1 = z2 + z4;
+		    tmp2 += z5;
+		    tmp3 += z5;
+		} else {
+		    /* d1 == 0, d3 != 0, d5 == 0, d7 == 0 */
+		    tmp0 = MULTIPLY(-d3, FIX_0_785694958);
+		    tmp1 = MULTIPLY(-d3, FIX_1_387039845);
+		    tmp2 = MULTIPLY(-d3, FIX_0_275899380);
+		    tmp3 = MULTIPLY(d3, FIX_1_175875602);
+		}
+	    } else {
+		if (d1) {
+		    /* d1 != 0, d3 == 0, d5 == 0, d7 == 0 */
+		    tmp0 = MULTIPLY(d1, FIX_0_275899380);
+		    tmp1 = MULTIPLY(d1, FIX_0_785694958);
+		    tmp2 = MULTIPLY(d1, FIX_1_175875602);
+		    tmp3 = MULTIPLY(d1, FIX_1_387039845);
+		} else {
+		    /* d1 == 0, d3 == 0, d5 == 0, d7 == 0 */
+		    tmp0 = tmp1 = tmp2 = tmp3 = 0;
+		}
+	    }
+	}
+    }
+}
+    /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */
+
+    dataptr[0] = (DCTELEM) DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS);
+    dataptr[7] = (DCTELEM) DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS);
+    dataptr[1] = (DCTELEM) DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS);
+    dataptr[6] = (DCTELEM) DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS);
+    dataptr[2] = (DCTELEM) DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS);
+    dataptr[5] = (DCTELEM) DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS);
+    dataptr[3] = (DCTELEM) DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS);
+    dataptr[4] = (DCTELEM) DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS);
+
+    dataptr += DCTSIZE;		/* advance pointer to next row */
+  }
+
+  /* Pass 2: process columns. */
+  /* Note that we must descale the results by a factor of 8 == 2**3, */
+  /* and also undo the PASS1_BITS scaling. */
+
+  dataptr = data;
+  for (rowctr = DCTSIZE-1; rowctr >= 0; rowctr--) {
+    /* Columns of zeroes can be exploited in the same way as we did with rows.
+     * However, the row calculation has created many nonzero AC terms, so the
+     * simplification applies less often (typically 5% to 10% of the time).
+     * On machines with very fast multiplication, it's possible that the
+     * test takes more time than it's worth.  In that case this section
+     * may be commented out.
+     */
+
+    d0 = dataptr[DCTSIZE*0];
+    d1 = dataptr[DCTSIZE*1];
+    d2 = dataptr[DCTSIZE*2];
+    d3 = dataptr[DCTSIZE*3];
+    d4 = dataptr[DCTSIZE*4];
+    d5 = dataptr[DCTSIZE*5];
+    d6 = dataptr[DCTSIZE*6];
+    d7 = dataptr[DCTSIZE*7];
+
+    /* Even part: reverse the even part of the forward DCT. */
+    /* The rotator is sqrt(2)*c(-6). */
+    if (d6) {
+	if (d4) {
+	    if (d2) {
+		if (d0) {
+		    /* d0 != 0, d2 != 0, d4 != 0, d6 != 0 */
+		    z1 = MULTIPLY(d2 + d6, FIX_0_541196100);
+		    tmp2 = z1 + MULTIPLY(-d6, FIX_1_847759065);
+		    tmp3 = z1 + MULTIPLY(d2, FIX_0_765366865);
+
+		    tmp0 = (d0 + d4) << CONST_BITS;
+		    tmp1 = (d0 - d4) << CONST_BITS;
+
+		    tmp10 = tmp0 + tmp3;
+		    tmp13 = tmp0 - tmp3;
+		    tmp11 = tmp1 + tmp2;
+		    tmp12 = tmp1 - tmp2;
+		} else {
+		    /* d0 == 0, d2 != 0, d4 != 0, d6 != 0 */
+		    z1 = MULTIPLY(d2 + d6, FIX_0_541196100);
+		    tmp2 = z1 + MULTIPLY(-d6, FIX_1_847759065);
+		    tmp3 = z1 + MULTIPLY(d2, FIX_0_765366865);
+
+		    tmp0 = d4 << CONST_BITS;
+
+		    tmp10 = tmp0 + tmp3;
+		    tmp13 = tmp0 - tmp3;
+		    tmp11 = tmp2 - tmp0;
+		    tmp12 = -(tmp0 + tmp2);
+		}
+	    } else {
+		if (d0) {
+		    /* d0 != 0, d2 == 0, d4 != 0, d6 != 0 */
+		    tmp2 = MULTIPLY(-d6, FIX_1_306562965);
+		    tmp3 = MULTIPLY(d6, FIX_0_541196100);
+
+		    tmp0 = (d0 + d4) << CONST_BITS;
+		    tmp1 = (d0 - d4) << CONST_BITS;
+
+		    tmp10 = tmp0 + tmp3;
+		    tmp13 = tmp0 - tmp3;
+		    tmp11 = tmp1 + tmp2;
+		    tmp12 = tmp1 - tmp2;
+		} else {
+		    /* d0 == 0, d2 == 0, d4 != 0, d6 != 0 */
+		    tmp2 = MULTIPLY(-d6, FIX_1_306562965);
+		    tmp3 = MULTIPLY(d6, FIX_0_541196100);
+
+		    tmp0 = d4 << CONST_BITS;
+
+		    tmp10 = tmp0 + tmp3;
+		    tmp13 = tmp0 - tmp3;
+		    tmp11 = tmp2 - tmp0;
+		    tmp12 = -(tmp0 + tmp2);
+		}
+	    }
+	} else {
+	    if (d2) {
+		if (d0) {
+		    /* d0 != 0, d2 != 0, d4 == 0, d6 != 0 */
+		    z1 = MULTIPLY(d2 + d6, FIX_0_541196100);
+		    tmp2 = z1 + MULTIPLY(-d6, FIX_1_847759065);
+		    tmp3 = z1 + MULTIPLY(d2, FIX_0_765366865);
+
+		    tmp0 = d0 << CONST_BITS;
+
+		    tmp10 = tmp0 + tmp3;
+		    tmp13 = tmp0 - tmp3;
+		    tmp11 = tmp0 + tmp2;
+		    tmp12 = tmp0 - tmp2;
+		} else {
+		    /* d0 == 0, d2 != 0, d4 == 0, d6 != 0 */
+		    z1 = MULTIPLY(d2 + d6, FIX_0_541196100);
+		    tmp2 = z1 + MULTIPLY(-d6, FIX_1_847759065);
+		    tmp3 = z1 + MULTIPLY(d2, FIX_0_765366865);
+
+		    tmp10 = tmp3;
+		    tmp13 = -tmp3;
+		    tmp11 = tmp2;
+		    tmp12 = -tmp2;
+		}
+	    } else {
+		if (d0) {
+		    /* d0 != 0, d2 == 0, d4 == 0, d6 != 0 */
+		    tmp2 = MULTIPLY(-d6, FIX_1_306562965);
+		    tmp3 = MULTIPLY(d6, FIX_0_541196100);
+
+		    tmp0 = d0 << CONST_BITS;
+
+		    tmp10 = tmp0 + tmp3;
+		    tmp13 = tmp0 - tmp3;
+		    tmp11 = tmp0 + tmp2;
+		    tmp12 = tmp0 - tmp2;
+		} else {
+		    /* d0 == 0, d2 == 0, d4 == 0, d6 != 0 */
+		    tmp2 = MULTIPLY(-d6, FIX_1_306562965);
+		    tmp3 = MULTIPLY(d6, FIX_0_541196100);
+
+		    tmp10 = tmp3;
+		    tmp13 = -tmp3;
+		    tmp11 = tmp2;
+		    tmp12 = -tmp2;
+		}
+	    }
+	}
+    } else {
+	if (d4) {
+	    if (d2) {
+		if (d0) {
+		    /* d0 != 0, d2 != 0, d4 != 0, d6 == 0 */
+		    tmp2 = MULTIPLY(d2, FIX_0_541196100);
+		    tmp3 = MULTIPLY(d2, FIX_1_306562965);
+
+		    tmp0 = (d0 + d4) << CONST_BITS;
+		    tmp1 = (d0 - d4) << CONST_BITS;
+
+		    tmp10 = tmp0 + tmp3;
+		    tmp13 = tmp0 - tmp3;
+		    tmp11 = tmp1 + tmp2;
+		    tmp12 = tmp1 - tmp2;
+		} else {
+		    /* d0 == 0, d2 != 0, d4 != 0, d6 == 0 */
+		    tmp2 = MULTIPLY(d2, FIX_0_541196100);
+		    tmp3 = MULTIPLY(d2, FIX_1_306562965);
+
+		    tmp0 = d4 << CONST_BITS;
+
+		    tmp10 = tmp0 + tmp3;
+		    tmp13 = tmp0 - tmp3;
+		    tmp11 = tmp2 - tmp0;
+		    tmp12 = -(tmp0 + tmp2);
+		}
+	    } else {
+		if (d0) {
+		    /* d0 != 0, d2 == 0, d4 != 0, d6 == 0 */
+		    tmp10 = tmp13 = (d0 + d4) << CONST_BITS;
+		    tmp11 = tmp12 = (d0 - d4) << CONST_BITS;
+		} else {
+		    /* d0 == 0, d2 == 0, d4 != 0, d6 == 0 */
+		    tmp10 = tmp13 = d4 << CONST_BITS;
+		    tmp11 = tmp12 = -tmp10;
+		}
+	    }
+	} else {
+	    if (d2) {
+		if (d0) {
+		    /* d0 != 0, d2 != 0, d4 == 0, d6 == 0 */
+		    tmp2 = MULTIPLY(d2, FIX_0_541196100);
+		    tmp3 = MULTIPLY(d2, FIX_1_306562965);
+
+		    tmp0 = d0 << CONST_BITS;
+
+		    tmp10 = tmp0 + tmp3;
+		    tmp13 = tmp0 - tmp3;
+		    tmp11 = tmp0 + tmp2;
+		    tmp12 = tmp0 - tmp2;
+		} else {
+		    /* d0 == 0, d2 != 0, d4 == 0, d6 == 0 */
+		    tmp2 = MULTIPLY(d2, FIX_0_541196100);
+		    tmp3 = MULTIPLY(d2, FIX_1_306562965);
+
+		    tmp10 = tmp3;
+		    tmp13 = -tmp3;
+		    tmp11 = tmp2;
+		    tmp12 = -tmp2;
+		}
+	    } else {
+		if (d0) {
+		    /* d0 != 0, d2 == 0, d4 == 0, d6 == 0 */
+		    tmp10 = tmp13 = tmp11 = tmp12 = d0 << CONST_BITS;
+		} else {
+		    /* d0 == 0, d2 == 0, d4 == 0, d6 == 0 */
+		    tmp10 = tmp13 = tmp11 = tmp12 = 0;
+		}
+	    }
+	}
+    }
+
+    /* Odd part per figure 8; the matrix is unitary and hence its
+     * transpose is its inverse.  i0..i3 are y7,y5,y3,y1 respectively.
+     */
+    if (d7) {
+	if (d5) {
+	    if (d3) {
+		if (d1) {
+		    /* d1 != 0, d3 != 0, d5 != 0, d7 != 0 */
+		    z1 = d7 + d1;
+		    z2 = d5 + d3;
+		    z3 = d7 + d3;
+		    z4 = d5 + d1;
+		    z5 = MULTIPLY(z3 + z4, FIX_1_175875602);
+		    
+		    tmp0 = MULTIPLY(d7, FIX_0_298631336); 
+		    tmp1 = MULTIPLY(d5, FIX_2_053119869);
+		    tmp2 = MULTIPLY(d3, FIX_3_072711026);
+		    tmp3 = MULTIPLY(d1, FIX_1_501321110);
+		    z1 = MULTIPLY(-z1, FIX_0_899976223);
+		    z2 = MULTIPLY(-z2, FIX_2_562915447);
+		    z3 = MULTIPLY(-z3, FIX_1_961570560);
+		    z4 = MULTIPLY(-z4, FIX_0_390180644);
+		    
+		    z3 += z5;
+		    z4 += z5;
+		    
+		    tmp0 += z1 + z3;
+		    tmp1 += z2 + z4;
+		    tmp2 += z2 + z3;
+		    tmp3 += z1 + z4;
+		} else {
+		    /* d1 == 0, d3 != 0, d5 != 0, d7 != 0 */
+		    z1 = d7;
+		    z2 = d5 + d3;
+		    z3 = d7 + d3;
+		    z5 = MULTIPLY(z3 + d5, FIX_1_175875602);
+		    
+		    tmp0 = MULTIPLY(d7, FIX_0_298631336); 
+		    tmp1 = MULTIPLY(d5, FIX_2_053119869);
+		    tmp2 = MULTIPLY(d3, FIX_3_072711026);
+		    z1 = MULTIPLY(-d7, FIX_0_899976223);
+		    z2 = MULTIPLY(-z2, FIX_2_562915447);
+		    z3 = MULTIPLY(-z3, FIX_1_961570560);
+		    z4 = MULTIPLY(-d5, FIX_0_390180644);
+		    
+		    z3 += z5;
+		    z4 += z5;
+		    
+		    tmp0 += z1 + z3;
+		    tmp1 += z2 + z4;
+		    tmp2 += z2 + z3;
+		    tmp3 = z1 + z4;
+		}
+	    } else {
+		if (d1) {
+		    /* d1 != 0, d3 == 0, d5 != 0, d7 != 0 */
+		    z1 = d7 + d1;
+		    z2 = d5;
+		    z3 = d7;
+		    z4 = d5 + d1;
+		    z5 = MULTIPLY(z3 + z4, FIX_1_175875602);
+		    
+		    tmp0 = MULTIPLY(d7, FIX_0_298631336); 
+		    tmp1 = MULTIPLY(d5, FIX_2_053119869);
+		    tmp3 = MULTIPLY(d1, FIX_1_501321110);
+		    z1 = MULTIPLY(-z1, FIX_0_899976223);
+		    z2 = MULTIPLY(-d5, FIX_2_562915447);
+		    z3 = MULTIPLY(-d7, FIX_1_961570560);
+		    z4 = MULTIPLY(-z4, FIX_0_390180644);
+		    
+		    z3 += z5;
+		    z4 += z5;
+		    
+		    tmp0 += z1 + z3;
+		    tmp1 += z2 + z4;
+		    tmp2 = z2 + z3;
+		    tmp3 += z1 + z4;
+		} else {
+		    /* d1 == 0, d3 == 0, d5 != 0, d7 != 0 */
+		    tmp0 = MULTIPLY(-d7, FIX_0_601344887); 
+		    z1 = MULTIPLY(-d7, FIX_0_899976223);
+		    z3 = MULTIPLY(-d7, FIX_1_961570560);
+		    tmp1 = MULTIPLY(-d5, FIX_0_509795579);
+		    z2 = MULTIPLY(-d5, FIX_2_562915447);
+		    z4 = MULTIPLY(-d5, FIX_0_390180644);
+		    z5 = MULTIPLY(d5 + d7, FIX_1_175875602);
+		    
+		    z3 += z5;
+		    z4 += z5;
+		    
+		    tmp0 += z3;
+		    tmp1 += z4;
+		    tmp2 = z2 + z3;
+		    tmp3 = z1 + z4;
+		}
+	    }
+	} else {
+	    if (d3) {
+		if (d1) {
+		    /* d1 != 0, d3 != 0, d5 == 0, d7 != 0 */
+		    z1 = d7 + d1;
+		    z3 = d7 + d3;
+		    z5 = MULTIPLY(z3 + d1, FIX_1_175875602);
+		    
+		    tmp0 = MULTIPLY(d7, FIX_0_298631336); 
+		    tmp2 = MULTIPLY(d3, FIX_3_072711026);
+		    tmp3 = MULTIPLY(d1, FIX_1_501321110);
+		    z1 = MULTIPLY(-z1, FIX_0_899976223);
+		    z2 = MULTIPLY(-d3, FIX_2_562915447);
+		    z3 = MULTIPLY(-z3, FIX_1_961570560);
+		    z4 = MULTIPLY(-d1, FIX_0_390180644);
+		    
+		    z3 += z5;
+		    z4 += z5;
+		    
+		    tmp0 += z1 + z3;
+		    tmp1 = z2 + z4;
+		    tmp2 += z2 + z3;
+		    tmp3 += z1 + z4;
+		} else {
+		    /* d1 == 0, d3 != 0, d5 == 0, d7 != 0 */
+		    z3 = d7 + d3;
+		    
+		    tmp0 = MULTIPLY(-d7, FIX_0_601344887); 
+		    z1 = MULTIPLY(-d7, FIX_0_899976223);
+		    tmp2 = MULTIPLY(d3, FIX_0_509795579);
+		    z2 = MULTIPLY(-d3, FIX_2_562915447);
+		    z5 = MULTIPLY(z3, FIX_1_175875602);
+		    z3 = MULTIPLY(-z3, FIX_0_785694958);
+		    
+		    tmp0 += z3;
+		    tmp1 = z2 + z5;
+		    tmp2 += z3;
+		    tmp3 = z1 + z5;
+		}
+	    } else {
+		if (d1) {
+		    /* d1 != 0, d3 == 0, d5 == 0, d7 != 0 */
+		    z1 = d7 + d1;
+		    z5 = MULTIPLY(z1, FIX_1_175875602);
+
+		    z1 = MULTIPLY(z1, FIX_0_275899380);
+		    z3 = MULTIPLY(-d7, FIX_1_961570560);
+		    tmp0 = MULTIPLY(-d7, FIX_1_662939225); 
+		    z4 = MULTIPLY(-d1, FIX_0_390180644);
+		    tmp3 = MULTIPLY(d1, FIX_1_111140466);
+
+		    tmp0 += z1;
+		    tmp1 = z4 + z5;
+		    tmp2 = z3 + z5;
+		    tmp3 += z1;
+		} else {
+		    /* d1 == 0, d3 == 0, d5 == 0, d7 != 0 */
+		    tmp0 = MULTIPLY(-d7, FIX_1_387039845);
+		    tmp1 = MULTIPLY(d7, FIX_1_175875602);
+		    tmp2 = MULTIPLY(-d7, FIX_0_785694958);
+		    tmp3 = MULTIPLY(d7, FIX_0_275899380);
+		}
+	    }
+	}
+    } else {
+	if (d5) {
+	    if (d3) {
+		if (d1) {
+		    /* d1 != 0, d3 != 0, d5 != 0, d7 == 0 */
+		    z2 = d5 + d3;
+		    z4 = d5 + d1;
+		    z5 = MULTIPLY(d3 + z4, FIX_1_175875602);
+		    
+		    tmp1 = MULTIPLY(d5, FIX_2_053119869);
+		    tmp2 = MULTIPLY(d3, FIX_3_072711026);
+		    tmp3 = MULTIPLY(d1, FIX_1_501321110);
+		    z1 = MULTIPLY(-d1, FIX_0_899976223);
+		    z2 = MULTIPLY(-z2, FIX_2_562915447);
+		    z3 = MULTIPLY(-d3, FIX_1_961570560);
+		    z4 = MULTIPLY(-z4, FIX_0_390180644);
+		    
+		    z3 += z5;
+		    z4 += z5;
+		    
+		    tmp0 = z1 + z3;
+		    tmp1 += z2 + z4;
+		    tmp2 += z2 + z3;
+		    tmp3 += z1 + z4;
+		} else {
+		    /* d1 == 0, d3 != 0, d5 != 0, d7 == 0 */
+		    z2 = d5 + d3;
+		    
+		    z5 = MULTIPLY(z2, FIX_1_175875602);
+		    tmp1 = MULTIPLY(d5, FIX_1_662939225);
+		    z4 = MULTIPLY(-d5, FIX_0_390180644);
+		    z2 = MULTIPLY(-z2, FIX_1_387039845);
+		    tmp2 = MULTIPLY(d3, FIX_1_111140466);
+		    z3 = MULTIPLY(-d3, FIX_1_961570560);
+		    
+		    tmp0 = z3 + z5;
+		    tmp1 += z2;
+		    tmp2 += z2;
+		    tmp3 = z4 + z5;
+		}
+	    } else {
+		if (d1) {
+		    /* d1 != 0, d3 == 0, d5 != 0, d7 == 0 */
+		    z4 = d5 + d1;
+		    
+		    z5 = MULTIPLY(z4, FIX_1_175875602);
+		    z1 = MULTIPLY(-d1, FIX_0_899976223);
+		    tmp3 = MULTIPLY(d1, FIX_0_601344887);
+		    tmp1 = MULTIPLY(-d5, FIX_0_509795579);
+		    z2 = MULTIPLY(-d5, FIX_2_562915447);
+		    z4 = MULTIPLY(z4, FIX_0_785694958);
+		    
+		    tmp0 = z1 + z5;
+		    tmp1 += z4;
+		    tmp2 = z2 + z5;
+		    tmp3 += z4;
+		} else {
+		    /* d1 == 0, d3 == 0, d5 != 0, d7 == 0 */
+		    tmp0 = MULTIPLY(d5, FIX_1_175875602);
+		    tmp1 = MULTIPLY(d5, FIX_0_275899380);
+		    tmp2 = MULTIPLY(-d5, FIX_1_387039845);
+		    tmp3 = MULTIPLY(d5, FIX_0_785694958);
+		}
+	    }
+	} else {
+	    if (d3) {
+		if (d1) {
+		    /* d1 != 0, d3 != 0, d5 == 0, d7 == 0 */
+		    z5 = d1 + d3;
+		    tmp3 = MULTIPLY(d1, FIX_0_211164243);
+		    tmp2 = MULTIPLY(-d3, FIX_1_451774981);
+		    z1 = MULTIPLY(d1, FIX_1_061594337);
+		    z2 = MULTIPLY(-d3, FIX_2_172734803);
+		    z4 = MULTIPLY(z5, FIX_0_785694958);
+		    z5 = MULTIPLY(z5, FIX_1_175875602);
+		    
+		    tmp0 = z1 - z4;
+		    tmp1 = z2 + z4;
+		    tmp2 += z5;
+		    tmp3 += z5;
+		} else {
+		    /* d1 == 0, d3 != 0, d5 == 0, d7 == 0 */
+		    tmp0 = MULTIPLY(-d3, FIX_0_785694958);
+		    tmp1 = MULTIPLY(-d3, FIX_1_387039845);
+		    tmp2 = MULTIPLY(-d3, FIX_0_275899380);
+		    tmp3 = MULTIPLY(d3, FIX_1_175875602);
+		}
+	    } else {
+		if (d1) {
+		    /* d1 != 0, d3 == 0, d5 == 0, d7 == 0 */
+		    tmp0 = MULTIPLY(d1, FIX_0_275899380);
+		    tmp1 = MULTIPLY(d1, FIX_0_785694958);
+		    tmp2 = MULTIPLY(d1, FIX_1_175875602);
+		    tmp3 = MULTIPLY(d1, FIX_1_387039845);
+		} else {
+		    /* d1 == 0, d3 == 0, d5 == 0, d7 == 0 */
+		    tmp0 = tmp1 = tmp2 = tmp3 = 0;
+		}
+	    }
+	}
+    }
+
+    /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */
+
+    dataptr[DCTSIZE*0] = (DCTELEM) DESCALE(tmp10 + tmp3,
+					   CONST_BITS+PASS1_BITS+3);
+    dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp10 - tmp3,
+					   CONST_BITS+PASS1_BITS+3);
+    dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp11 + tmp2,
+					   CONST_BITS+PASS1_BITS+3);
+    dataptr[DCTSIZE*6] = (DCTELEM) DESCALE(tmp11 - tmp2,
+					   CONST_BITS+PASS1_BITS+3);
+    dataptr[DCTSIZE*2] = (DCTELEM) DESCALE(tmp12 + tmp1,
+					   CONST_BITS+PASS1_BITS+3);
+    dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp12 - tmp1,
+					   CONST_BITS+PASS1_BITS+3);
+    dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp13 + tmp0,
+					   CONST_BITS+PASS1_BITS+3);
+    dataptr[DCTSIZE*4] = (DCTELEM) DESCALE(tmp13 - tmp0,
+					   CONST_BITS+PASS1_BITS+3);
+    
+    dataptr++;			/* advance pointer to next column */
+  }
+}
+
+
+/* here is the reference one, in case of problems with the normal one */
+
+/* idctref.c, Inverse Discrete Fourier Transform, double precision          */
+
+/* Copyright (C) 1994, MPEG Software Simulation Group. All Rights Reserved. */
+
+/*
+ * Disclaimer of Warranty
+ *
+ * These software programs are available to the user without any license fee or
+ * royalty on an "as is" basis.  The MPEG Software Simulation Group disclaims
+ * any and all warranties, whether express, implied, or statuary, including any
+ * implied warranties or merchantability or of fitness for a particular
+ * purpose.  In no event shall the copyright-holder be liable for any
+ * incidental, punitive, or consequential damages of any kind whatsoever
+ * arising from the use of these programs.
+ *
+ * This disclaimer of warranty extends to the user of these programs and user's
+ * customers, employees, agents, transferees, successors, and assigns.
+ *
+ * The MPEG Software Simulation Group does not represent or warrant that the
+ * programs furnished hereunder are free of infringement of any third-party
+ * patents.
+ *
+ * Commercial implementations of MPEG-1 and MPEG-2 video, including shareware,
+ * are subject to royalty fees to patent holders.  Many of these patents are
+ * general enough such that they are unavoidable regardless of implementation
+ * design.
+ *
+ */
+
+/*  Perform IEEE 1180 reference (64-bit floating point, separable 8x1
+ *  direct matrix multiply) Inverse Discrete Cosine Transform
+*/
+
+
+/* Here we use math.h to generate constants.  Compiler results may
+   vary a little */
+
+#ifndef PI
+#ifdef M_PI
+#define PI M_PI
+#else
+#define PI 3.14159265358979323846
+#endif
+#endif
+
+/* cosine transform matrix for 8x1 IDCT */
+static double itrans_coef[8][8];
+
+/* initialize DCT coefficient matrix */
+
+void init_idctref()
+{
+  int freq, time;
+  double scale;
+
+  for (freq=0; freq < 8; freq++)
+  {
+    scale = (freq == 0) ? sqrt(0.125) : 0.5;
+    for (time=0; time<8; time++)
+      itrans_coef[freq][time] = scale*cos((PI/8.0)*freq*(time + 0.5));
+  }
+}
+
+/* perform IDCT matrix multiply for 8x8 coefficient block */
+
+void reference_rev_dct(block)
+int16 *block;
+{
+  int i, j, k, v;
+  double partial_product;
+  double tmp[64];
+
+  for (i=0; i<8; i++)
+    for (j=0; j<8; j++)
+    {
+      partial_product = 0.0;
+
+      for (k=0; k<8; k++)
+        partial_product+= itrans_coef[k][j]*block[8*i+k];
+
+      tmp[8*i+j] = partial_product;
+    }
+
+  /* Transpose operation is integrated into address mapping by switching 
+     loop order of i and j */
+
+  for (j=0; j<8; j++)
+    for (i=0; i<8; i++)
+    {
+      partial_product = 0.0;
+
+      for (k=0; k<8; k++)
+        partial_product+= itrans_coef[k][i]*tmp[8*k+j];
+
+      v = floor(partial_product+0.5);
+      block[8*i+j] = (v<-256) ? -256 : ((v>255) ? 255 : v);
+    }
+}
diff --git a/converter/ppm/ppmtompeg/memory.c b/converter/ppm/ppmtompeg/memory.c
new file mode 100644
index 00000000..f117994a
--- /dev/null
+++ b/converter/ppm/ppmtompeg/memory.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*
+ *  $Header: /n/charlie-brown/project/mm/mpeg/mpeg_dist/mpeg_encode/RCS/memory.c,v 1.3 1995/01/19 23:08:43 eyhung Exp $
+ *  $Log: memory.c,v $
+ * Revision 1.3  1995/01/19  23:08:43  eyhung
+ * Changed copyrights
+ *
+ * Revision 1.2  1993/06/03  21:08:08  keving
+ * nothing
+ *
+ * Revision 1.1  1993/04/27  21:32:26  keving
+ * nothing
+ *
+ *
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "memory.h"
+
+
+/* memory handling routines
+ *
+ */
+
+long	totalMemory = 0;
+long	maxMemory = 0;
+
+
+char	*MemAlloc(size_t size)
+{
+    totalMemory += (long)size;
+    if ( totalMemory > maxMemory )
+    {
+	maxMemory = totalMemory;
+    }
+
+    return malloc(size);
+}
+
+void	MemFree(char *ptr, long bytes)
+{
+    totalMemory -= bytes;
+    free(ptr);
+}
+
+void	PrintMaxMemory(void)
+{
+    fprintf(stdout, "MMMMM-----MAX MEMORY-----MMMMM = %ld\n", maxMemory);
+}
diff --git a/converter/ppm/ppmtompeg/mfwddct.c b/converter/ppm/ppmtompeg/mfwddct.c
new file mode 100644
index 00000000..4643bf25
--- /dev/null
+++ b/converter/ppm/ppmtompeg/mfwddct.c
@@ -0,0 +1,393 @@
+
+/*
+ * mfwddct.c (derived from jfwddct.c, which carries the following info)
+ *
+ * Copyright (C) 1991, 1992, Thomas G. Lane. This file is part of the
+ * Independent JPEG Group's software. For conditions of distribution and use,
+ * see the accompanying README file.
+ *
+ * This file contains the basic DCT (Discrete Cosine Transform) transformation
+ * subroutine.
+ *
+ * This implementation is based on Appendix A.2 of the book "Discrete Cosine
+ * Transform---Algorithms, Advantages, Applications" by K.R. Rao and P. Yip
+ * (Academic Press, Inc, London, 1990). It uses scaled fixed-point arithmetic
+ * instead of floating point.
+ */
+
+#include "all.h"
+
+#include "dct.h"
+#include "mtypes.h"
+#include "opts.h"
+
+/*
+ * The poop on this scaling stuff is as follows:
+ *
+ * We have to do addition and subtraction of the integer inputs, which is no
+ * problem, and multiplication by fractional constants, which is a problem to
+ * do in integer arithmetic.  We multiply all the constants by DCT_SCALE and
+ * convert them to integer constants (thus retaining LG2_DCT_SCALE bits of
+ * precision in the constants).  After doing a multiplication we have to
+ * divide the product by DCT_SCALE, with proper rounding, to produce the
+ * correct output.  The division can be implemented cheaply as a right shift
+ * of LG2_DCT_SCALE bits.  The DCT equations also specify an additional
+ * division by 2 on the final outputs; this can be folded into the
+ * right-shift by shifting one more bit (see UNFIXH).
+ *
+ * If you are planning to recode this in assembler, you might want to set
+ * LG2_DCT_SCALE to 15.  This loses a bit of precision, but then all the
+ * multiplications are between 16-bit quantities (given 8-bit JSAMPLEs!) so
+ * you could use a signed 16x16=>32 bit multiply instruction instead of full
+ * 32x32 multiply.  Unfortunately there's no way to describe such a multiply
+ * portably in C, so we've gone for the extra bit of accuracy here.
+ */
+
+#define EIGHT_BIT_SAMPLES
+#ifdef EIGHT_BIT_SAMPLES
+#define LG2_DCT_SCALE 16
+#else
+#define LG2_DCT_SCALE 15	/* lose a little precision to avoid overflow */
+#endif
+
+#define ONE	((int32) 1)
+
+#define DCT_SCALE (ONE << LG2_DCT_SCALE)
+
+/* In some places we shift the inputs left by a couple more bits, */
+/* so that they can be added to fractional results without too much */
+/* loss of precision. */
+#define LG2_OVERSCALE 2
+#define OVERSCALE  (ONE << LG2_OVERSCALE)
+#define OVERSHIFT(x)  ((x) <<= LG2_OVERSCALE)
+
+/* Scale a fractional constant by DCT_SCALE */
+#define FIX(x)	((int32) ((x) * DCT_SCALE + 0.5))
+
+/* Scale a fractional constant by DCT_SCALE/OVERSCALE */
+/* Such a constant can be multiplied with an overscaled input */
+/* to produce something that's scaled by DCT_SCALE */
+#define FIXO(x)  ((int32) ((x) * DCT_SCALE / OVERSCALE + 0.5))
+
+/* Descale and correctly round a value that's scaled by DCT_SCALE */
+#define UNFIX(x)   RIGHT_SHIFT((x) + (ONE << (LG2_DCT_SCALE-1)), LG2_DCT_SCALE)
+
+/* Same with an additional division by 2, ie, correctly rounded UNFIX(x/2) */
+#define UNFIXH(x)  RIGHT_SHIFT((x) + (ONE << LG2_DCT_SCALE), LG2_DCT_SCALE+1)
+
+/* Take a value scaled by DCT_SCALE and round to integer scaled by OVERSCALE */
+#define UNFIXO(x)  RIGHT_SHIFT((x) + (ONE << (LG2_DCT_SCALE-1-LG2_OVERSCALE)),\
+			       LG2_DCT_SCALE-LG2_OVERSCALE)
+
+/* Here are the constants we need */
+/* SIN_i_j is sine of i*pi/j, scaled by DCT_SCALE */
+/* COS_i_j is cosine of i*pi/j, scaled by DCT_SCALE */
+
+#define SIN_1_4 FIX(0.707106781)
+#define COS_1_4 SIN_1_4
+
+#define SIN_1_8 FIX(0.382683432)
+#define COS_1_8 FIX(0.923879533)
+#define SIN_3_8 COS_1_8
+#define COS_3_8 SIN_1_8
+
+#define SIN_1_16 FIX(0.195090322)
+#define COS_1_16 FIX(0.980785280)
+#define SIN_7_16 COS_1_16
+#define COS_7_16 SIN_1_16
+
+#define SIN_3_16 FIX(0.555570233)
+#define COS_3_16 FIX(0.831469612)
+#define SIN_5_16 COS_3_16
+#define COS_5_16 SIN_3_16
+
+/* OSIN_i_j is sine of i*pi/j, scaled by DCT_SCALE/OVERSCALE */
+/* OCOS_i_j is cosine of i*pi/j, scaled by DCT_SCALE/OVERSCALE */
+
+#define OSIN_1_4 FIXO(0.707106781)
+#define OCOS_1_4 OSIN_1_4
+
+#define OSIN_1_8 FIXO(0.382683432)
+#define OCOS_1_8 FIXO(0.923879533)
+#define OSIN_3_8 OCOS_1_8
+#define OCOS_3_8 OSIN_1_8
+
+#define OSIN_1_16 FIXO(0.195090322)
+#define OCOS_1_16 FIXO(0.980785280)
+#define OSIN_7_16 OCOS_1_16
+#define OCOS_7_16 OSIN_1_16
+
+#define OSIN_3_16 FIXO(0.555570233)
+#define OCOS_3_16 FIXO(0.831469612)
+#define OSIN_5_16 OCOS_3_16
+#define OCOS_5_16 OSIN_3_16
+
+/* Prototypes */
+void reference_fwd_dct _ANSI_ARGS_((Block block, Block dest));
+void mp_fwd_dct_fast _ANSI_ARGS_((Block data2d, Block dest2d));
+void init_fdct _ANSI_ARGS_((void));
+
+/*
+ * --------------------------------------------------------------
+ *
+ * mp_fwd_dct_block2 --
+ *
+ * Select the appropriate mp_fwd_dct routine
+ *
+ * Results: None
+ *
+ * Side effects: None
+ *
+ * --------------------------------------------------------------
+ */
+extern boolean pureDCT;
+void
+mp_fwd_dct_block2(data, dest)
+    Block data, dest;
+{
+  if (pureDCT) reference_fwd_dct(data, dest);
+  else mp_fwd_dct_fast(data, dest);
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * mp_fwd_dct_fast --
+ *
+ * Perform the forward DCT on one block of samples.
+ *
+ * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT on each
+ * column.
+ *
+ * Results: None
+ *
+ * Side effects: Overwrites the input data
+ *
+ * --------------------------------------------------------------
+ */
+
+void
+mp_fwd_dct_fast(data2d, dest2d)
+    Block data2d, dest2d;
+{
+    int16 *data = (int16 *) data2d;	/* this algorithm wants
+					 * a 1-d array */
+    int16 *dest = (int16 *) dest2d;
+    int pass, rowctr;
+    register int16 *inptr, *outptr;
+    int16 workspace[DCTSIZE_SQ];
+    SHIFT_TEMPS
+
+#ifdef ndef
+    {
+	int y;
+
+	printf("fwd_dct (beforehand):\n");
+	for (y = 0; y < 8; y++)
+	    printf("%4d %4d %4d %4d %4d %4d %4d %4d\n",
+		   data2d[y][0], data2d[y][1],
+		   data2d[y][2], data2d[y][3],
+		   data2d[y][4], data2d[y][5],
+		   data2d[y][6], data2d[y][7]);
+    }
+#endif
+
+    /*
+     * Each iteration of the inner loop performs one 8-point 1-D DCT. It
+     * reads from a *row* of the input matrix and stores into a *column*
+     * of the output matrix.  In the first pass, we read from the data[]
+     * array and store into the local workspace[].  In the second pass,
+     * we read from the workspace[] array and store into data[], thus
+     * performing the equivalent of a columnar DCT pass with no variable
+     * array indexing.
+     */
+
+    inptr = data;		/* initialize pointers for first pass */
+    outptr = workspace;
+    for (pass = 1; pass >= 0; pass--) {
+	for (rowctr = DCTSIZE - 1; rowctr >= 0; rowctr--) {
+	    /*
+	     * many tmps have nonoverlapping lifetime -- flashy
+	     * register colorers should be able to do this lot
+	     * very well
+	     */
+	    int32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
+	    int32 tmp10, tmp11, tmp12, tmp13;
+	    int32 tmp14, tmp15, tmp16, tmp17;
+	    int32 tmp25, tmp26;
+	    /* SHIFT_TEMPS */
+
+	    /* temp0 through tmp7:  -512 to +512 */
+	    /* if I-block, then -256 to +256 */
+	    tmp0 = inptr[7] + inptr[0];
+	    tmp1 = inptr[6] + inptr[1];
+	    tmp2 = inptr[5] + inptr[2];
+	    tmp3 = inptr[4] + inptr[3];
+	    tmp4 = inptr[3] - inptr[4];
+	    tmp5 = inptr[2] - inptr[5];
+	    tmp6 = inptr[1] - inptr[6];
+	    tmp7 = inptr[0] - inptr[7];
+
+	    /* tmp10 through tmp13:  -1024 to +1024 */
+	    /* if I-block, then -512 to +512 */
+	    tmp10 = tmp3 + tmp0;
+	    tmp11 = tmp2 + tmp1;
+	    tmp12 = tmp1 - tmp2;
+	    tmp13 = tmp0 - tmp3;
+
+	    outptr[0] = (int16) UNFIXH((tmp10 + tmp11) * SIN_1_4);
+	    outptr[DCTSIZE * 4] = (int16) UNFIXH((tmp10 - tmp11) * COS_1_4);
+
+	    outptr[DCTSIZE * 2] = (int16) UNFIXH(tmp13 * COS_1_8 + tmp12 * SIN_1_8);
+	    outptr[DCTSIZE * 6] = (int16) UNFIXH(tmp13 * SIN_1_8 - tmp12 * COS_1_8);
+
+	    tmp16 = UNFIXO((tmp6 + tmp5) * SIN_1_4);
+	    tmp15 = UNFIXO((tmp6 - tmp5) * COS_1_4);
+
+	    OVERSHIFT(tmp4);
+	    OVERSHIFT(tmp7);
+
+	    /*
+	     * tmp4, tmp7, tmp15, tmp16 are overscaled by
+	     * OVERSCALE
+	     */
+
+	    tmp14 = tmp4 + tmp15;
+	    tmp25 = tmp4 - tmp15;
+	    tmp26 = tmp7 - tmp16;
+	    tmp17 = tmp7 + tmp16;
+
+	    outptr[DCTSIZE] = (int16) UNFIXH(tmp17 * OCOS_1_16 + tmp14 * OSIN_1_16);
+	    outptr[DCTSIZE * 7] = (int16) UNFIXH(tmp17 * OCOS_7_16 - tmp14 * OSIN_7_16);
+	    outptr[DCTSIZE * 5] = (int16) UNFIXH(tmp26 * OCOS_5_16 + tmp25 * OSIN_5_16);
+	    outptr[DCTSIZE * 3] = (int16) UNFIXH(tmp26 * OCOS_3_16 - tmp25 * OSIN_3_16);
+
+	    inptr += DCTSIZE;	/* advance inptr to next row */
+	    outptr++;		/* advance outptr to next column */
+	}
+	/* end of pass; in case it was pass 1, set up for pass 2 */
+	inptr = workspace;
+	outptr = dest;
+    }
+#ifdef ndef
+    {
+	int y;
+
+	printf("fwd_dct (afterward):\n");
+	for (y = 0; y < 8; y++)
+	    printf("%4d %4d %4d %4d %4d %4d %4d %4d\n",
+		   dest2d[y][0], dest2d[y][1],
+		   dest2d[y][2], dest2d[y][3],
+		   dest2d[y][4], dest2d[y][5],
+		   dest2d[y][6], dest2d[y][7]);
+    }
+#endif
+}
+
+
+/* Modifies from the MPEG2 verification coder */
+/* fdctref.c, forward discrete cosine transform, double precision           */
+
+/* Copyright (C) 1994, MPEG Software Simulation Group. All Rights Reserved. */
+
+/*
+ * Disclaimer of Warranty
+ *
+ * These software programs are available to the user without any license fee or
+ * royalty on an "as is" basis.  The MPEG Software Simulation Group disclaims
+ * any and all warranties, whether express, implied, or statuary, including any
+ * implied warranties or merchantability or of fitness for a particular
+ * purpose.  In no event shall the copyright-holder be liable for any
+ * incidental, punitive, or consequential damages of any kind whatsoever
+ * arising from the use of these programs.
+ *
+ * This disclaimer of warranty extends to the user of these programs and user's
+ * customers, employees, agents, transferees, successors, and assigns.
+ *
+ * The MPEG Software Simulation Group does not represent or warrant that the
+ * programs furnished hereunder are free of infringement of any third-party
+ * patents.
+ *
+ * Commercial implementations of MPEG-1 and MPEG-2 video, including shareware,
+ * are subject to royalty fees to patent holders.  Many of these patents are
+ * general enough such that they are unavoidable regardless of implementation
+ * design.
+ *
+ */
+
+#ifndef PI
+#ifdef M_PI
+#define PI M_PI
+#else
+#define PI 3.14159265358979323846
+#endif
+#endif
+
+/* private data */
+static double trans_coef[8][8]; /* transform coefficients */
+
+void init_fdct()
+{
+  int i, j;
+  double s;
+
+  for (i=0; i<8; i++)
+  {
+    s = (i==0) ? sqrt(0.125) : 0.5;
+
+    for (j=0; j<8; j++)
+      trans_coef[i][j] = s * cos((PI/8.0)*i*(j+0.5));
+  }
+}
+
+void reference_fwd_dct(block, dest)
+Block block, dest;
+{
+  int i, j, k;
+  double s;
+  double tmp[64];
+
+  if (DoLaplace) {
+    LaplaceNum++;
+  }
+
+  for (i=0; i<8; i++)
+    for (j=0; j<8; j++)
+    {
+      s = 0.0;
+
+      for (k=0; k<8; k++)
+        s += trans_coef[j][k] * block[i][k];
+
+      tmp[8*i+j] = s;
+    }
+
+  for (i=0; i<8; i++)
+    for (j=0; j<8; j++)
+    {
+      s = 0.0;
+
+      for (k=0; k<8; k++)
+        s += trans_coef[i][k] * tmp[8*k+j];
+
+      if (collect_quant) {
+	fprintf(collect_quant_fp, "%d %f\n", 8*i+j, s);
+      } 
+      if (DoLaplace) {
+	L1[LaplaceCnum][i*8+j] += s*s;
+	L2[LaplaceCnum][i*8+j] += s;
+      }
+
+
+      dest[i][j] = (int)floor(s+0.499999);
+      /*
+       * reason for adding 0.499999 instead of 0.5:
+       * s is quite often x.5 (at least for i and/or j = 0 or 4)
+       * and setting the rounding threshold exactly to 0.5 leads to an
+       * extremely high arithmetic implementation dependency of the result;
+       * s being between x.5 and x.500001 (which is now incorrectly rounded
+       * downwards instead of upwards) is assumed to occur less often
+       * (if at all)
+       */
+    }
+}
diff --git a/converter/ppm/ppmtompeg/mheaders.c b/converter/ppm/ppmtompeg/mheaders.c
new file mode 100644
index 00000000..2a5f2c63
--- /dev/null
+++ b/converter/ppm/ppmtompeg/mheaders.c
@@ -0,0 +1,1192 @@
+/*===========================================================================*
+ * mheaders.c								     *
+ *									     *
+ *	Procedures to generate MPEG headers				     *
+ *									     *
+ * EXPORTED PROCEDURES:							     *
+ *	Mhead_GenPictureHeader						     *
+ *	Mhead_GenSequenceHeader						     *
+ *	Mhead_GenSequenceEnder						     *
+ *	Mhead_GenGOPHeader						     *
+ *	Mhead_GenSliceHeader						     *
+ *	Mhead_GenSliceEnder						     *
+ *	Mhead_GenMBHeader						     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*
+ *  $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/RCS/mheaders.c,v 1.15 1995/08/07 21:45:19 smoot Exp $
+ *  $Log: mheaders.c,v $
+ *  Revision 1.15  1995/08/07 21:45:19  smoot
+ *  check for illegal MVs (shouldnt ever be called, but....)
+ *  fix bug which made us not weite Iframe Qscale changes
+ *  warns if writing a size=0 mpeg
+ *
+ *  Revision 1.14  1995/05/22 20:53:35  smoot
+ *  corrected bit_rate value in constrained params flag
+ *
+ * Revision 1.13  1995/05/02  01:50:38  eyhung
+ * made VidRateNum un-static
+ *
+ * Revision 1.12  1995/03/27  19:28:23  smoot
+ * auto-determines Qscale changes (was mb_quant)
+ *
+ * Revision 1.11  1995/02/16  09:12:39  eyhung
+ * fixed compile bug with HP7xx
+ *
+ * Revision 1.10  1995/01/25  22:53:50  smoot
+ * Better buf_size checking, and actually check constrained params
+ *
+ * Revision 1.9  1995/01/19  23:08:47  eyhung
+ * Changed copyrights
+ *
+ * Revision 1.8  1995/01/16  08:45:10  eyhung
+ * BLEAH'ed hsize and vsize
+ *
+ * Revision 1.7  1994/12/09  22:27:17  smoot
+ * Fixed buffer size in stream
+ *
+ * Revision 1.6  1994/11/12  02:11:54  keving
+ * nothing
+ *
+ * Revision 1.5  1994/03/15  00:27:11  keving
+ * nothing
+ *
+ * Revision 1.4  1993/12/22  19:19:01  keving
+ * nothing
+ *
+ * Revision 1.3  1993/07/22  22:23:43  keving
+ * nothing
+ *
+ * Revision 1.2  1993/06/30  20:06:09  keving
+ * nothing
+ *
+ * Revision 1.1  1993/06/03  21:08:08  keving
+ * nothing
+ *
+ * Revision 1.6  1993/03/01  23:03:40  keving
+ * nothing
+ *
+ * Revision 1.5  1993/02/17  23:18:20  dwallach
+ * checkin prior to keving's joining the project
+ *
+ * Revision 1.4  1993/01/18  10:20:02  dwallach
+ * *** empty log message ***
+ *
+ * Revision 1.3  1993/01/18  10:17:29  dwallach
+ * RCS headers installed, code indented uniformly
+ *
+ * Revision 1.3  1993/01/18  10:17:29  dwallach
+ * RCS headers installed, code indented uniformly
+ *
+ */
+
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "all.h"
+#include "bitio.h"
+#include "frames.h"
+#include "mheaders.h"
+
+
+/*==================*
+ * STATIC VARIABLES *
+ *==================*/
+
+static int gopStartFrame = 0;
+static int lastGOPStart = 0;
+static int lastQSSet;
+
+static uint32 mbAddrIncrTable[][2] = {
+    {0x0, 0},
+    {0x1, 1},
+    {0x3, 3},
+    {0x2, 3},
+    {0x3, 4},
+    {0x2, 4},
+    {0x3, 5},
+    {0x2, 5},
+    {0x7, 7},
+    {0x6, 7},
+    {0xb, 8},
+    {0xa, 8},
+    {0x9, 8},
+    {0x8, 8},
+    {0x7, 8},
+    {0x6, 8},
+    {0x17, 10},
+    {0x16, 10},
+    {0x15, 10},
+    {0x14, 10},
+    {0x13, 10},
+    {0x12, 10},
+    {0x23, 11},
+    {0x22, 11},
+    {0x21, 11},
+    {0x20, 11},
+    {0x1f, 11},
+    {0x1e, 11},
+    {0x1d, 11},
+    {0x1c, 11},
+    {0x1b, 11},
+    {0x1a, 11},
+    {0x19, 11},
+    {0x18, 11}};
+
+static uint32 mbMotionVectorTable[][2] = {
+    {0x19, 11},
+    {0x1b, 11},
+    {0x1d, 11},
+    {0x1f, 11},
+    {0x21, 11},
+    {0x23, 11},
+    {0x13, 10},
+    {0x15, 10},
+    {0x17, 10},
+    {0x7, 8},
+    {0x9, 8},
+    {0xb, 8},
+    {0x7, 7},
+    {0x3, 5},
+    {0x3, 4},
+    {0x3, 3},
+    {0x1, 1},
+    {0x2, 3},
+    {0x2, 4},
+    {0x2, 5},
+    {0x6, 7},
+    {0xa, 8},
+    {0x8, 8},
+    {0x6, 8},
+    {0x16, 10},
+    {0x14, 10},
+    {0x12, 10},
+    {0x22, 11},
+    {0x20, 11},
+    {0x1e, 11},
+    {0x1c, 11},
+    {0x1a, 11},
+    {0x18, 11}};
+
+static uint32 mbPatTable[][2] = {
+    {0x0, 0},
+    {0xb, 5},
+    {0x9, 5},
+    {0xd, 6},
+    {0xd, 4},
+    {0x17, 7},
+    {0x13, 7},
+    {0x1f, 8},
+    {0xc, 4},
+    {0x16, 7},
+    {0x12, 7},
+    {0x1e, 8},
+    {0x13, 5},
+    {0x1b, 8},
+    {0x17, 8},
+    {0x13, 8},
+    {0xb, 4},
+    {0x15, 7},
+    {0x11, 7},
+    {0x1d, 8},
+    {0x11, 5},
+    {0x19, 8},
+    {0x15, 8},
+    {0x11, 8},
+    {0xf, 6},
+    {0xf, 8},
+    {0xd, 8},
+    {0x3, 9},
+    {0xf, 5},
+    {0xb, 8},
+    {0x7, 8},
+    {0x7, 9},
+    {0xa, 4},
+    {0x14, 7},
+    {0x10, 7},
+    {0x1c, 8},
+    {0xe, 6},
+    {0xe, 8},
+    {0xc, 8},
+    {0x2, 9},
+    {0x10, 5},
+    {0x18, 8},
+    {0x14, 8},
+    {0x10, 8},
+    {0xe, 5},
+    {0xa, 8},
+    {0x6, 8},
+    {0x6, 9},
+    {0x12, 5},
+    {0x1a, 8},
+    {0x16, 8},
+    {0x12, 8},
+    {0xd, 5},
+    {0x9, 8},
+    {0x5, 8},
+    {0x5, 9},
+    {0xc, 5},
+    {0x8, 8},
+    {0x4, 8},
+    {0x4, 9},
+    {0x7, 3},
+    {0xa, 5},	/* grrr... 61, 62, 63 added - Kevin */
+    {0x8, 5},
+    {0xc, 6}
+};
+
+/*===========*
+ * CONSTANTS *
+ *===========*/
+
+#define SEQ_HEAD_CODE 0x000001b3
+#define EXT_START_CODE 0x000001b5
+#define USER_START_CODE 0x000001b2
+#define GOP_START_CODE 0x000001b8
+#define PICT_START_CODE 0x00000100
+#define SLICE_BASE_CODE 0x00000100
+
+#define SEQ_END_CODE	0x000001b7
+
+/* not static anymore because information is used for computing frame rate 
+ * and for statistics */
+const double VidRateNum[9]={1.0, 23.976, 24.0, 25.0, 29.97, 30.0,
+                             50.0 ,59.94, 60.0};
+
+
+/*===============================*
+ * INTERNAL PROCEDURE prototypes *
+ *===============================*/
+
+static void	GenMBAddrIncr _ANSI_ARGS_((BitBucket *bb, uint32 addr_incr));
+static void	GenPictHead _ANSI_ARGS_((BitBucket *bb, uint32 temp_ref,
+		    uint32 code_type, uint32 vbv_delay,
+		    int32 full_pel_forw_flag, uint32 forw_f_code,
+		    int32 full_pel_back_flag, uint32 back_f_code,
+		    uint8 *extra_info, uint32 extra_info_size,
+		    uint8 *ext_data, uint32 ext_data_size,
+		    uint8 *user_data, uint32 user_data_size));
+static void	GenMBType _ANSI_ARGS_((BitBucket *bb, uint32 pict_code_type,
+		  uint32 mb_quant, uint32 motion_forw, uint32 motion_back,
+		  uint32 mb_pattern, uint32 mb_intra));
+static void	GenMotionCode _ANSI_ARGS_((BitBucket *bb, int32 vector));
+static void	GenBlockPattern _ANSI_ARGS_((BitBucket *bb,
+					     uint32 mb_pattern));
+
+
+/*=====================*
+ * EXPORTED PROCEDURES *
+ *=====================*/
+
+
+/*===========================================================================*
+ *
+ * SetGOPStartTime
+ *
+ *	sets the start frame of the GOP; to be used with GenPictureHeader
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+SetGOPStartTime(index)
+    int index;
+{
+    lastGOPStart = gopStartFrame;
+    gopStartFrame = index;
+}
+
+
+/*===========================================================================*
+ *
+ * Mhead_GenPictureHeader
+ *
+ *	generate picture header with given frame type and picture count
+ *	append result to the specified bitstream
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+Mhead_GenPictureHeader(bbPtr, frameType, pictCount, f_code)
+    BitBucket *bbPtr;
+    int frameType;
+    int pictCount;
+    int f_code;
+{
+    int	    temporalRef;
+
+    if ( pictCount >= gopStartFrame ) {
+	temporalRef = (pictCount-gopStartFrame);
+    } else {
+	temporalRef = (pictCount-lastGOPStart);
+    }
+    temporalRef = (temporalRef % 1024);
+	
+    DBG_PRINT(("Picture Header\n"));
+    GenPictHead(bbPtr, temporalRef, frameType,
+		0 /* vbv_delay */,
+		pixelFullSearch /* full_pel_forw_flag */,
+		f_code /* forw_f_code */,
+		pixelFullSearch /* full_pel_back_flag */,
+		f_code /* back_f_code */,
+		NULL, 0, NULL, 0, NULL, 0);
+}
+
+
+/*===========================================================================*
+ *
+ * Mhead_GenSequenceHeader
+ *
+ *	generate sequence header with given attributes
+ *	append result to the specified bitstream
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+Mhead_GenSequenceHeader(BitBucket *   const bbPtr, 
+                        uint32        const hsize, 
+                        uint32        const vsize,
+                        int32         const pratio, 
+                        int32         const pict_rate, 
+                        int32         const bit_rate_arg,
+                        int32         const buf_size_arg, 
+                        int32         const c_param_flag_arg, 
+                        const int32 * const iq_matrix, 
+                        const int32 * const niq_matrix,
+                        uint8 *       const ext_data, 
+                        int32         const ext_data_size, 
+                        uint8 *       const user_data,
+                        int32         const user_data_size) {
+
+    extern int ZAG[];
+    int i;
+    int32 bit_rate;
+    int32 buf_size;
+    int32 c_param_flag;
+
+    /* Write seq start code. */
+
+    Bitio_Write(bbPtr, SEQ_HEAD_CODE, 32);
+
+    /* Write horiz. and vert. sizes. */
+
+#ifdef BLEAH
+    fprintf(stdout, "hsize, vsize = %d, %d\n", hsize, vsize);
+#endif
+
+    if (hsize==0 || vsize==0) {
+        fprintf(stderr, "Writing zero size to stream!\n");
+    }
+    Bitio_Write(bbPtr, hsize, 12);
+    Bitio_Write(bbPtr, vsize, 12);
+
+    /* Write pixel aspect ratio, negative values default to 1. */
+
+    if (pratio < 0) {
+        fprintf(stderr, "PROGRAMMER ERROR:  pratio = %d\n", pratio);
+        exit(1);
+    }
+    Bitio_Write(bbPtr, pratio, 4);
+
+    /* Wrtie picture rate, negative values default to 30 fps. */
+
+    if (pict_rate < 0) {
+        fprintf(stderr, "PROGRAMMER ERROR:  pict_rate = %d\n", pict_rate);
+        exit(1);
+    }
+    Bitio_Write(bbPtr, pict_rate, 4);
+
+    /* Write bit rate, negative values default to variable. */
+
+    if (bit_rate_arg < 0) {
+        bit_rate = -1;
+    } else {
+        bit_rate = bit_rate_arg / 400;
+    }
+
+    Bitio_Write(bbPtr, bit_rate, 18);
+
+    /* Marker bit. */
+    Bitio_Write(bbPtr, 0x1, 1);
+
+    /* Write VBV buffer size. Negative values default to zero. */
+    if (buf_size_arg < 0)
+        buf_size = 0;
+    else
+        buf_size = buf_size_arg;
+
+    buf_size = (buf_size + (16*1024 - 1)) / (16*1024);
+    if (buf_size>=0x400) 
+        buf_size=0x3ff;
+    Bitio_Write(bbPtr, buf_size, 10);
+
+    /* Write constrained parameter flag. */
+    {
+        int num_mb = ((hsize+15)/16) * ((vsize+15)/16);
+        /* At present we cheat on buffer size */
+        c_param_flag = ((bit_rate <= 4640) &&
+                        (bit_rate >0) &&
+                        (buf_size <= 20) &&
+                        (pict_rate >= 1) &&
+                        (pict_rate <= 5) &&
+                        (hsize <= 768) &&
+                        (vsize <= 576) &&
+                        (num_mb <= 396) &&
+                        (num_mb*VidRateNum[pict_rate] <= 9900) &&
+                        (fCodeP<=4) &&
+                        (fCodeB<=4));
+    }
+
+    if (c_param_flag) {
+        Bitio_Write(bbPtr, 0x01, 1);
+    } else {
+        Bitio_Write(bbPtr, 0x00, 1);
+    }
+
+    /* Write intra quant matrix if present. */
+
+    if (iq_matrix != NULL) {
+        Bitio_Write(bbPtr, 0x01, 1);
+        for (i = 0; i < 64; i++) {
+            Bitio_Write(bbPtr, iq_matrix[ZAG[i]], 8);
+        }
+    } else {
+        Bitio_Write(bbPtr, 0x00, 1);
+    }
+
+    /* Write non intra quant matrix if present. */
+
+    if (niq_matrix != NULL) {
+        Bitio_Write(bbPtr, 0x01, 1);
+        for (i = 0; i < 64; i++) {
+            Bitio_Write(bbPtr, niq_matrix[ZAG[i]], 8);
+        }
+    } else {
+        Bitio_Write(bbPtr, 0x00, 1);
+    }
+
+    /* next start code */
+    Bitio_BytePad(bbPtr);
+
+
+    /* Write ext data if present. */
+
+    if (ext_data != NULL) {
+        Bitio_Write(bbPtr, EXT_START_CODE, 32);
+
+        for (i = 0; i < ext_data_size; i++) {
+            Bitio_Write(bbPtr, ext_data[i], 8);
+        }
+        Bitio_BytePad(bbPtr);
+    }
+    /* Write user data if present. */
+    if ((user_data != NULL) && (user_data_size != 0)) {
+        Bitio_Write(bbPtr, USER_START_CODE, 32);
+
+        for (i = 0; i < user_data_size; i++) {
+            Bitio_Write(bbPtr, user_data[i], 8);
+        }
+        Bitio_BytePad(bbPtr);
+    }
+}
+
+
+/*===========================================================================*
+ *
+ * Mhead_GenSequenceEnder
+ *
+ *	generate sequence ender
+ *	append result to the specified bitstream
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+Mhead_GenSequenceEnder(bbPtr)
+    BitBucket *bbPtr;
+{
+    Bitio_Write(bbPtr, SEQ_END_CODE, 32);
+}
+
+
+/*===========================================================================*
+ *
+ * Mhead_GenGOPHeader
+ *
+ *	generate GOP header with specified attributes
+ *	append result to the specified bitstream
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+Mhead_GenGOPHeader(bbPtr, drop_frame_flag, tc_hrs, tc_min, tc_sec, tc_pict,
+		   closed_gop, broken_link, ext_data, ext_data_size,
+		   user_data, user_data_size)
+    BitBucket *bbPtr;
+    int32 drop_frame_flag;
+    int32 tc_hrs;
+    int32 tc_min;
+    int32 tc_sec;
+    int32 tc_pict;
+    int32 closed_gop;
+    int32 broken_link;
+    uint8 *ext_data;
+    int32 ext_data_size;
+    uint8 *user_data;
+    int32 user_data_size;
+{
+    int i;
+
+    /* Write gop start code. */
+    Bitio_Write(bbPtr, GOP_START_CODE, 32);
+
+		/* Construct and write timecode. */
+
+    /* Drop frame flag. */
+    if (drop_frame_flag) {
+	Bitio_Write(bbPtr, 0x01, 1);
+    } else {
+	Bitio_Write(bbPtr, 0x00, 1);
+    }
+
+    /* Time code hours. */
+    Bitio_Write(bbPtr, tc_hrs, 5);
+
+    /* Time code minutes. */
+    Bitio_Write(bbPtr, tc_min, 6);
+
+    /* Marker bit. */
+    Bitio_Write(bbPtr, 0x01, 1);
+
+    /* Time code seconds. */
+    Bitio_Write(bbPtr, tc_sec, 6);
+
+    /* Time code pictures. */
+    Bitio_Write(bbPtr, tc_pict, 6);
+
+
+    /* Closed gop flag. */
+    if (closed_gop) {
+	Bitio_Write(bbPtr, 0x01, 1);
+    } else {
+	Bitio_Write(bbPtr, 0x00, 1);
+    }
+
+    /* Broken link flag. */
+    if (broken_link) {
+	Bitio_Write(bbPtr, 0x01, 1);
+    } else {
+	Bitio_Write(bbPtr, 0x00, 1);
+    }
+
+    /* next start code */
+    Bitio_BytePad(bbPtr);
+
+    /* Write ext data if present. */
+
+    if (ext_data != NULL) {
+	Bitio_Write(bbPtr, EXT_START_CODE, 32);
+
+	for (i = 0; i < ext_data_size; i++) {
+	    Bitio_Write(bbPtr, ext_data[i], 8);
+	}
+	Bitio_BytePad(bbPtr);
+    }
+    /* Write user data if present. */
+    if (user_data != NULL) {
+	Bitio_Write(bbPtr, USER_START_CODE, 32);
+
+	for (i = 0; i < user_data_size; i++) {
+	    Bitio_Write(bbPtr, user_data[i], 8);
+	}
+	Bitio_BytePad(bbPtr);
+    }
+}
+
+
+/*===========================================================================*
+ *
+ * Mhead_GenSliceHeader
+ *
+ *	generate slice header with specified attributes
+ *	append result to the specified bitstream
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+Mhead_GenSliceHeader(bbPtr, verticalPos, qscale, extra_info, extra_info_size)
+    BitBucket *bbPtr;
+    uint32 verticalPos;
+    uint32 qscale;
+    uint8 *extra_info;
+    uint32 extra_info_size;
+{
+    int i;
+
+    /* Write slice start code. */
+    Bitio_Write(bbPtr, (SLICE_BASE_CODE + verticalPos), 32);
+
+    /* Quant. scale. */
+    Bitio_Write(bbPtr, qscale, 5);
+    lastQSSet = qscale;
+
+    /* Extra bit slice info. */
+
+    if (extra_info != NULL) {
+	for (i = 0; i < extra_info_size; i++) {
+	    Bitio_Write(bbPtr, 0x01, 1);
+	    Bitio_Write(bbPtr, extra_info[i], 8);
+	}
+    }
+
+    /* extra_bit_slice */
+    Bitio_Write(bbPtr, 0x00, 1);
+}
+
+
+/*===========================================================================*
+ *
+ * Mhead_GenSliceEnder
+ *
+ *	generate slice ender
+ *	append result to the specified bitstream
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+Mhead_GenSliceEnder(bbPtr)
+    BitBucket *bbPtr;
+{
+    Bitio_BytePad(bbPtr);
+}
+
+
+/*===========================================================================*
+ *
+ * Mhead_GenMBHeader
+ *
+ *	generate macroblock header with given attributes
+ *	append result to the specified bitstream
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+Mhead_GenMBHeader(bbPtr, pict_code_type, addr_incr, q_scale,
+		  forw_f_code, back_f_code, horiz_forw_r, vert_forw_r,
+		  horiz_back_r, vert_back_r, motion_forw, m_horiz_forw,
+		  m_vert_forw, motion_back, m_horiz_back, m_vert_back,
+		  mb_pattern, mb_intra)
+    BitBucket *bbPtr;
+    uint32 pict_code_type;
+    uint32 addr_incr;
+    uint32 q_scale;
+    uint32 forw_f_code;
+    uint32 back_f_code;
+    uint32 horiz_forw_r;
+    uint32 vert_forw_r;
+    uint32 horiz_back_r;
+    uint32 vert_back_r;
+    int32 motion_forw;
+    int32 m_horiz_forw;
+    int32 m_vert_forw;
+    int32 motion_back;
+    int32 m_horiz_back;
+    int32 m_vert_back;
+    uint32 mb_pattern;
+    uint32 mb_intra;
+{
+    uint32 mb_quant;
+
+    /* MB escape sequences if necessary. */
+
+#ifdef BLEAH
+if ( addr_incr != 1 )
+    fprintf(stdout, "Creating MB_INCR:  %d\n", addr_incr);
+#endif
+
+    while (addr_incr > 33) {
+	Bitio_Write(bbPtr, 0x008, 11);
+	addr_incr -= 33;
+    }
+
+    /* Generate addr incr code. */
+    GenMBAddrIncr(bbPtr, addr_incr);
+
+    /* Determine mb_quant  (true if change in q scale) */
+    if ((q_scale != lastQSSet) && ((mb_pattern != 0) || (mb_intra == TRUE))) {
+      mb_quant = TRUE;
+      lastQSSet = q_scale;
+    } else {
+      mb_quant = FALSE;
+    }
+
+    /* Generate mb type code. */
+    GenMBType(bbPtr, pict_code_type, mb_quant, motion_forw, motion_back, mb_pattern, mb_intra);
+
+    /* MB quant. */
+    if (mb_quant) {
+	Bitio_Write(bbPtr, q_scale, 5);
+    }
+    /* Forward predictive vector stuff. */
+
+    if (motion_forw) {
+	int forw_f, forw_r_size;
+
+	forw_r_size = forw_f_code - 1;
+	forw_f = 1 << forw_r_size;	/* 1 > 0 */
+	if ((m_horiz_forw > 16*forw_f-1) || (m_horiz_forw < -16*forw_f)) {
+	  fprintf(stderr, "Illegal motion? %d %d\n", m_horiz_forw, 16*forw_f);
+	}
+	if ((m_vert_forw > 16*forw_f-1) || (m_vert_forw < -16*forw_f)) {
+	  fprintf(stderr, "Illegal motion? %d %d\n", m_vert_forw, 16*forw_f);
+	}
+	GenMotionCode(bbPtr, m_horiz_forw);
+
+	if ((forw_f != 1) && (m_horiz_forw != 0)) {
+	    Bitio_Write(bbPtr, horiz_forw_r, forw_r_size);
+	}
+	GenMotionCode(bbPtr, m_vert_forw);
+
+	if ((forw_f != 1) && (m_vert_forw != 0)) {
+	    Bitio_Write(bbPtr, vert_forw_r, forw_r_size);
+	}
+    }
+    /* Back predicted vector stuff. */
+
+    if (motion_back) {
+	int back_f, back_r_size;
+
+	back_r_size = back_f_code - 1;
+	back_f = 1 << back_r_size;	/* 1 > 0 */
+
+	if ((m_horiz_back > 16*back_f-1) || (m_horiz_back < -16*back_f)) {
+	  fprintf(stderr, "Illegal motion? %d %d\n", m_horiz_back, 16*back_f);
+	}
+	if ((m_vert_back > 16*back_f-1) || (m_vert_back < -16*back_f)) {
+	  fprintf(stderr, "Illegal motion? %d %d\n", m_vert_back, 16*back_f);
+	}
+
+	GenMotionCode(bbPtr, m_horiz_back);
+
+	if ((back_f != 1) && (m_horiz_back != 0)) {
+	    Bitio_Write(bbPtr, horiz_back_r, back_r_size);
+	}
+	GenMotionCode(bbPtr, m_vert_back);
+
+	if ((back_f != 1) && (m_vert_back != 0)) {
+	    Bitio_Write(bbPtr, vert_back_r, back_r_size);
+	}
+    }
+    /* MB pattern. */
+
+    if (mb_pattern) {
+	GenBlockPattern(bbPtr, mb_pattern);
+    }
+}
+
+
+/*=====================*
+ * INTERNAL PROCEDURES *
+ *=====================*/
+
+/*===========================================================================*
+ *
+ * GenMBType
+ *
+ *	generate macroblock type with given attributes
+ *	append result to the specified bitstream
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+GenMBType(bbPtr, pict_code_type, mb_quant, motion_forw, motion_back,
+	  mb_pattern, mb_intra)
+    BitBucket *bbPtr;
+    uint32 pict_code_type;
+    uint32 mb_quant;
+    uint32 motion_forw;
+    uint32 motion_back;
+    uint32 mb_pattern;
+    uint32 mb_intra;
+{
+    int code;
+
+    switch (pict_code_type) {
+    case 1:
+	if ((motion_forw != 0) || (motion_back != 0) || (mb_pattern != 0) || (mb_intra != 1)) {
+	    perror("Illegal parameters for macroblock type.");
+	    exit(-1);
+	}
+	if (mb_quant) {
+	    Bitio_Write(bbPtr, 0x1, 2);
+	} else {
+	    Bitio_Write(bbPtr, 0x1, 1);
+	}
+	break;
+
+    case 2:
+	code = 0;
+	if (mb_quant) {
+	    code += 16;
+	}
+	if (motion_forw) {
+	    code += 8;
+	}
+	if (motion_back) {
+	    code += 4;
+	}
+	if (mb_pattern) {
+	    code += 2;
+	}
+	if (mb_intra) {
+	    code += 1;
+	}
+
+	switch (code) {
+	case 1:
+	    Bitio_Write(bbPtr, 0x3, 5);
+	    break;
+	case 2:
+	    Bitio_Write(bbPtr, 0x1, 2);
+	    break;
+	case 8:
+	    Bitio_Write(bbPtr, 0x1, 3);
+	    break;
+	case 10:
+	    Bitio_Write(bbPtr, 0x1, 1);
+	    break;
+	case 17:
+	    Bitio_Write(bbPtr, 0x1, 6);
+	    break;
+	case 18:
+	    Bitio_Write(bbPtr, 0x1, 5);
+	    break;
+	case 26:
+	    Bitio_Write(bbPtr, 0x2, 5);
+	    break;
+	default:
+	    perror("Illegal parameters for macroblock type.");
+	    exit(-1);
+	    break;
+	}
+	break;
+
+    case 3:
+	code = 0;
+	if (mb_quant) {
+	    code += 16;
+	}
+	if (motion_forw) {
+	    code += 8;
+	}
+	if (motion_back) {
+	    code += 4;
+	}
+	if (mb_pattern) {
+	    code += 2;
+	}
+	if (mb_intra) {
+	    code += 1;
+	}
+
+	switch (code) {
+	case 12:
+	    Bitio_Write(bbPtr, 0x2, 2);
+	    break;
+	case 14:
+	    Bitio_Write(bbPtr, 0x3, 2);
+	    break;
+	case 4:
+	    Bitio_Write(bbPtr, 0x2, 3);
+	    break;
+	case 6:
+	    Bitio_Write(bbPtr, 0x3, 3);
+	    break;
+	case 8:
+	    Bitio_Write(bbPtr, 0x2, 4);
+	    break;
+	case 10:
+	    Bitio_Write(bbPtr, 0x3, 4);
+	    break;
+	case 1:
+	    Bitio_Write(bbPtr, 0x3, 5);
+	    break;
+	case 30:
+	    Bitio_Write(bbPtr, 0x2, 5);
+	    break;
+	case 26:
+	    Bitio_Write(bbPtr, 0x3, 6);
+	    break;
+	case 22:
+	    Bitio_Write(bbPtr, 0x2, 6);
+	    break;
+	case 17:
+	    Bitio_Write(bbPtr, 0x1, 6);
+	    break;
+	default:
+	    perror("Illegal parameters for macroblock type.");
+	    exit(-1);
+	    break;
+	}
+	break;
+    }
+}
+
+
+/*===========================================================================*
+ *
+ * GenMotionCode
+ *
+ *	generate motion vector output with given value
+ *	append result to the specified bitstream
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+GenMotionCode(BitBucket * const bbPtr,
+              int32       const vector) {
+
+    uint32 code, num;
+
+    if ((vector < -16) || (vector > 16)) {
+	perror("Motion vector out of range.");
+	fprintf(stderr, "Motion vector out of range:  vector = %d\n", vector);
+	exit(-1);
+    }
+    code = mbMotionVectorTable[vector + 16][0];
+    num = mbMotionVectorTable[vector + 16][1];
+
+    Bitio_Write(bbPtr, code, num);
+}
+
+
+/*===========================================================================*
+ *
+ * GenBlockPattern
+ *
+ *	generate macroblock pattern output
+ *	append result to the specified bitstream
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+GenBlockPattern(bbPtr, mb_pattern)
+    BitBucket *bbPtr;
+    uint32 mb_pattern;
+{
+    uint32 code, num;
+
+    code = mbPatTable[mb_pattern][0];
+    num = mbPatTable[mb_pattern][1];
+
+    Bitio_Write(bbPtr, code, num);
+}
+
+
+/*===========================================================================*
+ *
+ * GenMBAddrIncr
+ *
+ *	generate macroblock address increment output
+ *	append result to the specified bitstream
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+GenMBAddrIncr(bbPtr, addr_incr)
+    BitBucket *bbPtr;
+    uint32 addr_incr;
+{
+    uint32 code;
+    uint32 num;
+
+    code = mbAddrIncrTable[addr_incr][0];
+    num = mbAddrIncrTable[addr_incr][1];
+
+    Bitio_Write(bbPtr, code, num);
+}
+
+
+/*===========================================================================*
+ *
+ * GenPictHead
+ *
+ *	generate picture header with given attributes
+ *	append result to the specified bitstream
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+GenPictHead(bbPtr, temp_ref, code_type, vbv_delay, full_pel_forw_flag,
+	    forw_f_code, full_pel_back_flag, back_f_code, extra_info,
+	    extra_info_size, ext_data, ext_data_size, user_data,
+	    user_data_size)
+    BitBucket *bbPtr;
+    uint32 temp_ref;
+    uint32 code_type;
+    uint32 vbv_delay;
+    int32 full_pel_forw_flag;
+    uint32 forw_f_code;
+    int32 full_pel_back_flag;
+    uint32 back_f_code;
+    uint8 *extra_info;
+    uint32 extra_info_size;
+    uint8 *ext_data;
+    uint32 ext_data_size;
+    uint8 *user_data;
+    uint32 user_data_size;
+{
+    int i;
+
+    /* Write picture start code. */
+    Bitio_Write(bbPtr, PICT_START_CODE, 32);
+
+    /* Temp reference. */
+    Bitio_Write(bbPtr, temp_ref, 10);
+
+    /* Code_type. */
+    if (code_type == 0) {
+	code_type = 1;
+    }
+    Bitio_Write(bbPtr, code_type, 3);
+
+    /* vbv_delay. */
+    vbv_delay = 0xffff;		    /* see page 36 (section 2.4.3.4) */
+    Bitio_Write(bbPtr, vbv_delay, 16);
+
+    if ((code_type == 2) || (code_type == 3)) {
+
+	/* Full pel forw flag. */
+
+	if (full_pel_forw_flag) {
+	    Bitio_Write(bbPtr, 0x01, 1);
+	} else {
+	    Bitio_Write(bbPtr, 0x00, 1);
+	}
+
+	/* Forw f code. */
+
+	Bitio_Write(bbPtr, forw_f_code, 3);
+    }
+    if (code_type == 3) {
+
+	/* Full pel back flag. */
+
+	if (full_pel_back_flag) {
+	    Bitio_Write(bbPtr, 0x01, 1);
+	} else {
+	    Bitio_Write(bbPtr, 0x00, 1);
+	}
+
+	/* Back f code. */
+
+	Bitio_Write(bbPtr, back_f_code, 3);
+    }
+    /* Extra bit picture info. */
+
+    if (extra_info != NULL) {
+	for (i = 0; i < extra_info_size; i++) {
+	    Bitio_Write(bbPtr, 0x01, 1);
+	    Bitio_Write(bbPtr, extra_info[i], 8);
+	}
+    }
+    Bitio_Write(bbPtr, 0x00, 1);
+
+    /* next start code */
+    Bitio_BytePad(bbPtr);
+
+    /* Write ext data if present. */
+
+    if (ext_data != NULL) {
+	Bitio_Write(bbPtr, EXT_START_CODE, 32);
+
+	for (i = 0; i < ext_data_size; i++) {
+	    Bitio_Write(bbPtr, ext_data[i], 8);
+	}
+	Bitio_BytePad(bbPtr);
+    }
+    /* Write user data if present. */
+    if (user_data != NULL) {
+	Bitio_Write(bbPtr, USER_START_CODE, 32);
+
+	for (i = 0; i < user_data_size; i++) {
+	    Bitio_Write(bbPtr, user_data[i], 8);
+	}
+	Bitio_BytePad(bbPtr);
+    }
+}
+
+
+#ifdef UNUSED_PROCEDURES
+
+/* GenMBEnd only used for `D` pictures. Shouldn't really ever be called. */
+/* - dwallach */
+void
+GenMBEnd(bbPtr)
+    BitBucket *bbPtr;
+{
+    Bitio_Write(bbPtr, 0x01, 1);
+}
+
+#endif /* UNUSED_PROCEDURES */
diff --git a/converter/ppm/ppmtompeg/moutput.c b/converter/ppm/ppmtompeg/moutput.c
new file mode 100644
index 00000000..b682efab
--- /dev/null
+++ b/converter/ppm/ppmtompeg/moutput.c
@@ -0,0 +1,442 @@
+/*===========================================================================*
+ * moutput.c								     *
+ *									     *
+ *	Procedures concerned with quantization and RLE			     *
+ *									     *
+ * EXPORTED PROCEDURES:							     *
+ *	mp_quant_zig_block						     *
+ *	mp_rle_huff_block						     *
+ *	mp_rle_huff_pblock						     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*
+ *  $Header: /n/charlie-brown/project/mm/mpeg/mpeg_dist/mpeg_encode/RCS/moutput.c,v 1.12 1995/01/19 23:08:49 eyhung Exp $
+ *  $Log: moutput.c,v $
+ * Revision 1.12  1995/01/19  23:08:49  eyhung
+ * Changed copyrights
+ *
+ * Revision 1.11  1993/07/22  22:23:43  keving
+ * nothing
+ *
+ * Revision 1.10  1993/06/30  20:06:09  keving
+ * nothing
+ *
+ * Revision 1.9  1993/06/03  21:08:08  keving
+ * nothing
+ *
+ * Revision 1.8  1993/02/24  18:57:19  keving
+ * nothing
+ *
+ * Revision 1.7  1993/02/23  22:58:36  keving
+ * nothing
+ *
+ * Revision 1.6  1993/02/23  22:54:56  keving
+ * nothing
+ *
+ * Revision 1.5  1993/02/17  23:18:20  dwallach
+ * checkin prior to keving's joining the project
+ *
+ * Revision 1.4  1993/01/18  10:20:02  dwallach
+ * *** empty log message ***
+ *
+ * Revision 1.3  1993/01/18  10:17:29  dwallach
+ * RCS headers installed, code indented uniformly
+ *
+ * Revision 1.3  1993/01/18  10:17:29  dwallach
+ * RCS headers installed, code indented uniformly
+ *
+ */
+
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "all.h"
+#include "mtypes.h"
+#include "mproto.h"
+#include "huff.h"
+
+
+/*==================*
+ * STATIC VARIABLES *
+ *==================*/
+
+/* ZAG[i] is the natural-order position of the i'th element of zigzag order. */
+static int ZAG[] =
+{
+    0, 1, 8, 16, 9, 2, 3, 10,
+    17, 24, 32, 25, 18, 11, 4, 5,
+    12, 19, 26, 33, 40, 48, 41, 34,
+    27, 20, 13, 6, 7, 14, 21, 28,
+    35, 42, 49, 56, 57, 50, 43, 36,
+    29, 22, 15, 23, 30, 37, 44, 51,
+    58, 59, 52, 45, 38, 31, 39, 46,
+    53, 60, 61, 54, 47, 55, 62, 63
+};
+
+
+/*
+ * possible optimization: reorder the qtable in the correct zigzag order, to
+ * reduce the number of necessary lookups
+ *
+ * this table comes from the MPEG draft, p. D-16, Fig. 2-D.15.
+ */
+static int qtable[] =
+{
+    8, 16, 19, 22, 26, 27, 29, 34,
+    16, 16, 22, 24, 27, 29, 34, 37,
+    19, 22, 26, 27, 29, 34, 34, 38,
+    22, 22, 26, 27, 29, 34, 37, 40,
+    22, 26, 27, 29, 32, 35, 40, 48,
+    26, 27, 29, 32, 35, 40, 48, 58,
+    26, 27, 29, 34, 38, 46, 56, 69,
+    27, 29, 35, 38, 46, 56, 69, 83};
+
+
+/*=====================*
+ * EXPORTED PROCEDURES *
+ *=====================*/
+
+
+void	UnQuantZig(FlatBlock in, Block out, int qscale, boolean iblock)
+{
+    register int index;
+    int	    start;
+    int	    position;
+    register int	    qentry;
+    int	    level, coeff;
+    register int16 temp;
+
+    if ( iblock )
+    {
+	((int16 *)out)[0] = (int16)(in[0]*qtable[0]);
+
+	start = 1;
+    }
+    else
+	start = 0;
+
+    for ( index = start; index < DCTSIZE_SQ; index++ )
+    {
+	position = ZAG[index];
+
+	if (iblock)
+	    qentry = qtable[position] * qscale;
+	else
+	    qentry = 16 * qscale;
+
+	level = in[index];
+        coeff = (level * qentry) >> 3;
+        if (level < 0) {
+            coeff += (coeff & 1);
+	} else {
+            coeff -= (coeff & 1);
+	}
+
+	((int16 *)out)[position] = coeff;
+    }
+
+#ifdef BLEAH
+    for ( index = 0; index < 64; index++ )
+	fprintf(stdout, "DCT[%d] = %d\n", index, 
+		((int16 *)out)[index]);
+#endif
+}
+
+
+/*
+ * --------------------------------------------------------------
+ *
+ * mp_quant_zig_block --
+ *
+ * Quantizes and zigzags a block -- removing information
+ *
+ * Results: TRUE iff resulting 'out' is non-zero, FALSE if all
+ *	    zero
+ *
+ * Side effects: Modifies the out block.
+ *
+ * --------------------------------------------------------------
+ */
+boolean mp_quant_zig_block(Block in, FlatBlock out, int qscale, int iblock)
+{
+    register int i;
+    register int y, x;
+    register int16 temp;
+    register int qentry;
+    int start;
+    boolean nonZero = FALSE;
+
+    DBG_PRINT(("mp_quant_zig_block...\n"));
+    if (iblock) {
+	/*
+	 * the DC coefficient is handled specially -- it's not
+	 * sensitive to qscale, but everything else is
+	 */
+	temp = ((int16 *) in)[ZAG[0]];
+	qentry = qtable[ZAG[0]];
+	if (temp < 0) {
+	    temp = -temp;
+	    temp += qentry >> 1;
+	    temp /= qentry;
+	    temp = -temp;
+	} else {
+	    temp += qentry >> 1;
+	    temp /= qentry;
+	}
+	if ( temp != 0 )
+	    nonZero = TRUE;
+	out[0] = temp;
+	start = 1;
+    } else
+	start = 0;
+
+    for (i = start; i < DCTSIZE_SQ; i++) {
+	x = ZAG[i] % 8;
+	y = ZAG[i] / 8;
+	temp = in[y][x];
+	DBG_PRINT(("    in[%d][%d] = %d;  ", y, x, temp));
+
+	if (iblock)
+	    qentry = qtable[ZAG[i]] * qscale;
+	else
+	    qentry = 16 * qscale;
+
+	DBG_PRINT(("quantized with %d = ", qentry));
+
+	if (temp < 0) {
+	    temp = -temp;
+	    temp *= 8;
+	    temp += qentry >> 1;
+	    temp /= qentry;
+	    temp = -temp;
+	} else {
+	    temp *= 8;
+	    temp += qentry >> 1;
+	    temp /= qentry;
+	}
+	if ( temp != 0 )
+	    nonZero = TRUE;
+	out[i] = temp;
+	DBG_PRINT(("%d\n", temp));
+    }
+
+    return nonZero;
+}
+
+
+
+/*
+ * --------------------------------------------------------------
+ *
+ * mp_rle_huff_block --
+ *
+ * Given a FlatBlock, generates the Huffman bits
+ *
+ * Results: None.
+ *
+ * Side effects: Output bits changed
+ *
+ * --------------------------------------------------------------
+ */
+
+void	mp_rle_huff_block(FlatBlock in, BitBucket *out)
+{
+    register int i;
+    register int nzeros = 0;
+    register int16 cur;
+    register int16 acur;
+    register uint32 code;
+    register int nbits;
+
+    /*
+     * yes, Virginia, we start at 1.  The DC coefficient is handled
+     * specially, elsewhere.  Not here.
+     */
+    for (i = 1; i < DCTSIZE_SQ; i++) {
+	cur = in[i];
+	acur = ABS(cur);
+	if (cur) {
+	    if (nzeros < HUFF_MAXRUN && acur < huff_maxlevel[nzeros]) {
+	        /*
+		 * encode using the Huffman tables
+		 */
+
+		DBG_PRINT(("rle_huff %02d: Run %02d, Level %02d\n", i, nzeros, cur));
+		assert(cur);
+
+		code = (huff_table[nzeros])[acur];
+		nbits = (huff_bits[nzeros])[acur];
+
+		assert(nbits);
+
+		if (cur < 0)
+		    code |= 1;	/* the sign bit */
+		Bitio_Write(out, code, nbits);
+	    } else {
+		/*
+		 * encode using the escape code
+		 */
+		DBG_PRINT(("Escape\n"));
+		Bitio_Write(out, 0x1, 6);	/* ESCAPE */
+		DBG_PRINT(("Run Length\n"));
+		Bitio_Write(out, nzeros, 6);	/* Run-Length */
+
+		assert(cur != 0);
+
+		/*
+	         * this shouldn't happen, but the other
+	         * choice is to bomb out and dump core...
+	         */
+		if (cur < -255)
+		    cur = -255;
+		else if (cur > 255)
+		    cur = 255;
+
+		DBG_PRINT(("Level\n"));
+		if (acur < 128) {
+		    Bitio_Write(out, cur, 8);
+		} else {
+		    if (cur < 0) {
+			Bitio_Write(out, 0x8001 + cur + 255, 16);
+		    } else
+			Bitio_Write(out, cur, 16);
+		}
+	    }
+	    nzeros = 0;
+	} else
+	    nzeros++;
+    }
+    DBG_PRINT(("End of block\n"));
+    Bitio_Write(out, 0x2, 2);	/* end of block marker */
+}
+
+
+/*
+ * --------------------------------------------------------------
+ *
+ * mp_rle_huff_pblock --
+ *
+ * Given a FlatBlock, generates the Huffman bits for P DCT
+ *
+ * Results: None.
+ *
+ * Side effects: Output bits changed
+ *
+ * --------------------------------------------------------------
+ */
+
+void	mp_rle_huff_pblock(FlatBlock in, BitBucket *out)
+{
+    register int i;
+    register int nzeros = 0;
+    register int16 cur;
+    register int16 acur;
+    register uint32 code;
+    register int nbits;
+    boolean first_dct = TRUE;
+
+    /*
+     * yes, Virginia, we start at 0.
+     */
+    for (i = 0; i < DCTSIZE_SQ; i++) {
+	cur = in[i];
+	acur = ABS(cur);
+	if (cur) {
+	    if (nzeros < HUFF_MAXRUN && acur < huff_maxlevel[nzeros]) {
+	        /*
+		 * encode using the Huffman tables
+		 */
+
+		DBG_PRINT(("rle_huff %02d: Run %02d, Level %02d\n", i, nzeros, cur));
+		assert(cur);
+
+		if ( first_dct && (nzeros == 0) && (acur == 1) )
+		{
+		    /* actually, only needs = 0x2 */
+		    code = (cur == 1) ? 0x2 : 0x3;
+		    nbits = 2;
+		}
+		else
+		{
+		    code = (huff_table[nzeros])[acur];
+		    nbits = (huff_bits[nzeros])[acur];
+		}
+
+		assert(nbits);
+
+		if (cur < 0)
+		    code |= 1;	/* the sign bit */
+		Bitio_Write(out, code, nbits);
+		first_dct = FALSE;
+	    } else {
+		/*
+		 * encode using the escape code
+		 */
+		DBG_PRINT(("Escape\n"));
+		Bitio_Write(out, 0x1, 6);	/* ESCAPE */
+		DBG_PRINT(("Run Length\n"));
+		Bitio_Write(out, nzeros, 6);	/* Run-Length */
+
+		assert(cur != 0);
+
+		/*
+	         * this shouldn't happen, but the other
+	         * choice is to bomb out and dump core...
+	         */
+		if (cur < -255)
+		    cur = -255;
+		else if (cur > 255)
+		    cur = 255;
+
+		DBG_PRINT(("Level\n"));
+		if (acur < 128) {
+		    Bitio_Write(out, cur, 8);
+		} else {
+		    if (cur < 0) {
+			Bitio_Write(out, 0x8001 + cur + 255, 16);
+		    } else
+			Bitio_Write(out, cur, 16);
+		}
+
+		first_dct = FALSE;
+	    }
+	    nzeros = 0;
+	} else
+	    nzeros++;
+    }
+
+    /* actually, should REALLY return FALSE and not use this! */
+    if ( first_dct )	/* have to give a first_dct even if all 0's */
+    {
+	fprintf(stdout, "HUFF called with all-zero coefficients\n");
+	fprintf(stdout, "exiting...\n");
+	exit(1);
+    }
+
+    DBG_PRINT(("End of block\n"));
+    Bitio_Write(out, 0x2, 2);	/* end of block marker */
+}
diff --git a/converter/ppm/ppmtompeg/mpeg.c b/converter/ppm/ppmtompeg/mpeg.c
new file mode 100644
index 00000000..a934ed09
--- /dev/null
+++ b/converter/ppm/ppmtompeg/mpeg.c
@@ -0,0 +1,1717 @@
+/*===========================================================================*
+ * mpeg.c
+ *
+ *  Procedures to generate the MPEG sequence
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#define _BSD_SOURCE   /* Make sure strdup() is in string.h */
+
+#include "all.h"
+#include <time.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+#ifdef MIPS
+#include <sys/types.h>
+#endif
+#include <sys/stat.h>
+
+#include "ppm.h"
+#include "nstring.h"
+
+#include "mtypes.h"
+#include "frames.h"
+#include "motion_search.h"
+#include "prototypes.h"
+#include "parallel.h"
+#include "param.h"
+#include "readframe.h"
+#include "fsize.h"
+#include "mheaders.h"
+#include "rate.h"
+#include "input.h"
+#include "frametype.h"
+#include "mpeg.h"
+
+
+/*===========*
+ *  VERSION  *
+ *===========*/
+
+#define VERSION "1.5b"
+
+
+/*===========*
+ * CONSTANTS *
+ *===========*/
+
+#define FPS_30  0x5   /* from MPEG standard sect. 2.4.3.2 */
+#define ASPECT_1    0x1 /* aspect ratio, from MPEG standard sect. 2.4.3.2 */
+
+
+/*==================*
+ * STATIC VARIABLES *
+ *==================*/
+
+static unsigned int framesOutput;
+static int      realStart, realEnd;
+static int  currentGOP;
+static int      timeMask;
+static int      numI, numP, numB;
+static boolean  frameCountsUnknown;
+
+
+/*==================*
+ * GLOBAL VARIABLES *   
+ *==================*/
+
+/* important -- don't initialize anything here */
+/* must be re-initted anyway in GenMPEGStream */
+
+extern int  IOtime;
+extern boolean  resizeFrame;
+extern int outputWidth, outputHeight;
+int     gopSize = 100;  /* default */
+int32       tc_hrs, tc_min, tc_sec, tc_pict, tc_extra;
+int     totalFramesSent;
+int     yuvWidth, yuvHeight;
+int     realWidth, realHeight;
+char        currentPath[MAXPATHLEN];
+char        statFileName[256];
+char        bitRateFileName[256];
+time_t      timeStart, timeEnd;
+FILE       *statFile;
+FILE       *bitRateFile = NULL;
+char       *framePattern;
+int     framePatternLen;
+int     referenceFrame;
+int     frameRate = FPS_30;
+int     frameRateRounded = 30;
+boolean     frameRateInteger = TRUE;
+int     aspectRatio = ASPECT_1;
+extern char userDataFileName[];
+extern int mult_seq_headers;
+
+int32 bit_rate, buf_size;
+
+/*===============================*
+ * INTERNAL PROCEDURE prototypes *
+ *===============================*/
+
+static void ComputeDHMSTime _ANSI_ARGS_((int32 someTime, char *timeText));
+static void OpenBitRateFile _ANSI_ARGS_((void));
+static void CloseBitRateFile _ANSI_ARGS_((void));
+
+
+static void
+ShowRemainingTime(boolean const childProcess) {
+/*----------------------------------------------------------------------------
+   Print out an estimate of the time left to encode
+-----------------------------------------------------------------------------*/
+
+    if (childProcess) {
+        /* nothing */;
+    } else if ( numI + numP + numB == 0 ) {
+        /* no time left */
+    } else if ( timeMask != 0 ) {   
+        /* haven't encoded all types yet */
+    } else {
+        static int  lastTime = 0;
+        float   total;
+        time_t  nowTime;
+        float   secondsPerFrame;
+        
+        time(&nowTime);
+        secondsPerFrame = (nowTime-timeStart)/(float)framesOutput;
+        total = secondsPerFrame*(float)(numI+numP+numB);
+
+        if ((quietTime >= 0) && (!realQuiet) && (!frameCountsUnknown) &&
+            ((lastTime < (int)total) || ((lastTime-(int)total) >= quietTime) ||
+             (lastTime == 0) || (quietTime == 0))) {
+            if (total > 270.0)
+                pm_message("ESTIMATED TIME OF COMPLETION:  %d minutes",
+                           ((int)total+30)/60);
+            else
+                pm_message("ESTIMATED TIME OF COMPLETION:  %d seconds",
+                           (int)total);
+        }
+        lastTime = (int)total;
+    }
+}
+
+
+
+static void
+initTCTime(unsigned int const firstFrameNumber) {
+
+    unsigned int frameNumber;
+    
+    tc_hrs = 0; tc_min = 0; tc_sec = 0; tc_pict = 0; tc_extra = 0;
+    for (frameNumber = 0; frameNumber < firstFrameNumber; ++frameNumber)
+        IncrementTCTime();
+}
+
+
+
+/*===========================================================================*
+ *
+ * IncrementTCTime
+ *
+ *  increment the tc time by one second (and update min, hrs if necessary)
+ *  also increments totalFramesSent
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    totalFramesSent, tc_pict, tc_sec, tc_min, tc_hrs, tc_extra
+ *
+ *===========================================================================*/
+void
+IncrementTCTime() {
+    /* if fps = an integer, then tc_extra = 0 and is ignored
+
+       otherwise, it is the number of extra 1/1001 frames we've passed by
+
+       so far; for example, if fps = 24000/1001, then 24 frames = 24024/24000
+       seconds = 1 second + 24/24000 seconds = 1 + 1/1000 seconds; similary,
+       if fps = 30000/1001, then 30 frames = 30030/30000 = 1 + 1/1000 seconds
+       and if fps = 60000/1001, then 60 frames = 1 + 1/1000 seconds
+
+       if fps = 24000/1001, then 1/1000 seconds = 24/1001 frames
+       if fps = 30000/1001, then 1/1000 seconds = 30/1001 frames
+       if fps = 60000/1001, then 1/1000 seconds = 60/1001 frames     
+     */
+
+    totalFramesSent++;
+    tc_pict++;
+    if ( tc_pict >= frameRateRounded ) {
+        tc_pict = 0;
+        tc_sec++;
+        if ( tc_sec == 60 ) {
+            tc_sec = 0;
+            tc_min++;
+            if ( tc_min == 60 ) {
+                tc_min = 0;
+                tc_hrs++;
+            }
+        }
+        if ( ! frameRateInteger ) {
+            tc_extra += frameRateRounded;
+            if ( tc_extra >= 1001 ) {   /* a frame's worth */
+                tc_pict++;
+                tc_extra -= 1001;
+            }
+        }
+    }
+}
+
+
+
+static void
+initializeRateControl(bool const wantUnderflowWarning,
+                      bool const wantOverflowWarning) {
+/*----------------------------------------------------------------------------
+   Initialize rate control
+-----------------------------------------------------------------------------*/
+    int32 const bitstreamMode = getRateMode();
+
+    if (bitstreamMode == FIXED_RATE) {
+        initRateControl(wantUnderflowWarning, wantOverflowWarning);
+        /*
+          SetFrameRate();
+        */
+    }
+}
+    
+
+
+/*===========================================================================*
+ *
+ * SetReferenceFrameType
+ *
+ *  set the reference frame type to be original or decoded
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    referenceFrame
+ *
+ *===========================================================================*/
+void
+SetReferenceFrameType(const char * const type) {
+
+    if (strcmp(type, "ORIGINAL") == 0)
+        referenceFrame = ORIGINAL_FRAME;
+    else if ( strcmp(type, "DECODED") == 0 )
+        referenceFrame = DECODED_FRAME;
+    else
+        pm_error("INTERNAL ERROR: Illegal reference frame type: '%s'", type);
+}
+
+
+
+void
+SetBitRateFileName(fileName)
+    char *fileName;
+{
+    strcpy(bitRateFileName, fileName);
+}
+
+
+
+
+static void
+finishFrameOutput(MpegFrame * const frameP,
+                  BitBucket * const bbP,
+                  boolean     const separateFiles,
+                  int         const referenceFrame,
+                  boolean     const childProcess,
+                  boolean     const remoteIO) {
+
+    if ((referenceFrame == DECODED_FRAME) && 
+        childProcess && NonLocalRefFrame(frameP->id)) {
+        if (remoteIO)
+            SendDecodedFrame(frameP);
+        else
+            WriteDecodedFrame(frameP);
+            
+        NotifyDecodeServerReady(frameP->id);
+    }
+    
+    if (separateFiles) {
+        if (remoteIO)
+            SendRemoteFrame(frameP->id, bbP);
+        else {
+            Bitio_Flush(bbP);
+            Bitio_Close(bbP);
+        }
+    }
+}
+
+    
+
+
+static void
+outputIFrame(MpegFrame * const frameP,
+             BitBucket * const bb,
+             int         const realStart,
+             int         const realEnd,
+             MpegFrame * const pastRefFrameP,
+             boolean     const separateFiles) {
+      
+    /* only start a new GOP with I */
+    /* don't start GOP if only doing frames */
+    if ((!separateFiles) && (currentGOP >= gopSize)) {
+        boolean const closed = 
+            (totalFramesSent == frameP->id || pastRefFrameP == NULL);
+
+        static int num_gop = 0;
+    
+        /* first, check to see if closed GOP */
+    
+        /* new GOP */
+        if (num_gop != 0 && mult_seq_headers && 
+            num_gop % mult_seq_headers == 0) {
+            if (!realQuiet) {
+                fprintf(stdout, 
+                        "Creating new Sequence before GOP %d\n", num_gop);
+                fflush(stdout);
+            }
+      
+            Mhead_GenSequenceHeader(
+                bb, Fsize_x, Fsize_y,
+                /* pratio */    aspectRatio,
+                /* pict_rate */ frameRate, /* bit_rate */ bit_rate,
+                /* buf_size */  buf_size,  /* c_param_flag */ 1,
+                /* iq_matrix */ customQtable, 
+                /* niq_matrix */ customNIQtable,
+                /* ext_data */ NULL,  /* ext_data_size */ 0,
+                /* user_data */ NULL, /* user_data_size */ 0);
+        }
+    
+        if (!realQuiet)
+            pm_message("Creating new GOP (closed = %s) before frame %d\n",
+                       closed ? "YES" : "NO", frameP->id);
+    
+        ++num_gop;
+        Mhead_GenGOPHeader(bb,  /* drop_frame_flag */ 0,
+                           tc_hrs, tc_min, tc_sec, tc_pict,
+                           closed, /* broken_link */ 0,
+                           /* ext_data */ NULL, /* ext_data_size */ 0,
+                           /* user_data */ NULL, /* user_data_size */ 0);
+        currentGOP -= gopSize;
+        if (pastRefFrameP == NULL)
+            SetGOPStartTime(0);
+        else
+            SetGOPStartTime(pastRefFrameP->id+1);
+    }
+      
+    if ((frameP->id >= realStart) && (frameP->id <= realEnd))
+        GenIFrame(bb, frameP);
+      
+    numI--;
+    timeMask &= 0x6;
+      
+    currentGOP++;
+    IncrementTCTime();
+}
+
+
+
+static void
+outputPFrame(MpegFrame * const frameP,
+             BitBucket * const bbP,
+             int         const realStart,
+             int         const realEnd,
+             MpegFrame * const pastRefFrameP) {
+
+    if ((frameP->id >= realStart) && (frameP->id <= realEnd))
+        GenPFrame(bbP, frameP, pastRefFrameP);
+
+    numP--;
+    timeMask &= 0x5;
+    
+    currentGOP++;
+    IncrementTCTime();
+}
+
+
+
+static BitBucket *
+bitioNew(const char * const outputFileName,
+         unsigned int const frameNumber,
+         boolean      const remoteIO) {
+
+    BitBucket * bbP;
+
+    if (remoteIO)
+        bbP = Bitio_New(NULL);
+    else {
+        const char * fileName;
+
+        asprintfN(&fileName, "%s.frame.%d", outputFileName, frameNumber);
+
+        bbP = Bitio_New_Filename(fileName);
+
+        strfree(fileName);
+    }
+    return bbP;
+}
+
+
+
+static void
+getBFrame(int                  const frameNum,
+          struct inputSource * const inputSourceP,
+          MpegFrame *          const pastRefFrameP,
+          boolean              const childProcess,
+          boolean              const remoteIO,
+          MpegFrame **         const bFramePP,
+          int *                const IOtimeP,
+          unsigned int *       const framesReadP) {
+/*----------------------------------------------------------------------------
+   Get Frame 'frameNum', which is a B frame related to previous reference
+   frame 'pastRefFrameP'.  Return it as *bFramePP.
+
+   We have various ways of getting the frame, corresponding to the
+   multitude of modes in which Ppmtompeg works.
+-----------------------------------------------------------------------------*/
+    if (!inputSourceP->stdinUsed) {
+        time_t tempTimeStart, tempTimeEnd;
+        MpegFrame * bFrameP;
+        bool endOfStream;
+
+        bFrameP = Frame_New(frameNum, 'b');
+
+        time(&tempTimeStart);
+
+        ReadNthFrame(inputSourceP, frameNum, remoteIO, childProcess,
+                     separateConversion, slaveConversion, inputConversion,
+                     bFrameP, &endOfStream);
+
+        assert(!endOfStream);  /* Because it's not a stream */
+
+        time(&tempTimeEnd);
+        *IOtimeP += (tempTimeEnd-tempTimeStart);
+
+        ++(*framesReadP);
+        *bFramePP = bFrameP;
+    } else {
+        /* As the frame input is serial, we can't read the B frame now.
+           Rather, Caller has already read it and chained it to 
+           the previous reference frame.  So we get that copy now.
+        */
+        *bFramePP = pastRefFrameP->next;
+        pastRefFrameP->next = (*bFramePP)->next;  /* unlink from list */
+    }
+}
+
+
+
+static void
+processBFrames(MpegFrame *          const pastRefFrameP,
+               MpegFrame *          const futureRefFrameP,
+               int                  const realStart,
+               int                  const realEnd,
+               struct inputSource * const inputSourceP,
+               boolean              const remoteIo,
+               boolean              const childProcess,
+               int *                const IOtimeP,
+               BitBucket *          const wholeStreamBbP,
+               const char *         const outputFileName,
+               unsigned int *       const framesReadP,
+               unsigned int *       const framesOutputP,
+               int *                const currentGopP) {
+/*----------------------------------------------------------------------------
+   Process the B frames that go between 'pastRefFrameP' and
+   'futureRefFrame' in the movie (but go after 'futureRefFrameP' in the
+   MPEG stream, so reader doesn't have to read ahead).
+
+   Remember that a B frame is one which is described by data in the
+   MPEG stream that describes the frame with respect to a frame somewhere
+   before it, and a frame somewhere after it (i.e. reference frames).
+
+   But do only those B frames whose frame numbers are within the range
+   'realStart' through 'realEnd'.
+-----------------------------------------------------------------------------*/
+    boolean const separateFiles = (wholeStreamBbP == NULL);
+    unsigned int const firstBFrameNum = pastRefFrameP->id + 1;
+
+    int frameNum;
+
+    assert(pastRefFrameP != NULL);
+    assert(futureRefFrameP != NULL);
+    
+    for (frameNum = MAX(realStart, firstBFrameNum); 
+         frameNum < MIN(realEnd, futureRefFrameP->id); 
+         ++frameNum) {
+
+        MpegFrame * bFrame;
+        BitBucket * bbP;
+
+        getBFrame(frameNum, inputSourceP, pastRefFrameP, childProcess, 
+                  remoteIO,
+                  &bFrame, IOtimeP, framesReadP);
+
+        if (separateFiles)
+            bbP = bitioNew(outputFileName, bFrame->id, remoteIO);
+        else
+            bbP = wholeStreamBbP;
+
+        GenBFrame(bbP, bFrame, pastRefFrameP, futureRefFrameP);
+        ++(*framesOutputP);
+
+        if (separateFiles) {
+            if (remoteIO)
+                SendRemoteFrame(bFrame->id, bbP);
+            else {
+                Bitio_Flush(bbP);
+                Bitio_Close(bbP);
+            }
+        }
+
+        /* free this B frame right away */
+        Frame_Free(bFrame);
+
+        numB--;
+        timeMask &= 0x3;
+        ShowRemainingTime(childProcess);
+
+        ++(*currentGopP);
+        IncrementTCTime();
+    }
+}
+
+
+
+static void
+processRefFrame(MpegFrame *    const frameP, 
+                BitBucket *    const bb_arg,
+                int            const realStart,
+                int            const realEnd,
+                MpegFrame *    const pastRefFrameP,
+                boolean        const childProcess,
+                const char *   const outputFileName,
+                unsigned int * const framesReadP,
+                unsigned int * const framesOutputP) {
+/*----------------------------------------------------------------------------
+   Process an I or P frame.  Encode and output it.
+
+   But only if its frame number is within the range 'realStart'
+   through 'realEnd'.
+-----------------------------------------------------------------------------*/
+    if (frameP->id >= realStart && frameP->id <= realEnd) {
+        boolean separateFiles;
+        BitBucket * bb;
+  
+        separateFiles = (bb_arg == NULL);
+  
+        if ( separateFiles )
+            bb = bitioNew(outputFileName, frameP->id, remoteIO);
+        else
+            bb = bb_arg;
+  
+        /* first, output this reference frame */
+        switch (frameP->type) {
+        case TYPE_IFRAME:
+            outputIFrame(frameP, bb, realStart, realEnd, pastRefFrameP, 
+                         separateFiles);
+            break;
+        case TYPE_PFRAME:
+            outputPFrame(frameP, bb, realStart, realEnd, pastRefFrameP);
+            ShowRemainingTime(childProcess);
+            break;
+        default:
+            pm_error("INTERNAL ERROR: non-reference frame passed to "
+                     "ProcessRefFrame()");
+        }  
+        
+        ++(*framesOutputP);
+        
+        finishFrameOutput(frameP, bb, separateFiles, referenceFrame,
+                          childProcess, remoteIO);
+    }
+}
+
+
+
+static void
+countFrames(unsigned int const firstFrame,
+            unsigned int const lastFrame,
+            boolean      const stdinUsed,
+            int *        const numIP,
+            int *        const numPP,
+            int *        const numBP,
+            int *        const timeMaskP,
+            boolean *    const frameCountsUnknownP) {
+/*----------------------------------------------------------------------------
+  Count number of I, P, and B frames
+-----------------------------------------------------------------------------*/
+    unsigned int numI, numP, numB;
+    unsigned int timeMask;
+            
+    numI = 0; numP = 0; numB = 0;
+    timeMask = 0;
+    if (stdinUsed) {
+        numI = numP = numB = MAXINT/4;
+        *frameCountsUnknownP = TRUE;
+    } else {
+        unsigned int i;
+        for (i = firstFrame; i <= lastFrame; ++i) {
+            char const frameType = FType_Type(i);
+            switch(frameType) {
+            case 'i':        numI++;            timeMask |= 0x1;    break;
+            case 'p':        numP++;            timeMask |= 0x2;    break;
+            case 'b':        numB++;            timeMask |= 0x4;    break;
+            }
+        }
+        *frameCountsUnknownP = FALSE;
+    }
+
+    *numIP     = numI;
+    *numPP     = numP;
+    *numBP     = numB;
+    *timeMaskP = timeMask;
+    *frameCountsUnknownP = frameCountsUnknown;
+}
+
+
+
+static void
+readAndSaveFrame(struct inputSource * const inputSourceP,
+                 unsigned int         const frameNumber,
+                 char                 const frameType,
+                 const char *         const inputConversion,
+                 MpegFrame *          const pastRefFrameP,
+                 unsigned int *       const framesReadP,
+                 int *                const ioTimeP,
+                 bool *               const endOfStreamP) {
+/*----------------------------------------------------------------------------
+   Read the next frame from Standard Input and add it to the linked list
+   at *pastRefFrameP.  Assume it is Frame Number 'frameNumber' and is of
+   type 'frameType'.
+
+   Increment *framesReadP.
+   
+   Add the time it took to read it, in seconds, to *iotimeP.
+
+   Iff we can't read because we hit end of file, return
+   *endOfStreamP == TRUE and *framesReadP and *iotimeP untouched.
+-----------------------------------------------------------------------------*/
+    /* This really should be part of ReadNthFrame.  The frame should be chained
+       to the input object, not the past reference frame.
+    */
+       
+    MpegFrame * p;
+    MpegFrame * frameP;
+    time_t ioTimeStart, ioTimeEnd;
+    
+    time(&ioTimeStart);
+
+    frameP = Frame_New(frameNumber, frameType);
+    ReadFrame(frameP, inputSourceP, frameNumber, inputConversion,
+              endOfStreamP);
+
+    if (*endOfStreamP)
+        Frame_Free(frameP);
+    else {
+        ++(*framesReadP);
+    
+        time(&ioTimeEnd);
+        *ioTimeP += (ioTimeEnd - ioTimeStart);
+
+        /* Add the B frame to the end of the queue of B-frames 
+           for later encoding.
+        */
+        assert(pastRefFrameP != NULL);
+        
+        p = pastRefFrameP;
+        while (p->next != NULL)
+            p = p->next;
+        p->next = frameP;
+    }
+}
+
+
+
+static void
+doFirstFrameStuff(enum frameContext const context,
+                  const char *      const userDataFileName,
+                  BitBucket *       const bb,
+                  int               const fsize_x,
+                  int               const fsize_y,
+                  int               const aspectRatio,
+                  int               const frameRate,
+                  int32             const qtable[],
+                  int32             const niqtable[],
+                  unsigned int *    const inputFrameBitsP) {
+/*----------------------------------------------------------------------------
+   Do stuff we have to do after reading the first frame in a sequence
+   of frames requested of GenMPEGStream().
+-----------------------------------------------------------------------------*/
+    *inputFrameBitsP = 24*Fsize_x*Fsize_y;
+    SetBlocksPerSlice();
+          
+    if (context == CONTEXT_WHOLESTREAM) {
+        int32 const bitstreamMode = getRateMode();
+        char * userData;
+        unsigned int userDataSize;
+
+        assert(bb != NULL);
+
+        DBG_PRINT(("Generating sequence header\n"));
+        if (bitstreamMode == FIXED_RATE) {
+            bit_rate = getBitRate();
+            buf_size = getBufferSize();
+        } else {
+            bit_rate = -1;
+            buf_size = -1;
+        }
+        
+        if (strlen(userDataFileName) != 0) {
+            struct stat statbuf;
+            FILE *fp;
+          
+            stat(userDataFileName,&statbuf);
+            userDataSize = statbuf.st_size;
+            userData = malloc(userDataSize);
+            fp = fopen(userDataFileName,"rb");
+            if (fp == NULL) {
+                pm_message("Could not open userdata file '%s'.",
+                           userDataFileName);
+                userData = NULL;
+                userDataSize = 0;
+            } else {
+                size_t bytesRead;
+
+                bytesRead = fread(userData,1,userDataSize,fp);
+                if (bytesRead != userDataSize) {
+                    pm_message("Could not read %d bytes from "
+                               "userdata file '%s'.",
+                               userDataSize,userDataFileName);
+                    userData = NULL;
+                    userDataSize = 0;
+                }
+            }
+        } else { /* Put in our UserData Header */
+            const char * userDataString;
+            time_t now;
+                    
+            time(&now);
+            asprintfN(&userDataString,"MPEG stream encoded by UCB Encoder "
+                      "(mpeg_encode) v%s on %s.",
+                      VERSION, ctime(&now));
+            userData = strdup(userDataString);
+            userDataSize = strlen(userData);
+            strfree(userDataString);
+        }
+        Mhead_GenSequenceHeader(bb, Fsize_x, Fsize_y,
+                                /* pratio */ aspectRatio,
+                                /* pict_rate */ frameRate, 
+                                /* bit_rate */ bit_rate,
+                                /* buf_size */ buf_size,
+                                /*c_param_flag */ 1,
+                                /* iq_matrix */ qtable, 
+                                /* niq_matrix */ niqtable,
+                                /* ext_data */ NULL,
+                                /* ext_data_size */ 0,
+                                /* user_data */ (uint8*) userData,
+                                /* user_data_size */ userDataSize);
+    }
+}
+
+
+
+static void
+getPreviousFrame(unsigned int         const frameStart,
+                 int                  const referenceFrame,
+                 struct inputSource * const inputSourceP,
+                 boolean              const childProcess,
+                 const char *         const slaveConversion,
+                 const char *         const inputConversion,
+                 MpegFrame **         const framePP,
+                 unsigned int *       const framesReadP,
+                 int *                const ioTimeP) {
+
+    /* This needs to be modularized.  It shouldn't issue messages about
+       encoding GOPs and B frames, since it knows nothing about those.
+       It should work for Standard Input too, through a generic Standard
+       Input reader that buffers stuff for backward reading.
+    */
+
+    MpegFrame * frameP;
+    time_t ioTimeStart, ioTimeEnd;
+
+    /* can't find the previous frame interactively */
+    if (inputSourceP->stdinUsed)
+        pm_error("Cannot encode GOP from stdin when "
+                 "first frame is a B-frame.");
+
+    if (frameStart < 1)
+        pm_error("Cannot encode GOP when first frame is a B-frame "
+                 "and is not preceded by anything.");
+
+    /* need to load in previous frame; call it an I frame */
+    frameP = Frame_New(frameStart-1, 'i');
+
+    time(&ioTimeStart);
+
+    if ((referenceFrame == DECODED_FRAME) && childProcess) {
+        WaitForDecodedFrame(frameStart);
+
+        if (remoteIO)
+            GetRemoteDecodedRefFrame(frameP, frameStart - 1);
+        else
+            ReadDecodedRefFrame(frameP, frameStart - 1);
+    } else {
+        bool endOfStream;
+        ReadNthFrame(inputSourceP, frameStart - 1, remoteIO, childProcess,
+                     separateConversion, slaveConversion, inputConversion,
+                     frameP, &endOfStream);
+        assert(!endOfStream);  /* Because Stdin causes failure above */
+    }            
+    ++(*framesReadP);
+    
+    time(&ioTimeEnd);
+    *ioTimeP += (ioTimeEnd-ioTimeStart);
+
+    *framePP = frameP;
+}
+
+
+
+static void
+computeFrameRange(unsigned int         const frameStart,
+                  unsigned int         const frameEnd,
+                  enum frameContext    const context, 
+                  struct inputSource * const inputSourceP,
+                  unsigned int *       const firstFrameP,
+                  unsigned int *       const lastFrameP) {
+
+    switch (context) {
+    case CONTEXT_GOP:
+        *firstFrameP = frameStart;
+        *lastFrameP  = frameEnd;
+        break;
+    case CONTEXT_JUSTFRAMES: {
+        /* if first frame is P or B, need to read in P or I frame before it */
+        if (FType_Type(frameStart) != 'i') {
+            /* can't find the previous frame interactively */
+            if (inputSourceP->stdinUsed)
+                pm_error("Cannot encode frames from Standard Input "
+                         "when first frame is not an I-frame.");
+
+            *firstFrameP = FType_PastRef(frameStart);
+        } else
+            *firstFrameP = frameStart;
+
+        /* if last frame is B, need to read in P or I frame after it */
+        if ((FType_Type(frameEnd) == 'b') && 
+            (frameEnd != inputSourceP->numInputFiles-1)) {
+            /* can't find the next reference frame interactively */
+            if (inputSourceP->stdinUsed)
+                pm_error("Cannot encode frames from Standard Input "
+                         "when last frame is a B-frame.");
+            
+            *lastFrameP = FType_FutureRef(frameEnd);
+        } else
+            *lastFrameP = frameEnd;
+    }
+    break;
+    case CONTEXT_WHOLESTREAM:
+        *firstFrameP = frameStart;
+        *lastFrameP  = frameEnd;
+    }
+}
+
+
+
+static void
+getFrame(MpegFrame **         const framePP,
+         struct inputSource * const inputSourceP,
+         unsigned int         const frameNumber,
+         char                 const frameType,
+         unsigned int         const realStart,
+         unsigned int         const realEnd,
+         int                  const referenceFrame,
+         boolean              const childProcess,
+         boolean              const remoteIo,
+         boolean              const separateConversion,
+         const char *         const slaveConversion,
+         const char *         const inputConversion,
+         unsigned int *       const framesReadP,
+         int *                const ioTimeP) {
+/*----------------------------------------------------------------------------
+   Get frame with number 'frameNumber' as *frameP.
+
+   Increment *framesReadP.
+
+   Add to *ioTimeP the time in seconds we spent reading it.
+
+   Iff we fail to get the frame because the stream ends, return
+   *frameP == NULL, don't increment *framesReadP, and leave
+   *ioTimeP unchanged.
+-----------------------------------------------------------------------------*/
+    time_t ioTimeStart, ioTimeEnd;
+    MpegFrame * frameP;
+    bool endOfStream;
+    
+    time(&ioTimeStart);
+
+    frameP = Frame_New(frameNumber, frameType);
+            
+    if ((referenceFrame == DECODED_FRAME) &&
+        ((frameNumber < realStart) || (frameNumber > realEnd)) ) {
+        WaitForDecodedFrame(frameNumber);
+
+        if (remoteIo)
+            GetRemoteDecodedRefFrame(frameP, frameNumber);
+        else
+            ReadDecodedRefFrame(frameP, frameNumber);
+
+        /* I don't know what this block of code does, so I don't know
+           what endOfStream should be.  Here's a guess:
+        */
+        endOfStream = FALSE;
+    } else
+        ReadNthFrame(inputSourceP, frameNumber, remoteIO, childProcess,
+                     separateConversion, slaveConversion, inputConversion,
+                     frameP, &endOfStream);
+    
+    if (endOfStream) {
+        Frame_Free(frameP);
+        *framePP = NULL;
+    } else {
+        ++(*framesReadP);
+            
+        time(&ioTimeEnd);
+        *ioTimeP += (ioTimeEnd - ioTimeStart);
+
+        *framePP = frameP;
+    }
+}
+
+
+
+static void
+handleBitRate(unsigned int const realEnd,
+              unsigned int const numBits,
+              boolean      const childProcess,
+              boolean      const showBitRatePerFrame) {
+
+    extern void PrintItoIBitRate (int numBits, int frameNum);
+
+    if (FType_Type(realEnd) != 'i')
+        PrintItoIBitRate(numBits, realEnd+1);
+
+    if ((!childProcess) && showBitRatePerFrame)
+        CloseBitRateFile();
+}
+
+
+
+static void
+doAFrame(unsigned int         const frameNumber,
+         struct inputSource * const inputSourceP,
+         enum frameContext    const context, 
+         unsigned int         const frameStart, 
+         unsigned int         const frameEnd, 
+         unsigned int         const realStart,
+         unsigned int         const realEnd,
+         bool                 const childProcess,
+         const char *         const outputFileName,
+         MpegFrame *          const pastRefFrameP,
+         MpegFrame **         const newPastRefFramePP,
+         unsigned int *       const framesReadP,
+         unsigned int *       const framesOutputP,
+         bool *               const firstFrameDoneP,
+         BitBucket *          const bbP,
+         unsigned int *       const inputFrameBitsP,
+         bool *               const endOfStreamP) {
+/*----------------------------------------------------------------------------
+   *endOfStreamP returned means we were unable to do a frame because
+   the input stream has ended.  In that case, none of the other outputs
+   are valid.
+-----------------------------------------------------------------------------*/
+    char const frameType = FType_Type(frameNumber);
+    
+    *endOfStreamP = FALSE;  /* initial assumption */
+
+    if (frameType == 'b') {
+        /* We'll process this non-reference frame later.  If reading
+           from stdin, we read it now and save it.  Otherwise, we can
+           just read it later.
+        */
+        *newPastRefFramePP = pastRefFrameP;
+        if (inputSourceP->stdinUsed) 
+            readAndSaveFrame(inputSourceP,
+                             frameNumber, frameType, inputConversion,
+                             pastRefFrameP, framesReadP, &IOtime,
+                             endOfStreamP);
+    } else {
+        MpegFrame * frameP;
+        
+        getFrame(&frameP, inputSourceP, frameNumber, frameType,
+                 realStart, realEnd, referenceFrame, childProcess,
+                 remoteIO,
+                 separateConversion, slaveConversion, inputConversion,
+                 framesReadP, &IOtime);
+        
+        if (frameP) {
+            *endOfStreamP = FALSE;
+
+            if (!*firstFrameDoneP) {
+                doFirstFrameStuff(context, userDataFileName,
+                                  bbP, Fsize_x, Fsize_y, aspectRatio,
+                                  frameRate, qtable, niqtable, 
+                                  inputFrameBitsP);
+            
+                *firstFrameDoneP = TRUE;
+            }
+            processRefFrame(frameP, bbP, frameStart, frameEnd,
+                            pastRefFrameP, childProcess, outputFileName, 
+                            framesReadP, framesOutputP);
+                
+            if (pastRefFrameP) {
+                processBFrames(pastRefFrameP, frameP, realStart, realEnd,
+                               inputSourceP, remoteIO, childProcess, 
+                               &IOtime, bbP, outputFileName,
+                               framesReadP, framesOutputP, &currentGOP);
+            }
+            if (pastRefFrameP != NULL)
+                Frame_Free(pastRefFrameP);
+        
+            *newPastRefFramePP = frameP;
+        } else
+            *endOfStreamP = TRUE;
+    }
+}
+
+
+
+void
+GenMPEGStream(struct inputSource * const inputSourceP,
+              enum frameContext    const context, 
+              unsigned int         const frameStart, 
+              unsigned int         const frameEnd, 
+              int32                const qtable[], 
+              int32                const niqtable[], 
+              bool                 const childProcess,
+              FILE *               const ofP, 
+              const char *         const outputFileName,
+              bool                 const wantVbvUnderflowWarning,
+              bool                 const wantVbvOverflowWarning,
+              unsigned int *       const inputFrameBitsP,
+              unsigned int *       const totalBitsP) {
+/*----------------------------------------------------------------------------
+   Encode a bunch of frames into an MPEG sequence stream or a part thereof.
+
+   'context' tells what in addition to the frames themselves must go into
+   the stream:
+
+      CONTEXT_JUSTFRAMES:  Nothing but the indicated frames
+      CONTEXT_GOP:         GOP header/trailer stuff to make a single GOP
+                           that contains the indicated frames
+      CONTEXT_WHOLESTREAM: A whole stream consisting of the indicated
+                           frames -- a sequence of whole GOPS, with stream
+                           header/trailer stuff as well.
+
+   'frameStart' and 'frameEnd' are the numbers of the first and last
+   frames we are to encode, except that if the input source is a stream,
+   we stop where the stream ends if that is before 'frameEnd'.
+
+-----------------------------------------------------------------------------*/
+    BitBucket * bbP;
+    unsigned int frameNumber;
+    bool endOfStream;
+    bool firstFrameDone;
+    int numBits;
+    unsigned int firstFrame, lastFrame;
+    /* Frame numbers of the first and last frames we look at.  This
+       could be more than the the frames we actually encode because
+       we may need context (i.e. to encode a B frame, we need the subsequent
+       I or P frame).
+    */
+    unsigned int framesRead;
+        /* Number of frames we have read; for statistical purposes */
+    MpegFrame * pastRefFrameP;
+        /* The frame that will be the past reference frame for the next
+           B or P frame that we put into the stream
+        */
+    if (frameEnd + 1 > inputSourceP->numInputFiles)
+        pm_error("Last frame (number %u) is beyond the end of the stream "
+                 "(%u frames)", frameEnd, inputSourceP->numInputFiles);
+
+    if (context == CONTEXT_WHOLESTREAM &&
+        !inputSourceP->stdinUsed && 
+        FType_Type(inputSourceP->numInputFiles-1) == 'b')
+        pm_message("WARNING:  "
+                   "One or more B-frames at end will not be encoded.  "
+                   "See FORCE_ENCODE_LAST_FRAME parameter file statement.");
+
+    time(&timeStart);
+
+    framesRead = 0;
+
+    ResetIFrameStats();
+    ResetPFrameStats();
+    ResetBFrameStats();
+
+    Fsize_Reset();
+
+    framesOutput = 0;
+
+    if (childProcess && separateConversion)
+        SetFileType(slaveConversion);
+    else
+        SetFileType(inputConversion);
+
+    realStart = frameStart;
+    realEnd   = frameEnd;
+
+    computeFrameRange(frameStart, frameEnd, context, inputSourceP,
+                      &firstFrame, &lastFrame);
+
+    if (context == CONTEXT_GOP && FType_Type(frameStart) == 'b')
+        getPreviousFrame(frameStart, referenceFrame, inputSourceP,
+                         childProcess, slaveConversion, inputConversion,
+                         &pastRefFrameP, &framesRead, &IOtime);
+    else
+        pastRefFrameP = NULL;
+
+    countFrames(firstFrame, lastFrame, inputSourceP->stdinUsed,
+                &numI, &numP, &numB, &timeMask, &frameCountsUnknown);
+
+    if (showBitRatePerFrame)
+        OpenBitRateFile();  /* May modify showBitRatePerFrame */
+
+    if (context == CONTEXT_WHOLESTREAM)
+        bbP = Bitio_New(ofP);
+    else
+        bbP = NULL;
+
+    initTCTime(firstFrame);
+
+    totalFramesSent = firstFrame;
+    currentGOP = gopSize;        /* so first I-frame generates GOP Header */
+
+    initializeRateControl(wantVbvUnderflowWarning, wantVbvOverflowWarning);
+
+    firstFrameDone = FALSE;
+    for (frameNumber = firstFrame, endOfStream = FALSE;
+         frameNumber <= lastFrame && !endOfStream;
+         ++frameNumber) {
+
+        doAFrame(frameNumber, inputSourceP, context, 
+                 frameStart, frameEnd, realStart, realEnd,
+                 childProcess, outputFileName,
+                 pastRefFrameP, &pastRefFrameP,
+                 &framesRead, &framesOutput, &firstFrameDone, bbP,
+                 inputFrameBitsP, &endOfStream);
+    }
+    
+    if (pastRefFrameP != NULL)
+        Frame_Free(pastRefFrameP);
+    
+    /* SEQUENCE END CODE */
+    if (context == CONTEXT_WHOLESTREAM)
+        Mhead_GenSequenceEnder(bbP);
+    
+    if (context == CONTEXT_WHOLESTREAM)
+        numBits = bbP->cumulativeBits;
+    else {
+        /* What should the correct value be?  Most likely 1.  "numBits" is
+           used below, so we need to make sure it's properly initialized 
+           to somthing (anything).  
+        */
+        numBits = 1;
+    }
+
+    if (context != CONTEXT_JUSTFRAMES) {
+        Bitio_Flush(bbP);
+        bbP = NULL;
+        fclose(ofP);
+    }
+    handleBitRate(realEnd, numBits, childProcess, showBitRatePerFrame);
+
+    *totalBitsP  = numBits;
+}
+
+
+
+/*===========================================================================*
+ *
+ * SetStatFileName
+ *
+ *  set the statistics file name
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    statFileName
+ *
+ *===========================================================================*/
+void
+SetStatFileName(const char * const fileName) {
+    strcpy(statFileName, fileName);
+}
+
+
+/*===========================================================================*
+ *
+ * SetGOPSize
+ *
+ *  set the GOP size (frames per GOP)
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    gopSize
+ *
+ *===========================================================================*/
+void
+SetGOPSize(size)
+    int size;
+{
+    gopSize = size;
+}
+
+
+/*===========================================================================*
+ *
+ * PrintStartStats
+ *
+ *  print out the starting statistics (stuff from the param file)
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+PrintStartStats(time_t               const startTime, 
+                bool                 const specificFrames,
+                unsigned int         const firstFrame, 
+                unsigned int         const lastFrame,
+                struct inputSource * const inputSourceP) {
+
+    FILE *fpointer;
+    int i;
+
+    if (statFileName[0] == '\0') {
+        statFile = NULL;
+    } else {
+        statFile = fopen(statFileName, "a");    /* open for appending */
+        if (statFile == NULL) {
+            fprintf(stderr, "ERROR:  Could not open stat file:  %s\n",
+                    statFileName);
+            fprintf(stderr, "        Sending statistics to stdout only.\n");
+            fprintf(stderr, "\n\n");
+        } else if (! realQuiet) {
+            fprintf(stdout, "Appending statistics to file:  %s\n",
+                    statFileName);
+            fprintf(stdout, "\n\n");
+        }
+    }
+    
+    for (i = 0; i < 2; ++i) {
+        if ( ( i == 0 ) && (! realQuiet) ) {
+            fpointer = stdout;
+        } else if ( statFile != NULL ) {
+            fpointer = statFile;
+        } else {
+            continue;
+        }
+
+        fprintf(fpointer, "MPEG ENCODER STATS (%s)\n",VERSION);
+        fprintf(fpointer, "------------------------\n");
+        fprintf(fpointer, "TIME STARTED:  %s", ctime(&startTime));
+        if (getenv("HOST") != NULL)
+            fprintf(fpointer, "MACHINE:  %s\n", getenv("HOST"));
+        else
+            fprintf(fpointer, "MACHINE:  unknown\n");
+
+        if (inputSourceP->stdinUsed)
+            fprintf(fpointer, "INPUT:  stdin\n");
+        else {
+            const char * inputFileName;
+
+            fprintf(fpointer, "INPUT FROM FILES:\n");
+
+            GetNthInputFileName(inputSourceP, 0, &inputFileName);
+            fprintf(fpointer, "FIRST FILE:  %s/%s\n", 
+                    currentPath, inputFileName);
+            strfree(inputFileName);
+            GetNthInputFileName(inputSourceP, inputSourceP->numInputFiles-1, 
+                                &inputFileName);
+            fprintf(fpointer, "LAST FILE:  %s/%s\n", 
+                    currentPath, inputFileName);
+            strfree(inputFileName);
+        }    
+        fprintf(fpointer, "OUTPUT:  %s\n", outputFileName);
+
+        if (resizeFrame)
+            fprintf(fpointer, "RESIZED TO:  %dx%d\n",
+                    outputWidth, outputHeight);
+        fprintf(fpointer, "PATTERN:  %s\n", framePattern);
+        fprintf(fpointer, "GOP_SIZE:  %d\n", gopSize);
+        fprintf(fpointer, "SLICES PER FRAME:  %d\n", slicesPerFrame);
+        if (searchRangeP==searchRangeB)
+            fprintf(fpointer, "RANGE:  +/-%d\n", searchRangeP/2);
+        else fprintf(fpointer, "RANGES:  +/-%d %d\n", 
+                     searchRangeP/2,searchRangeB/2);
+        fprintf(fpointer, "PIXEL SEARCH:  %s\n", 
+                pixelFullSearch ? "FULL" : "HALF");
+        fprintf(fpointer, "PSEARCH:  %s\n", PSearchName());
+        fprintf(fpointer, "BSEARCH:  %s\n", BSearchName());
+        fprintf(fpointer, "QSCALE:  %d %d %d\n", qscaleI, 
+                GetPQScale(), GetBQScale());
+        if (specificsOn) 
+            fprintf(fpointer, "(Except as modified by Specifics file)\n");
+        if ( referenceFrame == DECODED_FRAME ) {
+            fprintf(fpointer, "REFERENCE FRAME:  DECODED\n");
+        } else if ( referenceFrame == ORIGINAL_FRAME ) {
+            fprintf(fpointer, "REFERENCE FRAME:  ORIGINAL\n");
+        } else
+            pm_error("Illegal referenceFrame!!!");
+
+        /*  For new Rate control parameters */
+        if (getRateMode() == FIXED_RATE) {
+            fprintf(fpointer, "PICTURE RATE:  %d\n", frameRateRounded);
+            if (getBitRate() != -1) {
+                fprintf(fpointer, "\nBIT RATE:  %d\n", getBitRate());
+            }
+            if (getBufferSize() != -1) {
+                fprintf(fpointer, "BUFFER SIZE:  %d\n", getBufferSize());
+            }
+        }
+    }
+    if (!realQuiet)
+        fprintf(stdout, "\n\n");
+}
+
+
+
+boolean
+NonLocalRefFrame(int const id) {
+/*----------------------------------------------------------------------------
+   Return TRUE if frame number 'id' might be referenced from a non-local
+   process.  This is a conservative estimate.  We return FALSE iff there
+   is no way based on the information we have that the frame could be
+   referenced by a non-local process.
+-----------------------------------------------------------------------------*/
+    boolean retval;
+
+    int const lastIPid = FType_PastRef(id);
+    
+    /* might be accessed by B-frame */
+    
+    if (lastIPid+1 < realStart)
+        retval = TRUE;
+    else {
+        unsigned int const nextIPid = FType_FutureRef(id);
+        
+        /* if B-frame is out of range, then current frame can be
+           ref'd by it 
+        */
+        
+        /* might be accessed by B-frame */
+        if (nextIPid > realEnd+1)
+            retval = TRUE;
+        
+        /* might be accessed by P-frame */
+        if ((nextIPid > realEnd) && (FType_Type(nextIPid) == 'p'))
+            retval = TRUE;
+    }
+    return retval;
+}
+
+
+ 
+/*===========================================================================*
+ *
+ * SetFrameRate
+ *
+ *  sets global frame rate variables.  value passed is MPEG frame rate code.
+ *
+ * RETURNS: TRUE or FALSE
+ *
+ * SIDE EFFECTS:    frameRateRounded, frameRateInteger
+ *
+ *===========================================================================*/
+void
+SetFrameRate()
+{
+    switch(frameRate) {
+    case 1:
+        frameRateRounded = 24;
+        frameRateInteger = FALSE;
+        break;
+    case 2:
+        frameRateRounded = 24;
+        frameRateInteger = TRUE;
+        break;
+    case 3:
+        frameRateRounded = 25;
+        frameRateInteger = TRUE;
+        break;
+    case 4:
+        frameRateRounded = 30;
+        frameRateInteger = FALSE;
+        break;
+    case 5:
+        frameRateRounded = 30;
+        frameRateInteger = TRUE;
+        break;
+    case 6:
+        frameRateRounded = 50;
+        frameRateInteger = TRUE;
+        break;
+    case 7:
+        frameRateRounded = 60;
+        frameRateInteger = FALSE;
+        break;
+    case 8:
+        frameRateRounded = 60;
+        frameRateInteger = TRUE;
+        break;
+    }
+    printf("frame rate(%d) set to %d\n", frameRate, frameRateRounded);
+}
+
+
+/*=====================*
+ * INTERNAL PROCEDURES *
+ *=====================*/
+
+/*===========================================================================*
+ *
+ * ComputeDHMSTime
+ *
+ *  turn some number of seconds (someTime) into a string which
+ *  summarizes that time according to scale (days, hours, minutes, or
+ *  seconds)
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+ComputeDHMSTime(someTime, timeText)
+    int32 someTime;
+    char *timeText;
+{
+    int     days, hours, mins, secs;
+
+    days = someTime / (24*60*60);
+    someTime -= days*24*60*60;
+    hours = someTime / (60*60);
+    someTime -= hours*60*60;
+    mins = someTime / 60;
+    secs = someTime - mins*60;
+
+    if ( days > 0 ) {
+        sprintf(timeText, "Total time:  %d days and %d hours", days, hours);
+    } else if ( hours > 0 ) {
+        sprintf(timeText, "Total time:  %d hours and %d minutes", hours, mins);
+    } else if ( mins > 0 ) {
+        sprintf(timeText, "Total time:  %d minutes and %d seconds", mins, secs);
+    } else {
+    sprintf(timeText, "Total time:  %d seconds", secs);
+    }
+}
+
+
+
+void
+ComputeGOPFrames(int            const whichGOP, 
+                 unsigned int * const firstFrameP, 
+                 unsigned int * const lastFrameP, 
+                 unsigned int   const numFrames) {
+/*----------------------------------------------------------------------------
+   Figure out which frames are in GOP number 'whichGOP'.
+-----------------------------------------------------------------------------*/
+    unsigned int passedB;
+    unsigned int currGOP;
+    unsigned int gopNum;
+    unsigned int frameNum;
+    unsigned int firstFrame, lastFrame;
+    bool foundGop;
+
+    /* calculate first, last frames of whichGOP GOP */
+
+    gopNum = 0;
+    frameNum = 0;
+    passedB = 0;
+    currGOP = 0;
+    foundGop = FALSE;
+
+    while (!foundGop) {
+        if (frameNum >= numFrames)
+            pm_error("There aren't that many GOPs!");
+
+        if (gopNum == whichGOP) {
+            foundGop = TRUE;
+            firstFrame = frameNum;
+        }           
+
+        /* go past one gop */
+        /* must go past at least one frame */
+        do {
+            currGOP += (1 + passedB);
+
+            ++frameNum;
+
+            passedB = 0;
+            while ((frameNum < numFrames) && (FType_Type(frameNum) == 'b')) {
+                ++frameNum;
+                ++passedB;
+            }
+        } while ((frameNum < numFrames) && 
+                 ((FType_Type(frameNum) != 'i') || (currGOP < gopSize)));
+
+        currGOP -= gopSize;
+
+        if (gopNum == whichGOP)
+            lastFrame = (frameNum - passedB - 1);
+
+        ++gopNum;
+    }
+    *firstFrameP = firstFrame;
+    *lastFrameP  = lastFrame;
+}
+
+
+
+static void
+doEndStats(FILE *       const fpointer,
+           time_t       const startTime,
+           time_t       const endTime,
+           unsigned int const inputFrameBits,
+           unsigned int const totalBits,
+           float        const totalCPU) {
+
+    int32 const diffTime = endTime - startTime;
+
+    char    timeText[256];
+
+    ComputeDHMSTime(diffTime, timeText);
+
+    fprintf(fpointer, "TIME COMPLETED:  %s", ctime(&endTime));
+    fprintf(fpointer, "%s\n\n", timeText);
+        
+    ShowIFrameSummary(inputFrameBits, totalBits, fpointer);
+    ShowPFrameSummary(inputFrameBits, totalBits, fpointer);
+    ShowBFrameSummary(inputFrameBits, totalBits, fpointer);
+
+    fprintf(fpointer, "---------------------------------------------\n");
+    fprintf(fpointer, "Total Compression:  %3d:1     (%9.4f bpp)\n",
+            framesOutput*inputFrameBits/totalBits,
+            24.0*(float)(totalBits)/(float)(framesOutput*inputFrameBits));
+    if (diffTime > 0) {
+        fprintf(fpointer, "Total Frames Per Sec Elapsed:  %f (%ld mps)\n",
+                (float)framesOutput/(float)diffTime,
+                (long)((float)framesOutput * 
+                       (float)inputFrameBits /
+                       (256.0*24.0*(float)diffTime)));
+    } else {
+        fprintf(fpointer, "Total Frames Per Sec Elapsed:  Infinite!\n");
+    }
+    if ( totalCPU == 0.0 ) {
+        fprintf(fpointer, "CPU Time:  NONE!\n");
+    } else {
+        fprintf(fpointer, "Total Frames Per Sec CPU    :  %f (%ld mps)\n",
+                (float)framesOutput/totalCPU,
+                (long)((float)framesOutput *
+                       (float)inputFrameBits/(256.0*24.0*totalCPU)));
+    }
+    fprintf(fpointer, "Total Output Bit Rate (%d fps):  %d bits/sec\n",
+            frameRateRounded, frameRateRounded*totalBits/framesOutput);
+    fprintf(fpointer, "MPEG file created in :  %s\n", outputFileName);
+    fprintf(fpointer, "\n\n");
+        
+    if ( computeMVHist ) {
+        ShowPMVHistogram(fpointer);
+        ShowBBMVHistogram(fpointer);
+        ShowBFMVHistogram(fpointer);
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * PrintEndStats
+ *
+ *  print end statistics (summary, time information)
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+PrintEndStats(time_t       const startTime,
+              time_t       const endTime,
+              unsigned int const inputFrameBits, 
+              unsigned int const totalBits) {
+
+    float   totalCPU;
+
+    totalCPU = 0.0;
+    totalCPU += IFrameTotalTime();
+    totalCPU += PFrameTotalTime();
+    totalCPU += BFrameTotalTime();
+
+    if (!realQuiet) {
+        fprintf(stdout, "\n\n");
+        doEndStats(stdout, startTime, endTime, inputFrameBits,
+                   totalBits, totalCPU);
+    }
+    
+    if (statFile) {
+        doEndStats(statFile, startTime, endTime, inputFrameBits,
+                   totalBits, totalCPU);
+
+        fclose(statFile);
+    }
+}
+
+
+
+void
+ReadDecodedRefFrame(MpegFrame *  const frameP, 
+                    unsigned int const frameNumber) {
+
+    FILE    *fpointer;
+    char    fileName[256];
+    int width, height;
+    register int y;
+
+    width = Fsize_x;
+    height = Fsize_y;
+
+    sprintf(fileName, "%s.decoded.%u", outputFileName, frameNumber);
+    if (! realQuiet) {
+        fprintf(stdout, "reading %s\n", fileName);
+        fflush(stdout);
+    }
+
+    if ((fpointer = fopen(fileName, "rb")) == NULL) {
+        sleep(1);
+        if ((fpointer = fopen(fileName, "rb")) == NULL) {
+            fprintf(stderr, "Cannot open %s\n", fileName);
+            exit(1);
+        }}
+
+    Frame_AllocDecoded(frameP, TRUE);
+    
+    for ( y = 0; y < height; y++ ) {
+        size_t bytesRead;
+
+        bytesRead = fread(frameP->decoded_y[y], 1, width, fpointer);
+        if (bytesRead != width)
+            pm_error("Could not read enough bytes from '%s;", fileName);
+    }
+    
+    for (y = 0; y < (height >> 1); y++) {           /* U */
+        size_t const bytesToRead = width/2;
+        size_t bytesRead;
+
+        bytesRead = fread(frameP->decoded_cb[y], 1, bytesToRead, fpointer);
+        if (bytesRead != bytesToRead)
+            pm_message("Could not read enough bytes from '%s'", fileName);
+    }
+    
+    for (y = 0; y < (height >> 1); y++) {           /* V */
+        size_t const bytesToRead = width/2;
+        size_t bytesRead;
+        bytesRead = fread(frameP->decoded_cr[y], 1, bytesToRead, fpointer);
+        if (bytesRead != bytesToRead)
+            pm_message("Could not read enough bytes from '%s'", fileName);
+    }
+    fclose(fpointer);
+}
+
+
+
+static void
+OpenBitRateFile() {
+    bitRateFile = fopen(bitRateFileName, "w");
+    if ( bitRateFile == NULL ) {
+        pm_message("ERROR:  Could not open bit rate file:  '%s'", 
+                   bitRateFileName);
+        showBitRatePerFrame = FALSE;
+    }
+}
+
+
+
+static void
+CloseBitRateFile() {
+    fclose(bitRateFile);
+}
diff --git a/converter/ppm/ppmtompeg/mquant.c b/converter/ppm/ppmtompeg/mquant.c
new file mode 100644
index 00000000..1f8ca63a
--- /dev/null
+++ b/converter/ppm/ppmtompeg/mquant.c
@@ -0,0 +1,44 @@
+#include "mtypes.h"
+#include "mproto.h"
+
+static int qtable[][8] = {
+    { 8,16,19,22,26,27,29,34},
+    {16,16,22,24,27,29,34,37},
+    {19,22,26,27,29,34,34,38},
+    {22,22,26,27,29,34,37,40},
+    {22,26,27,29,32,35,40,48},
+    {26,27,29,32,35,40,48,58},
+    {26,27,29,34,38,46,56,69},
+    {27,29,35,38,46,56,69,83} };
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * mp_quant_block --
+ *
+ *	Quantizes a block -- removing information
+ *	It's safe for out == in.
+ *
+ * Results:
+ *	None.
+ *
+ * Side effects:
+ *	Modifies the out block.
+ *
+ *--------------------------------------------------------------
+ */
+void mp_quant_block(Block in, Block out) {
+    int i;
+
+    for(i=0;i<8;i++) {
+	out[i][0] = in[i][0] / qtable[i][0];
+	out[i][1] = in[i][1] / qtable[i][1];
+	out[i][2] = in[i][2] / qtable[i][2];
+	out[i][3] = in[i][3] / qtable[i][3];
+	out[i][4] = in[i][4] / qtable[i][4];
+	out[i][5] = in[i][5] / qtable[i][5];
+	out[i][6] = in[i][6] / qtable[i][6];
+	out[i][7] = in[i][7] / qtable[i][7];
+    }
+}
diff --git a/converter/ppm/ppmtompeg/nojpeg.c b/converter/ppm/ppmtompeg/nojpeg.c
new file mode 100644
index 00000000..38c05a9e
--- /dev/null
+++ b/converter/ppm/ppmtompeg/nojpeg.c
@@ -0,0 +1,61 @@
+/*===========================================================================*
+ * nojpeg.c								     *
+ *									     *
+ *	procedures to deal with JPEG files				     *
+ *									     *
+ * EXPORTED PROCEDURES:							     *
+ *	JMovie2JPEG							     *
+ *      ReadJPEG							     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "all.h"
+#include "mtypes.h"
+#include "frames.h"
+#include "prototypes.h"
+#include "param.h"
+#include "readframe.h"
+#include "fsize.h"
+#include "rgbtoycc.h"
+#include "jpeg.h"
+
+
+void
+JMovie2JPEG(const char * const infilename,
+            const char * const obase,
+            int          const start,
+            int          const end) {
+
+    pm_error("This program has not been built with the "
+             "ability to handle JPEG input files");
+}
+
+
+void
+ReadJPEG(MpegFrame * const mf,
+         FILE *      const fp) {
+
+    pm_error("This program has not been built with the "
+             "ability to handle JPEG input files");
+}
diff --git a/converter/ppm/ppmtompeg/noparallel.c b/converter/ppm/ppmtompeg/noparallel.c
new file mode 100644
index 00000000..016e3c44
--- /dev/null
+++ b/converter/ppm/ppmtompeg/noparallel.c
@@ -0,0 +1,229 @@
+/*===========================================================================*
+ *  noparallel.c
+ *
+ *  Would be procedures to make encoder to run in parallel -- except
+ *  this machine doesn't have sockets, so we can only run sequentially
+ *  so this file has dummy procedures which lets it compile
+ *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include <time.h>
+
+#include <pm.h>
+
+#include "all.h"
+#include "mtypes.h"
+#include "parallel.h"
+#include "frame.h"
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+int parallelTestFrames = 10;
+int parallelTimeChunks = 60;
+const char *IOhostName;
+int ioPortNumber;
+int combinePortNumber;
+int decodePortNumber;
+boolean niceProcesses = FALSE;
+boolean forceIalign = FALSE;
+int     machineNumber = -1;
+boolean remoteIO = FALSE;
+boolean separateConversion;
+time_t  IOtime = 0;
+
+
+/*=====================*
+ * EXPORTED PROCEDURES *
+ *=====================*/
+
+/*=================*
+ * IO SERVER STUFF *
+ *=================*/
+
+
+void
+IoServer(struct inputSource * const inputSourceP,
+         const char *         const parallelHostName, 
+         int                  const portNum) {
+
+    pm_error("This version of Ppmtompeg cannot run an I/O server because "
+             "it does not have socket capability.");
+}
+
+
+
+void
+SetIOConvert(boolean const separate) {
+    /* do nothing -- this may be called during non-parallel execution */
+}
+
+
+
+void
+SetParallelPerfect(boolean const val) {
+    /* do nothing -- this may be called during non-parallel execution */
+}
+
+
+void
+SetRemoteShell(const char * const shell) {
+    /* do nothing -- this may be called during non-parallel execution */
+}
+
+
+
+void
+NoteFrameDone(int const frameStart,
+              int const frameEnd) {
+    fprintf(stdout, 
+            "ERROR:  (NoteFrameDone) "
+            "This machine can NOT run parallel version\n");
+    exit(1);
+}
+
+
+
+/* SendRemoteFrame
+ */
+void
+SendRemoteFrame(int         const frameNumber,
+                BitBucket * const bb) {
+    fprintf(stdout, "ERROR:  (SendRemoteFrame) "
+            "This machine can NOT run parallel version\n");
+    exit(1);
+}
+
+
+
+/* GetRemoteFrame
+ */
+void
+GetRemoteFrame(MpegFrame * const frame,
+               int         const frameNumber) {
+
+    fprintf(stdout, "ERROR:  (GetRemoteFrame) "
+            "This machine can NOT run parallel version\n");
+    exit(1);
+}
+
+
+
+void
+WaitForOutputFile(int const number) {
+    fprintf(stdout, "ERROR:  (WaitForOutputFile) "
+            "This machine can NOT run parallel version\n");
+    exit(1);
+}
+
+
+
+/*=======================*
+ * PARALLEL SERVER STUFF *
+ *=======================*/
+
+
+void
+MasterServer(struct inputSource * const inputSourceP,
+             const char *         const paramFileName, 
+             const char *         const outputFileName) {
+
+    pm_error("This version of Ppmtompeg cannot run a master server because "
+             "it does not have socket capability.");
+}
+
+
+
+void
+CombineServer(int          const numFrames, 
+              const char * const masterHostName, 
+              int          const masterPortNum) {
+
+    pm_error("This version of Ppmtompeg cannot run combine server because "
+             "it does not have socket capability.");
+}
+
+
+
+void
+DecodeServer(int          const numInputFiles, 
+             const char * const decodeFileName, 
+             const char * const masterHostName, 
+             int          const masterPortNum) {
+
+    pm_error("This version of Ppmtompeg cannot run a decode server because "
+             "it does not have socket capability.");
+}
+
+
+
+void
+NotifyMasterDone(const char * const hostName, 
+                 int          const portNum, 
+                 int          const machineNumber, 
+                 unsigned int const seconds,
+                 boolean *    const moreWorkToDoP,
+                 int *        const frameStartP,
+                 int *        const frameEndP) {
+    pm_error("This version of Ppmtompeg cannot run parallel mode because "
+             "it does not have socket capability.");
+}
+
+
+
+void
+NotifyDecodeServerReady(int const id) {
+    pm_error("This version of Ppmtompeg cannot run parallel mode because "
+             "it does not have socket capability.");
+}
+
+
+
+void
+WaitForDecodedFrame(int const id) {
+    pm_error("This version of Ppmtompeg cannot run parallel mode because "
+             "it does not have socket capability.");
+}
+
+
+
+void
+SendDecodedFrame(MpegFrame * const frame) {
+    pm_error("This version of Ppmtompeg cannot run parallel mode because "
+             "it does not have socket capability.");
+}
+
+
+
+void
+GetRemoteDecodedRefFrame(MpegFrame * const frame,
+                         int         const frameNumber) {
+    pm_error("This version of Ppmtompeg cannot run parallel mode because "
+             "it does not have socket capability.");
+}
diff --git a/converter/ppm/ppmtompeg/opts.c b/converter/ppm/ppmtompeg/opts.c
new file mode 100644
index 00000000..c4d0c0b3
--- /dev/null
+++ b/converter/ppm/ppmtompeg/opts.c
@@ -0,0 +1,536 @@
+/*===========================================================================*
+ * opts.c								     *
+ *									     *
+ *      Special C code to handle TUNEing options                             *
+ *									     *
+ * EXPORTED PROCEDURES:							     *
+ *      Tune_Init                                                            *
+ *      CollectQuantStats                                                    *
+ *									     *
+ *===========================================================================*/
+
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ * 
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ * 
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include "opts.h"
+
+/*==============*
+ * EXTERNALS    *
+ *==============*/
+
+extern char    outputFileName[];
+extern boolean pureDCT;
+extern int32   qtable[], niqtable[];
+extern int     ZAG[];
+extern boolean printSNR, decodeRefFrames;
+
+void init_idctref _ANSI_ARGS_((void));
+void init_fdct _ANSI_ARGS_((void));
+
+
+/*===================*
+ * GLOBALS MADE HERE *
+ *===================*/
+
+boolean tuneingOn = FALSE;
+int block_bound = 128;
+boolean collect_quant = FALSE;
+int collect_quant_detailed = 0;
+FILE *collect_quant_fp;
+int kill_dim = FALSE;
+int kill_dim_break, kill_dim_end;
+float kill_dim_slope;
+int SearchCompareMode = DEFAULT_SEARCH;
+boolean squash_small_differences = FALSE;
+int SquashMaxLum, SquashMaxChr;
+float LocalDCTRateScale = 1.0, LocalDCTDistortScale = 1.0;
+boolean IntraPBAllowed = TRUE;
+boolean WriteDistortionNumbers = FALSE;
+int collect_distortion_detailed = 0;
+FILE *distortion_fp;
+FILE *fp_table_rate[31], *fp_table_dist[31];
+boolean DoLaplace = FALSE;
+double **L1, **L2, **Lambdas;
+int LaplaceNum, LaplaceCnum;
+boolean BSkipBlocks = TRUE;
+
+/* define this as it too much of a pain to find toupper on different arch'es */
+#define ASCII_TOUPPER(c) ((c>='a') && (c<='z')) ? c-'a'+'A' : c
+
+/*===============*
+ * Internals     *
+ *===============*/
+
+/*===========================================================================*
+ *
+ * SkipSpacesTabs
+ *
+ *	skip all spaces and tabs
+ *
+ * RETURNS:	point to next character not a space or tab
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static const char *
+SkipSpacesTabs(const char * const start) {
+
+    const char * p;
+
+    for (p = start; *p == ' ' || *p == '\t'; ++p);
+
+    return p;
+}
+
+
+
+/*===========================================================================*
+ *
+ * SetupCollectQuantStats
+ *
+ *     Setup variables to collect statistics on quantization values
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    sets collect_quant and collect_quant_fp
+ *
+ *===========================================================================*/
+static void 
+SetupCollectQuantStats(const char * const charPtr)
+{
+  char fname[256];
+  const char * cp;
+  cp = charPtr;
+  while ( (*cp != ' ') && (*cp != '\t') && (*cp != '\n')) {
+    cp++;
+  }
+
+  strncpy(fname, charPtr, cp-charPtr);
+  fname[cp-charPtr] = '\0';
+  collect_quant = TRUE;
+  if ((collect_quant_fp = fopen(fname,"w")) == NULL) {
+    fprintf(stderr, "Error opening %s for quant statistics\n", fname);
+    fprintf(stderr, "Using stdout (ick!)\n");
+    collect_quant_fp = stdout;
+  }
+
+  cp = SkipSpacesTabs(cp);
+  if (*cp != '\n') {
+    switch (*cp) {
+    case 'c':
+      collect_quant_detailed = 1;
+      break;
+    default:
+      fprintf(stderr, "Unknown TUNE parameter setting format %s\n", cp);
+    }}
+}
+
+
+
+
+/*===========================================================================*
+ *
+ * SetupKillDimAreas
+ *
+ *     Do a transform on small lum values
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    sets kill_dim, kill_dim_break, kill_dim_end
+ *
+ *===========================================================================*/
+static void 
+SetupKillDimAreas(const char * const charPtr)
+{
+  int items_scanned;
+
+  kill_dim = TRUE;
+  items_scanned = sscanf(charPtr, "%d %d %f", 
+			 &kill_dim_break, &kill_dim_end, &kill_dim_slope);
+  if (items_scanned != 3) {
+    kill_dim_slope = 0.25;
+    items_scanned = sscanf(charPtr, "%d %d", 
+			   &kill_dim_break, &kill_dim_end);
+    if (items_scanned != 2) {
+      /* Use defaults */
+      kill_dim_break = 20;
+      kill_dim_end   = 25;
+    }
+  }
+  /* check values */
+  if (kill_dim_break > kill_dim_end) {
+    fprintf(stderr, "TUNE parameter k: break > end is illegal.\n");
+    exit(-1);
+  }
+  if (kill_dim_slope < 0) {
+    fprintf(stderr, "TUNE parameter k: slope < 0 is illegal.\n");
+    exit(-1);
+  }
+}
+
+
+
+/*===========================================================================*
+ *
+ * SetupSquashSmall
+ *
+ *     Setup encoder to squash small changes in Y or Cr/Cb values
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    sets squash_max_differences SquashMaxLum SquashMaxChr 
+ *
+ *===========================================================================*/
+static void 
+SetupSquashSmall(const char * const charPtr)
+{
+  squash_small_differences = TRUE;
+
+  if (sscanf(charPtr, "%d %d", &SquashMaxLum, &SquashMaxChr) == 1) {
+    /* Only set one, do both */
+    SquashMaxChr = SquashMaxLum;
+  }
+}
+
+
+/*===========================================================================*
+ *
+ * SetupLocalDCT
+ *
+ *     Setup encoder to use DCT for rate-distortion estimat ein Psearches
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    sets SearchCompareMode and
+ *                        can change LocalDCTRateScale, LocalDCTDistortScale
+ *
+ *===========================================================================*/
+static void 
+SetupLocalDCT(const char * const charPtr)
+{
+  int num_scales=0;
+
+  SearchCompareMode = LOCAL_DCT;
+
+  /* Set scaling factors if present */
+  num_scales = sscanf(charPtr, "%f %f", &LocalDCTRateScale, &LocalDCTDistortScale);
+  if (num_scales == 1) {
+    fprintf(stderr, "Invalid number of scaling factors for local DCT\n");
+    fprintf(stderr, "Must specify Rate Scale and Distorion scale (both floats)\n");
+    fprintf(stderr, "Continuing with 1.0 1.0\n");
+    LocalDCTRateScale = 1.0;
+    LocalDCTDistortScale = 1.0;
+  }
+}
+
+
+/*===========================================================================*
+ *
+ * SetupLaplace
+ *
+ *     Setup encoder to find distrubution for I-frames, and use for -snr
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    sets DoLaplace, L1, L2, and Lambdas
+ *
+ *===========================================================================*/
+static void 
+SetupLaplace()
+{
+  int i;
+
+  DoLaplace = TRUE;
+  LaplaceNum = 0;
+  L1 = (double **)malloc(sizeof(double *)*3);
+  L2 = (double **)malloc(sizeof(double *)*3);
+  Lambdas = (double **)malloc(sizeof(double *)*3);
+  if (L1 == NULL || L2 == NULL || Lambdas == NULL) {
+    fprintf(stderr,"Out of memory!!!\n");
+    exit(1);
+  }
+  for (i = 0; i < 3; i++) {
+    L1[i] = (double *)calloc(64, sizeof(double));
+    L2[i] = (double *)calloc(64, sizeof(double));
+    Lambdas[i] = (double *)malloc(sizeof(double) * 64);
+    if (L1[i] == NULL || L2[i] == NULL || Lambdas[i] == NULL) {
+      fprintf(stderr,"Out of memory!!!\n");
+      exit(1);
+    }
+  }
+}
+
+static void
+SetupWriteDistortions(const char * const charPtr)
+{
+  char fname[256];
+  const char * cp;
+  int i;
+
+  WriteDistortionNumbers = TRUE;
+  cp = charPtr;
+  while ( (*cp != ' ') && (*cp != '\t') && (*cp != '\n')) {
+    cp++;
+  }
+
+  strncpy(fname, charPtr, cp-charPtr);
+  fname[cp-charPtr] = '\0';
+  collect_quant = TRUE;
+  if ((distortion_fp = fopen(fname,"w")) == NULL) {
+    fprintf(stderr, "Error opening %s for quant statistics\n", fname);
+    fprintf(stderr, "Using stdout (ick!)\n");
+    distortion_fp = stdout;
+  }
+
+  cp = SkipSpacesTabs(cp);
+  if (*cp != '\n') {
+    switch (*cp) {
+    case 'c':
+      collect_distortion_detailed = TRUE;
+      break;
+    case 't': {
+      char scratch[256];
+      collect_distortion_detailed = 2;
+      for (i = 1;  i < 32;  i++) {
+	sprintf(scratch, "%srate%d", fname, i);
+	fp_table_rate[i-1] = fopen(scratch, "w");
+	sprintf(scratch, "%sdist%d", fname, i);
+	fp_table_dist[i-1] = fopen(scratch, "w");
+	}}
+      break;
+    default:
+      fprintf(stderr, "Unknown TUNE parameter setting format %s\n", cp);
+    }}
+}  
+
+/*=====================*
+ * EXPORTED PROCEDURES *
+ *=====================*/
+
+void 
+CalcLambdas(void) {
+
+  int i,j,n;
+  double var;
+  
+  n = LaplaceNum;
+  for (i = 0;   i < 3;  i++) {
+    for (j = 0;  j < 64;  j++) {
+      var = (n*L1[i][j] + L2[i][j]*L2[i][j]) / (n*(n-1));
+      Lambdas[i][j] = sqrt(2.0) / sqrt(var);
+    }
+  }
+}
+
+
+/*===========================================================================*
+ *
+ * Mpost_UnQuantZigBlockLaplace
+ *
+ *	unquantize and zig-zag (decode) a single block, using the distrib to get vals
+ *      Iblocks only now
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+Mpost_UnQuantZigBlockLaplace(in, out, qscale, iblock)
+    FlatBlock in;
+    Block out;
+    int qscale;
+    boolean iblock;
+{
+    register int index;
+    int	    position;
+    register int	    qentry;
+    int	    level, coeff;
+    double low, high;
+    double mid,lam;
+
+    /* qtable[0] must be 8 */
+    out[0][0] = (int16)(in[0] * 8);
+    
+    for ( index = 1;  index < DCTSIZE_SQ;  index++ ) {
+      position = ZAG[index];
+      level = in[index];
+      
+      if (level == 0) {
+	((int16 *)out)[position] = 0;
+	continue;
+      }
+      qentry = qtable[position] * qscale;
+      coeff = (level*qentry)/8;
+      low = ((ABS(level)-.5)*qentry)/8;
+      high = ((ABS(level)+.5)*qentry)/8;
+      lam = Lambdas[LaplaceCnum][position];
+      mid = (1.0/lam) * log(0.5*(exp(-lam*low)+exp(-lam*high)));
+      mid = ABS(mid);
+      if (mid - floor(mid) > .4999) {
+	mid = ceil(mid);
+      } else {
+	mid = floor(mid);
+      }
+      if (level<0) {mid = -mid;}
+/*printf("(%2.1lf-%2.1lf): old: %d vs %d\n",low,high,coeff,(int) mid);*/
+      coeff = mid;
+      if ( (coeff & 1) == 0 ) {
+	if ( coeff < 0 ) {
+	  coeff++;
+	} else if ( coeff > 0 ) {
+	  coeff--;
+	}
+      }
+      ((int16 *)out)[position] = coeff;
+    }
+}
+
+int 
+mse(Block blk1, Block blk2)
+{
+  register int index, error, tmp;
+  int16 *bp1, *bp2;
+
+  bp1 = (int16 *)blk1;
+  bp2 = (int16 *)blk2;
+  error = 0;
+  for ( index = 0;  index < DCTSIZE_SQ;  index++ ) {
+    tmp = *bp1++ - *bp2++;
+    error += tmp*tmp;
+  }
+  return error;
+}
+
+
+
+
+/*===========================================================================*
+ *
+ * Tune_Init
+ *
+ *     Do any setup needed before coding stream
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:  varies
+ *
+ *===========================================================================*/
+void Tune_Init()
+{
+  int i;
+
+  /* Just check for each, and do whats needed */
+  if (collect_quant) {
+    if (!pureDCT) {
+      pureDCT = TRUE;
+      init_idctref();
+      init_fdct();
+    }
+    fprintf(collect_quant_fp, "# %s\n", outputFileName);
+    fprintf(collect_quant_fp, "#");
+    for (i=0; i<64; i++) 
+      fprintf(collect_quant_fp, " %d", qtable[i]);
+    fprintf(collect_quant_fp, "\n#");
+    for (i=0; i<64; i++) 
+      fprintf(collect_quant_fp, " %d", niqtable[i]);
+    fprintf(collect_quant_fp, "\n# %d %d %d\n\n", 
+	    GetIQScale(), GetPQScale(), GetBQScale());
+    
+  }
+
+  if (DoLaplace) {
+    if (!pureDCT) {
+      pureDCT = TRUE;
+      init_idctref();
+      init_fdct();
+    }
+    decodeRefFrames = TRUE;
+    printSNR = TRUE;
+  }
+    
+}
+
+/*===========================================================================*
+ *
+ * ParseTuneParam
+ *
+ *     Handle the strings following TUNE
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:  varies
+ *
+ *===========================================================================*/
+void ParseTuneParam(const char * const charPtr)
+{
+  switch (ASCII_TOUPPER(*charPtr)) {
+  case 'B': 
+    if (1 != sscanf(charPtr+2, "%d", &block_bound)) {
+      fprintf(stderr, "Invalid tuning parameter (b) in parameter file.\n");
+    }
+    break;
+  case 'C':
+    SetupCollectQuantStats(charPtr+2);
+    break;
+  case 'D':
+    SetupLocalDCT(SkipSpacesTabs(charPtr+1));
+    break;
+  case 'K':
+    SetupKillDimAreas(SkipSpacesTabs(charPtr+1));
+    break;
+  case 'L':
+    SetupLaplace();
+    break;
+  case 'N':
+    SearchCompareMode = NO_DC_SEARCH;
+    break;
+  case 'Q':
+    SearchCompareMode = DO_Mean_Squared_Distortion;
+    break;
+  case 'S':
+    SetupSquashSmall(SkipSpacesTabs(charPtr+1));
+    break;
+  case 'W':
+    SetupWriteDistortions(SkipSpacesTabs(charPtr+1));
+    break;
+  case 'U':
+    BSkipBlocks = FALSE;
+    break;
+  case 'Z':
+     IntraPBAllowed = FALSE;
+    break;
+  default:
+    fprintf(stderr, "Unknown tuning (%s) in parameter file.\n",charPtr);
+    break;
+  }
+}
+
+
diff --git a/converter/ppm/ppmtompeg/parallel.c b/converter/ppm/ppmtompeg/parallel.c
new file mode 100644
index 00000000..021e6d2b
--- /dev/null
+++ b/converter/ppm/ppmtompeg/parallel.c
@@ -0,0 +1,2278 @@
+/*===========================================================================*
+ * parallel.c              
+ *                         
+ *  Procedures to make encoder run in parallel   
+ *                             
+ *===========================================================================*/
+
+/* COPYRIGHT INFORMATION IS AT THE END OF THIS FILE */
+
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#define _XOPEN_SOURCE 500 /* Make sure stdio.h contains pclose() */
+/* _ALL_SOURCE is needed on AIX to make the C library include the 
+   socket services (e.g. define struct sockaddr) 
+
+   Note that AIX standards.h actually sets feature declaration macros such
+   as _XOPEN_SOURCE, unless they are already set.
+*/
+#define _ALL_SOURCE
+
+/* On AIX, pm_config.h includes standards.h, which expects to be included
+   after feature declaration macros such as _XOPEN_SOURCE.  So we include
+   pm_config.h as late as possible.
+*/
+
+
+#include <stdarg.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <netdb.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/times.h>
+
+#include "mallocvar.h"
+#include "nstring.h"
+
+#include "pm.h"
+
+#include "all.h"
+#include "param.h"
+#include "mpeg.h"
+#include "prototypes.h"
+#include "readframe.h"
+#include "fsize.h"
+#include "combine.h"
+#include "frames.h"
+#include "input.h"
+#include "psocket.h"
+#include "frametype.h"
+#include "gethostname.h"
+
+#include "parallel.h"
+
+
+struct childState {
+    boolean      finished;
+    unsigned int startFrame;
+    unsigned int numFrames;
+    unsigned int lastNumFrames;
+    unsigned int numSeconds;
+    float        fps;
+};
+
+
+struct scheduler {
+    /* This tracks the state of the subsystem that determines the assignments
+       for the children
+    */
+    unsigned int nextFrame;
+        /* The next frame that needs to be assigned to a child */
+    unsigned int numFramesInJob;
+        /* Total number of frames in the whole run of Ppmtompeg */
+    unsigned int numMachines;
+};
+
+
+
+#define MAX_IO_SERVERS  10
+#ifndef SOMAXCONN
+#define SOMAXCONN 5
+#endif
+
+/*==================*
+ * CONSTANTS        *
+ *==================*/
+
+#define TERMINATE_PID_SIGNAL    SIGTERM  /* signal used to terminate forked childs */
+#ifndef MAXARGS
+#define MAXARGS     1024   /* Max Number of arguments in safe_fork command */
+#endif
+
+/*==================*
+ * STATIC VARIABLES *
+ *==================*/
+
+static char rsh[256];
+static struct hostent *hostEntry = NULL;
+static boolean  *frameDone;
+static int  outputServerSocket;
+static int  decodeServerSocket;
+static boolean  parallelPerfect = FALSE;
+static  int current_max_forked_pid=0;
+
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+extern int yuvHeight, yuvWidth;
+extern char statFileName[256];
+extern FILE *statFile;
+extern boolean debugMachines;
+extern boolean debugSockets;
+int parallelTestFrames = 10;
+int parallelTimeChunks = 60;
+const char *IOhostName;
+int ioPortNumber;
+int decodePortNumber;
+boolean niceProcesses = FALSE;
+boolean forceIalign = FALSE;
+int     machineNumber = -1;
+boolean remoteIO = FALSE;
+bool separateConversion;
+    /* The I/O server will convert from the input format to the base format,
+       and the slave will convert from the base format to the YUV internal
+       format.  If false, the I/O server assumes the input format is the
+       base format and converts from the base format to the YUV internal
+       format; the slave does no conversion.
+    */
+time_t  IOtime = 0;
+extern char encoder_name[];
+int     ClientPid[MAX_MACHINES+4];
+
+
+/*=====================*
+ * INTERNAL PROCEDURES *
+ *=====================*/
+
+
+static void PM_GNU_PRINTF_ATTR(1,2)
+machineDebug(const char format[], ...) {
+
+    va_list args;
+
+    va_start(args, format);
+
+    if (debugMachines) {
+        const char * const hostname = GetHostName();
+        fprintf(stderr, "%s: ---", hostname);
+        strfree(hostname);
+        vfprintf(stderr, format, args);
+        fputc('\n', stderr);
+    }
+    va_end(args);
+}
+
+
+
+static void PM_GNU_PRINTF_ATTR(1,2)
+errorExit(const char format[], ...) {
+
+    const char * const hostname = GetHostName();
+
+    va_list args;
+
+    va_start(args, format);
+
+    fprintf(stderr, "%s: FATAL ERROR.  ", hostname);
+    strfree(hostname);
+    vfprintf(stderr, format, args);
+    fputc('\n', stderr);
+
+    exit(1);
+
+    va_end(args);
+}
+
+
+
+static void
+TransmitPortNum(const char * const hostName, 
+                int          const portNum, 
+                int          const newPortNum) {
+/*----------------------------------------------------------------------------
+   Transmit the port number 'newPortNum' to the master on port 'portNum'
+   of host 'hostName'.
+-----------------------------------------------------------------------------*/
+    int clientSocket;
+    const char * error;
+    
+    ConnectToSocket(hostName, portNum, &hostEntry, &clientSocket, &error);
+    
+    if (error)
+        errorExit("Can't connect in order to transmit port number.  %s",
+                  error);
+
+    WriteInt(clientSocket, newPortNum);
+    
+    close(clientSocket);
+}
+
+
+
+static void
+readYUVDecoded(int          const socketFd,
+               unsigned int const Fsize_x,
+               unsigned int const Fsize_y,
+               MpegFrame *  const frameP) {
+    
+    unsigned int y;
+    
+    for (y = 0; y < Fsize_y; ++y)         /* Y */
+        ReadBytes(socketFd, 
+                  (unsigned char *)frameP->decoded_y[y], Fsize_x);
+    
+    for (y = 0; y < (Fsize_y >> 1); ++y)  /* U */
+        ReadBytes(socketFd, 
+                  (unsigned char *)frameP->decoded_cb[y], (Fsize_x >> 1));
+    
+    for (y = 0; y < (Fsize_y >> 1); ++y)  /* V */
+        ReadBytes(socketFd, 
+                  (unsigned char *)frameP->decoded_cr[y], (Fsize_x >> 1));
+}
+
+
+
+static void
+writeYUVDecoded(int          const socketFd,
+                unsigned int const Fsize_x,
+                unsigned int const Fsize_y,
+                MpegFrame *  const frameP) {
+    
+    unsigned int y;
+    
+    for (y = 0; y < Fsize_y; ++y)         /* Y */
+        WriteBytes(socketFd, 
+                  (unsigned char *)frameP->decoded_y[y], Fsize_x);
+    
+    for (y = 0; y < (Fsize_y >> 1); ++y)  /* U */
+        WriteBytes(socketFd, 
+                   (unsigned char *)frameP->decoded_cb[y], (Fsize_x >> 1));
+    
+    for (y = 0; y < (Fsize_y >> 1); ++y)  /* V */
+        WriteBytes(socketFd, 
+                   (unsigned char *)frameP->decoded_cr[y], (Fsize_x >> 1));
+}
+
+
+
+static void
+writeYUVOrig(int          const socketFd,
+             unsigned int const Fsize_x,
+             unsigned int const Fsize_y,
+             MpegFrame *  const frameP) {
+    
+    unsigned int y;
+    
+    for (y = 0; y < Fsize_y; ++y)         /* Y */
+        WriteBytes(socketFd, 
+                  (unsigned char *)frameP->orig_y[y], Fsize_x);
+    
+    for (y = 0; y < (Fsize_y >> 1); ++y)  /* U */
+        WriteBytes(socketFd, 
+                   (unsigned char *)frameP->orig_cb[y], (Fsize_x >> 1));
+    
+    for (y = 0; y < (Fsize_y >> 1); ++y)  /* V */
+        WriteBytes(socketFd, 
+                   (unsigned char *)frameP->orig_cr[y], (Fsize_x >> 1));
+}
+
+
+
+static void
+readYUVOrig(int          const socketFd,
+            unsigned int const Fsize_x,
+            unsigned int const Fsize_y,
+            MpegFrame *  const frameP) {
+    
+    unsigned int y;
+    
+    for (y = 0; y < Fsize_y; ++y)         /* Y */
+        ReadBytes(socketFd, 
+                  (unsigned char *)frameP->orig_y[y], Fsize_x);
+    
+    for (y = 0; y < (Fsize_y >> 1); ++y)  /* U */
+        ReadBytes(socketFd, 
+                  (unsigned char *)frameP->orig_cb[y], (Fsize_x >> 1));
+    
+    for (y = 0; y < (Fsize_y >> 1); ++y)  /* V */
+        ReadBytes(socketFd, 
+                  (unsigned char *)frameP->orig_cr[y], (Fsize_x >> 1));
+}
+
+
+
+/*===========================================================================*
+ *
+ * EndIOServer
+ *
+ *  called by the master process -- tells the I/O server to commit
+ *  suicide
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+  EndIOServer()
+{
+  /* send signal to IO server:  -1 as frame number */
+  GetRemoteFrame(NULL, -1);
+}
+
+
+/*===========================================================================*
+ *
+ * NotifyDecodeServerReady
+ *
+ *  called by a slave to the Decode Server to tell it a decoded frame
+ *  is ready and waiting
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+NotifyDecodeServerReady(int const id) {
+
+    int   clientSocket;
+    time_t  tempTimeStart, tempTimeEnd;
+    const char * error;
+    
+    time(&tempTimeStart);
+    
+    ConnectToSocket(IOhostName, decodePortNumber, &hostEntry, &clientSocket,
+                    &error);
+    
+    if (error)
+        errorExit("CHILD: Can't connect to decode server to tell it a frame "
+                "is ready.  %s", error);
+    
+    WriteInt(clientSocket, id);
+    
+    close(clientSocket);
+    
+    time(&tempTimeEnd);
+    IOtime += (tempTimeEnd-tempTimeStart);
+}
+
+
+
+/*===========================================================================*
+ *
+ * WaitForDecodedFrame
+ *
+ *  blah blah blah
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+  WaitForDecodedFrame(id)
+int id;
+{
+  int const negativeTwo = -2;
+  int   clientSocket;
+  int     ready;
+  const char * error;
+
+  /* wait for a decoded frame */
+  if ( debugSockets ) {
+    fprintf(stdout, "WAITING FOR DECODED FRAME %d\n", id);
+  }
+
+  ConnectToSocket(IOhostName, decodePortNumber, &hostEntry, &clientSocket,
+                  &error);
+
+  if (error)
+      errorExit("CHILD: Can't connect to decode server "
+                "to get decoded frame.  %s",
+                error);
+
+  /* first, tell DecodeServer we're waiting for this frame */
+  WriteInt(clientSocket, negativeTwo);
+
+  WriteInt(clientSocket, id);
+
+  ReadInt(clientSocket, &ready);
+
+  if ( ! ready ) {
+    int     waitSocket;
+    int     waitPort;
+    int     otherSock;
+    const char * error;
+
+    /* it's not ready; set up a connection and wait for decode server */
+    CreateListeningSocket(&waitSocket, &waitPort, &error);
+    if (error)
+        errorExit("Unable to create socket on which to listen for "
+                  "decoded frame.  %s", error);
+
+    /* tell decode server where we are */
+    WriteInt(clientSocket, machineNumber);
+
+    WriteInt(clientSocket, waitPort);
+
+    close(clientSocket);
+
+    if ( debugSockets ) {
+      fprintf(stdout, "SLAVE:  WAITING ON SOCKET %d\n", waitPort);
+      fflush(stdout);
+    }
+
+    AcceptConnection(waitSocket, &otherSock, &error);
+    if (error)
+        errorExit("I/O SERVER: Failed to accept next connection.  %s", error);
+
+    /* should we verify this is decode server? */
+    /* for now, we won't */
+
+    close(otherSock);
+
+    close(waitSocket);
+  } else {
+    close(clientSocket);
+  }
+
+  if ( debugSockets ) {
+    fprintf(stdout, "YE-HA FRAME %d IS NOW READY\n", id);
+  }
+}
+
+
+/*===========================================================================*
+ *
+ * SendDecodedFrame
+ *
+ *  Send the frame to the decode server.
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+SendDecodedFrame(MpegFrame * const frameP) {
+/*----------------------------------------------------------------------------
+   Send frame *frameP to the decode server.
+-----------------------------------------------------------------------------*/
+    int const negativeTwo = -2;
+    
+    int clientSocket;
+    const char * error;
+    
+    /* send to IOServer */
+    ConnectToSocket(IOhostName, ioPortNumber, &hostEntry,
+                    &clientSocket, &error);
+    if (error)
+        errorExit("CHILD: Can't connect to decode server to "
+                  "give it a decoded frame.  %s", error);
+    
+    WriteInt(clientSocket, negativeTwo);
+    
+    WriteInt(clientSocket, frameP->id);
+
+    writeYUVDecoded(clientSocket, Fsize_x, Fsize_y, frameP);
+    
+    close(clientSocket);
+}
+
+
+/*===========================================================================*
+ *
+ * GetRemoteDecodedFrame
+ *
+ *  get the decoded frame from the decode server.
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:   
+ *
+ *===========================================================================*/
+void
+GetRemoteDecodedRefFrame(MpegFrame * const frameP, 
+                         int         const frameNumber) {
+/*----------------------------------------------------------------------------
+   Get decoded frame number 'frameNumber' *frameP from the decode server.
+-----------------------------------------------------------------------------*/
+  int const negativeThree = -3;
+  int clientSocket;
+  const char * error;
+
+  /* send to IOServer */
+  ConnectToSocket(IOhostName, ioPortNumber, &hostEntry,
+                  &clientSocket, &error);
+  if (error)
+      errorExit("CHILD: Can't connect to decode server "
+                "to get a decoded frame.  %s",
+                error);
+
+  /* ask IOServer for decoded frame */
+  WriteInt(clientSocket, negativeThree);
+
+  WriteInt(clientSocket, frameP->id);
+
+  readYUVDecoded(clientSocket, Fsize_x, Fsize_y, frameP);
+
+  close(clientSocket);
+}
+
+
+/*********
+  routines handling forks, execs, PIDs and signals
+  save, system-style forks
+  apian@ise.fhg.de
+  *******/
+
+
+/*===========================================================================*
+ *
+ * cleanup_fork
+ *
+ *  Kill all the children, to be used when we get killed
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:   kills other processes
+ *
+ *===========================================================================*/
+static void cleanup_fork( dummy )       /* try to kill all child processes */
+     int dummy;
+{
+  register int i;
+  for (i = 0;  i < current_max_forked_pid;  ++i ) {
+
+#ifdef DEBUG_FORK
+    fprintf(stderr, "cleanup_fork: killing PID %d\n", ClientPid[i]);
+#endif
+
+    if (kill(ClientPid[i], TERMINATE_PID_SIGNAL)) {
+      fprintf(stderr, "cleanup_fork: killed PID=%d failed (errno %d)\n", 
+          ClientPid[i], errno);
+    }
+  }
+}
+
+/*===========================================================================*
+ *
+ * safe_fork
+ *
+ *  fork a command
+ *
+ * RETURNS:     success/failure
+ *
+ * SIDE EFFECTS:   Fork the command, and save to PID so you can kil it later!
+ *
+ *===========================================================================*/
+static int safe_fork(command)       /* fork child process and remember its PID */
+     char *command;
+{
+  static int init=0;
+  char *argis[MAXARGS];
+  register int i=1;
+  
+  if (!(argis[0] = strtok(command, " \t"))) return(0); /* tokenize */
+  while ((argis[i] = strtok(NULL, " \t")) && i < MAXARGS) ++i;
+  argis[i] = NULL;
+  
+#ifdef DEBUG_FORK
+  {register int i=0; 
+   fprintf(stderr, "Command %s becomes:\n", command);
+   while(argis[i]) {fprintf(stderr, "--%s--\n", argis[i]); ++i;} }
+#endif
+  
+  if (!init) {          /* register clean-up routine */
+    signal (SIGQUIT, cleanup_fork);
+    signal (SIGTERM, cleanup_fork);
+    signal (SIGINT , cleanup_fork);
+    init=1;
+  }
+  
+  if (-1 == (ClientPid[current_max_forked_pid] = fork()) )  {
+    perror("safe_fork: fork failed ");
+    return(-1);
+  }
+  if( !ClientPid[current_max_forked_pid]) { /* we are in child process */
+    execvp(argis[0], argis );
+    perror("safe_fork child: exec failed ");
+    exit(1);
+  }
+#ifdef DEBUG_FORK
+  fprintf(stderr, "parallel: forked PID=%d\n", ClientPid[current_max_forked_pid]);
+#endif
+  current_max_forked_pid++;
+  return(0);
+}
+
+
+/*=====================*
+ * EXPORTED PROCEDURES *
+ *=====================*/
+
+            /*=================*
+             * IO SERVER STUFF *
+             *=================*/
+
+
+/*===========================================================================*
+ *
+ * SetIOConvert
+ *
+ *  sets the IO conversion to be separate or not.  If separate, then
+ *  some post-processing is done at slave end
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+SetIOConvert(bool const separate) {
+    separateConversion = separate;
+}
+
+
+/*===========================================================================*
+ *
+ * SetParallelPerfect
+ *
+ *  If this is called, then frames will be divided up completely, and
+ *  evenly (modulo rounding) between all the processors
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    Sets parallelPerfect ....
+ *
+ *===========================================================================*/
+void
+SetParallelPerfect(val)
+boolean val;
+{
+    parallelPerfect = val;
+}
+
+
+/*===========================================================================*
+ *
+ * SetRemoteShell
+ *
+ *  sets the remote shell program (usually rsh, but different on some
+ *  machines)
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+SetRemoteShell(const char * const shell) {
+    strcpy(rsh, shell);
+}
+
+
+static void
+decodedFrameToDisk(int const otherSock) {
+/*----------------------------------------------------------------------------
+  Get a decoded from from socket 'otherSock' and write it to disk.
+-----------------------------------------------------------------------------*/
+    int frameNumber;
+    MpegFrame * frameP;
+
+    ReadInt(otherSock, &frameNumber);
+    
+    if (debugSockets) {
+        fprintf(stdout, "INPUT SERVER:  GETTING DECODED FRAME %d\n", 
+                frameNumber);
+        fflush(stdout);
+    }
+
+    /* should read frame from socket, then write to disk */
+    frameP = Frame_New(frameNumber, 'i');
+    
+    Frame_AllocDecoded(frameP, TRUE);
+    
+    readYUVDecoded(otherSock, Fsize_x, Fsize_y, frameP);
+
+    /* now output to disk */
+    WriteDecodedFrame(frameP);
+
+    Frame_Free(frameP);
+}        
+
+
+
+static void
+decodedFrameFromDisk(int const otherSock) {
+
+    /* request for decoded frame from disk */
+            
+    int frameNumber;
+    MpegFrame * frameP;
+
+    ReadInt(otherSock, &frameNumber);
+    
+    if (debugSockets) {
+        fprintf(stdout, "INPUT SERVER:  READING DECODED FRAME %d "
+                "from DISK\n", frameNumber);
+        fflush(stdout);
+    }
+
+    /* should read frame from disk, then write to socket */
+    frameP = Frame_New(frameNumber, 'i');
+    
+    Frame_AllocDecoded(frameP, TRUE);
+    
+    ReadDecodedRefFrame(frameP, frameNumber);
+    
+    writeYUVDecoded(otherSock, Fsize_x, Fsize_y, frameP);
+    
+    Frame_Free(frameP);
+}        
+
+
+
+static void
+routeFromSocketToDisk(int              const otherSock,
+                      unsigned char ** const bigBufferP,
+                      unsigned int *   const bigBufferSizeP) {
+
+    /* routing output frame from socket to disk */
+
+    int frameNumber;
+    int numBytes;
+    unsigned char * bigBuffer;
+    unsigned int bigBufferSize;
+    const char * fileName;
+    FILE * filePtr;
+
+    bigBuffer     = *bigBufferP;
+    bigBufferSize = *bigBufferSizeP;
+
+    ReadInt(otherSock, &frameNumber);
+    ReadInt(otherSock, &numBytes);
+
+    /* Expand bigBuffer if necessary to fit this frame */
+    if (numBytes > bigBufferSize) {
+        bigBufferSize = numBytes;
+        if (bigBuffer != NULL)
+            free(bigBuffer);
+
+        MALLOCARRAY_NOFAIL(bigBuffer, bigBufferSize);
+    }
+    
+    /* now read in the bytes */
+    ReadBytes(otherSock, bigBuffer, numBytes);
+    
+    /* open file to output this stuff to */
+    asprintfN(&fileName, "%s.frame.%d", outputFileName, frameNumber);
+    filePtr = fopen(fileName, "wb");
+
+    if (filePtr == NULL)
+        errorExit("I/O SERVER: Could not open output file(3):  %s", fileName);
+
+    strfree(fileName);
+
+    /* now write the bytes here */
+    fwrite(bigBuffer, sizeof(char), numBytes, filePtr);
+    
+    fclose(filePtr);
+    
+    if (debugSockets) {
+        fprintf(stdout, "====I/O SERVER:  WROTE FRAME %d to disk\n",
+                frameNumber);
+        fflush(stdout);
+    }
+
+    *bigBufferP     = bigBuffer;
+    *bigBufferSizeP = bigBufferSize;
+}        
+
+
+
+static void
+readConvertWriteToSocket(struct inputSource * const inputSourceP,
+                         int                  const otherSock,
+                         int                  const frameNumber,
+                         bool *               const endOfStreamP) {
+/*----------------------------------------------------------------------------
+   Get the frame numbered 'frameNumber' from input source
+   *inputSourceP, apply format conversion User requested, and write
+   the "base format" result to socket 'otherSock'.
+-----------------------------------------------------------------------------*/
+    FILE * convertedFileP;
+    
+    convertedFileP = ReadIOConvert(inputSourceP, frameNumber);
+    if (convertedFileP) {
+        bool eof;
+        eof = FALSE;  /* initial value */
+        while (!eof) {
+            unsigned char buffer[1024];
+            unsigned int numBytes;
+            
+            numBytes = fread(buffer, 1, sizeof(buffer), convertedFileP);
+            
+            if (numBytes > 0) {
+                WriteInt(otherSock, numBytes);
+                WriteBytes(otherSock, buffer, numBytes);
+            } else
+                eof = TRUE;
+        }
+        
+        if (strcmp(ioConversion, "*") == 0 )
+            fclose(convertedFileP);
+        else
+            pclose(convertedFileP);
+        
+        *endOfStreamP = FALSE;
+    } else
+        *endOfStreamP = TRUE;
+}
+
+
+
+static void
+readWriteYuvToSocket(struct inputSource * const inputSourceP,
+                     int                  const otherSock,
+                     int                  const frameNumber,
+                     bool *               const endOfStreamP) {
+/*----------------------------------------------------------------------------
+   Read Frame number 'frameNumber' from the input source *inputSourceP,
+   assuming it is in base format, and write its contents in YUV format
+   to socket 'otherSock'.
+
+   Wait for acknowledgement that consumer has received it.
+-----------------------------------------------------------------------------*/
+    MpegFrame * frameP;
+
+    frameP = Frame_New(frameNumber, 'i');
+    
+    ReadFrame(frameP, inputSourceP, frameNumber, inputConversion,
+              endOfStreamP);
+    
+    if (!*endOfStreamP) {
+        writeYUVOrig(otherSock, Fsize_x, Fsize_y, frameP);
+        
+        {
+            /* Make sure we don't leave until other processor read
+               everything
+            */
+            int dummy;
+            ReadInt(otherSock, &dummy);
+            assert(dummy == 0);
+        }
+    }
+    Frame_Free(frameP);
+}
+
+
+
+static void
+readFrameWriteToSocket(struct inputSource * const inputSourceP,
+                       int                  const otherSock,
+                       int                  const frameNumber,
+                       bool *               const endOfStreamP) {
+/*----------------------------------------------------------------------------
+   Read Frame number 'frameNumber' from the input source *inputSourceP
+   and write it to socket 'otherSock'.
+-----------------------------------------------------------------------------*/
+    if (debugSockets) {
+        fprintf(stdout, "I/O SERVER GETTING FRAME %d\n", frameNumber);
+        fflush(stdout);
+    }
+
+    if (separateConversion)
+        readConvertWriteToSocket(inputSourceP, otherSock, frameNumber,
+                                 endOfStreamP);
+    else
+        readWriteYuvToSocket(inputSourceP, otherSock, frameNumber,
+                             endOfStreamP);
+
+    if (debugSockets) {
+        fprintf(stdout, "====I/O SERVER:  READ FRAME %d\n", frameNumber);
+    }
+}
+
+
+
+static void
+processNextConnection(int                  const serverSocket,
+                      struct inputSource * const inputSourceP,
+                      bool *               const doneP,
+                      unsigned char **     const bigBufferP,
+                      unsigned int *       const bigBufferSizeP) {
+
+    int          otherSock;
+    int          command;
+    const char * error;
+    
+    AcceptConnection(serverSocket, &otherSock, &error);
+    if (error)
+        errorExit("I/O SERVER: Failed to accept next connection.  %s", error);
+
+    ReadInt(otherSock, &command);
+
+    switch (command) {
+    case -1:
+        *doneP = TRUE;
+        break;
+    case -2:
+        decodedFrameToDisk(otherSock);
+        break;
+    case -3:
+        decodedFrameFromDisk(otherSock);
+        break;
+    case -4:
+        routeFromSocketToDisk(otherSock, bigBufferP, bigBufferSizeP);
+        break;
+    default: {
+        unsigned int const frameNumber = command;
+
+        bool endOfStream;
+
+        readFrameWriteToSocket(inputSourceP, otherSock, frameNumber,
+                               &endOfStream);
+
+        if (endOfStream) {
+            /* We don't do anything.  Closing the socket with having written
+               anything is our signal that there is no more input.
+
+               (Actually, Ppmtompeg cannot handle stream input in parallel
+               mode -- this code is just infrastructure so maybe it can some
+               day).
+            */
+        }
+    }
+    }
+    close(otherSock);
+}
+ 
+
+
+void
+IoServer(struct inputSource * const inputSourceP,
+         const char *         const parallelHostName, 
+         int                  const portNum) {
+/*----------------------------------------------------------------------------
+   Execute an I/O server.
+
+   An I/O server is the partner on the master machine of a child process
+   on a "remote" system.  "Remote" here doesn't just mean on another system.
+   It means on a system that isn't even in the same cluster -- specifically,
+   a system that doesn't have access to the same filesystem as the master.
+
+   The child process passes frame contents between it and the master via
+   the I/O server.
+-----------------------------------------------------------------------------*/
+    int       ioPortNum;
+    int       serverSocket;
+    boolean   done;
+    unsigned char   *bigBuffer;
+        /* A work buffer that we keep around permanently.  We increase
+           its size as needed, but never shrink it.
+        */
+    unsigned int bigBufferSize;
+        /* The current allocated size of bigBuffer[] */
+    const char * error;
+
+    bigBufferSize = 0;  /* Start with no buffer */
+    bigBuffer = NULL;
+    
+    /* once we get IO port num, should transmit it to parallel server */
+
+    CreateListeningSocket(&serverSocket, &ioPortNum, &error);
+    if (error)
+        errorExit("Unable to create socket on which to listen for "
+                  "reports from children.  %s", error);
+
+    if (debugSockets)
+        fprintf(stdout, "====I/O USING PORT %d\n", ioPortNum);
+
+    TransmitPortNum(parallelHostName, portNum, ioPortNum);
+
+    if (separateConversion)
+        SetFileType(ioConversion);  /* for reading */
+    else
+        SetFileType(inputConversion);
+
+    done = FALSE;  /* initial value */
+
+    while (!done)
+        processNextConnection(serverSocket, inputSourceP,
+                              &done, &bigBuffer, &bigBufferSize);
+
+    close(serverSocket);
+
+    if ( debugSockets ) {
+        fprintf(stdout, "====I/O SERVER:  Shutting Down\n");
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * SendRemoteFrame
+ *
+ *  called by a slave to the I/O server; sends an encoded frame
+ *  to the server to be sent to disk
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+SendRemoteFrame(int const frameNumber, BitBucket * const bb) {
+
+    int const negativeFour = -4;
+    int clientSocket;
+    time_t  tempTimeStart, tempTimeEnd;
+    const char * error;
+
+    time(&tempTimeStart);
+
+    ConnectToSocket(IOhostName, ioPortNumber, &hostEntry,
+                    &clientSocket, &error);
+    if (error)
+        errorExit("CHILD: Can't connect to I/O server to deliver results.  %s",
+                  error);
+
+    WriteInt(clientSocket, negativeFour);
+
+    WriteInt(clientSocket, frameNumber);
+    
+    if (frameNumber != -1) {
+        /* send number of bytes */
+        
+        WriteInt(clientSocket, (bb->totalbits+7)>>3);
+    
+        /* now send the bytes themselves */
+        Bitio_WriteToSocket(bb, clientSocket);
+    }
+
+    close(clientSocket);
+
+    time(&tempTimeEnd);
+    IOtime += (tempTimeEnd-tempTimeStart);
+}
+
+
+
+void
+GetRemoteFrame(MpegFrame * const frameP, 
+               int         const frameNumber) {
+/*----------------------------------------------------------------------------
+   Get a frame from the I/O server.
+   
+   This is intended for use by a child.
+-----------------------------------------------------------------------------*/
+    int           clientSocket;
+    const char * error;
+
+    Fsize_Note(frameNumber, yuvWidth, yuvHeight);
+
+    if (debugSockets) {
+        fprintf(stdout, "MACHINE %s REQUESTING connection for FRAME %d\n",
+                getenv("HOST"), frameNumber);
+        fflush(stdout);
+    }
+
+    ConnectToSocket(IOhostName, ioPortNumber, &hostEntry,
+                    &clientSocket, &error);
+
+    if (error)
+        errorExit("CHILD: Can't connect to I/O server to get a frame.  %s", 
+                  error);
+
+    WriteInt(clientSocket, frameNumber);
+
+    if (frameNumber != -1) {
+        if (separateConversion) {
+            unsigned char buffer[1024];
+            /* This is by design the exact size of the data per message (except
+               the last message for a frame) the I/O server sends.
+            */
+            int numBytes;  /* Number of data bytes in message */
+            FILE * filePtr = pm_tmpfile();
+
+            /* read in stuff, write to file, perform local conversion */
+            do {
+                ReadInt(clientSocket, &numBytes);
+
+                if (numBytes > sizeof(buffer))
+                    errorExit("Invalid message received: numBytes = %d, "
+                              "which is greater than %d\n", 
+                              numBytes, sizeof(numBytes));
+                ReadBytes(clientSocket, buffer, numBytes);
+
+                fwrite(buffer, 1, numBytes, filePtr);
+            } while ( numBytes == sizeof(buffer) );
+            fflush(filePtr);
+            {
+                bool endOfStream;
+                rewind(filePtr);
+                /* I/O Server gave us base format.  Read it as an MpegFrame */
+                ReadFrameFile(frameP, filePtr, slaveConversion, &endOfStream);
+                assert(!endOfStream);
+            }
+            fclose(filePtr);
+        } else {
+            Frame_AllocYCC(frameP);
+
+            if (debugSockets) {
+                fprintf(stdout, "MACHINE %s allocated YCC FRAME %d\n",
+                        getenv("HOST"), frameNumber);
+                fflush(stdout);
+            }
+            /* I/O Server gave us internal YUV format.  Read it as MpegFrame */
+            readYUVOrig(clientSocket, yuvWidth, yuvHeight, frameP);
+        }
+    }
+
+    WriteInt(clientSocket, 0);
+
+    close(clientSocket);
+
+    if (debugSockets) {
+        fprintf(stdout, "MACHINE %s READ COMPLETELY FRAME %d\n",
+                getenv("HOST"), frameNumber);
+        fflush(stdout);
+    }
+}
+
+
+struct combineControl {
+    unsigned int numFrames;
+};
+
+
+
+
+static void
+getAndProcessACombineConnection(int const outputServerSocket) {
+    int          otherSock;
+    int          command;
+    const char * error;
+
+    AcceptConnection(outputServerSocket, &otherSock, &error);
+
+    if (error)
+        errorExit("COMBINE SERVER: "
+                  "Failed to accept next connection.  %s", error);
+    
+    ReadInt(otherSock, &command);
+    
+    if (command == -2) {
+        /* this is notification from non-remote process that a
+           frame is done.
+            */
+        int frameStart, frameEnd;
+
+        ReadInt(otherSock, &frameStart);
+        ReadInt(otherSock, &frameEnd);
+            
+        machineDebug("COMBINE_SERVER: Frames %d - %d done",
+                     frameStart, frameEnd);
+        {
+            unsigned int i;
+            for (i = frameStart; i <= frameEnd; ++i)
+                frameDone[i] = TRUE;
+        }
+    } else
+        errorExit("COMBINE SERVER: Unrecognized command %d received.",
+                  command);
+
+    close(otherSock);
+}
+
+
+
+#define READ_ATTEMPTS 5 /* number of times (seconds) to retry an input file */
+
+
+static void
+openInputFile(const char * const fileName,
+              FILE **      const inputFilePP) {
+
+    FILE * inputFileP;
+    unsigned int attempts;
+    
+    inputFileP = NULL;
+    attempts = 0;
+
+    while (!inputFileP && attempts < READ_ATTEMPTS) {
+        inputFileP = fopen(fileName, "rb");
+        if (inputFileP == NULL) {
+            pm_message("ERROR  Couldn't read frame file '%s' errno = %d (%s)"
+                       "attempt %d", 
+                       fileName, errno, strerror(errno), attempts);
+            sleep(1);
+        }
+        ++attempts;
+    }
+    if (inputFileP == NULL)
+        pm_error("Unable to open file '%s' after %d attempts.", 
+                 fileName, attempts);
+
+    *inputFilePP = inputFileP;
+}
+
+
+
+static void
+waitForOutputFile(void *        const inputHandle,
+                  unsigned int  const frameNumber,
+                  FILE **       const ifPP) {
+/*----------------------------------------------------------------------------
+   Keep handling output events until we get the specified frame number.
+   Open the file it's in and return the stream handle.
+-----------------------------------------------------------------------------*/
+    struct combineControl * const combineControlP = (struct combineControl *)
+        inputHandle;
+
+    if (frameNumber >= combineControlP->numFrames)
+        *ifPP = NULL;
+    else {
+        const char * fileName;
+
+        while (!frameDone[frameNumber]) {
+            machineDebug("COMBINE_SERVER: Waiting for frame %u done", 
+                         frameNumber);
+
+            getAndProcessACombineConnection(outputServerSocket);
+        }
+        machineDebug("COMBINE SERVER: Wait for frame %u over", frameNumber);
+
+        asprintfN(&fileName, "%s.frame.%u", outputFileName, frameNumber);
+
+        openInputFile(fileName, ifPP);
+
+        strfree(fileName);
+    }
+}
+
+
+
+static void
+unlinkFile(void *       const inputHandle,
+           unsigned int const frameNumber) {
+
+    if (!keepTempFiles) {
+        const char * fileName;
+
+        asprintfN(&fileName, "%s.frame.%u", outputFileName, frameNumber);
+
+        unlink(fileName);
+
+        strfree(fileName);
+    }
+}
+
+
+
+void
+CombineServer(int          const numFrames, 
+              const char * const masterHostName, 
+              int          const masterPortNum,
+              const char * const outputFileName) {
+/*----------------------------------------------------------------------------
+   Execute a combine server.
+
+   This handles combination of frames.
+-----------------------------------------------------------------------------*/
+  int    combinePortNum;
+  FILE * ofP;
+  const char * error;
+  struct combineControl combineControl;
+  
+  /* once we get Combine port num, should transmit it to parallel server */
+  
+  CreateListeningSocket(&outputServerSocket, &combinePortNum, &error);
+  if (error)
+      errorExit("Unable to create socket on which to listen.  %s", error);
+
+  machineDebug("COMBINE SERVER: LISTENING ON PORT %d", combinePortNum);
+  
+  TransmitPortNum(masterHostName, masterPortNum, combinePortNum);
+  
+  MALLOCARRAY_NOFAIL(frameDone, numFrames);
+  {
+      unsigned int i;
+      for (i = 0; i < numFrames; ++i)
+          frameDone[i] = FALSE;
+  }
+  ofP = pm_openw(outputFileName);
+  
+  combineControl.numFrames = numFrames;
+
+  FramesToMPEG(ofP, &combineControl, &waitForOutputFile, &unlinkFile);
+
+  machineDebug("COMBINE SERVER: Shutting down");
+  
+  /* tell Master server we are done */
+  TransmitPortNum(masterHostName, masterPortNum, combinePortNum);
+  
+  close(outputServerSocket);
+
+  fclose(ofP);
+}
+
+
+/*=====================*
+ * MASTER SERVER STUFF *
+ *=====================*/
+
+
+static void
+startCombineServer(const char * const encoderName,
+                   unsigned int const numMachines,
+                   const char * const masterHostName,
+                   int          const masterPortNum,
+                   unsigned int const numInputFiles,
+                   const char * const paramFileName,
+                   int          const masterSocket,
+                   int *        const combinePortNumP) {
+
+    char         command[1024];
+    int          otherSock;
+    const char * error;
+
+    snprintf(command, sizeof(command), 
+             "%s %s -max_machines %d -output_server %s %d %d %s",
+             encoderName, 
+             debugMachines ? "-debug_machines" : "",
+             numMachines, masterHostName, masterPortNum, 
+             numInputFiles, paramFileName);
+
+    machineDebug("MASTER: Starting combine server with shell command '%s'",
+                 command);
+
+    safe_fork(command);
+    
+    machineDebug("MASTER: Listening for connection back from "
+                 "new Combine server");
+
+    AcceptConnection(masterSocket, &otherSock, &error);
+    if (error)
+        errorExit("MASTER SERVER: "
+                  "Failed to accept next connection.  %s", error);
+
+    ReadInt(otherSock, combinePortNumP);
+    close(otherSock);
+
+    machineDebug("MASTER:  Combine port number = %d", *combinePortNumP);
+}
+
+
+
+static void
+startDecodeServer(const char * const encoderName,
+                  unsigned int const numMachines,
+                  const char * const masterHostName,
+                  int          const masterPortNum,
+                  unsigned int const numInputFiles,
+                  const char * const paramFileName,
+                  int          const masterSocket,
+                  int *        const decodePortNumP) {
+
+    char         command[1024];
+    int          otherSock;
+    const char * error;
+
+    snprintf(command, sizeof(command), 
+             "%s %s -max_machines %d -decode_server %s %d %d %s",
+             encoder_name, 
+             debugMachines ? "-debug_machines" : "",
+             numMachines, masterHostName, masterPortNum,
+             numInputFiles, paramFileName);
+
+    machineDebug("MASTER: Starting decode server with shell command '%s'",
+                 command);
+
+    safe_fork(command);
+
+    machineDebug("MASTER: Listening for connection back from "
+                 "new Decode server");
+
+    AcceptConnection(masterSocket, &otherSock, &error);
+    if (error)
+        errorExit("MASTER SERVER: "
+                  "Failed to accept connection back from the new "
+                  "decode server.  %s", error);
+
+    ReadInt(otherSock, decodePortNumP);
+    
+    close(otherSock);
+    
+    machineDebug("MASTER:  Decode port number = %d", *decodePortNumP);
+}
+
+
+
+static void
+startIoServer(const char *   const encoderName,
+              unsigned int   const numChildren,
+              const char *   const masterHostName,
+              int            const masterPortNum,
+              int            const masterSocket,
+              const char *   const paramFileName,
+              int *          const ioPortNumP) {
+              
+    char         command[1024];
+    int          otherSock;
+    const char * error;
+    
+    sprintf(command, "%s -max_machines %d -io_server %s %d %s",
+            encoderName, numChildren, masterHostName, masterPortNum,
+            paramFileName);
+
+    machineDebug("MASTER: Starting I/O server with remote shell command '%s'",
+                 command);
+
+    safe_fork(command);
+    
+    machineDebug("MASTER: Listening for connection back from "
+                 "new I/O server");
+
+    AcceptConnection(masterSocket, &otherSock, &error);
+    if (error)
+        errorExit("MASTER SERVER: "
+                  "Failed to accept connection back from the new "
+                  "I/O server.  %s", error);
+    
+    ReadInt(otherSock, ioPortNumP);
+    close(otherSock);
+    
+    machineDebug("MASTER:  I/O port number = %d", *ioPortNumP);
+}    
+    
+    
+
+static void
+extendToEndOfPattern(unsigned int * const nFramesP,
+                     unsigned int   const startFrame,
+                     unsigned int   const framePatternLen,
+                     unsigned int   const numFramesInStream) {
+
+    assert(framePatternLen >= 1);
+        
+    while (startFrame + *nFramesP < numFramesInStream &&
+           (startFrame + *nFramesP) % framePatternLen != 0)
+        ++(*nFramesP);
+}
+
+
+
+static void
+allocateInitialFrames(struct scheduler * const schedulerP,
+                      boolean            const parallelPerfect,
+                      boolean            const forceIalign,
+                      unsigned int       const framePatternLen,
+                      unsigned int       const parallelTestFrames,
+                      unsigned int       const childNum,
+                      unsigned int *     const startFrameP,
+                      unsigned int *     const nFramesP) {
+/*----------------------------------------------------------------------------
+   Choose which frames, to hand out to the new child numbered 'childNum'.
+-----------------------------------------------------------------------------*/
+    unsigned int const framesPerChild = 
+        MAX(1, ((schedulerP->numFramesInJob - schedulerP->nextFrame) /
+                (schedulerP->numMachines - childNum)));
+
+    unsigned int nFrames;
+
+    if (parallelPerfect)
+        nFrames = framesPerChild;
+    else {
+        assert(parallelTestFrames >= 1);
+
+        nFrames = MIN(parallelTestFrames, framesPerChild);
+    }
+    if (forceIalign)
+        extendToEndOfPattern(&nFrames, schedulerP->nextFrame,
+                             framePatternLen, schedulerP->numFramesInJob);
+
+    nFrames = MIN(nFrames, schedulerP->numFramesInJob - schedulerP->nextFrame);
+
+    *startFrameP = schedulerP->nextFrame;
+    *nFramesP = nFrames;
+    schedulerP->nextFrame += nFrames;
+}
+
+
+
+static float
+taperedGoalTime(struct childState const childState[],
+                unsigned int      const remainingFrameCount) {
+        
+    float        goalTime;
+    float        allChildrenFPS;
+    float        remainingJobTime;
+        /* How long we expect it to be before the whole movie is encoded*/
+    float        sum;
+    int          numMachinesToEstimate;
+    unsigned int childNum;
+    
+    /* frames left = lastFrameInStream - startFrame + 1 */
+    for (childNum = 0, sum = 0.0, numMachinesToEstimate = 0; 
+         childNum < numMachines; ++childNum) {
+        if (!childState[childNum].finished) {
+            if (childState[childNum].fps < 0.0 )
+                ++numMachinesToEstimate;
+            else
+                sum += childState[childNum].fps;
+        }
+    }
+    
+    allChildrenFPS = (float)numMachines *
+        (sum/(float)(numMachines-numMachinesToEstimate));
+    
+    remainingJobTime = (float)remainingFrameCount/allChildrenFPS;
+    
+    goalTime = MAX(5.0, remainingJobTime/2);
+
+    return goalTime;
+}
+
+
+
+static void
+allocateMoreFrames(struct scheduler * const schedulerP,
+                   unsigned int       const childNum,
+                   struct childState  const childState[],
+                   bool               const forceIalign,
+                   unsigned int       const framePatternLen,
+                   bool               const goalTimeSpecified,
+                   unsigned int       const goalTimeArg,
+                   unsigned int *     const startFrameP,
+                   unsigned int *     const nFramesP) {
+/*----------------------------------------------------------------------------
+   Decide which frames should be child 'childNum''s next assignment,
+   given the state/history of all children is childState[].
+
+   The lowest numbered frame which needs yet to be encoded is frame
+   number 'startFrame' and 'lastFrameInStream' is the highest.
+
+   The allocation always starts at the lowest numbered frame that
+   hasn't yet been allocated and is sequential.  We return as
+   *startFrameP the frame number of the first frame in the allocation
+   and as *nFramesP the number of frames.
+
+   If 'goalTimeSpecified' is true, we try to make the assignment take
+   'goalTimeArg' seconds.  If 'goalTimeSpecified' is not true, we choose
+   a goal time ourselves, which is based on how long we think it will
+   take for all the children to finish all the remaining frames.
+-----------------------------------------------------------------------------*/
+    float goalTime;
+        /* Number of seconds we want the assignment to take.  We size the
+           assignment to try to meet this goal.
+        */
+    unsigned int nFrames;
+    float avgFps;
+
+    if (!goalTimeSpecified) {
+        goalTime = taperedGoalTime(childState,
+                                   schedulerP->numFramesInJob - 
+                                   schedulerP->nextFrame);
+    
+        pm_message("MASTER: ASSIGNING %s %.2f seconds of work",
+                   machineName[childNum], goalTime);
+    } else
+        goalTime = goalTimeArg;
+    
+    if (childState[childNum].numSeconds != 0)
+        avgFps = (float)childState[childNum].numFrames / 
+            childState[childNum].numSeconds;
+    else
+        avgFps = 0.1;       /* arbitrary small value */
+
+    nFrames = MAX(1u, (unsigned int)(goalTime * avgFps + 0.5));
+    
+    nFrames = MIN(nFrames, 
+                  schedulerP->numFramesInJob - schedulerP->nextFrame);
+
+    if (forceIalign)
+        extendToEndOfPattern(&nFrames, schedulerP->nextFrame,
+                             framePatternLen, schedulerP->numFramesInJob);
+
+    *startFrameP = schedulerP->nextFrame;
+    *nFramesP = nFrames;
+    schedulerP->nextFrame += nFrames;
+}
+
+
+
+static void
+startChildren(struct scheduler *   const schedulerP,
+              const char *         const encoderName,
+              const char *         const masterHostName,
+              int                  const masterPortNum,
+              const char *         const paramFileName,
+              boolean              const parallelPerfect,
+              boolean              const forceIalign,
+              unsigned int         const framePatternLen,
+              unsigned int         const parallelTestFrames,
+              boolean              const beNice,
+              int                  const masterSocket,
+              int                  const combinePortNum,
+              int                  const decodePortNum,
+              int *                const ioPortNum,
+              unsigned int *       const numIoServersP,
+              struct childState ** const childStateP) {
+/*----------------------------------------------------------------------------
+   Start up the children.  Tell them to work for the master at
+   'masterHostName':'masterPortNum'.
+
+   Start I/O servers (as processes on this system) as required and return
+   the port numbers of the TCP ports on which they listen as
+   ioPortNum[] and the number of them as *numIoServersP.
+
+   Give each of the children some initial work to do.  This may be just
+   a small amount for timing purposes.
+
+   We access and manipulate the various global variables that represent
+   the state of the children, and the scheduler structure.
+-----------------------------------------------------------------------------*/
+    struct childState * childState;  /* malloc'ed */
+    unsigned int childNum;
+    unsigned int numIoServers;
+    unsigned int childrenLeftCurrentIoServer;
+        /* The number of additional children we can hook up to the
+           current I/O server before reaching our maximum children per
+           I/O server.  0 if there is no current I/O server.
+        */
+
+    MALLOCARRAY_NOFAIL(childState, schedulerP->numMachines);
+
+    childrenLeftCurrentIoServer = 0;  /* No current I/O server yet */
+    
+    numIoServers = 0;  /* None created yet */
+
+    for (childNum = 0; childNum < schedulerP->numMachines; ++childNum) {
+        char command[1024];
+        unsigned int startFrame;
+        unsigned int nFrames;
+
+        childState[childNum].fps        = -1.0;  /* illegal value as flag */
+        childState[childNum].numSeconds = 0;
+
+        allocateInitialFrames(schedulerP, parallelPerfect, forceIalign,
+                              framePatternLen, parallelTestFrames,
+                              childNum, &startFrame, &nFrames);
+
+        if (nFrames == 0) {
+            childState[childNum].finished = TRUE;
+            machineDebug("MASTER: No more frames; not starting child '%s'",
+                         machineName[childNum]);
+        } else {
+            childState[childNum].finished   = FALSE;
+        
+            if (remote[childNum]) {
+                if (childrenLeftCurrentIoServer == 0) {
+                    startIoServer(encoderName, schedulerP->numMachines, 
+                                  masterHostName, masterPortNum, masterSocket,
+                                  paramFileName, &ioPortNum[numIoServers++]);
+                    
+                    childrenLeftCurrentIoServer = SOMAXCONN;
+                }
+                --childrenLeftCurrentIoServer;
+            } 
+            snprintf(command, sizeof(command),
+                     "%s %s -l %s %s "
+                     "%s %s -child %s %d %d %d %d %d %d "
+                     "-frames %d %d %s",
+                     rsh,
+                     machineName[childNum], userName[childNum],
+                     beNice ? "nice" : "",
+                     executable[childNum],
+                     debugMachines ? "-debug_machines" : "",
+                     masterHostName, masterPortNum, 
+                     remote[childNum] ? ioPortNum[numIoServers-1] : 0,
+                     combinePortNum, decodePortNum, childNum,
+                     remote[childNum] ? 1 : 0,
+                     startFrame, startFrame + nFrames - 1,
+                     remote[childNum] ? 
+                         remoteParamFile[childNum] : paramFileName
+            );
+        
+            machineDebug("MASTER: Starting child server "
+                         "with shell command '%s'", command);
+
+            safe_fork(command);
+
+            machineDebug("MASTER: Frames %d-%d assigned to new child %s",
+                         startFrame, startFrame + nFrames - 1, 
+                         machineName[childNum]);
+        }
+        childState[childNum].startFrame = startFrame;
+        childState[childNum].lastNumFrames = nFrames;
+        childState[childNum].numFrames = childState[childNum].lastNumFrames;
+    }
+    *childStateP   = childState;
+    *numIoServersP = numIoServers;
+}
+
+
+
+static void
+noteFrameDone(const char * const combineHostName,
+              int          const combinePortNum,
+              unsigned int const frameStart, 
+              unsigned int const frameEnd) {
+/*----------------------------------------------------------------------------
+   Tell the Combine server that frames 'frameStart' through 'frameEnd'
+   are done.
+-----------------------------------------------------------------------------*/
+    int const negativeTwo = -2;
+    int clientSocket;
+    time_t  tempTimeStart, tempTimeEnd;
+    const char * error;
+    struct hostent * hostEntP;
+
+    time(&tempTimeStart);
+
+    hostEntP = NULL;
+
+    ConnectToSocket(combineHostName, combinePortNum, &hostEntP,
+                    &clientSocket, &error);
+    
+    if (error)
+        errorExit("MASTER: Can't connect to Combine server to tell it frames "
+                  "are done.  %s", error);
+
+    WriteInt(clientSocket, negativeTwo);
+
+    WriteInt(clientSocket, frameStart);
+
+    WriteInt(clientSocket, frameEnd);
+
+    close(clientSocket);
+
+    time(&tempTimeEnd);
+    IOtime += (tempTimeEnd-tempTimeStart);
+}
+
+
+
+static void
+feedTheChildren(struct scheduler * const schedulerP,
+                struct childState        childState[],
+                int                const masterSocket,
+                const char *       const combineHostName,
+                int                const combinePortNum,
+                bool               const forceIalign,
+                unsigned int       const framePatternLen,
+                bool               const goalTimeSpecified,
+                unsigned int       const goalTime) {
+/*----------------------------------------------------------------------------
+   Listen for children to tell us they have finished their assignments
+   and give them new assignments, until all the frames have been assigned
+   and all the children have finished.
+
+   As children finish assignments, inform the combine server at
+   'combineHostName':'combinePortNum' of such.
+
+   Note that the children got initial assigments when they were created.
+   So the first thing we do is wait for them to finish those.
+-----------------------------------------------------------------------------*/
+    unsigned int numFinished;
+        /* Number of child machines that have been excused because there
+           is no more work for them.
+        */
+    unsigned int framesDone;
+
+    numFinished = 0;
+    framesDone = 0;
+
+    while (numFinished != schedulerP->numMachines) {
+        int                 otherSock;
+        int                 childNum;
+        int                 seconds;
+        float               framesPerSecond;
+        struct childState * csP;
+        const char *        error;
+        unsigned int nextFrame;
+        unsigned int nFrames;
+
+        machineDebug("MASTER: Listening for a connection...");
+
+        AcceptConnection(masterSocket, &otherSock, &error);
+        if (error)
+            errorExit("MASTER SERVER: "
+                      "Failed to accept next connection.  %s", error);
+
+        ReadInt(otherSock, &childNum);
+        ReadInt(otherSock, &seconds);
+
+        csP = &childState[childNum];
+        
+        csP->numSeconds += seconds;
+        csP->fps = (float)csP->numFrames / (float)csP->numSeconds;
+
+        if (seconds != 0)
+            framesPerSecond = (float)csP->lastNumFrames / (float)seconds;
+        else
+            framesPerSecond = (float)csP->lastNumFrames * 2.0;
+
+        machineDebug("MASTER: Child %s FINISHED ASSIGNMENT.  "
+                     "%f frames per second", 
+                     machineName[childNum], framesPerSecond);
+
+        noteFrameDone(combineHostName, combinePortNum, csP->startFrame,
+                      csP->startFrame + csP->lastNumFrames - 1);
+
+        framesDone += csP->lastNumFrames;
+
+        allocateMoreFrames(schedulerP, childNum, childState,
+                           forceIalign, framePatternLen,
+                           goalTimeSpecified, goalTime,
+                           &nextFrame, &nFrames);
+
+        if (nFrames == 0) {
+            WriteInt(otherSock, -1);
+            WriteInt(otherSock, 0);
+
+            ++numFinished;
+
+            machineDebug("MASTER: NO MORE WORK FOR CHILD %s.  "
+                         "(%d of %d children now done)",
+                         machineName[childNum], numFinished, numMachines);
+        } else {
+            WriteInt(otherSock, nextFrame);
+            WriteInt(otherSock, nextFrame + nFrames - 1);
+
+            machineDebug("MASTER: Frames %d-%d assigned to child %s",
+                         nextFrame, nextFrame + nFrames - 1,
+                         machineName[childNum]);
+
+            csP->startFrame    = nextFrame;
+            csP->lastNumFrames = nFrames;
+            csP->numFrames    += csP->lastNumFrames;
+        }
+        close(otherSock);
+
+        machineDebug("MASTER: %d/%d DONE; %d ARE ASSIGNED",
+                     framesDone, schedulerP->numFramesInJob, 
+                     schedulerP->nextFrame - framesDone);
+    }
+}
+
+
+
+static void
+stopIoServers(const char * const hostName,
+              int          const ioPortNum[],
+              unsigned int const numIoServers) {
+
+    unsigned int childNum;
+
+    IOhostName = hostName;
+    for (childNum = 0; childNum < numIoServers; ++childNum) {
+        ioPortNumber = ioPortNum[childNum];
+        EndIOServer();
+    }
+}
+
+
+
+static void
+waitForCombineServerToTerminate(int const masterSocket) {
+
+    int otherSock;
+    const char * error;
+
+    machineDebug("MASTER SERVER: Waiting for combine server to terminate");
+
+    AcceptConnection(masterSocket, &otherSock, &error);
+    if (error)
+        errorExit("MASTER SERVER: "
+                  "Failed to accept connection expected from a "
+                  "terminating combine server.  %s", error);
+
+    {
+        int dummy;
+        ReadInt(otherSock, &dummy);
+    }
+    close(otherSock);
+}
+    
+
+
+static void
+printFinalStats(FILE *            const statfileP,
+                time_t            const startUpBegin,
+                time_t            const startUpEnd,
+                time_t            const shutDownBegin,
+                time_t            const shutDownEnd,
+                unsigned int      const numChildren,
+                struct childState const childState[],
+                unsigned int      const numFrames) {
+
+    unsigned int pass;
+    FILE * fileP;
+
+    for (pass = 0; pass < 2; ++pass) {
+        if (pass == 0)
+            fileP = stdout;
+        else
+            fileP = statfileP;
+
+        if (fileP) {
+            unsigned int childNum;
+            float totalFPS;
+
+            fprintf(fileP, "\n\n");
+            fprintf(fileP, "PARALLEL SUMMARY\n");
+            fprintf(fileP, "----------------\n");
+            fprintf(fileP, "\n");
+            fprintf(fileP, "START UP TIME:  %u seconds\n",
+                    (unsigned int)(startUpEnd - startUpBegin));
+            fprintf(fileP, "SHUT DOWN TIME:  %u seconds\n",
+                    (unsigned int)(shutDownEnd - shutDownBegin));
+            
+            fprintf(fileP, 
+                    "%14.14s %8.8s %8.8s %12.12s %9.9s\n",
+                    "MACHINE", "Frames", "Seconds", "Frames/Sec",
+                    "Self Time");
+
+            fprintf(fileP, 
+                    "%14.14s %8.8s %8.8s %12.12s %9.9s\n",
+                    "--------------", "--------", "--------", "------------",
+                    "---------");
+
+            totalFPS = 0.0;
+            for (childNum = 0; childNum < numChildren; ++childNum) {
+                float const localFPS = 
+                    (float)childState[childNum].numFrames /
+                    childState[childNum].numSeconds;
+                fprintf(fileP, "%14.14s %8u %8u %12.4f %8u\n",
+                        machineName[childNum], 
+                        childState[childNum].numFrames, 
+                        childState[childNum].numSeconds,
+                        localFPS, 
+                        (unsigned int)((float)numFrames/localFPS));
+                totalFPS += localFPS;
+            }
+
+            fprintf(fileP, 
+                    "%14.14s %8.8s %8.8s %12.12s %9.9s\n",
+                    "--------------", "--------", "--------", "------------",
+                    "---------");
+
+            fprintf(fileP, "%14s %8.8s %8u %12.4f\n", 
+                    "OPTIMAL", "", 
+                    (unsigned int)((float)numFrames/totalFPS),
+                    totalFPS);
+            
+            {
+                unsigned int const diffTime = shutDownEnd - startUpBegin;
+                
+                fprintf(fileP, "%14s %8.8s %8u %12.4f\n", 
+                        "ACTUAL", "", diffTime, 
+                        (float)numFrames / diffTime);
+            }
+            fprintf(fileP, "\n\n");
+        }
+    }
+}
+
+
+
+void
+MasterServer(struct inputSource * const inputSourceP,
+             const char *         const paramFileName, 
+             const char *         const outputFileName) {
+/*----------------------------------------------------------------------------
+   Execute the master server function.
+
+   Start all the other servers.
+-----------------------------------------------------------------------------*/
+    const char *hostName;
+    int       portNum;
+    int       masterSocket;
+        /* The file descriptor for the socket on which the master listens */
+    int ioPortNum[MAX_IO_SERVERS];
+    int       combinePortNum, decodePortNum;
+    struct childState * childState;  /* malloc'ed */
+    unsigned int numIoServers;
+    time_t  startUpBegin, startUpEnd;
+    time_t  shutDownBegin, shutDownEnd;
+    const char * error;
+    struct scheduler scheduler;
+
+    time(&startUpBegin);
+
+    scheduler.nextFrame = 0;
+    scheduler.numFramesInJob = inputSourceP->numInputFiles;
+    scheduler.numMachines = numMachines;
+
+    PrintStartStats(startUpBegin, FALSE, 0, 0, inputSourceP);
+
+    hostName = GetHostName();
+
+    hostEntry = gethostbyname(hostName);
+    if (hostEntry == NULL)
+        errorExit("Could not find host name '%s' in database", hostName);
+
+    CreateListeningSocket(&masterSocket, &portNum, &error);
+    if (error)
+        errorExit("Unable to create socket on which to listen.  %s", error);
+
+    if (debugSockets)
+        fprintf(stdout, "---MASTER USING PORT %d\n", portNum);
+
+    startCombineServer(encoder_name, numMachines, hostName, portNum,
+                       inputSourceP->numInputFiles, 
+                       paramFileName, masterSocket, 
+                       &combinePortNum);
+
+    if (referenceFrame == DECODED_FRAME)
+        startDecodeServer(encoder_name, numMachines, hostName, portNum,
+                          inputSourceP->numInputFiles, 
+                          paramFileName, masterSocket,
+                          &decodePortNum);
+
+    startChildren(&scheduler, encoder_name, hostName, portNum,
+                  paramFileName, parallelPerfect, forceIalign,
+                  framePatternLen, parallelTestFrames, 
+                  niceProcesses,
+                  masterSocket, combinePortNum, decodePortNum, 
+                  ioPortNum, &numIoServers,
+                  &childState);
+
+    time(&startUpEnd);
+
+    feedTheChildren(&scheduler, childState,
+                    masterSocket, hostName, combinePortNum,
+                    forceIalign, framePatternLen,
+                    parallelTimeChunks != -1, parallelTimeChunks);
+
+    assert(scheduler.nextFrame == scheduler.numFramesInJob);
+
+    time(&shutDownBegin);
+
+    stopIoServers(hostName, ioPortNum, numIoServers);
+
+    waitForCombineServerToTerminate(masterSocket);
+
+    close(masterSocket);
+
+    time(&shutDownEnd);
+
+    printFinalStats(statFile, startUpBegin, startUpEnd,
+                    shutDownBegin, shutDownEnd, numMachines,
+                    childState, inputSourceP->numInputFiles);
+
+    if (statFile)
+        fclose(statFile);
+
+    free(childState);
+    strfree(hostName);
+}
+
+
+
+void
+NotifyMasterDone(const char * const masterHostName, 
+                 int          const masterPortNum, 
+                 int          const childNum,
+                 unsigned int const seconds, 
+                 boolean *    const moreWorkToDoP,
+                 int *        const nextFrameStartP,
+                 int *        const nextFrameEndP) {
+/*----------------------------------------------------------------------------
+   Tell the master, at 'masterHostName':'masterPortNum' that child
+   number 'childNum' has finished its assignment, and the decoding
+   took 'seconds' wall clock seconds.  Get the next assignment, if
+   any, from the master.
+
+   If the master gives us a new assignment, return *moreWorkToDoP ==
+   TRUE and the frames the master wants us to do as *nextFrameStartP
+   and nextFrameEndP.  Otherwise (there is no more work for machine
+   'childNum' to do), return *moreWorkToDoP == FALSE.
+-----------------------------------------------------------------------------*/
+    int    clientSocket;
+    time_t tempTimeStart, tempTimeEnd;
+    const char * error;
+
+    machineDebug("CHILD: NOTIFYING MASTER Machine %d assignment complete", 
+                 childNum);
+
+    time(&tempTimeStart);
+    
+    ConnectToSocket(masterHostName, masterPortNum, &hostEntry,
+                    &clientSocket, &error);
+    if (error)
+        errorExit("CHILD: Can't connect to master to tell him we've finished "
+                  "our assignment.  %s", error);
+
+    WriteInt(clientSocket, childNum);
+    WriteInt(clientSocket, seconds);
+
+    ReadInt(clientSocket, nextFrameStartP);
+    ReadInt(clientSocket, nextFrameEndP);
+
+    *moreWorkToDoP = (*nextFrameStartP >= 0);
+
+    if (*moreWorkToDoP)
+        machineDebug("CHILD: Master says next assignment: start %d end %d",
+                     *nextFrameStartP, *nextFrameEndP);
+    else
+        machineDebug("CHILD: Master says no more work for us.");
+
+    close(clientSocket);
+
+    time(&tempTimeEnd);
+    IOtime += (tempTimeEnd-tempTimeStart);
+}
+
+
+
+void
+DecodeServer(int          const numInputFiles, 
+             const char * const decodeFileName, 
+             const char * const masterHostName, 
+             int          const masterPortNum) {
+/*----------------------------------------------------------------------------
+   Execute the decode server.
+
+   The decode server handles transfer of decoded frames to/from processes.
+
+   It is necessary only if referenceFrame == DECODED_FRAME.
+
+   Communicate to the master at hostname 'masterHostName':'masterPortNum'.
+
+-----------------------------------------------------------------------------*/
+    int     otherSock;
+    int     decodePortNum;
+    int     frameReady;
+    boolean *ready;
+    int     *waitMachine;
+    int     *waitPort;
+    int     *waitList;
+    int     slaveNumber;
+    int     slavePort;
+    int     waitPtr;
+    struct hostent *nullHost = NULL;
+    int     clientSocket;
+    const char * error;
+
+    /* should keep list of port numbers to notify when frames become ready */
+
+    ready = (boolean *) calloc(numInputFiles, sizeof(boolean));
+    waitMachine = (int *) calloc(numInputFiles, sizeof(int));
+    waitPort = (int *) malloc(numMachines*sizeof(int));
+    waitList = (int *) calloc(numMachines, sizeof(int));
+
+    CreateListeningSocket(&decodeServerSocket, &decodePortNum, &error);
+    if (error)
+        errorExit("Unable to create socket on which to listen.  %s", error);
+
+    machineDebug("DECODE SERVER LISTENING ON PORT %d", decodePortNum);
+
+    TransmitPortNum(masterHostName, masterPortNum, decodePortNum);
+
+    frameDone = (boolean *) malloc(numInputFiles*sizeof(boolean));
+    memset((char *)frameDone, 0, numInputFiles*sizeof(boolean));
+
+    /* wait for ready signals and requests */
+    while ( TRUE ) {
+        const char * error;
+
+        AcceptConnection(decodeServerSocket, &otherSock, &error);
+        if (error)
+            errorExit("DECODE SERVER: "
+                      "Failed to accept next connection.  %s", error);
+
+        ReadInt(otherSock, &frameReady);
+
+        if ( frameReady == -2 ) {
+            ReadInt(otherSock, &frameReady);
+
+            machineDebug("DECODE SERVER:  REQUEST FOR FRAME %d", frameReady);
+
+            /* now respond if it's ready yet */
+            WriteInt(otherSock, frameDone[frameReady]);
+
+            if ( ! frameDone[frameReady] ) {
+                /* read machine number, port number */
+                ReadInt(otherSock, &slaveNumber);
+                ReadInt(otherSock, &slavePort);
+
+                machineDebug("DECODE SERVER: WAITING:  SLAVE %d, PORT %d",
+                             slaveNumber, slavePort);
+
+                waitPort[slaveNumber] = slavePort;
+                if ( waitMachine[frameReady] == 0 ) {
+                    waitMachine[frameReady] = slaveNumber+1;
+                } else {
+                    /* someone already waiting for this frame */
+                    /* follow list of waiters to the end */
+                    waitPtr = waitMachine[frameReady]-1;
+                    while ( waitList[waitPtr] != 0 ) {
+                        waitPtr = waitList[waitPtr]-1;
+                    }
+
+                    waitList[waitPtr] = slaveNumber+1;
+                    waitList[slaveNumber] = 0;
+                }
+            }
+        } else {
+            frameDone[frameReady] = TRUE;
+            
+            machineDebug("DECODE SERVER:  FRAME %d READY", frameReady);
+
+            if ( waitMachine[frameReady] ) {
+                /* need to notify one or more machines it's ready */
+                waitPtr = waitMachine[frameReady]-1;
+                while ( waitPtr >= 0 ) {
+                    const char * error;
+                    ConnectToSocket(machineName[waitPtr], waitPort[waitPtr],
+                                    &nullHost,
+                                    &clientSocket, &error);
+                    if (error)
+                        errorExit("DECODE SERVER: "
+                                  "Can't connect to child machine.  %s", 
+                                  error);
+                    close(clientSocket);
+                    waitPtr = waitList[waitPtr]-1;
+                }
+            }
+        }
+
+        close(otherSock);
+    }
+    
+    machineDebug("DECODE SERVER:  Shutting down");
+
+    /* tell Master server we are done */
+    TransmitPortNum(masterHostName, masterPortNum, decodePortNum);
+
+    close(decodeServerSocket);
+}
+
+
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
diff --git a/converter/ppm/ppmtompeg/param.c b/converter/ppm/ppmtompeg/param.c
new file mode 100644
index 00000000..5ea69ab6
--- /dev/null
+++ b/converter/ppm/ppmtompeg/param.c
@@ -0,0 +1,1077 @@
+/*===========================================================================*
+ * param.c              
+ *                      
+ *  Procedures to read in parameter file  
+ *                                    
+ *===========================================================================*/
+
+/* COPYRIGHT INFORMATION IS AT THE END OF THIS FILE */
+
+#define _XOPEN_SOURCE 500
+    /* This makes sure popen() is in stdio.h.  In GNU libc 2.1.3, 
+     _POSIX_C_SOURCE = 2 is sufficient, but on AIX 4.3, the higher level
+     _XOPEN_SOURCE is required.  2000.09.09 
+
+     This also makes sure strdup() is in string.h.
+    */
+#define _BSD_SOURCE 1
+    /* Make sure string.h defines strncasecmp */
+
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "nstring.h"
+#include "mallocvar.h"
+#include "all.h"
+#include "mtypes.h"
+#include "mpeg.h"
+#include "motion_search.h"
+#include "prototypes.h"
+#include "parallel.h"
+#include "readframe.h"
+#include "fsize.h"
+#include "frames.h"
+#include "jpeg.h"
+#include "input.h"
+#include "frametype.h"
+#include "param.h"
+
+#include "rate.h"
+#include "opts.h"
+
+/*===========*
+ * CONSTANTS *
+ *===========*/
+
+#define INPUT_ENTRY_BLOCK_SIZE   128
+
+#define FIRST_OPTION           0
+#define OPTION_GOP             0
+#define OPTION_PATTERN         1
+#define OPTION_PIXEL           2
+#define OPTION_PQSCALE         3
+#define OPTION_OUTPUT          4
+#define OPTION_RANGE           5
+#define OPTION_PSEARCH_ALG     6
+#define OPTION_IQSCALE         7
+#define OPTION_INPUT_DIR       8
+#define OPTION_INPUT_CONVERT   9
+#define OPTION_INPUT          10
+#define OPTION_BQSCALE        11
+#define OPTION_BASE_FORMAT    12
+#define OPTION_SPF            13
+#define OPTION_BSEARCH_ALG    14
+#define OPTION_REF_FRAME      15
+#define LAST_OPTION           15
+
+/* put any non-required options after LAST_OPTION */
+#define OPTION_RESIZE	      16
+#define OPTION_IO_CONVERT     17
+#define OPTION_SLAVE_CONVERT  18
+#define OPTION_IQTABLE	      19
+#define OPTION_NIQTABLE	      20
+#define OPTION_FRAME_RATE     21
+#define OPTION_ASPECT_RATIO   22
+#define OPTION_YUV_SIZE	      23
+#define OPTION_SPECIFICS      24
+#define OPTION_DEFS_SPECIFICS 25
+#define OPTION_BUFFER_SIZE    26
+#define OPTION_BIT_RATE       27
+#define OPTION_USER_DATA      28
+#define OPTION_YUV_FORMAT     29
+#define OPTION_GAMMA          30
+#define OPTION_PARALLEL       31
+#define OPTION_FRAME_INPUT    32
+#define OPTION_GOP_INPUT      33
+
+#define NUM_OPTIONS           33
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+extern char currentPath[MAXPATHLEN];
+char	outputFileName[256];
+int	outputWidth, outputHeight;
+char inputConversion[1024];
+char ioConversion[1024];
+char slaveConversion[1024];
+char yuvConversion[256];
+char specificsFile[256],specificsDefines[1024]="";
+boolean GammaCorrection=FALSE;
+float   GammaValue;
+char userDataFileName[256]={0};
+boolean specificsOn = FALSE;
+char currentGOPPath[MAXPATHLEN];
+char currentFramePath[MAXPATHLEN];
+boolean keepTempFiles;
+
+static const char * const optionText[LAST_OPTION+1] = { 
+    "GOP_SIZE", "PATTERN", "PIXEL", "PQSCALE",
+    "OUTPUT", "RANGE", "PSEARCH_ALG", "IQSCALE", "INPUT_DIR",
+    "INPUT_CONVERT", "INPUT", "BQSCALE", "BASE_FILE_FORMAT",
+    "SLICES_PER_FRAME", "BSEARCH_ALG", "REFERENCE_FRAME"};
+static boolean optionSeen[NUM_OPTIONS+1];
+    /* optionSeen[x] means we have seen option x in the parameter file we've
+       been reading.
+    */
+
+int numMachines;
+char	machineName[MAX_MACHINES][256];
+char	userName[MAX_MACHINES][256];
+char	executable[MAX_MACHINES][1024];
+char	remoteParamFile[MAX_MACHINES][1024];
+boolean	remote[MAX_MACHINES];
+int mult_seq_headers = 0;  /* 0 for none, N for header/N GOPs */
+
+
+/*===========================================================================*
+ *
+ * SkipSpacesTabs
+ *
+ *	skip all spaces and tabs
+ *
+ * RETURNS:	point to next character not a space or tab
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static const char *
+SkipSpacesTabs(const char * const start) {
+
+    const char * p;
+
+    for (p = start; *p == ' ' || *p == '\t'; ++p);
+
+    return p;
+}
+
+
+
+/*===========================================================================*
+ *
+ * GetAspectRatio
+ *
+ * take a character string with the pixel aspect ratio
+ * and returns the correct aspect ratio code for use in the Sequence header
+ *
+ * RETURNS: aspect ratio code as per MPEG-I spec
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static int
+GetAspectRatio(const char * const p)
+{
+  float   ratio;
+  int	  ttRatio;
+  int     retval;
+
+  sscanf(p, "%f", &ratio);
+  ttRatio = (int)(0.5+ratio*10000.0);
+
+  if ( ttRatio == 10000 )	      retval = 1;
+  else if ( ttRatio ==  6735 )    retval = 2;
+  else if ( ttRatio ==  7031 )    retval = 3;
+  else if ( ttRatio ==  7615 )    retval = 4;
+  else if ( ttRatio ==  8055 )    retval = 5;
+  else if ( ttRatio ==  8437 )    retval = 6;
+  else if ( ttRatio ==  8935 )    retval = 7;
+  else if ( ttRatio ==  9157 )    retval = 8;
+  else if ( ttRatio ==  9815 )    retval = 9;
+  else if ( ttRatio == 10255 )    retval = 10;
+  else if ( ttRatio == 10695 )    retval = 11;
+  else if ( ttRatio == 10950 )    retval = 12;
+  else if ( ttRatio == 11575 )    retval = 13;
+  else if ( ttRatio == 12015 )    retval = 14;
+  else {
+    fprintf(stderr,"INVALID ASPECT RATIO: %s frames/sec\n", p);
+    exit(1);
+  }
+  return retval;
+}
+
+
+
+/*===========================================================================*
+ *
+ * ReadMachineNames
+ *
+ *	read a list of machine names for parallel execution
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    machine info updated
+ *
+ *===========================================================================*/
+static void
+ReadMachineNames(FILE * const fpointer)
+{
+  char    input[256];
+  const char *charPtr;
+
+  while ( (fgets(input, 256, fpointer) != NULL) &&
+	 (strncmp(input, "END_PARALLEL", 12) != 0) ) {
+    if ( input[0] == '#' || input[0] == '\n') {
+      continue;
+    }
+
+    if ( strncmp(input, "REMOTE", 6) == 0 ) {
+      charPtr = SkipSpacesTabs(&input[6]);
+      remote[numMachines] = TRUE;
+
+      sscanf(charPtr, "%s %s %s %s", machineName[numMachines],
+	     userName[numMachines], executable[numMachines],
+	     remoteParamFile[numMachines]);
+    } else {
+      remote[numMachines] = FALSE;
+
+      sscanf(input, "%s %s %s", machineName[numMachines],
+	     userName[numMachines], executable[numMachines]);
+    }
+
+    numMachines++;
+  }
+}
+
+
+/*===========================================================================*
+ *
+ * GetFrameRate
+ *
+ * take a character string with the input frame rate 
+ * and return the correct frame rate code for use in the Sequence header
+ *
+ * RETURNS: frame rate code as per MPEG-I spec
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static int
+GetFrameRate(const char * const p)
+{
+  float   rate;
+  int	  thouRate;
+  int     retval;
+
+  sscanf(p, "%f", &rate);
+  thouRate = (int)(0.5+1000.0*rate);
+
+  if ( thouRate == 23976 )	       retval = 1;
+  else if ( thouRate == 24000 )    retval = 2;
+  else if ( thouRate == 25000 )    retval = 3;
+  else if ( thouRate == 29970 )    retval = 4;
+  else if ( thouRate == 30000 )    retval = 5;
+  else if ( thouRate == 50000 )    retval = 6;
+  else if ( thouRate == 59940 )    retval = 7;
+  else if ( thouRate == 60000 )    retval = 8;
+  else {
+    fprintf(stderr,"INVALID FRAME RATE: %s frames/sec\n", p);
+    exit(1);
+  }
+  return retval;
+}
+
+
+
+static void
+mergeInputSource(struct inputSource * const baseSourceP,
+                 struct inputSource * const addedSourceP) {
+
+    unsigned int i;
+
+    baseSourceP->ifArraySize += addedSourceP->numInputFileEntries;
+    REALLOCARRAY_NOFAIL(baseSourceP->inputFileEntries, 
+                        baseSourceP->ifArraySize);
+    for (i = 0; i < addedSourceP->numInputFileEntries; ++i)
+        baseSourceP->inputFileEntries[baseSourceP->numInputFileEntries++] =
+            addedSourceP->inputFileEntries[i];
+
+    free(addedSourceP);
+    /* Note the space allocated for the *addedSourceP input file
+       entries themselves is still allocated, and used by 
+       *baseSourceP.
+    */
+}
+
+
+
+/* Forward declaration for recursively called function */
+static void
+ReadInputFileNames(FILE *               const fpointer, 
+                   const char *         const endInput,
+                   struct inputSource * const inputSourceP);
+
+static void
+expandBackTickLine(const char *         const input,
+                   struct inputSource * const inputSourceP) {
+
+    FILE *fp;
+    char cmd[300];
+    const char * start;
+    const char * end;
+    char cdcmd[110];
+
+    start = &input[1];
+    end = &input[strlen(input)-1];
+
+    while (*end != '`') {
+        end--;
+    }
+
+    end--;
+
+    if (optionSeen[OPTION_INPUT_DIR])
+        sprintf(cdcmd,"cd %s;",currentPath);
+    else
+        strcpy(cdcmd,"");
+
+    {
+        char tmp[300];
+        strncpy(tmp,start,end-start+1);
+        sprintf(cmd,"(%s %s)", cdcmd, tmp);
+    }
+
+    fp = popen(cmd, "r");
+    if (fp == NULL) {
+        pm_error("Unable to resolve backtick entry in input file list.  "
+                 "Could not open piped command: '%s'", cmd);
+    } else {
+        struct inputSource subInputSource;
+        ReadInputFileNames(fp, "HOPE-THIS_ISNT_A_FILENAME.xyz5555",
+                           &subInputSource);
+
+        mergeInputSource(inputSourceP, &subInputSource);
+    }
+}
+
+
+
+static void
+ReadInputFileNames(FILE *               const fpointer, 
+                   const char *         const endInput,
+                   struct inputSource * const inputSourceP) {
+/*----------------------------------------------------------------------------
+   Read a list of input file names from the parameter file.  Add 
+   the information to *inputSourceP.
+
+   If inputSourceP == NULL, read off the list, but ignore it.
+-----------------------------------------------------------------------------*/
+    char input[256];
+    bool endStatementRead;
+
+    /* read param file up through endInput statement.  Each line until
+       endInput is a file specification.
+    */
+    endStatementRead = FALSE;
+
+    while (!endStatementRead) {
+        char * rc;
+        rc = fgets(input, sizeof(input), fpointer);
+
+        if (feof(fpointer))
+            pm_error("End of file before section end statement '%s'",
+                     endInput);
+        else if (rc == NULL)
+            pm_error("Error reading file names from parameter file.");
+        else {
+            if (strncmp(input, endInput, strlen(endInput)) == 0)
+                endStatementRead = TRUE;
+            else if ((input[0] == '#') || (input[0] == '\n')) { 
+                /* It's a comment or blank line.  Ignore it */
+            } else if (input[0] == '`' ) {
+                expandBackTickLine(input, inputSourceP);
+            } else {
+                /* get rid of trailing whitespace including newline */
+                while (ISSPACE(input[strlen(input)-1]))
+                    input[strlen(input)-1] = '\0';
+                if (inputSourceP)
+                    AddInputFiles(inputSourceP, input);
+            }
+        }
+    }
+}
+
+
+
+static void
+initOptionSeen(void) {
+
+    unsigned int index;
+    
+    for (index = FIRST_OPTION; index < NUM_OPTIONS; ++index)
+        optionSeen[index] = FALSE;
+}
+
+
+
+static void
+verifyNoMissingEncodeFramesOption(void) {
+    if (!optionSeen[OPTION_GOP])
+        pm_error("GOP_SIZE option missing");
+    if (!optionSeen[OPTION_PATTERN])
+        pm_error("PATTERN option missing");
+    if (!optionSeen[OPTION_PIXEL])
+        pm_error("PIXEL option missing");
+    if (!optionSeen[OPTION_PQSCALE])
+        pm_error("PQSCALE option missing");
+    if (!optionSeen[OPTION_OUTPUT])
+        pm_error("OUTPUT option missing");
+    if (!optionSeen[OPTION_RANGE])
+        pm_error("RANGE option missing");
+    if (!optionSeen[OPTION_PSEARCH_ALG])
+        pm_error("PSEARCH_ALG option missing");
+    if (!optionSeen[OPTION_IQSCALE])
+        pm_error("IQSCALE option missing");
+    if (!optionSeen[OPTION_INPUT_DIR])
+        pm_error("INPUT_DIR option missing");
+    if (!optionSeen[OPTION_INPUT_CONVERT])
+        pm_error("INPUT_CONVERT option missing");
+    if (!optionSeen[OPTION_BQSCALE])
+        pm_error("BQSCALE option missing");
+    if (!optionSeen[OPTION_BASE_FORMAT])
+        pm_error("BASE_FILE_FORMAT option missing");
+    if (!optionSeen[OPTION_SPF])
+        pm_error("SLICES_PER_FRAME option missing");
+    if (!optionSeen[OPTION_BSEARCH_ALG])
+        pm_error("BSEARCH_ALG option missing");
+    if (!optionSeen[OPTION_REF_FRAME])
+        pm_error("REFERENCE_FRAME option missing");
+}
+
+
+
+static void
+verifyNoMissingCombineGopsOption(void) {
+
+    if (!optionSeen[OPTION_GOP_INPUT])
+        pm_error("GOP_INPUT option missing");
+    if (!optionSeen[OPTION_YUV_SIZE])
+        pm_error("YUV_SIZE option missing");
+    if (!optionSeen[OPTION_OUTPUT])
+        pm_error("OUTPUT option missing");
+}
+
+
+
+static void
+verifyNoMissingCombineFramesOption(void) {
+
+    if (!optionSeen[OPTION_GOP])
+        pm_error("GOP_SIZE option missing");
+    if (!optionSeen[OPTION_YUV_SIZE])
+        pm_error("YUV_SIZE option missing");
+    if (!optionSeen[OPTION_OUTPUT])
+        pm_error("OUTPUT option missing");
+}
+
+
+
+static void
+verifyNoMissingOption(int  const function) {
+/*----------------------------------------------------------------------------
+  Verify that the parameter file contains every option it is supposed to. 
+  Abort program if not.
+-----------------------------------------------------------------------------*/
+    switch(function) {
+    case ENCODE_FRAMES:
+        verifyNoMissingEncodeFramesOption();
+        break;
+    case COMBINE_GOPS:
+        verifyNoMissingCombineGopsOption();
+        break;
+    case COMBINE_FRAMES:
+        verifyNoMissingCombineFramesOption();
+        break;
+    default:
+        pm_error("Internal error - impossible value for 'function': %d",
+                 function);
+    }
+}
+
+
+
+static void
+processIqtable(FILE * const fpointer) {
+
+    unsigned int row;
+    unsigned int col;
+    char input[256];
+
+    for (row = 0; row < 8; ++row) {
+        const char * charPtr;
+
+        fgets(input, 256, fpointer);
+        charPtr = input;
+        if (8 != sscanf(charPtr,"%d %d %d %d %d %d %d %d",
+                        &qtable[row*8+0],  &qtable[row*8+1],
+                        &qtable[row*8+2],  &qtable[row*8+3],
+                        &qtable[row*8+4],  &qtable[row*8+5],
+                        &qtable[row*8+6],  &qtable[row*8+7])) {
+            pm_error("Line %d of IQTABLE doesn't have 8 elements!", 
+                     row);
+        }
+        for (col = 0; col < 8; ++col) {
+            if ((qtable[row*8+col]<1) || (qtable[row*8+col]>255)) {
+                pm_message(
+                    "Warning:  IQTable Element %1d,%1d (%d) "
+                    "corrected to 1-255.",
+                    row+1, col+1, qtable[row*8+col]);
+                qtable[row*8+col] = (qtable[row*8+col]<1)?1:255;
+            }
+        }
+    }
+            
+    if (qtable[0] != 8) {
+        pm_message("Warning:  IQTable Element 1,1 reset to 8, "
+                   "since it must be 8.");
+        qtable[0] = 8;
+    }
+    customQtable = qtable;
+}
+
+
+
+static void
+setInputSource(int                   const function,
+               struct inputSource ** const inputSourcePP,
+               struct inputSource *  const inputSourceP,
+               struct inputSource *  const gopInputSourceP,
+               struct inputSource *  const frameInputSourceP) {
+/*----------------------------------------------------------------------------
+   Given three the three input sources described by the parameter
+   file, 'inputSourceP', 'gopInputSourceP', and 'frameInputSourceP',
+   return as *inputSourcePP the appropriate one of them for the
+   function 'function', and destroy the other two.
+-----------------------------------------------------------------------------*/
+    switch (function) {
+    case ENCODE_FRAMES:
+        *inputSourcePP = inputSourceP;
+        DestroyInputSource(gopInputSourceP);
+        DestroyInputSource(frameInputSourceP);
+        break;
+    case COMBINE_GOPS:
+        *inputSourcePP = gopInputSourceP;
+        DestroyInputSource(inputSourceP);
+        DestroyInputSource(frameInputSourceP);
+        break;
+    case COMBINE_FRAMES:
+        *inputSourcePP = frameInputSourceP;
+        DestroyInputSource(inputSourceP);
+        DestroyInputSource(gopInputSourceP);
+        break;
+    default:
+      pm_error("INTERNAL ERROR: invalid 'function' %d", function);
+    }
+}
+
+
+
+/*=====================*
+ * EXPORTED PROCEDURES *
+ *=====================*/
+
+
+
+static void
+removeTrailingWhite(const char *  const rawLine,
+                    const char ** const editedLineP) {
+
+    char * buffer;
+    char * p;
+
+    buffer = strdup(rawLine);
+
+    if (!buffer)
+        pm_error("Unable to get memory to process parameter file");
+
+    p = buffer + strlen(buffer) - 1;  /* Point to last character */
+    
+    /* Position p to just before the trailing white space (might be one
+       character before beginning of string!)
+    */
+    while (p >= buffer && isspace(*p))
+        --p;
+    
+    ++p;  /* Move to first trailing whitespace character */
+    
+    *p = '\0';  /* Chop off all the trailing whitespace */
+
+    *editedLineP = buffer;
+}
+
+
+
+static void
+readNiqTable(FILE * const fpointer) {
+
+    unsigned int row;
+    for (row = 0; row < 8; ++row) {
+        char input[256];
+        unsigned int col;
+        fgets(input, sizeof(input), fpointer);
+        if (8 != sscanf(input, "%d %d %d %d %d %d %d %d",
+                        &niqtable[row*8+0],  &niqtable[row*8+1],
+                        &niqtable[row*8+2],  &niqtable[row*8+3],
+                        &niqtable[row*8+4],  &niqtable[row*8+5],
+                        &niqtable[row*8+6],  &niqtable[row*8+7])) {
+            pm_error("Line %d of NIQTABLE doesn't have 8 elements!", 
+                     row);
+        }
+        for ( col = 0; col < 8; col++ ) {
+            if ((niqtable[row*8+col]<1) || (niqtable[row*8+col]>255)) {
+                pm_message(
+                    "Warning:  NIQTable Element %1d,%1d (%d) "
+                    "corrected to 1-255.",
+                    row + 1, col + 1, niqtable[row * 8 + col]);
+                niqtable[row * 8 + col] = (niqtable[row*8+col] < 1) ? 1 : 255;
+            }
+        }
+    }
+
+    customNIQtable = niqtable;
+}
+
+
+
+static void
+processRanges(const char * const arg) {
+
+    int numRanges;
+    int a, b;
+
+    numRanges = sscanf(arg, "%d %d", &a, &b);
+
+    if (numRanges == 2)
+        SetSearchRange(a, b);
+    else if (sscanf(arg, "%d [%d]", &a, &b) == 2)
+        SetSearchRange(a, b);
+    else
+        SetSearchRange(a, a);
+}
+
+
+
+static void
+processParamLine(char const input[],
+                 FILE * const fpointer,
+                 bool * const yuvUsedP,
+                 struct inputSource * const inputSourceP,
+                 struct inputSource * const frameInputSourceP,
+                 struct inputSource * const gopInputSourceP,
+                 struct params * const paramP) {
+
+    switch(input[0]) {
+    case 'A':
+        if (STRNEQ(input, "ASPECT_RATIO", 12)) {
+            aspectRatio = GetAspectRatio(SkipSpacesTabs(&input[12]));
+            optionSeen[OPTION_ASPECT_RATIO] = TRUE;
+        }
+        break;
+        
+    case 'B':
+        if (STRNEQ(input, "BQSCALE", 7)) {
+            SetBQScale(atoi(SkipSpacesTabs(&input[7])));
+            optionSeen[OPTION_BQSCALE] = TRUE;
+        } else if (STRNEQ(input, "BASE_FILE_FORMAT", 16)) {
+            const char * arg = SkipSpacesTabs(&input[16]);
+            SetFileFormat(arg);
+            if (STRNEQ(arg, "YUV", 3) || STREQ(arg, "Y"))
+                *yuvUsedP = TRUE;
+            optionSeen[OPTION_BASE_FORMAT] = TRUE;
+        } else if (STRNEQ(input, "BSEARCH_ALG", 11)) {
+            SetBSearchAlg(SkipSpacesTabs(&input[11]));
+            optionSeen[OPTION_BSEARCH_ALG] = TRUE;
+        } else if (STRNEQ(input, "BIT_RATE", 8)) {
+            setBitRate(SkipSpacesTabs(&input[8]));
+            optionSeen[OPTION_BIT_RATE] = TRUE;
+        } else if (STRNEQ(input, "BUFFER_SIZE", 11)) {
+            setBufferSize(SkipSpacesTabs(&input[11]));
+            optionSeen[OPTION_BUFFER_SIZE] = TRUE;                  
+        }
+        break;
+
+    case 'C':
+        if (STRNEQ(input, "CDL_FILE", 8)) {
+            strcpy(specificsFile, SkipSpacesTabs(&input[8]));
+            specificsOn = TRUE;
+            optionSeen[OPTION_SPECIFICS] = TRUE;
+        } else if (STRNEQ(input, "CDL_DEFINES", 11)) {
+            strcpy(specificsDefines, SkipSpacesTabs(&input[11]));
+            optionSeen[OPTION_DEFS_SPECIFICS] = TRUE;
+        }
+        break;
+
+    case 'F':
+        if (STRNEQ(input, "FRAME_INPUT_DIR", 15)) {
+            const char * const arg = SkipSpacesTabs(&input[15]);
+            if (STRNCASEEQ(arg, "stdin", 5))
+                SetStdinInput(frameInputSourceP);
+
+            strcpy(currentFramePath, arg);
+        } else if (STRNEQ(input, "FRAME_INPUT", 11)) {
+            ReadInputFileNames(fpointer, "FRAME_END_INPUT", 
+                               frameInputSourceP->stdinUsed ? 
+                               NULL : frameInputSourceP);
+            optionSeen[OPTION_FRAME_INPUT] = TRUE;
+        } else if (STRNEQ(input, "FORCE_I_ALIGN", 13)) {
+            forceIalign = TRUE;
+        } else if (STRNEQ(input, "FORCE_ENCODE_LAST_FRAME", 23)) {
+            /* NO-OP.  We used to drop trailing B frames by default and you
+               needed this option to change the last frame to I so you could
+               encode all the frames.  Now we just do that all the time.  
+               Why wouldn't we?
+            */
+        } else if (STRNEQ(input, "FRAME_RATE", 10)) {
+            frameRate = GetFrameRate(SkipSpacesTabs(&input[10]));
+            frameRateRounded = (int) VidRateNum[frameRate];
+            if ((frameRate % 3) == 1)
+                frameRateInteger = FALSE;
+            optionSeen[OPTION_FRAME_RATE] = TRUE;
+        }
+        break;
+        
+    case 'G':
+        if (STRNEQ(input, "GOP_SIZE", 8)) {
+            SetGOPSize(atoi(SkipSpacesTabs(&input[8])));
+            optionSeen[OPTION_GOP] = TRUE;
+        } else if (STRNEQ(input, "GOP_INPUT_DIR", 13)) {
+            const char * const arg = SkipSpacesTabs(&input[13]);
+            if (STRNCASEEQ(arg, "stdin", 5))
+                SetStdinInput(gopInputSourceP);
+
+            strcpy(currentGOPPath, arg);
+        } else if (STRNEQ(input, "GOP_INPUT", 9)) {
+            ReadInputFileNames(fpointer, "GOP_END_INPUT", 
+                               gopInputSourceP->stdinUsed ? 
+                               NULL : gopInputSourceP);
+            optionSeen[OPTION_GOP_INPUT] = TRUE;
+        } else if (STRNEQ(input, "GAMMA", 5)) {
+            GammaCorrection = TRUE;
+            sscanf(SkipSpacesTabs(&input[5]), "%f", &GammaValue);
+            optionSeen[OPTION_GAMMA] = TRUE;
+        }
+        break;
+        
+    case 'I':
+        if (STRNEQ(input, "IQSCALE", 7)) {
+            SetIQScale(atoi(SkipSpacesTabs(&input[7])));
+            optionSeen[OPTION_IQSCALE] = TRUE;
+        } else if (STRNEQ(input, "INPUT_DIR", 9)) {
+            const char * const arg = SkipSpacesTabs(&input[9]);
+            if (STRNCASEEQ(arg, "stdin", 5))
+                SetStdinInput(inputSourceP);
+            strcpy(currentPath, arg);
+            optionSeen[OPTION_INPUT_DIR] = TRUE;
+        } else if (STRNEQ(input, "INPUT_CONVERT", 13)) {
+            strcpy(inputConversion, SkipSpacesTabs(&input[13]));
+            optionSeen[OPTION_INPUT_CONVERT] = TRUE;
+        } else if (STREQ(input, "INPUT")) {
+            ReadInputFileNames(fpointer, "END_INPUT", 
+                               inputSourceP->stdinUsed ?
+                               NULL : inputSourceP);
+            optionSeen[OPTION_INPUT] = TRUE;
+        } else if (STRNEQ(input, "IO_SERVER_CONVERT", 17)) {
+            strcpy(ioConversion, SkipSpacesTabs(&input[17]));
+            optionSeen[OPTION_IO_CONVERT] = TRUE;
+        } else if (STRNEQ(input, "IQTABLE", 7)) {
+            processIqtable(fpointer);
+
+            optionSeen[OPTION_IQTABLE] = TRUE;
+        }
+        break;
+
+    case 'K':
+        if (STRNEQ(input, "KEEP_TEMP_FILES", 15))
+            keepTempFiles = TRUE;
+        break;
+        
+    case 'N':
+      if (STRNEQ(input, "NIQTABLE", 8)) {
+          readNiqTable(fpointer);
+
+          optionSeen[OPTION_NIQTABLE] = TRUE;
+      }
+      break;
+
+    case 'O':
+        if (STRNEQ(input, "OUTPUT", 6)) {
+            const char * const arg = SkipSpacesTabs(&input[6]);
+            if ( whichGOP == -1 )
+                strcpy(outputFileName, arg);
+            else
+                sprintf(outputFileName, "%s.gop.%d", arg, whichGOP);
+            
+            optionSeen[OPTION_OUTPUT] = TRUE;
+        }
+        break;
+        
+    case 'P':
+        if (STRNEQ(input, "PATTERN", 7)) {
+            SetFramePattern(SkipSpacesTabs(&input[7]));
+            optionSeen[OPTION_PATTERN] = TRUE;
+        } else if (STRNEQ(input, "PIXEL", 5)) {
+            SetPixelSearch(SkipSpacesTabs(&input[5]));
+            optionSeen[OPTION_PIXEL] = TRUE;
+        } else if (STRNEQ(input, "PQSCALE", 7)) {
+            SetPQScale(atoi(SkipSpacesTabs(&input[7])));
+            optionSeen[OPTION_PQSCALE] = TRUE;
+        } else if (STRNEQ(input, "PSEARCH_ALG", 11)) {
+            SetPSearchAlg(SkipSpacesTabs(&input[11]));
+            optionSeen[OPTION_PSEARCH_ALG] = TRUE;
+        } else if (STRNEQ(input, "PARALLEL_TEST_FRAMES", 20)) {
+            SetParallelPerfect(FALSE);
+            parallelTestFrames = atoi(SkipSpacesTabs(&input[20]));
+        } else if (STRNEQ(input, "PARALLEL_TIME_CHUNKS", 20)) {
+            SetParallelPerfect(FALSE);
+            parallelTimeChunks = atoi(SkipSpacesTabs(&input[20]));
+        } else if (STRNEQ(input, "PARALLEL_CHUNK_TAPER", 20)) {
+            SetParallelPerfect(FALSE);
+            parallelTimeChunks = -1;
+        } else if (STRNEQ(input, "PARALLEL_PERFECT", 16)) {
+            SetParallelPerfect(TRUE);
+        } else if (STRNEQ(input, "PARALLEL", 8)) {
+            ReadMachineNames(fpointer);
+            optionSeen[OPTION_PARALLEL] = TRUE;
+        }
+        break;
+        
+    case 'R':
+        if (STRNEQ(input, "RANGE", 5)) {
+            processRanges(SkipSpacesTabs(&input[5]));
+            optionSeen[OPTION_RANGE] = TRUE;
+        } else if (STRNEQ(input, "REFERENCE_FRAME", 15)) {
+            SetReferenceFrameType(SkipSpacesTabs(&input[15]));
+            optionSeen[OPTION_REF_FRAME] = TRUE;
+        } else if (STRNEQ(input, "RSH", 3)) {
+            SetRemoteShell(SkipSpacesTabs(&input[3]));
+        } else if (STRNEQ(input, "RESIZE", 6)) {
+            const char * const arg = SkipSpacesTabs(&input[6]);
+            sscanf(arg, "%dx%d", &outputWidth, &outputHeight);
+            outputWidth &= ~(DCTSIZE * 2 - 1);
+            outputHeight &= ~(DCTSIZE * 2 - 1);
+            optionSeen[OPTION_RESIZE] = TRUE;
+        }
+        break;
+
+    case 'S':
+        if (STRNEQ(input, "SLICES_PER_FRAME", 16)) {
+            SetSlicesPerFrame(atoi(SkipSpacesTabs(&input[16])));
+            optionSeen[OPTION_SPF] = TRUE;
+        } else if (STRNEQ(input, "SLAVE_CONVERT", 13)) {
+            strcpy(slaveConversion, SkipSpacesTabs(&input[13]));
+            optionSeen[OPTION_SLAVE_CONVERT] = TRUE;
+        } else if (STRNEQ(input, "SPECIFICS_FILE", 14)) {
+            strcpy(specificsFile, SkipSpacesTabs(&input[14]));
+            specificsOn = TRUE;
+            optionSeen[OPTION_SPECIFICS] = TRUE;
+        } else if (STRNEQ(input, "SPECIFICS_DEFINES", 16)) {
+            strcpy(specificsDefines, SkipSpacesTabs(&input[17]));
+            optionSeen[OPTION_DEFS_SPECIFICS] = TRUE;
+        } else if (STRNEQ(input, "SEQUENCE_SIZE", 13)) {
+            mult_seq_headers = atoi(SkipSpacesTabs(&input[13]));
+        } else if (STRNEQ(input, "SIZE", 4)) {
+            const char * const arg = SkipSpacesTabs(&input[4]);
+            sscanf(arg, "%dx%d", &yuvWidth, &yuvHeight);
+            realWidth = yuvWidth;
+            realHeight = yuvHeight;
+            Fsize_Validate(&yuvWidth, &yuvHeight);
+            optionSeen[OPTION_YUV_SIZE] = TRUE;
+        }
+        break;
+
+    case 'T':
+        if (STRNEQ(input, "TUNE", 4)) {
+            tuneingOn = TRUE;
+            ParseTuneParam(SkipSpacesTabs(&input[4]));
+        }
+        break;
+
+    case 'U':
+        if (STRNEQ(input, "USER_DATA", 9)) {
+            strcpy(userDataFileName, SkipSpacesTabs(&input[9]));
+            optionSeen[OPTION_USER_DATA] = TRUE;
+        }
+        break;
+        
+    case 'W':
+        if (STRNEQ(input, "WARN_UNDERFLOW", 14))
+            paramP->warnUnderflow = TRUE;
+        if (STRNEQ(input, "WARN_OVERFLOW", 13))
+            paramP->warnOverflow = TRUE;
+        break;
+        
+    case 'Y':
+        if (STRNEQ(input, "YUV_SIZE", 8)) {
+            const char * const arg = SkipSpacesTabs(&input[8]);
+            sscanf(arg, "%dx%d", &yuvWidth, &yuvHeight);
+            realWidth = yuvWidth;
+            realHeight = yuvHeight;
+            Fsize_Validate(&yuvWidth, &yuvHeight);
+            optionSeen[OPTION_YUV_SIZE] = TRUE;
+        } else if (STRNEQ(input, "Y_SIZE", 6)) {
+            const char * const arg = SkipSpacesTabs(&input[6]);
+            sscanf(arg, "%dx%d", &yuvWidth, &yuvHeight);
+            realWidth = yuvWidth;
+            realHeight = yuvHeight;
+            Fsize_Validate(&yuvWidth, &yuvHeight);
+            optionSeen[OPTION_YUV_SIZE] = TRUE;
+        } else if (STRNEQ(input, "YUV_FORMAT", 10)) {
+            strcpy(yuvConversion,  SkipSpacesTabs(&input[10]));
+            optionSeen[OPTION_YUV_FORMAT] = TRUE;
+        }
+        break;
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * ReadParamFile
+ *
+ *	read the parameter file
+ *	function is ENCODE_FRAMES, COMBINE_GOPS, or COMBINE_FRAMES, and
+ *	    will slightly modify the procedure's behavior as to what it
+ *	    is looking for in the parameter file
+ *
+ * SIDE EFFECTS:    sets parameters accordingly, as well as machine info for
+ *		    parallel execution and input file names
+ *
+ *===========================================================================*/
+void
+ReadParamFile(const char *    const fileName, 
+              int             const function,
+              struct params * const paramP) {
+
+  FILE *fpointer;
+  char    buffer[256];
+  bool yuvUsed;
+  struct inputSource * inputSourceP;
+      /* Contents of INPUT section */
+  struct inputSource * frameInputSourceP;
+      /* Contents of FRAME_INPUT section */
+  struct inputSource * gopInputSourceP;
+      /* Contents of GOP_INPUT section */
+
+  fpointer = fopen(fileName, "r");
+  if (fpointer == NULL)
+      pm_error("Error:  Cannot open parameter file:  %s", fileName);
+
+  CreateInputSource(&inputSourceP);
+  CreateInputSource(&frameInputSourceP);
+  CreateInputSource(&gopInputSourceP);
+
+  /* should set defaults */
+  numMachines = 0;
+  sprintf(currentPath, ".");
+  sprintf(currentGOPPath, ".");
+  sprintf(currentFramePath, ".");
+  SetRemoteShell("rsh");
+  keepTempFiles = FALSE;
+  paramP->warnOverflow = FALSE;
+  paramP->warnUnderflow = FALSE;
+
+  initOptionSeen();
+
+  yuvUsed = FALSE;  /* initial value */
+
+  while (fgets(buffer, 256, fpointer) != NULL) {
+      const char * input;
+
+      removeTrailingWhite(buffer, &input);
+
+      if (strlen(input) == 0) {
+          /* Ignore blank line */
+      } else if (input[0] == '#') {
+          /* Ignore comment */
+      } else
+          processParamLine(input, fpointer, &yuvUsed,
+                           inputSourceP, frameInputSourceP, gopInputSourceP,
+                           paramP);
+              /* May read additional lines from file */
+
+      strfree(input);
+  }
+
+  fclose(fpointer);
+
+  setInputSource(function, &paramP->inputSourceP,
+                 inputSourceP, gopInputSourceP, frameInputSourceP);
+
+  verifyNoMissingOption(function);
+
+  /* error checking */
+
+  if (!paramP->inputSourceP->stdinUsed &&
+      paramP->inputSourceP->numInputFiles == 0)
+      pm_error("You have not specified any input.  See the "
+               "INPUT_DIR, INPUT, GOP_INPUT_DIR, GOP_INPUT, "
+               "FRAME_INPUT_DIR, and FRAME_INPUT options");
+
+  if (yuvUsed) {
+      if (!optionSeen[OPTION_YUV_SIZE])
+          pm_error("YUV format used but YUV_SIZE not given");
+      
+      if (!optionSeen[OPTION_YUV_FORMAT]) {
+          strcpy (yuvConversion, "EYUV");
+          pm_message("WARNING:  YUV format not specified; "
+                     "defaulting to Berkeley YUV (EYUV)");
+      }
+  }
+
+  if (paramP->inputSourceP->stdinUsed && optionSeen[OPTION_PARALLEL] )
+      pm_error("stdin reading for parallel execution not enabled yet.");
+
+  if (optionSeen[OPTION_PARALLEL] && !optionSeen[OPTION_YUV_SIZE])
+      pm_error("Specify SIZE WxH for parallel encoding");
+
+  if (optionSeen[OPTION_IO_CONVERT] != optionSeen[OPTION_SLAVE_CONVERT])
+      pm_error("Must have either both IO_SERVER_CONVERT and SLAVE_CONVERT "
+               "or neither");
+      
+  if (optionSeen[OPTION_DEFS_SPECIFICS] && !optionSeen[OPTION_SPECIFICS])
+      pm_error("Does not make sense to define Specifics file options, "
+               "but no specifics file!");
+
+  SetIOConvert(optionSeen[OPTION_IO_CONVERT]);
+
+  SetResize(optionSeen[OPTION_RESIZE]);
+
+  if (function == ENCODE_FRAMES) {
+      SetFCode();
+
+      if (psearchAlg == PSEARCH_TWOLEVEL)
+          SetPixelSearch("HALF");
+  }
+}
+
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
diff --git a/converter/ppm/ppmtompeg/parse_huff.pl b/converter/ppm/ppmtompeg/parse_huff.pl
new file mode 100644
index 00000000..1fc6466c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/parse_huff.pl
@@ -0,0 +1,198 @@
+# 
+# Copyright (c) 1993 The Regents of the University of California.
+# All rights reserved.
+# 
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose, without fee, and without written agreement is
+# hereby granted, provided that the above copyright notice and the following
+# two paragraphs appear in all copies of this software.
+# 
+# IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+# OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+# CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# 
+# THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+# ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+# PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+# 
+
+#  
+#  $Header: /n/picasso/users/dwallach/vid2/mpeg_encode/RCS/parse_huff.pl,v 1.3 1993/01/18 10:56:03 dwallach Exp $
+#  $Log: parse_huff.pl,v $
+# Revision 1.3  1993/01/18  10:56:03  dwallach
+# got RCS headers in huff.c and huff.h working
+#
+# Revision 1.3  1993/01/18  10:56:03  dwallach
+# got RCS headers in huff.c and huff.h working
+#
+# Revision 1.2  1993/01/18  10:17:29  dwallach
+# RCS headers installed, code indented uniformly
+#
+# Revision 1.2  1993/01/18  10:17:29  dwallach
+# RCS headers installed, code indented uniformly
+#
+#
+
+# this program's purpose in life is to parse the Huffman tables
+# and output C code which is awfully useful for translation
+
+# typical usage:
+# perl parse_huff.pl huffman.table
+# ---> generates huff.c and huff.h
+
+# source format:  # is a comment character
+# Otherwise, there are three tab-separated fields in a line:
+#    Run, Level, and VLC Code
+# Run and Level are integers, corresponding to the RLE coding.
+# The VLC code is what bits to generate, and is expected to be
+#    composed of 1, 0, space, and 's'.  Spaces are ignored, and
+#    s corresponds to the sign bit.  In the output of this program,
+#    We'll completely right-shift the data, with a 0 for the sign
+#    bit.  The encoder will make appropriate changes before outputing.
+
+
+open(HUFFC, "> huff.c") || die "Can't write huff.c: $!\n";
+open(HUFFH, "> huff.h") || die "Can't write huff.h: $!\n";
+
+sub encode {
+    local($run, $level, $vlc) = @_;
+    local($result) = 0;
+    local($tmp);
+    $vlc =~ s/\s//g;           # remove whitespace
+
+    local($bits) = length($vlc);
+
+    foreach (split(//, $vlc)) {
+	$_ = 0 if $_ eq 's';
+	$result = ($result << 1) + $_;
+    }
+
+    $tmp = sprintf("0x%x", $result);
+    eval "\$vlc$run[$level] = \$tmp";
+    eval "\$bits$run[$level] = \$bits";
+}
+
+while(<>) {
+    chop;
+    s/\s*#.*$//;       # remove comments
+    next if /^\s*$/;   # continue if the line is purely whitespace
+
+    ($run, $level, $vlc) = split(/\t/);
+    &encode($run, $level, $vlc);
+
+    #
+    # we have the +1's to insure the sizes of C arrays are correct
+    #
+    $maxlevel[$run] = $level+1 if $level >= $maxlevel[$run];
+    $maxlevel = $level+1 if $level >= $maxlevel;
+    $maxrun = $run+1 if $run >= $maxrun;
+}
+
+#
+# fix the entries that aren't defined
+#
+for($run = 0; $run < $maxrun; $run++) {
+    eval "\$vlc$run[0] = '0x0' if \$vlc$run[0] eq ''";
+    eval "\$bits$run[0] = '0' if \$bits$run[0] eq ''";
+}
+
+#
+# now, output some C code
+#
+
+printf HUFFC <<'EOF', $maxrun, join(", ", @maxlevel);
+/*
+ * Copyright (c) 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /n/picasso/users/dwallach/vid2/mpeg_encode/RCS/parse_huff.pl,v 1.3 1993/01/18 10:56:03 dwallach Exp $
+ */
+
+/*  
+ *  THIS FILE IS MACHINE GENERATED!  DO NOT EDIT!
+ */
+#include "mtypes.h"
+#include "huff.h"
+
+int huff_maxlevel[%s] = { %s };
+
+EOF
+for($run = 0; $run < $maxrun; $run++) {
+    printf HUFFC <<EOF, eval "join(', ', \@vlc$run)", eval "join(', ', \@bits$run)";
+uint32 huff_table$run[$maxlevel[$run]] = { %s };
+int huff_bits$run[$maxlevel[$run]] = { %s };
+
+EOF
+}
+
+printf HUFFC "uint32 *huff_table[$maxrun] = { ";
+for($run = 0; $run < $maxrun; $run++) {
+    printf HUFFC "%shuff_table$run", ($run)?", ":"";
+}
+printf HUFFC " };\n";
+
+printf HUFFC "int *huff_bits[$maxrun] = { ";
+for($run = 0; $run < $maxrun; $run++) {
+    printf HUFFC "%shuff_bits$run", ($run)?", ":"";
+}
+printf HUFFC " };\n";
+close HUFFC;
+
+printf HUFFH <<'EOF', $maxrun, $maxlevel;
+/*
+ * Copyright (c) 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /n/picasso/users/dwallach/vid2/mpeg_encode/RCS/parse_huff.pl,v 1.3 1993/01/18 10:56:03 dwallach Exp $
+ */
+
+/*  
+ *  THIS FILE IS MACHINE GENERATED!  DO NOT EDIT!
+ */
+#define HUFF_MAXRUN	%s
+#define HUFF_MAXLEVEL	%s
+
+extern int huff_maxlevel[];
+extern uint32 *huff_table[];
+extern int *huff_bits[];
+EOF
+
diff --git a/converter/ppm/ppmtompeg/pframe.c b/converter/ppm/ppmtompeg/pframe.c
new file mode 100644
index 00000000..e72fe5d6
--- /dev/null
+++ b/converter/ppm/ppmtompeg/pframe.c
@@ -0,0 +1,1072 @@
+/*===========================================================================*
+ * pframe.c                                  
+ *                                       
+ *  Procedures concerned with generation of P-frames             
+ *                                       
+ * EXPORTED PROCEDURES:                              
+ *  GenPFrame                                
+ *  ResetPFrameStats                             
+ *  ShowPFrameSummary                            
+ *  EstimateSecondsPerPFrame                         
+ *  ComputeHalfPixelData                             
+ *  SetPQScale                               
+ *  GetPQScale                               
+ *                                                                           
+ * NOTE:  when motion vectors are passed as arguments, they are passed as    
+ *        twice their value.  In other words, a motion vector of (3,4) will  
+ *        be passed as (6,8).  This allows half-pixel motion vectors to be   
+ *        passed as integers.  This is true throughout the program.          
+ *                                       
+ *===========================================================================*/
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include <assert.h>
+#include <sys/param.h>
+#include "pm.h"
+#include "pm_c_util.h"
+#include "all.h"
+#include "mtypes.h"
+#include "bitio.h"
+#include "frames.h"
+#include "motion_search.h"
+#include "prototypes.h"
+#include "block.h"
+#include "param.h"
+#include "mheaders.h"
+#include "fsize.h"
+#include "postdct.h"
+#include "mpeg.h"
+#include "parallel.h"
+#include "rate.h"
+#include "opts.h"
+#include "specifics.h"
+
+/*==================*
+ * STATIC VARIABLES *
+ *==================*/
+
+static int32    zeroDiff;
+static int      numPIBlocks = 0;
+static int      numPPBlocks = 0;
+static int      numPSkipped = 0;
+static int      numPIBits = 0;
+static int      numPPBits = 0;
+static int      numFrames = 0;
+static int      numFrameBits = 0;
+static int32    totalTime = 0;
+static int      qscaleP;
+static float    totalSNR = 0.0;
+static float    totalPSNR = 0.0;
+extern Block    **dct, **dctr, **dctb;
+extern dct_data_type   **dct_data;
+
+/*=====================*
+ * INTERNAL PROCEDURES *
+ *=====================*/
+
+static vector
+halfVector(vector const vector) {
+    struct vector half;
+
+    half.y = vector.y/2;
+    half.x = vector.x/2;
+
+    return half;
+}
+
+/*===========================================================================*
+ *
+ *  decide if (0,0) motion is better than the given motion vector
+ *
+ * RETURNS: TRUE if (0,0) is better, FALSE if (my,mx) is better
+ *
+ * SIDE EFFECTS:    none
+ *
+ * PRECONDITIONS:   The relevant block in 'current' is valid (it has not
+ *          been dct'd).  'zeroDiff' has already been computed
+ *          as the LumMotionError() with (0,0) motion
+ *
+ * NOTES:   This procedure follows the algorithm described on
+ *      page D-48 of the MPEG-1 specification
+ *
+ *===========================================================================*/
+static boolean
+ZeroMotionBetter(const LumBlock * const currentBlockP,
+                 MpegFrame *      const prev,
+                 int              const by,
+                 int              const bx,
+                 vector           const m) {
+
+    int bestDiff;
+    int CompareMode;
+
+    /* Junk needed to adapt for TUNEing */ 
+    CompareMode = SearchCompareMode;
+    SearchCompareMode = DEFAULT_SEARCH;
+    bestDiff = LumMotionError(currentBlockP, prev, by, bx, m, 0x7fffffff);
+    SearchCompareMode = CompareMode;
+
+    if ( zeroDiff < 256*3 ) {
+    if ( 2*bestDiff >= zeroDiff ) {
+        return TRUE;
+    }
+    } else {
+    if ( 11*bestDiff >= 10*zeroDiff ) {
+        return TRUE;
+    }
+    }
+    return FALSE;
+}
+
+
+/*===========================================================================*
+ *
+ *                USER-MODIFIABLE
+ *
+ * DoIntraCode
+ *
+ *  decide if intra coding is necessary
+ *
+ * RETURNS: TRUE if intra-block coding is better; FALSE if not
+ *
+ * SIDE EFFECTS:    none
+ *
+ * PRECONDITIONS:   The relevant block in 'current' is valid (it has not
+ *          been dct'd).
+ *
+ * NOTES:   This procedure follows the algorithm described on
+ *      page D-49 of the MPEG-1 specification
+ *
+ *===========================================================================*/
+static boolean
+DoIntraCode(const LumBlock * const currentBlockP,
+            MpegFrame *      const prev,
+            int              const by,
+            int              const bx,
+            vector           const motion) {
+
+    unsigned int y;
+    int32 sum = 0, vard = 0, varc = 0;
+    int32 currPixel, prevPixel;
+    LumBlock motionBlock;
+
+    ComputeMotionLumBlock(prev, by, bx, motion, &motionBlock);
+
+    for (y = 0; y < 16; ++y) {
+        unsigned int x;
+        for (x = 0; x < 16; ++x) {
+            currPixel = currentBlockP->l[y][x];
+            prevPixel = motionBlock.l[y][x];
+            
+            sum += currPixel;
+            varc += currPixel*currPixel;
+            
+            vard += SQR(currPixel - prevPixel);
+        }
+    }
+    
+    vard /= 256;     /* divide by 256; assumes mean is close to zero */
+    varc = (varc/256) - (sum/256) * (sum/256);
+
+    if (vard <= 64)
+        return FALSE;
+    else if (vard < varc)
+        return FALSE;
+    else
+        return TRUE;
+}
+
+
+
+/*===========================================================================*
+ *
+ *                USER-MODIFIABLE
+ *
+ * ZeroMotionSufficient
+ *
+ *  decide if zero motion is sufficient without DCT correction
+ *
+ * RETURNS: TRUE no DCT required; FALSE otherwise
+ *
+ * SIDE EFFECTS:    none
+ *
+ * PRECONDITIONS:   The relevant block in 'current' is raw YCC data
+ *
+ *===========================================================================*/
+static boolean
+ZeroMotionSufficient(const LumBlock * const currentBlockP,
+                     MpegFrame *      const prev,
+                     int              const by,
+                     int              const bx) {
+
+    LumBlock motionBlock;
+    int fy, fx;
+    unsigned int y;
+
+    fy = by * DCTSIZE;
+    fx = bx * DCTSIZE;
+    for (y = 0; y < 16; ++y) {
+        unsigned int x;
+        for (x = 0; x < 16; ++x) {
+            motionBlock.l[y][x] = prev->ref_y[fy+y][fx+x];
+        }
+    }
+
+    zeroDiff = LumBlockMAD(currentBlockP, &motionBlock, 0x7fffffff);
+
+    return (zeroDiff <= 256);
+}
+                 
+
+
+static void 
+computeCurrentBlock(MpegFrame * const current, 
+                    int         const y,
+                    int         const x,
+                    LumBlock *  const currentBlockP) {
+    int fy, fx;
+    int iy;
+
+    BLOCK_TO_FRAME_COORD(y, x, fy, fx);
+    for ( iy = 0; iy < 16; iy++ ) {
+        int ix;
+        for ( ix = 0; ix < 16; ix++ ) {
+            currentBlockP->l[iy][ix] = 
+                (int16)current->orig_y[fy+iy][fx+ix];
+        }
+    }
+}
+
+
+
+static void
+computeMotionVectors(bool             const specificsOn, 
+                     bool             const IntraPBAllowed,
+                     MpegFrame *      const current, 
+                     MpegFrame *      const prev,
+                     int              const mbAddress,
+                     BlockMV **       const infoP,
+                     int              const QScale, 
+                     const LumBlock * const currentBlockP,
+                     int              const y, 
+                     int              const x,
+                     bool *           const useMotionP, 
+                     vector *         const motionP) {
+
+    bool useCached;
+    BlockMV * info;
+
+    /* See if we have a cached answer */
+    if (specificsOn) {
+        SpecLookup(current->id, 2, mbAddress, &info, QScale);
+        if (info != (BlockMV*)NULL) 
+            useCached = TRUE;
+        else
+            useCached = FALSE;
+    } else
+        useCached = FALSE;
+
+    if (useCached) {
+        if (info->typ == TYP_SKIP) {
+            motionP->x = motionP->y = 0;
+            *useMotionP = TRUE;
+        } else {        /* assume P, since we're a P frame.... */
+            motionP->x = info->fx;
+            motionP->y = info->fy;
+            *useMotionP = TRUE;
+        }
+    } else {
+        /* see if we should use motion vectors, and if so, what those
+         * vectors should be
+         */
+        if (ZeroMotionSufficient(currentBlockP, prev, y, x)) {
+            motionP->x = 0;
+            motionP->y = 0;
+            *useMotionP = TRUE;
+        } else {
+            vector motion;
+            motion.y = motion.x = 0;  /* initial values */
+            PMotionSearch(currentBlockP, prev, y, x, &motion);
+            if (ZeroMotionBetter(currentBlockP, prev, y, x, motion)) {
+                motionP->y = 0;
+                motionP->x = 0;
+            } else
+                *motionP = motion;
+            if (IntraPBAllowed) 
+                *useMotionP = !DoIntraCode(currentBlockP, prev, y, x, motion);
+            else
+                *useMotionP = TRUE;
+        }
+    }
+    *infoP = info;
+}
+
+
+
+static void
+calculateForwardDcts(MpegFrame * const current, 
+                     int const y, int const x,
+                     Block ** const dct) {
+
+    /* calculate forward dct's */
+    if (collect_quant && (collect_quant_detailed & 1)) 
+        fprintf(collect_quant_fp, "l\n");
+
+    mp_fwd_dct_block2(current->y_blocks[y][x], dct[y][x]);
+    mp_fwd_dct_block2(current->y_blocks[y][x+1], dct[y][x+1]);
+    mp_fwd_dct_block2(current->y_blocks[y+1][x], dct[y+1][x]);
+    mp_fwd_dct_block2(current->y_blocks[y+1][x+1], dct[y+1][x+1]);
+
+    if (collect_quant && (collect_quant_detailed & 1)) 
+        fprintf(collect_quant_fp, "c\n");
+
+    mp_fwd_dct_block2(current->cb_blocks[y/2][x/2], dctb[y/2][x/2]);
+
+    mp_fwd_dct_block2(current->cr_blocks[y/2][x/2], dctr[y/2][x/2]);
+}
+
+
+
+static void
+computeMotionAndDct(int         const lastBlockY,
+                    int         const lastBlockX,
+                    bool        const specificsOn,
+                    bool        const IntraPBAllowed,
+                    MpegFrame * const current,
+                    MpegFrame * const prev,
+                    BlockMV **  const infoP,
+                    int         const QScale,
+                    int         const searchRangeP,
+                    Block **    const dct,
+                    int *       const numPBlocksP,
+                    int *       const numIBlocksP,
+                    int **      const pmvHistogram) {
+/*----------------------------------------------------------------------------
+   Loop through the frame finding motion/not and DCTing
+-----------------------------------------------------------------------------*/
+    int mbAddress;
+    int y;
+
+    mbAddress = 0;                        
+
+    for (y = 0; y < lastBlockY; y += 2) {
+        int x;
+        for (x = 0; x < lastBlockX; x += 2) {
+            LumBlock currentBlock;
+            vector motion;
+            bool useMotion;
+
+            computeCurrentBlock(current, y, x, &currentBlock);
+
+            computeMotionVectors(specificsOn,  IntraPBAllowed,
+                                 current, prev, mbAddress, infoP,
+                                 QScale, &currentBlock, y, x,
+                                 &useMotion, &motion);
+
+            dct_data[y][x].useMotion = useMotion;
+
+            if (useMotion) {
+                int pattern;
+
+                (*numPBlocksP)++;
+
+                pattern = 63;
+                ComputeDiffDCTs(current, prev, y, x, motion, &pattern);
+
+                assert(motion.x + searchRangeP + 1 >= 0);
+                assert(motion.y + searchRangeP + 1 >= 0);
+
+                if (computeMVHist) {
+                    assert(motion.x + searchRangeP + 1 <= 2*searchRangeP + 2);
+                    assert(motion.y + searchRangeP + 1 <= 2*searchRangeP + 2);
+                    ++pmvHistogram[motion.x + searchRangeP + 1]
+                        [motion.y + searchRangeP + 1];
+                }
+                /* Save specs for next loops */
+                dct_data[y][x].pattern  = pattern;
+                dct_data[y][x].fmotionX = motion.x;
+                dct_data[y][x].fmotionY = motion.y;
+            } else {
+                /* output I-block inside a P-frame */
+                ++*numIBlocksP;
+
+                calculateForwardDcts(current, y, x, dct);
+            }
+            ++mbAddress;
+        }
+    }
+}
+
+
+
+/*=====================*
+ * EXPORTED PROCEDURES *
+ *=====================*/
+
+/*===========================================================================*
+ *
+ * GenPFrame
+ *
+ *  generate a P-frame from previous frame, adding the result to the
+ *  given bit bucket
+ *
+ * RETURNS: frame appended to bb
+ *
+ *===========================================================================*/
+void
+GenPFrame(BitBucket * const bb,
+          MpegFrame * const current, 
+          MpegFrame * const prev) {
+
+    extern int **pmvHistogram;
+    FlatBlock fba[6], fb[6];
+    Block   dec[6];
+    int32 y_dc_pred, cr_dc_pred, cb_dc_pred;
+    int x, y;
+    vector motion;
+    vector oldMotion;
+    int offsetX, offsetY;
+    vector motionRem;
+    vector motionQuot;
+    int pattern;
+    int mbAddrInc = 1;
+    int numIBlocks = 0;
+    int numPBlocks = 0;
+    int numSkipped = 0;
+    int numIBits = 0;
+    int numPBits = 0;
+    int totalBits;
+    int totalFrameBits;
+    int32    startTime, endTime;
+    int lastBlockX, lastBlockY;
+    int lastX, lastY;
+    int mbAddress;
+    int slicePos;
+    register int index;
+    float   snr[3], psnr[3];
+    int QScale;
+    BlockMV *info;
+    int bitstreamMode, newQScale;
+    int rc_blockStart = 0;
+    boolean overflowChange = FALSE;
+    int     overflowValue  = 0;
+
+
+    oldMotion.x = oldMotion.y = 0;
+
+    if (collect_quant) {fprintf(collect_quant_fp, "# P\n");}
+    if (dct==NULL) AllocDctBlocks();
+    numFrames++;
+    totalFrameBits = bb->cumulativeBits;
+    startTime = time_elapsed();
+
+    DBG_PRINT(("Generating pframe\n"));
+
+    QScale = GetPQScale();
+    /*   bit allocation for rate control purposes */
+    bitstreamMode = getRateMode();
+    if (bitstreamMode == FIXED_RATE) {
+        targetRateControl(current);
+    }
+ 
+    Mhead_GenPictureHeader(bb, P_FRAME, current->id, fCodeP);
+    /* Check for Qscale change */  
+    if (specificsOn) {
+        /* Set a Qscale for this frame? */
+        newQScale = 
+            SpecLookup(current->id, 0, 0 /* junk */, &info /*junk*/, QScale);
+        if (newQScale != -1) {
+            QScale = newQScale;
+        }
+        /* Set for slice? */
+        newQScale = SpecLookup(current->id, 1, 1, &info /*junk*/, QScale);
+        if (newQScale != -1) {
+            QScale = newQScale;
+        }
+    }
+
+    DBG_PRINT(("Slice Header\n"));
+    Mhead_GenSliceHeader(bb, 1, QScale, NULL, 0);
+
+    if ( referenceFrame == DECODED_FRAME ) {
+        Frame_AllocDecoded(current, TRUE);
+    } else if ( printSNR ) {
+        Frame_AllocDecoded(current, FALSE);
+    }
+
+    /* don't do dct on blocks yet */
+    Frame_AllocBlocks(current);
+    BlockifyFrame(current);
+
+    /* for I-blocks */
+    y_dc_pred = cr_dc_pred = cb_dc_pred = 128;
+
+    totalBits = bb->cumulativeBits;
+
+    if ( (! pixelFullSearch) && (! prev->halfComputed) ) {
+        ComputeHalfPixelData(prev);
+    }
+
+    lastBlockX = Fsize_x>>3;
+    lastBlockY = Fsize_y>>3;
+    lastX = lastBlockX-2;
+    lastY = lastBlockY-2;
+
+    computeMotionAndDct(lastBlockY, lastBlockX,
+                        specificsOn, IntraPBAllowed, current, prev,
+                        &info, QScale, searchRangeP, dct, 
+                        &numPBlocks, &numIBlocks, pmvHistogram);
+    
+    mbAddress = 0;
+    for (y = 0; y < lastBlockY; y += 2) {
+        for (x = 0; x < lastBlockX; x += 2) {
+            slicePos = (mbAddress % blocksPerSlice);
+
+            if ( (slicePos == 0) && (mbAddress != 0) ) {
+                if (specificsOn) {
+                    /* Make sure no slice Qscale change */
+                    newQScale = 
+                        SpecLookup(current->id, 1, mbAddress/blocksPerSlice,
+                                   &info /*junk*/, QScale);
+                    if (newQScale != -1) QScale = newQScale;
+                }
+
+                Mhead_GenSliceEnder(bb);
+                Mhead_GenSliceHeader(bb, 1+(y>>1), QScale, NULL, 0);
+
+                /* reset everything */
+                oldMotion.x = oldMotion.y = 0;
+                y_dc_pred = cr_dc_pred = cb_dc_pred = 128;
+
+                mbAddrInc = 1+(x>>1);
+            }
+        
+            /*  Determine if new Qscale needed for Rate Control purposes  */
+            if (bitstreamMode == FIXED_RATE) {
+                rc_blockStart =  bb->cumulativeBits;
+                newQScale = needQScaleChange(qscaleP,
+                                             current->y_blocks[y][x],
+                                             current->y_blocks[y][x+1],
+                                             current->y_blocks[y+1][x],
+                                             current->y_blocks[y+1][x+1]);
+                if (newQScale > 0) {
+                    QScale = newQScale;
+                }
+            }
+        
+            /* Check for Qscale change */
+            if (specificsOn) {
+                newQScale = 
+                    SpecLookup(current->id, 2, mbAddress, &info, QScale);
+                if (newQScale != -1) {
+                    QScale = newQScale;
+                }
+            }
+
+            if (! dct_data[y][x].useMotion) {
+                GEN_I_BLOCK(P_FRAME, current, bb, mbAddrInc, QScale);
+                mbAddrInc = 1;
+
+                numIBits += (bb->cumulativeBits-totalBits);
+                totalBits = bb->cumulativeBits;
+
+                /* reset because intra-coded */
+                oldMotion.x = oldMotion.y = 0;
+
+                if ( decodeRefFrames ) {
+                    /* need to decode block we just encoded */
+                    Mpost_UnQuantZigBlock(fb[0], dec[0], QScale, TRUE);
+                    Mpost_UnQuantZigBlock(fb[1], dec[1], QScale, TRUE);
+                    Mpost_UnQuantZigBlock(fb[2], dec[2], QScale, TRUE);
+                    Mpost_UnQuantZigBlock(fb[3], dec[3], QScale, TRUE);
+                    Mpost_UnQuantZigBlock(fb[4], dec[4], QScale, TRUE);
+                    Mpost_UnQuantZigBlock(fb[5], dec[5], QScale, TRUE);
+
+                    /* now, reverse the DCT transform */
+                    for ( index = 0; index < 6; index++ ) {
+                        mpeg_jrevdct((int16 *)dec[index]);
+                    }
+
+                    /* now, unblockify */
+                    BlockToData(current->decoded_y, dec[0], y, x);
+                    BlockToData(current->decoded_y, dec[1], y, x+1);
+                    BlockToData(current->decoded_y, dec[2], y+1, x);
+                    BlockToData(current->decoded_y, dec[3], y+1, x+1);
+                    BlockToData(current->decoded_cb, dec[4], y>>1, x>>1);
+                    BlockToData(current->decoded_cr, dec[5], y>>1, x>>1);
+                }
+            } else {
+                int fCode = fCodeP;
+
+                /* reset because non-intra-coded */
+                y_dc_pred = cr_dc_pred = cb_dc_pred = 128;
+
+                pattern = dct_data[y][x].pattern;
+                motion.x = dct_data[y][x].fmotionX;
+                motion.y = dct_data[y][x].fmotionY;
+
+#ifdef BLEAH
+                ComputeAndPrintPframeMAD(currentBlock, prev, y, x, motion, 
+                                         mbAddress);
+#endif
+
+                if ( pixelFullSearch ) { /* should be even */
+                    motion.y /= 2;
+                    motion.x /= 2;
+                }
+
+                /* transform the motion vector into the appropriate values */
+                offsetX = motion.x - oldMotion.x;
+                offsetY = motion.y - oldMotion.y;
+                /*  if ((offsetX+(8*x)) >= (Fsize_x-8)) log(10.0); */
+                encodeMotionVector(offsetX, offsetY, &motionQuot, &motionRem,
+                                   FORW_F, fCode);
+
+#ifdef BLEAH
+                if ( (motion.x != 0) || (motion.y != 0) ) {
+                    fprintf(stdout, "FRAME (y, x)  %d, %d (block %d)\n", 
+                            y, x, mbAddress);
+                    fprintf(stdout, "motion.x = %d, motion.y = %d\n", 
+                            motion.x, motion.y);
+                    fprintf(stdout, 
+                            "    mxq, mxr = %d, %d    myq, myr = %d, %d\n",
+                            motionQuot.x, motionRem.x,
+                            motionQuot.y, motionRem.y);
+                }
+#endif
+
+                oldMotion = motion;
+
+                if ( pixelFullSearch ) { /* reset for use with PMotionSearch */
+                    motion.y *= 2;
+                    motion.x *= 2;
+                }
+            calc_blocks:
+                /* create flat blocks and update pattern if necessary */
+                /* Note DoQuant references QScale, overflowChange,
+                   overflowValue, pattern, and the calc_blocks label */
+                DoQuant(0x20, dct[y][x], fba[0]);
+                DoQuant(0x10, dct[y][x+1], fba[1]);
+                DoQuant(0x08, dct[y+1][x], fba[2]);
+                DoQuant(0x04, dct[y+1][x+1], fba[3]);
+                DoQuant(0x02, dctb[y/2][x/2], fba[4]);
+                DoQuant(0x01, dctr[y/2][x/2], fba[5]);
+
+                if ( decodeRefFrames) {
+                    for ( index = 0; index < 6; index++ ) {
+                        if ( pattern & (1 << (5-index))) {
+                            Mpost_UnQuantZigBlock(fba[index], dec[index], 
+                                                  QScale, FALSE);
+                            mpeg_jrevdct((int16 *)dec[index]);
+                        } else {
+                            memset((char *)dec[index], 0, sizeof(Block));
+                        }
+                    }
+
+                    /* now add the motion block */
+                    AddMotionBlock(dec[0], prev->decoded_y, y, x, motion);
+                    AddMotionBlock(dec[1], prev->decoded_y, y, x+1, motion);
+                    AddMotionBlock(dec[2], prev->decoded_y, y+1, x, motion);
+                    AddMotionBlock(dec[3], prev->decoded_y, y+1, x+1, motion);
+                    AddMotionBlock(dec[4], prev->decoded_cb, y/2, x/2,
+                                   halfVector(motion));
+                    AddMotionBlock(dec[5], prev->decoded_cr, y/2, x/2, 
+                                   halfVector(motion));
+
+                    /* now, unblockify */
+                    BlockToData(current->decoded_y, dec[0], y, x);
+                    BlockToData(current->decoded_y, dec[1], y, x+1);
+                    BlockToData(current->decoded_y, dec[2], y+1, x);
+                    BlockToData(current->decoded_y, dec[3], y+1, x+1);
+                    BlockToData(current->decoded_cb, dec[4], y/2, x/2);
+                    BlockToData(current->decoded_cr, dec[5], y/2, x/2);
+                } 
+
+                if ((motion.x == 0) && (motion.y == 0)) {
+                    if ( pattern == 0 ) {
+                        /* can only skip if:
+                         *     1)  not the last block in frame
+                         *     2)  not the last block in slice
+                         *     3)  not the first block in slice
+                         */
+
+                        if ( ((y < lastY) || (x < lastX)) &&
+                             (slicePos+1 != blocksPerSlice) &&
+                             (slicePos != 0) ) {
+                            mbAddrInc++;    /* skipped macroblock */
+                            numSkipped++;
+                            numPBlocks--;
+                        } else {        /* first/last macroblock */
+                            Mhead_GenMBHeader(bb, 2 /* pict_code_type */, 
+                                              mbAddrInc /* addr_incr */,
+                                              QScale /* q_scale */,
+                                              fCode /* forw_f_code */, 
+                                              1 /* back_f_code */,
+                                              motionRem.x /* horiz_forw_r */, 
+                                              motionRem.y /* vert_forw_r */,
+                                              0 /* horiz_back_r */, 
+                                              0 /* vert_back_r */,
+                                              1 /* motion_forw */, 
+                                              motionQuot.x /* m_horiz_forw */,
+                                              motionQuot.y /* m_vert_forw */, 
+                                              0 /* motion_back */,
+                                              0 /* m_horiz_back */, 
+                                              0 /* m_vert_back */,
+                                              0 /* mb_pattern */, 
+                                              0 /* mb_intra */);
+                            mbAddrInc = 1;
+                        }
+                    } else {
+                        DBG_PRINT(("MB Header(%d,%d)\n", x, y));
+                        Mhead_GenMBHeader(bb, 2 /* pict_code_type */, 
+                                          mbAddrInc /* addr_incr */,
+                                          QScale /* q_scale */,
+                                          fCode /* forw_f_code */, 
+                                          1 /* back_f_code */,
+                                          0 /* horiz_forw_r */, 
+                                          0 /* vert_forw_r */,
+                                          0 /* horiz_back_r */, 
+                                          0 /* vert_back_r */,
+                                          0 /* motion_forw */, 
+                                          0 /* m_horiz_forw */,
+                                          0 /* m_vert_forw */, 
+                                          0 /* motion_back */,
+                                          0 /* m_horiz_back */, 
+                                          0 /* m_vert_back */,
+                                          pattern /* mb_pattern */, 
+                                          0 /* mb_intra */);
+                        mbAddrInc = 1;
+                    }
+                } else {
+                    /*      DBG_PRINT(("MB Header(%d,%d)\n", x, y));  */
+          
+                    Mhead_GenMBHeader(bb, 2 /* pict_code_type */, 
+                                      mbAddrInc /* addr_incr */,
+                                      QScale /* q_scale */,
+                                      fCode /* forw_f_code */, 
+                                      1 /* back_f_code */,
+                                      motionRem.x /* horiz_forw_r */, 
+                                      motionRem.y /* vert_forw_r */,
+                                      0 /* horiz_back_r */, 
+                                      0 /* vert_back_r */,
+                                      1 /* motion_forw */, 
+                                      motionQuot.x /* m_horiz_forw */,
+                                      motionQuot.y /* m_vert_forw */, 
+                                      0 /* motion_back */,
+                                      0 /* m_horiz_back */, 
+                                      0 /* m_vert_back */,
+                                      pattern /* mb_pattern */, 
+                                      0 /* mb_intra */);
+                    mbAddrInc = 1;
+                }
+
+                /* now output the difference */
+                {
+                    unsigned int x;
+                    for (x = 0; x < 6; ++x) {
+                        if (GET_ITH_BIT(pattern, 5-x))
+                            Mpost_RLEHuffPBlock(fba[x], bb);
+                    }
+                }
+                numPBits += (bb->cumulativeBits-totalBits);
+                totalBits = bb->cumulativeBits;
+            }
+
+            if (overflowChange) {
+                /* undo an overflow-caused Qscale change */
+                overflowChange = FALSE;
+                QScale -= overflowValue;
+                overflowValue = 0;
+            }
+
+            mbAddress++;
+            /*   Rate Control  */
+            if (bitstreamMode == FIXED_RATE) {
+                incMacroBlockBits( bb->cumulativeBits- rc_blockStart);
+                rc_blockStart = bb->cumulativeBits;
+                MB_RateOut(TYPE_PFRAME);
+            }
+        }
+    }
+
+    if ( printSNR ) {
+        BlockComputeSNR(current,snr,psnr);
+        totalSNR += snr[0];
+        totalPSNR += psnr[0];
+    }
+
+#ifdef BLEAHBLEAH
+    {
+        FILE *filePtr;
+
+        filePtr = fopen("PFRAME.yuv", "wb");
+
+        for ( y = 0; y < Fsize_y; y++ )
+        {
+            for ( x = 0; x < Fsize_x; x++ )
+                fprintf(filePtr, "%d ", current->decoded_y[y][x]);
+            fprintf(filePtr, "\n");
+        }
+
+        fclose(filePtr);
+    }
+#endif
+
+    Mhead_GenSliceEnder(bb);
+    /*   Rate Control */
+    if (bitstreamMode == FIXED_RATE) {
+        updateRateControl(TYPE_PFRAME);
+    }
+
+    /* UPDATE STATISTICS */
+    endTime = time_elapsed();
+    totalTime += (endTime-startTime);
+
+    if ( showBitRatePerFrame ) {
+        /* ASSUMES 30 FRAMES PER SECOND */
+        fprintf(bitRateFile, "%5d\t%8d\n", current->id,
+                30*(bb->cumulativeBits-totalFrameBits));
+    }
+
+    if ( frameSummary && (! realQuiet) ) {
+        fprintf(stdout, "FRAME %d (P):  I BLOCKS:  %d;  "
+                "P BLOCKS:  %d   SKIPPED:  %d  (%ld seconds)\n",
+                current->id, numIBlocks, numPBlocks, numSkipped, 
+                (long)(endTime-startTime)/TIME_RATE);
+        if ( printSNR ) {
+            fprintf(stdout, "FRAME %d:  SNR:  %.1f\t%.1f\t%.1f\t"
+                    "PSNR:  %.1f\t%.1f\t%.1f\n",
+                    current->id, snr[0], snr[1], snr[2],
+                    psnr[0], psnr[1], psnr[2]);
+        }
+    }
+
+    numFrameBits += (bb->cumulativeBits-totalFrameBits);
+    numPIBlocks += numIBlocks;
+    numPPBlocks += numPBlocks;
+    numPSkipped += numSkipped;
+    numPIBits += numIBits;
+    numPPBits += numPBits;
+}
+
+
+/*===========================================================================*
+ *
+ * ResetPFrameStats
+ *
+ *  reset the P-frame statistics
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+  ResetPFrameStats()
+{
+  numPIBlocks = 0;
+  numPPBlocks = 0;
+  numPSkipped = 0;
+  numPIBits = 0;
+  numPPBits = 0;
+  numFrames = 0;
+  numFrameBits = 0;
+  totalTime = 0;
+}
+
+
+/*===========================================================================*
+ *
+ * SetPQScale
+ *
+ *  set the P-frame Q-scale
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    qscaleP
+ *
+ *===========================================================================*/
+void
+  SetPQScale(qP)
+int qP;
+{
+  qscaleP = qP;
+}
+
+
+/*===========================================================================*
+ *
+ * GetPQScale
+ *
+ *  return the P-frame Q-scale
+ *
+ * RETURNS: the P-frame Q-scale
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+int
+  GetPQScale()
+{
+  return qscaleP;
+}
+
+
+float
+PFrameTotalTime(void) {
+    return (float)totalTime/(float)TIME_RATE;
+}
+
+
+
+void
+ShowPFrameSummary(unsigned int const inputFrameBits, 
+                  unsigned int const totalBits, 
+                  FILE *       const fpointer) {
+
+    if (numFrames > 0) {
+
+        fprintf(fpointer, "-------------------------\n");
+        fprintf(fpointer, "*****P FRAME SUMMARY*****\n");
+        fprintf(fpointer, "-------------------------\n");
+
+        if ( numPIBlocks != 0 ) {
+            fprintf(fpointer, "  I Blocks:  %5d     (%6d bits)     (%5d bpb)\n",
+                    numPIBlocks, numPIBits, numPIBits/numPIBlocks);
+        } else {
+            fprintf(fpointer, "  I Blocks:  %5d\n", 0);
+        }
+
+        if ( numPPBlocks != 0 ) {
+            fprintf(fpointer, "  P Blocks:  %5d     (%6d bits)     (%5d bpb)\n",
+                    numPPBlocks, numPPBits, numPPBits/numPPBlocks);
+        } else {
+            fprintf(fpointer, "  P Blocks:  %5d\n", 0);
+        }
+
+        fprintf(fpointer, "  Skipped:   %5d\n", numPSkipped);
+
+        fprintf(fpointer, "  Frames:    %5d     (%6d bits)     (%5d bpf)     (%2.1f%% of total)\n",
+                numFrames, numFrameBits, numFrameBits/numFrames,
+                100.0*(float)numFrameBits/(float)totalBits);
+        fprintf(fpointer, "  Compression:  %3d:1     (%9.4f bpp)\n",
+                numFrames*inputFrameBits/numFrameBits,
+                24.0*(float)numFrameBits/(float)(numFrames*inputFrameBits));
+        if ( printSNR )
+            fprintf(fpointer, "  Avg Y SNR/PSNR:  %.1f     %.1f\n",
+                    totalSNR/(float)numFrames, totalPSNR/(float)numFrames);
+        if ( totalTime == 0 ) {
+            fprintf(fpointer, "  Seconds:  NONE\n");
+        } else {
+            fprintf(fpointer, "  Seconds:  %9ld     (%9.4f fps)  (%9ld pps)  (%9ld mps)\n",
+                    (long)(totalTime/TIME_RATE),
+                    (float)((float)(TIME_RATE*numFrames)/(float)totalTime),
+                    (long)((float)TIME_RATE*(float)numFrames*(float)inputFrameBits/(24.0*(float)totalTime)),
+                    (long)((float)TIME_RATE*(float)numFrames*(float)inputFrameBits/(256.0*24.0*(float)totalTime)));
+        }
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * EstimateSecondsPerPFrame
+ *
+ *  compute an estimate of the number of seconds required per P-frame
+ *
+ * RETURNS: the estimate, in seconds
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+float
+  EstimateSecondsPerPFrame()
+{
+  if ( numFrames == 0 ) {
+    return 10.0;
+  } else {
+    return (float)totalTime/((float)TIME_RATE*(float)numFrames);
+  }
+}
+
+
+/*===========================================================================*
+ *
+ * ComputeHalfPixelData
+ *
+ *  compute all half-pixel data required for half-pixel motion vector
+ *  search (luminance only)
+ *
+ * RETURNS: frame->halfX, ->halfY, and ->halfBoth modified
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+  ComputeHalfPixelData(frame)
+MpegFrame *frame;
+{
+  register int x, y;
+
+  /* we add 1 before dividing by 2 because .5 is supposed to be rounded up
+   * (see MPEG-1, page D-31)
+   */
+
+  if ( frame->halfX == NULL ) { /* need to allocate memory */
+    Frame_AllocHalf(frame);
+  }
+
+  /* compute halfX */
+  for ( y = 0; y < Fsize_y; y++ ) {
+    for ( x = 0; x < Fsize_x-1; x++ ) {
+      frame->halfX[y][x] = (frame->ref_y[y][x]+
+                frame->ref_y[y][x+1]+1)>>1;
+    }
+  }
+
+  /* compute halfY */
+  for ( y = 0; y < Fsize_y-1; y++ ) {
+    for ( x = 0; x < Fsize_x; x++ ) {
+      frame->halfY[y][x] = (frame->ref_y[y][x]+
+                frame->ref_y[y+1][x]+1)>>1;
+    }
+  }
+
+  /* compute halfBoth */
+  for ( y = 0; y < Fsize_y-1; y++ ) {
+    for ( x = 0; x < Fsize_x-1; x++ ) {
+      frame->halfBoth[y][x] = (frame->ref_y[y][x]+
+                   frame->ref_y[y][x+1]+
+                   frame->ref_y[y+1][x]+
+                   frame->ref_y[y+1][x+1]+2)>>2;
+    }
+  }
+
+  frame->halfComputed = TRUE;
+}
+
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
diff --git a/converter/ppm/ppmtompeg/postdct.c b/converter/ppm/ppmtompeg/postdct.c
new file mode 100644
index 00000000..56a23de9
--- /dev/null
+++ b/converter/ppm/ppmtompeg/postdct.c
@@ -0,0 +1,626 @@
+/*===========================================================================*
+ * postdct.c								     *
+ *									     *
+ *	Procedures concerned with MPEG post-DCT processing:		     *
+ *	    quantization and RLE Huffman encoding			     *
+ *									     *
+ * EXPORTED PROCEDURES:							     *
+ *	Mpost_QuantZigBlock						     *
+ *	Mpost_RLEHuffIBlock						     *
+ *	Mpost_RLEHuffPBlock						     *
+ *	Mpost_UnQuantZigBlock						     *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*
+ *  $Header: /n/picasso/project/mm/mpeg/mpeg_dist/mpeg_encode/RCS/postdct.c,v 1.12 1995/06/21 18:26:39 smoot Exp $
+ *  $Log: postdct.c,v $
+ * Revision 1.12  1995/06/21  18:26:39  smoot
+ * added length estimator for P-blocks
+ *
+ * Revision 1.11  1995/04/23  23:22:59  eyhung
+ * nothing changed
+ *
+ * Revision 1.10  1995/04/14  23:10:46  smoot
+ * Added overflow detection to MPOST_DCT so it will adjust Qscales (above)
+ *
+ * Revision 1.9  1995/02/15  23:15:32  smoot
+ * killed useless asserts
+ *
+ * Revision 1.8  1995/02/01  21:48:41  smoot
+ * assure out is set properly, short circuit 0 revquant
+ *
+ * Revision 1.7  1995/01/30  19:56:37  smoot
+ * Killed a <0 shift
+ *
+ * Revision 1.6  1995/01/25  23:07:33  smoot
+ * Better DBG_PRINTs, multiply/divide instead of shifts
+ *
+ * Revision 1.5  1995/01/19  23:09:10  eyhung
+ * Changed copyrights
+ *
+ * Revision 1.4  1995/01/16  08:17:08  eyhung
+ * added realQuiet
+ *
+ * Revision 1.3  1994/11/12  02:11:58  keving
+ * nothing
+ *
+ * Revision 1.2  1994/03/15  00:27:11  keving
+ * nothing
+ *
+ * Revision 1.2  1994/03/15  00:27:11  keving
+ * nothing
+ *
+ * Revision 1.1  1993/12/22  19:19:01  keving
+ * nothing
+ *
+ * Revision 1.11  1993/07/22  22:23:43  keving
+ * nothing
+ *
+ * Revision 1.10  1993/06/30  20:06:09  keving
+ * nothing
+ *
+ * Revision 1.9  1993/06/03  21:08:08  keving
+ * nothing
+ *
+ * Revision 1.8  1993/02/24  18:57:19  keving
+ * nothing
+ *
+ * Revision 1.7  1993/02/23  22:58:36  keving
+ * nothing
+ *
+ * Revision 1.6  1993/02/23  22:54:56  keving
+ * nothing
+ *
+ * Revision 1.5  1993/02/17  23:18:20  dwallach
+ * checkin prior to keving's joining the project
+ *
+ * Revision 1.4  1993/01/18  10:20:02  dwallach
+ * *** empty log message ***
+ *
+ * Revision 1.3  1993/01/18  10:17:29  dwallach
+ * RCS headers installed, code indented uniformly
+ *
+ * Revision 1.3  1993/01/18  10:17:29  dwallach
+ * RCS headers installed, code indented uniformly
+ *
+ */
+
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include <assert.h>
+#include "all.h"
+#include "mtypes.h"
+#include "bitio.h"
+#include "huff.h"
+#include "postdct.h"
+#include "opts.h"
+
+/*==================*
+ * STATIC VARIABLES *
+ *==================*/
+
+/* ZAG[i] is the natural-order position of the i'th element of zigzag order. */
+int ZAG[] = {
+    0, 1, 8, 16, 9, 2, 3, 10,
+    17, 24, 32, 25, 18, 11, 4, 5,
+    12, 19, 26, 33, 40, 48, 41, 34,
+    27, 20, 13, 6, 7, 14, 21, 28,
+    35, 42, 49, 56, 57, 50, 43, 36,
+    29, 22, 15, 23, 30, 37, 44, 51,
+    58, 59, 52, 45, 38, 31, 39, 46,
+    53, 60, 61, 54, 47, 55, 62, 63
+};
+
+/*
+ * possible optimization: reorder the qtable in the correct zigzag order, to
+ * reduce the number of necessary lookups
+ *
+ * this table comes from the MPEG draft, p. D-16, Fig. 2-D.15.
+ */
+int32 qtable[] = {
+    8, 16, 19, 22, 26, 27, 29, 34,
+    16, 16, 22, 24, 27, 29, 34, 37,
+    19, 22, 26, 27, 29, 34, 34, 38,
+    22, 22, 26, 27, 29, 34, 37, 40,
+    22, 26, 27, 29, 32, 35, 40, 48,
+    26, 27, 29, 32, 35, 40, 48, 58,
+    26, 27, 29, 34, 38, 46, 56, 69,
+    27, 29, 35, 38, 46, 56, 69, 83
+};
+
+int32 niqtable[] = {
+     16, 16, 16, 16, 16, 16, 16, 16,
+     16, 16, 16, 16, 16, 16, 16, 16,
+     16, 16, 16, 16, 16, 16, 16, 16,
+     16, 16, 16, 16, 16, 16, 16, 16,
+     16, 16, 16, 16, 16, 16, 16, 16,
+     16, 16, 16, 16, 16, 16, 16, 16,
+     16, 16, 16, 16, 16, 16, 16, 16,
+     16, 16, 16, 16, 16, 16, 16, 16
+};
+
+int32	*customQtable = NULL;
+int32	*customNIQtable = NULL;
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+extern boolean realQuiet;
+
+/*=====================*
+ * EXPORTED PROCEDURES *
+ *=====================*/
+
+/*===========================================================================*
+ *
+ * Mpost_UnQuantZigBlock
+ *
+ *	unquantize and zig-zag (decode) a single block
+ *	see section 2.4.4.1 of MPEG standard
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+Mpost_UnQuantZigBlock(in, out, qscale, iblock)
+    FlatBlock in;
+    Block out;
+    int qscale;
+    boolean iblock;
+{
+    register int index;
+    int	    start;
+    int	    position;
+    register int	    qentry;
+    int	    level, coeff;
+    
+    if ( iblock ) {
+	/* qtable[0] must be 8 */
+	out[0][0] = (int16)(in[0] * 8);
+
+	/* don't need to do anything fancy here, because we saved orig
+	    value, not encoded dc value */
+	start = 1;
+    } else {
+	start = 0;
+    }
+
+    for ( index = start;  index < DCTSIZE_SQ;  index++ ) {
+	position = ZAG[index];
+	level = in[index];
+
+	if (level == 0) {
+	  ((int16 *)out)[position] = 0;
+	  continue;
+	}
+
+
+	if ( iblock ) {
+	    qentry = qtable[position] * qscale;
+	    coeff = (level*qentry)/8;
+	    if ( (coeff & 1) == 0 ) {
+		if ( coeff < 0 ) {
+		    coeff++;
+		} else if ( coeff > 0 ) {
+		    coeff--;
+		}
+	    }
+	} else {
+	    qentry = niqtable[position] * qscale;
+	    if ( level == 0 ) {
+		coeff = 0;
+	    } else if ( level < 0 ) {
+		coeff = (((2*level)-1)*qentry) / 16;
+		if ( (coeff & 1) == 0 ) {
+		    coeff++;
+		}
+	    } else {
+		coeff = (((2*level)+1)*qentry) >> 4;
+		if ( (coeff & 1) == 0 ) {
+		    coeff--;
+		}
+	    }
+
+	    if ( coeff > 2047 ) {
+		coeff = 2047;
+	    } else if ( coeff < -2048 ) {
+		coeff = -2048;
+	    }
+        }
+
+	((int16 *)out)[position] = coeff;
+    }
+}
+
+
+/*===========================================================================*
+ *
+ * Mpost_QuantZigBlock
+ *
+ *	quantize and zigzags a block
+ *
+ * RETURNS:	MPOST_OVERFLOW if a generated value is outside |255|
+ *              MPOST_ZERO     if all coeffs are zero
+ *              MPOST_NON_ZERO otherwisw
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+int
+Mpost_QuantZigBlock(in, out, qscale, iblock)
+    Block in;
+    FlatBlock out;
+    register int qscale;
+    int iblock;
+{
+  register int i;
+  register int16 temp;
+  register int qentry;
+  register int position;
+  boolean nonZero = FALSE;
+  boolean overflow = FALSE;
+  
+  DBG_PRINT(("Mpost_QuantZigBlock...\n"));
+  if (iblock) {
+    /*
+     * the DC coefficient is handled specially -- it's not
+     * sensitive to qscale, but everything else is
+     */
+    temp = ((int16 *) in)[ZAG[0]];
+    qentry = qtable[ZAG[0]];
+    
+    if (temp < 0) {
+      temp = -temp;
+      temp += (qentry >> 1);
+      temp /= qentry;
+      temp = -temp;
+    } else {
+      temp += (qentry >> 1);
+      temp /= qentry;
+    }
+    if ( temp != 0 ) {
+      nonZero = TRUE;
+    }
+    out[0] = temp;
+    
+    for (i = 1; i < DCTSIZE_SQ; i++) {
+      position = ZAG[i];
+      temp = ((int16 *) in)[position];
+      qentry = qtable[position] * qscale;
+      
+      /* see 1993 MPEG doc, section D.6.3.4 */
+      if (temp < 0) {
+	temp = -temp;
+	temp = (temp << 3);	/* temp > 0 */
+	temp += (qentry >> 1);
+	temp /= qentry;
+	temp = -temp;
+      } else {
+	temp = (temp << 3);	/* temp > 0 */
+	temp += (qentry >> 1);
+	temp /= qentry;
+      }
+      
+      if ( temp != 0 ) {
+	nonZero = TRUE;
+	out[i] = temp;
+	if (temp < -255) {
+	  temp = -255;
+	  overflow = TRUE;
+	} else if (temp > 255) {
+	  temp = 255;
+	  overflow = TRUE;
+	}
+      } else out[i]=0;
+    }
+  } else {
+    for (i = 0; i < DCTSIZE_SQ; i++) {
+      position = ZAG[i];
+      temp = ((int16 *) in)[position];
+      
+      /* multiply by non-intra qtable */
+      qentry = qscale * niqtable[position];
+      
+      /* see 1993 MPEG doc, D.6.4.5 */
+      temp *= 8;
+      temp /= qentry;	    /* truncation toward 0 -- correct */
+      
+      if ( temp != 0 ) {
+	nonZero = TRUE;
+	out[i] = temp;
+	if (temp < -255) {
+	  temp = -255;
+	  overflow = TRUE;
+	} else if (temp > 255) {
+	  temp = 255;
+	  overflow = TRUE;
+	}
+	
+      } else out[i]=0;
+    }
+  }
+
+ if (overflow) return MPOST_OVERFLOW;
+ if (nonZero)  return MPOST_NON_ZERO;
+ return MPOST_ZERO;
+}
+
+
+
+/*===========================================================================*
+ *
+ * Mpost_RLEHuffIBlock
+ *
+ *	generate the huffman bits from an I-block
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+Mpost_RLEHuffIBlock(in, out)
+    FlatBlock in;
+    BitBucket *out;
+{
+    register int i;
+    register int nzeros = 0;
+    register int16 cur;
+    register int16 acur;
+    register uint32 code;
+    register int nbits;
+
+    /*
+     * yes, Virginia, we start at 1.  The DC coefficient is handled
+     * specially, elsewhere.  Not here.
+     */
+    for (i = 1; i < DCTSIZE_SQ; i++) {
+	cur = in[i];
+	acur = ABS(cur);
+	if (cur) {
+	    if ( (nzeros < HUFF_MAXRUN) && (acur < huff_maxlevel[nzeros])) {
+	        /*
+		 * encode using the Huffman tables
+		 */
+
+		DBG_PRINT(("rle_huff %02d.%02d: Run %02d, Level %4d\n", i,  ZAG[i], nzeros, cur));
+		code = (huff_table[nzeros])[acur];
+		nbits = (huff_bits[nzeros])[acur];
+
+		if (cur < 0) {
+		    code |= 1;	/* the sign bit */
+		}
+		Bitio_Write(out, code, nbits);
+	    } else {
+		/*
+		 * encode using the escape code
+		 */
+		DBG_PRINT(("Escape\n"));
+		Bitio_Write(out, 0x1, 6);	/* ESCAPE */
+		DBG_PRINT(("Run Length\n"));
+		Bitio_Write(out, nzeros, 6);	/* Run-Length */
+
+		/*
+	         * this shouldn't happen, but the other
+	         * choice is to bomb out and dump core...
+		 * Hmmm, seems to happen with small Qtable entries (1) -srs
+	         */
+		if (cur < -255) {
+		    cur = -255;
+		} else if (cur > 255) {
+		    cur = 255;
+		}
+
+		DBG_PRINT(("Level\n"));
+		if (acur < 128) {
+		    Bitio_Write(out, cur, 8);
+		} else {
+		    if (cur < 0) {
+			Bitio_Write(out, 0x8001 + cur + 255, 16);
+		    } else {
+			Bitio_Write(out, cur, 16);
+		    }
+		}
+	    }
+	    nzeros = 0;
+	} else {
+	    nzeros++;
+	}
+    }
+    DBG_PRINT(("End of block\n"));
+    Bitio_Write(out, 0x2, 2);	/* end of block marker */
+}
+
+
+/*===========================================================================*
+ *
+ * Mpost_RLEHuffPBlock
+ *
+ *	generate the huffman bits from an P-block
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+Mpost_RLEHuffPBlock(in, out)
+    FlatBlock in;
+    BitBucket *out;
+{
+    register int i;
+    register int nzeros = 0;
+    register int16 cur;
+    register int16 acur;
+    register uint32 code;
+    register int nbits;
+    boolean first_dct = TRUE;
+
+    /*
+     * yes, Virginia, we start at 0.
+     */
+    for (i = 0; i < DCTSIZE_SQ; i++) {
+	cur = in[i];
+	acur = ABS(cur);
+	if (cur) {
+	    if ((nzeros < HUFF_MAXRUN) && (acur < huff_maxlevel[nzeros])) {
+	        /*
+		 * encode using the Huffman tables
+		 */
+
+		DBG_PRINT(("rle_huff %02d.%02d: Run %02d, Level %4d\n", i, ZAG[i], nzeros, cur));
+		if ( first_dct && (nzeros == 0) && (acur == 1) ) {
+		    /* actually, only needs = 0x2 */
+		    code = (cur == 1) ? 0x2 : 0x3;
+		    nbits = 2;
+		} else {
+		    code = (huff_table[nzeros])[acur];
+		    nbits = (huff_bits[nzeros])[acur];
+		  }
+
+		assert(nbits);
+
+		if (cur < 0) {
+		    code |= 1;	/* the sign bit */
+		}
+		Bitio_Write(out, code, nbits);
+		first_dct = FALSE;
+	    } else {
+		/*
+		 * encode using the escape code
+		 */
+		DBG_PRINT(("Escape\n"));
+		Bitio_Write(out, 0x1, 6);	/* ESCAPE */
+		DBG_PRINT(("Run Length\n"));
+		Bitio_Write(out, nzeros, 6);	/* Run-Length */
+
+		/*
+	         * this shouldn't happen, but the other
+	         * choice is to bomb out and dump core...
+		 * Hmmm, seems to happen with small Qtable entries (1) -srs
+	         */
+		if (cur < -255) {
+		  cur = -255;
+		} else if (cur > 255) {
+		  cur = 255;
+		}
+
+		DBG_PRINT(("Level\n"));
+		if (acur < 128) {
+		    Bitio_Write(out, cur, 8);
+		} else {
+		    if (cur < 0) {
+			Bitio_Write(out, 0x8001 + cur + 255, 16);
+		    } else {
+			Bitio_Write(out, cur, 16);
+		    }
+		}
+
+		first_dct = FALSE;
+	    }
+	    nzeros = 0;
+	} else {
+	    nzeros++;
+	}
+    }
+
+    /* actually, should REALLY return FALSE and not use this! */
+
+    if ( first_dct ) {	/* have to give a first_dct even if all 0's */
+	fprintf(stderr, "HUFF called with all-zero coefficients\n");
+	fprintf(stderr, "exiting...\n");
+	exit(1);
+    }
+
+    DBG_PRINT(("End of block\n"));
+    Bitio_Write(out, 0x2, 2);	/* end of block marker */
+}
+
+
+/*===========================================================================*
+ *
+ * CalcRLEHuffLength
+ *
+ *	count the huffman bits for an P-block
+ *
+ * RETURNS:	number of bits
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+int
+CalcRLEHuffLength(in)
+    FlatBlock in;
+{
+  register int i;
+  register int nzeros = 0;
+  register int16 cur;
+  register int16 acur;
+  register int nbits;
+  register int countbits=0;
+  boolean first_dct = TRUE;
+  
+  for (i = 0; i < DCTSIZE_SQ; i++) {
+    cur = in[i];
+    acur = ABS(cur);
+    if (cur) {
+      if ((nzeros < HUFF_MAXRUN) && (acur < huff_maxlevel[nzeros])) {
+	/*
+	 * encode using the Huffman tables
+	 */
+
+	if ( first_dct && (nzeros == 0) && (acur == 1) ) {
+	  nbits = 2;
+	} else {
+	  nbits = (huff_bits[nzeros])[acur];
+	}
+	countbits += nbits;
+	first_dct = FALSE;
+      } else {
+	countbits += 12;	/* ESCAPE + runlength */
+
+	if (acur < 128) {
+	  countbits += 8;
+	} else {
+	  countbits += 16;
+	}
+
+	first_dct = FALSE;
+      }
+      nzeros = 0;
+    } else {
+      nzeros++;
+    }
+  }
+  
+  countbits += 2; /* end of block marker */
+  return countbits;
+}
diff --git a/converter/ppm/ppmtompeg/ppmtompeg.c b/converter/ppm/ppmtompeg/ppmtompeg.c
new file mode 100644
index 00000000..2296e2cb
--- /dev/null
+++ b/converter/ppm/ppmtompeg/ppmtompeg.c
@@ -0,0 +1,722 @@
+/*===========================================================================*
+ * main.c
+ *
+ * Main procedure
+ *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#define _BSD_SOURCE   /* Make sure strdup() is in string.h */
+
+#include <assert.h>
+#include <sys/utsname.h>
+
+#include "all.h"
+#include "mtypes.h"
+#include "mpeg.h"
+#include "motion_search.h"
+#include "prototypes.h"
+#include "param.h"
+#include "parallel.h"
+#include "readframe.h"
+#include "combine.h"
+#include "frames.h"
+#include "jpeg.h"
+#include "specifics.h"
+#include "opts.h"
+#include "frametype.h"
+#include "input.h"
+#include "gethostname.h"
+
+#include "pm_c_util.h"
+#include "ppm.h"
+#include "nstring.h"
+
+#include <time.h>
+
+int main _ANSI_ARGS_((int argc, char **argv));
+
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+boolean showBitRatePerFrame;
+boolean frameSummary;
+
+extern time_t IOtime;
+int whichGOP = -1;
+boolean ioServer = FALSE;
+boolean outputServer = FALSE;
+boolean decodeServer = FALSE;
+int quietTime = 0;
+boolean realQuiet = FALSE;
+boolean noFrameSummaryOption = FALSE;
+boolean debugSockets = FALSE;
+boolean debugMachines = FALSE;
+boolean bitRateInfoOption = FALSE;
+boolean computeMVHist = FALSE;
+int     baseFormat;
+extern  boolean specificsOn;
+extern  FrameSpecList *fsl;
+boolean pureDCT=FALSE;
+char    encoder_name[1024];
+const char * hostname;
+
+
+/*================================*
+ * External PROCEDURE prototypes  *
+ *================================*/
+
+void init_idctref _ANSI_ARGS_((void));
+void init_fdct _ANSI_ARGS_((void));
+
+
+struct cmdlineInfo {
+    bool         childProcess;
+    int          function;
+    const char * masterHostname;
+    int          masterPortNumber;
+    unsigned int outputFrames;
+    int          maxMachines;
+    const char * paramFileName;
+    bool         specificFrames;
+    unsigned int frameStart;
+    unsigned int frameEnd;
+};
+
+
+
+static void
+parseArgs(int     const argc,
+          char ** const argv,
+          struct cmdlineInfo * const cmdlineP) {
+
+    int idx;
+
+    if (argc-1 < 1)
+        pm_error("You must specify at least one argument: the parameter "
+                 "file name");
+
+    cmdlineP->function = ENCODE_FRAMES;
+    cmdlineP->childProcess = FALSE;  /* initial assumption */
+    cmdlineP->outputFrames = 0;
+    cmdlineP->maxMachines = MAXINT;
+    cmdlineP->specificFrames = FALSE;
+    
+    /* parse the arguments */
+    idx = 1;
+    while ( idx < argc-1 ) {
+        if ( argv[idx][0] != '-' )
+            pm_error("argument '%s', which must be an option because "
+                     "it is not the last argument, "
+                     "does not start with '-'", argv[idx]);
+
+        if ( strcmp(argv[idx], "-stat") == 0 ) {
+            if ( idx+1 < argc-1 ) {
+                SetStatFileName(argv[idx+1]);
+                idx += 2;
+            } else {
+                pm_error("Invalid -stat option");
+            }
+        } else if ( strcmp(argv[idx], "-gop") == 0 ) {
+            if ((cmdlineP->function != ENCODE_FRAMES) || 
+                (cmdlineP->specificFrames))
+                pm_error("Invalid -gop option");
+            
+            if ( idx+1 < argc-1 ) {
+                whichGOP = atoi(argv[idx+1]);
+                idx += 2;
+            } else {
+                pm_error("Invalid -gop option");
+            }
+        } else if ( strcmp(argv[idx], "-frames") == 0 ) {
+            if ( (cmdlineP->function != ENCODE_FRAMES) || (whichGOP != -1) ) {
+                pm_error("invalid -frames option");
+            }
+
+            if ( idx+2 < argc-1 ) {
+                int const frameStart = atoi(argv[idx+1]);
+                int const frameEnd = atoi(argv[idx+2]);
+
+                if (frameStart > frameEnd)
+                    pm_error("Start frame number %d is greater than end "
+                             "frame number %d", frameStart, frameEnd);
+                if (frameStart < 0)
+                    pm_error("Start frame number %d is less than zero",
+                             frameStart);
+
+                cmdlineP->specificFrames = TRUE;
+                cmdlineP->frameStart = frameStart;
+                cmdlineP->frameEnd   = frameEnd;
+                
+                idx += 3;
+            } else
+                pm_error("-frames needs to be followed by two values");
+        } else if (strcmp(argv[idx], "-combine_gops") == 0) {
+            if ((cmdlineP->function != ENCODE_FRAMES) || (whichGOP != -1) || 
+                (cmdlineP->specificFrames)) {
+                pm_error("Invalid -combine_gops option");
+            }
+
+            cmdlineP->function = COMBINE_GOPS;
+            idx++;
+        } else if (strcmp(argv[idx], "-combine_frames") == 0) {
+            if ((cmdlineP->function != ENCODE_FRAMES) || (whichGOP != -1) ||
+                (cmdlineP->specificFrames))
+                pm_error("Invalid -combine_frames option");
+
+            cmdlineP->function = COMBINE_FRAMES;
+            idx++;
+        } else if ( strcmp(argv[idx], "-child") == 0 ) {
+            if ( idx+7 < argc-1 ) {
+                int combinePortNumber;
+                    /* This used to be important information, when the child
+                       notified the combine server.  Now the master notifies
+                       the combine server after the child notifies the master
+                       it is done.  So this value is unused.
+                    */
+                cmdlineP->masterHostname = argv[idx+1];
+                cmdlineP->masterPortNumber = atoi(argv[idx+2]);
+                ioPortNumber = atoi(argv[idx+3]);
+                combinePortNumber = atoi(argv[idx+4]);
+                decodePortNumber = atoi(argv[idx+5]);
+                machineNumber = atoi(argv[idx+6]);
+                remoteIO = atoi(argv[idx+7]);
+
+                IOhostName = cmdlineP->masterHostname;
+            } else
+                pm_error("Not enough option values for -child option.  "
+                         "Need 7.");
+
+            cmdlineP->childProcess = TRUE;
+            idx += 8;
+        } else if ( strcmp(argv[idx], "-io_server") == 0 ) {
+            if ( idx+2 < argc-1 ) {
+                cmdlineP->masterHostname = argv[idx+1];
+                cmdlineP->masterPortNumber = atoi(argv[idx+2]);
+            } else {
+                pm_error("Invalid -io_server option");
+            }
+
+            ioServer = TRUE;
+            idx += 3;
+        } else if ( strcmp(argv[idx], "-output_server") == 0 ) {
+            if ( idx+3 < argc-1 ) {
+                cmdlineP->masterHostname = argv[idx+1];
+                cmdlineP->masterPortNumber = atoi(argv[idx+2]);
+                cmdlineP->outputFrames = atoi(argv[idx+3]);
+            } else {
+                pm_error("-output_server option requires 3 option values.  "
+                         "You specified %d", argc-1 - idx);
+            }
+
+            outputServer = TRUE;
+            idx += 4;
+        } else if ( strcmp(argv[idx], "-decode_server") == 0 ) {
+            if ( idx+3 < argc-1 ) {
+                cmdlineP->masterHostname = argv[idx+1];
+                cmdlineP->masterPortNumber = atoi(argv[idx+2]);
+                cmdlineP->outputFrames = atoi(argv[idx+3]);
+            } else {
+                pm_error("Invalid -decode_server option");
+            }
+
+            cmdlineP->function = COMBINE_FRAMES;
+            decodeServer = TRUE;
+            idx += 4;
+        } else if ( strcmp(argv[idx], "-nice") == 0 ) {
+            niceProcesses = TRUE;
+            idx++;
+        } else if ( strcmp(argv[idx], "-max_machines") == 0 ) {
+            if ( idx+1 < argc-1 ) {
+                cmdlineP->maxMachines = atoi(argv[idx+1]);
+            } else {
+                pm_error("Invalid -max_machines option");
+            }
+
+            idx += 2;
+        } else if ( strcmp(argv[idx], "-quiet") == 0 ) {
+            if ( idx+1 < argc-1 ) {
+                quietTime = atoi(argv[idx+1]);
+            } else {
+                pm_error("Invalid -quiet option");
+            }
+
+            idx += 2;
+        } else if ( strcmp(argv[idx], "-realquiet") == 0 ) {
+            realQuiet = TRUE;
+            idx++;
+        } else if (( strcmp(argv[idx], "-float_dct") == 0 ) ||
+                   ( strcmp(argv[idx], "-float-dct") == 0 )) {
+            pureDCT = TRUE;
+            init_idctref();
+            init_fdct();
+            idx++;
+        } else if ( strcmp(argv[idx], "-no_frame_summary") == 0 ) {
+            if ( idx < argc-1 ) {
+                noFrameSummaryOption = TRUE;
+            } else {
+                pm_error("Invalid -no_frame_summary option");
+            }
+
+            idx++;
+        } else if ( strcmp(argv[idx], "-snr") == 0 ) {
+            printSNR = TRUE;
+            idx++;
+        } else if ( strcmp(argv[idx], "-mse") == 0 ) {
+            printSNR =  printMSE = TRUE;
+            idx++;
+        } else if ( strcmp(argv[idx], "-debug_sockets") == 0 ) {
+            debugSockets = TRUE;
+            idx++;
+        } else if ( strcmp(argv[idx], "-debug_machines") == 0 ) {
+            debugMachines = TRUE;
+            idx++;
+        } else if ( strcmp(argv[idx], "-bit_rate_info") == 0 ) {
+            if ( idx+1 < argc-1 ) {
+                bitRateInfoOption = TRUE;
+                SetBitRateFileName(argv[idx+1]);
+                idx += 2;
+            } else {
+                pm_error("Invalid -bit_rate_info option");
+            }
+        } else if ( strcmp(argv[idx], "-mv_histogram") == 0 ) {
+            computeMVHist = TRUE;
+            idx++;
+        } else {
+            pm_error("Unrecognized option: '%s'", argv[idx]);
+        }
+    }
+
+    cmdlineP->paramFileName = argv[argc-1];
+}
+
+
+
+static void
+compileTests() {
+
+    /* There was code here (assert() calls) that verified that uint16
+       is 16 bits, etc.  It caused compiler warnings that said, "Of
+       course it is!"  (actually, "statement has no effect").  There
+       isn't enough chance that uint16, etc. are defined incorrectly
+       to warrant those asserts.  2000.05.07
+    */
+
+    if ( (-8 >> 3) != -1 ) {
+        fprintf(stderr, "ERROR:  Right shifts are NOT arithmetic!!!\n");
+        fprintf(stderr, "Change >> to multiplies by powers of 2\n");
+        exit(1);
+    }
+}
+
+
+
+static void
+announceJob(enum frameContext const context,
+            bool              const childProcess,
+            unsigned int      const frameStart,
+            unsigned int      const frameEnd,
+            const char *      const outputFileName) {
+
+    if (!realQuiet) {
+        const char * outputDest;
+        const char * combineDest;
+
+        if (context == CONTEXT_JUSTFRAMES)
+            asprintfN(&outputDest, "to individual frame files");
+        else
+            asprintfN(&outputDest, "to file '%s'", outputFileName);
+
+        if (childProcess)
+            combineDest = strdup("for delivery to combine server");
+        else
+            combineDest = strdup("");
+    
+        pm_message("%s:  ENCODING FRAMES %u-%u to %s %s",
+                   hostname, frameStart, frameEnd, outputDest, combineDest);
+
+        strfree(combineDest);
+        strfree(outputDest);
+    }
+}
+
+
+
+static void
+encodeSomeFrames(struct inputSource * const inputSourceP,
+                 boolean              const childProcess,
+                 enum frameContext    const context, 
+                 unsigned int         const frameStart,
+                 unsigned int         const frameEnd,
+                 int32                const qtable[],
+                 int32                const niqtable[],
+                 FILE *               const ofp,
+                 const char *         const outputFileName,
+                 bool                 const wantVbvUnderflowWarning,
+                 bool                 const wantVbvOverflowWarning,
+                 boolean              const printStats,
+                 unsigned int *       const encodeTimeP) {
+    
+    time_t framesTimeStart, framesTimeEnd;
+    unsigned int inputFrameBits;
+    unsigned int totalBits;
+
+    announceJob(context, childProcess, frameStart, frameEnd, outputFileName);
+
+    time(&framesTimeStart);
+    if (printStats)
+        PrintStartStats(framesTimeStart, context == CONTEXT_JUSTFRAMES,
+                        frameStart, frameEnd, inputSourceP);
+
+    GenMPEGStream(inputSourceP, context, frameStart, frameEnd,
+                  qtable, niqtable, childProcess, ofp, outputFileName, 
+                  wantVbvUnderflowWarning, wantVbvOverflowWarning,
+                  &inputFrameBits, &totalBits);
+
+    time(&framesTimeEnd);
+
+    *encodeTimeP = (unsigned int)(framesTimeEnd - framesTimeStart);
+
+    if (!realQuiet)
+        pm_message("%s:  COMPLETED FRAMES %u-%u (%u seconds)",
+                   hostname, frameStart, frameEnd, *encodeTimeP);
+
+    if (printStats)
+        PrintEndStats(framesTimeStart, framesTimeEnd, 
+                      inputFrameBits, totalBits);
+}
+
+
+
+
+static void
+encodeFrames(struct inputSource * const inputSourceP,
+             boolean              const childProcess,
+             const char *         const masterHostname,
+             int                  const masterPortNumber,
+             int                  const whichGOP,
+             bool                 const specificFrames,
+             unsigned int         const whichFrameStart,
+             unsigned int         const whichFrameEnd,
+             int32                const qtable[],
+             int32                const niqtable[],
+             FILE *               const ofp,
+             const char *         const outputFileName,
+             bool                 const wantVbvUnderflowWarning,
+             bool                 const wantVbvOverflowWarning) {
+/*----------------------------------------------------------------------------
+  Encode the stream.  If 'specificFrames' is true, then encode frames
+  'whichFrameStart' through 'whichFrameEnd' individually.  Otherwise,
+  encode the entire input stream as a complete MPEG stream.
+  
+  'childProcess' means to do it as a child process that is under the
+  supervision of a master process and is possibly doing only part of
+  a larger batch.
+  
+  (If we had proper modularity, we wouldn't care, but parallel operation
+  was glued on to this program after it was complete).
+  
+  One thing we don't do when running as a child process is print
+  statistics; our master will do that for the whole job.
+----------------------------------------------------------------------------*/
+    unsigned int frameStart, frameEnd;
+    enum frameContext context;
+    unsigned int      lastEncodeTime;
+        /* How long it took the encoder to do the last set of frames */
+    boolean           printStats;
+        /* We want the encoder to print start & end stats */
+
+    if (whichGOP != -1) {
+        /* He wants just one particular GOP from the middle of the movie. */
+        ComputeGOPFrames(whichGOP, &frameStart, &frameEnd, 
+                         inputSourceP->numInputFiles);
+        context = CONTEXT_GOP;
+    } else if (specificFrames) {
+        /* He wants some pure frames from the middle of the movie */
+        if (whichFrameStart > whichFrameEnd)
+            pm_error("You specified a starting frame number (%d) that is "
+                     "greater than the ending frame number (%d) "
+                     "you specified.", whichFrameStart, whichFrameEnd);
+        if (whichFrameEnd + 1 > inputSourceP->numInputFiles)
+            pm_error("You specified ending frame number %d, which is "
+                     "beyond the number of input files you supplied (%u)",
+                     whichFrameEnd, inputSourceP->numInputFiles);
+
+        frameStart = whichFrameStart;
+        frameEnd   = whichFrameEnd;
+        context = CONTEXT_JUSTFRAMES;
+    } else {
+        /* He wants the whole movie */
+        frameStart = 0;
+        frameEnd   = inputSourceP->numInputFiles - 1;
+        context = CONTEXT_WHOLESTREAM;
+    }
+    
+    printStats = !childProcess;
+    
+    encodeSomeFrames(inputSourceP, childProcess, context, frameStart, frameEnd,
+                     customQtable, customNIQtable,
+                     ofp, outputFileName, 
+                     wantVbvUnderflowWarning, wantVbvOverflowWarning,
+                     printStats,
+                     &lastEncodeTime);
+
+    if (childProcess) {
+        boolean moreWorkToDo;
+        
+        /* A child is not capable of generating GOP or stream headers */
+        assert(context == CONTEXT_JUSTFRAMES);
+        
+        moreWorkToDo = TRUE;  /* initial assumption */
+        while (moreWorkToDo) {
+            int nextFrameStart, nextFrameEnd;
+
+            NotifyMasterDone(masterHostname, masterPortNumber, machineNumber, 
+                             lastEncodeTime, &moreWorkToDo,
+                             &nextFrameStart, &nextFrameEnd);
+            if (moreWorkToDo)
+                encodeSomeFrames(inputSourceP, childProcess, 
+                                 CONTEXT_JUSTFRAMES, 
+                                 nextFrameStart, nextFrameEnd,
+                                 customQtable, customNIQtable,
+                                 NULL, outputFileName, 
+                                 wantVbvUnderflowWarning, 
+                                 wantVbvOverflowWarning,
+                                 FALSE,
+                                 &lastEncodeTime);
+        }
+        if (!realQuiet)
+            pm_message("%s: Child exiting.  Master has no more work.",
+                       hostname);
+    }
+}
+
+
+
+
+static void
+runMaster(struct inputSource * const inputSourceP,
+          const char *         const paramFileName,
+          const char *         const outputFileName) {
+
+    if (paramFileName[0] != '/' && paramFileName[0] != '~')
+        pm_error("For parallel mode, you must "
+                 "use an absolute path for parameter file.  "
+                 "You specified '%s'", paramFileName);
+    else
+        MasterServer(inputSourceP, paramFileName, outputFileName);
+}
+
+
+
+static void
+getUserFrameFile(void *       const handle,
+                 unsigned int const frameNumber,
+                 FILE **      const ifPP) {
+    
+    struct inputSource * const inputSourceP = (struct inputSource *) handle;
+
+    if (inputSourceP->stdinUsed)
+        pm_error("You cannot combine frames from Standard Input.");
+            /* Because Caller detects end of frames by EOF */
+    
+    if (frameNumber >= inputSourceP->numInputFiles)
+        *ifPP = NULL;
+    else {
+        const char * fileName;
+        const char * inputFileName;
+
+        GetNthInputFileName(inputSourceP, frameNumber, &inputFileName);
+        
+        asprintfN(&fileName, "%s/%s", currentFramePath, inputFileName);
+        
+        *ifPP = fopen(fileName, "rb");
+        if (*ifPP == NULL)
+            pm_error("Unable to open file '%s'.  Errno = %d (%s)",
+                     fileName, errno, strerror(errno));
+        
+        strfree(inputFileName);
+        strfree(fileName);
+    }
+}
+
+
+
+static void
+nullDisposeFile(void *       const handle,
+                unsigned int const frameNumber) {
+
+
+}
+
+
+
+static unsigned int
+framePoolSize(bool const sequentialInput) {
+/*----------------------------------------------------------------------------
+   Return the number of frames that our frame memory pool needs to have.
+
+   'sequentialInput' means we'll be reading from input that comes in frame
+   number order and we can't go back, so we'll have to have a bigger buffer
+   of frames than otherwise.
+-----------------------------------------------------------------------------*/
+    unsigned int numOfFrames;
+
+    numOfFrames = 0;  /* initial value */
+
+    if (sequentialInput) {
+        unsigned int idx;
+        unsigned int bcount;
+
+        for ( idx = 0, bcount = 0; idx < strlen(framePattern); idx++) {
+
+            /* counts the maximum number of B frames between two reference
+             * frames. 
+             */
+            
+            switch( framePattern[idx] ) {
+            case 'b': 
+                bcount++;
+                break;
+            case 'i':
+            case 'p':
+                if (bcount > numOfFrames)
+                    numOfFrames = bcount;
+                bcount = 0;
+                break;
+            }
+            
+            /* add 2 to hold the forward and past reference frames in addition
+             * to the maximum number of B's 
+             */
+        }
+        
+        numOfFrames += 2;
+
+    } else {
+        /* non-interactive, only 3 frames needed */
+        numOfFrames = 3;
+    }
+    return numOfFrames;
+}
+
+
+
+int
+main(int argc, char **argv) {
+    FILE *ofP;
+    time_t  initTimeStart;
+    struct cmdlineInfo cmdline;
+    struct params params;
+
+    ppm_init(&argc, argv);
+
+    strcpy(encoder_name, argv[0]);
+
+    compileTests();
+
+    time(&initTimeStart);
+
+    SetStatFileName("");
+
+    hostname = GetHostName();
+
+    parseArgs(argc, argv, &cmdline);
+
+    ReadParamFile(cmdline.paramFileName, cmdline.function, &params);
+
+    /* Jim Boucher's stuff:
+       if we are using a movie format then break up into frames
+    */
+    if ((!cmdline.childProcess) && (baseFormat == JMOVIE_FILE_TYPE))
+        JM2JPEG(params.inputSourceP);
+
+    if (printSNR || (referenceFrame == DECODED_FRAME))
+        decodeRefFrames = TRUE;
+
+    showBitRatePerFrame = (bitRateInfoOption && !cmdline.childProcess);
+    frameSummary = (!noFrameSummaryOption && !cmdline.childProcess);
+
+    numMachines = min(numMachines, cmdline.maxMachines);
+
+    Tune_Init();
+    Frame_Init(framePoolSize(params.inputSourceP->stdinUsed));
+
+    if (specificsOn) 
+        Specifics_Init();
+
+    ComputeFrameTable(params.inputSourceP->stdinUsed ? 
+                      0 : params.inputSourceP->numInputFiles);
+
+    if (ioServer) {
+        IoServer(params.inputSourceP, cmdline.masterHostname, 
+                 cmdline.masterPortNumber);
+        return 0;
+    } else if (outputServer) {
+        CombineServer(cmdline.outputFrames, 
+                      cmdline.masterHostname, cmdline.masterPortNumber,
+                      outputFileName);
+    } else if (decodeServer) {
+        DecodeServer(cmdline.outputFrames, outputFileName, 
+                     cmdline.masterHostname, cmdline.masterPortNumber);
+    } else {
+        if ((!cmdline.specificFrames) &&
+            ((numMachines == 0) || (cmdline.function != ENCODE_FRAMES)) ) {
+            ofP = fopen(outputFileName, "wb");
+            if (ofP == NULL)
+                pm_error("Could not open output file!");
+        } else
+            ofP = NULL;
+        
+        if (cmdline.function == ENCODE_FRAMES) {
+            if ((numMachines == 0) || (cmdline.specificFrames)) {
+                encodeFrames(params.inputSourceP,
+                             cmdline.childProcess, 
+                             cmdline.masterHostname, cmdline.masterPortNumber,
+                             whichGOP, cmdline.specificFrames,
+                             cmdline.frameStart, cmdline.frameEnd,
+                             customQtable, customNIQtable,
+                             ofP, outputFileName,
+                             params.warnUnderflow, params.warnOverflow);
+                
+            } else
+                runMaster(params.inputSourceP,
+                          cmdline.paramFileName, outputFileName);
+        } else if (cmdline.function == COMBINE_GOPS)
+            GOPsToMPEG(params.inputSourceP, outputFileName, ofP);
+        else if (cmdline.function == COMBINE_FRAMES)
+            FramesToMPEG(ofP, params.inputSourceP,
+                         &getUserFrameFile, &nullDisposeFile);
+    } 
+    Frame_Exit();
+        
+    strfree(hostname);
+
+    return 0;
+}
diff --git a/converter/ppm/ppmtompeg/psearch.c b/converter/ppm/ppmtompeg/psearch.c
new file mode 100644
index 00000000..aea1a29b
--- /dev/null
+++ b/converter/ppm/ppmtompeg/psearch.c
@@ -0,0 +1,989 @@
+/*===========================================================================*
+ * psearch.c                                     
+ *                                       
+ *  Procedures concerned with the P-frame motion search          
+ *                                       
+ *===========================================================================*/
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "all.h"
+#include "mtypes.h"
+#include "frames.h"
+#include "motion_search.h"
+#include "prototypes.h"
+#include "fsize.h"
+#include "param.h"
+
+
+/*==================*
+ * STATIC VARIABLES *
+ *==================*/
+
+/* none */
+
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+int **pmvHistogram = NULL;  /* histogram of P-frame motion vectors */
+int **bbmvHistogram = NULL; /* histogram of B-frame bkwd motion vectors */
+int **bfmvHistogram = NULL; /* histogram of B-frame fwd motion vectors */
+int pixelFullSearch;
+int searchRangeP,searchRangeB;
+/* The range, in half pixels in each direction, that we are to search
+       when detecting motion.  Specified by RANGE statement in parameter file.
+    */
+int psearchAlg;
+/* specified by parameter file. */
+
+/*===============================*
+ * INTERNAL PROCEDURE prototypes *
+ *===============================*/
+
+
+/*=====================*
+ * EXPORTED PROCEDURES *
+ *=====================*/
+
+/*===========================================================================*
+ *
+ *  Compute the best P-frame motion vector we can.  If it's better than
+ *  *motionP, update *motionP to it.
+ *
+ * PRECONDITIONS:   The relevant block in 'current' is valid (it has not
+ *          been dct'd).  Thus, the data in 'current' can be
+ *          accesed through y_blocks, cr_blocks, and cb_blocks.
+ *          This is not the case for the blocks in 'prev.'
+ *          Therefore, references into 'prev' should be done
+ *          through the struct items ref_y, ref_cr, ref_cb
+ *
+ * POSTCONDITIONS:  current, prev unchanged.
+ *          Some computation could be saved by requiring
+ *          the dct'd difference to be put into current's block
+ *          elements here, depending on the search technique.
+ *          However, it was decided that it mucks up the code
+ *          organization a little, and the saving in computation
+ *          would be relatively little (if any).
+ *
+ * NOTES:   the search procedure need not check the (0,0) motion vector
+ *      the calling procedure has a preference toward (0,0) and it
+ *      will check it itself
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+void
+PMotionSearch(const LumBlock * const currentBlockP, 
+              MpegFrame *      const prev, 
+              int              const by,
+              int              const bx, 
+              vector *         const motionP) {
+
+    /* CALL SEARCH PROCEDURE */
+
+    switch(psearchAlg) {
+    case PSEARCH_SUBSAMPLE:
+        PSubSampleSearch(currentBlockP, prev, by, bx, motionP, searchRangeP);
+        break;
+    case PSEARCH_EXHAUSTIVE:
+        PLocalSearch(currentBlockP, prev, by, bx, 
+                     motionP, INT_MAX, searchRangeP);
+        break;
+    case PSEARCH_LOGARITHMIC:
+        PLogarithmicSearch(currentBlockP, prev, by, bx, motionP, searchRangeP);
+        break;
+    case PSEARCH_TWOLEVEL:
+        PTwoLevelSearch(currentBlockP, prev, by, bx, 
+                        motionP, INT_MAX, searchRangeP);
+        break;
+    default:
+        pm_error("IMPOSSIBLE PSEARCH ALG:  %d", psearchAlg);
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * SetPixelSearch
+ *
+ *  set the pixel search type (half or full)
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    pixelFullSearch
+ *
+ *===========================================================================*/
+void
+SetPixelSearch(const char * const searchType) {
+    if ( (strcmp(searchType, "FULL") == 0 ) || 
+         ( strcmp(searchType, "WHOLE") == 0 )) {
+        pixelFullSearch = TRUE;
+    } else if ( strcmp(searchType, "HALF") == 0 ) {
+        pixelFullSearch = FALSE;
+    } else {
+        fprintf(stderr, "ERROR:  Invalid pixel search type:  %s\n",
+                searchType);
+        exit(1);
+    }
+}
+
+
+/*===========================================================================*
+ *
+ * SetPSearchAlg
+ *
+ *  set the P-search algorithm
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    psearchAlg
+ *
+ *===========================================================================*/
+void
+SetPSearchAlg(const char * const alg)
+{
+    if ( strcmp(alg, "EXHAUSTIVE") == 0 ) {
+        psearchAlg = PSEARCH_EXHAUSTIVE;
+    } else if (strcmp(alg, "SUBSAMPLE") == 0 ) {
+        psearchAlg = PSEARCH_SUBSAMPLE;
+    } else if ( strcmp(alg, "LOGARITHMIC") == 0 ) {
+        psearchAlg = PSEARCH_LOGARITHMIC;
+    } else if ( strcmp(alg, "TWOLEVEL") == 0 ) {
+        psearchAlg = PSEARCH_TWOLEVEL;
+    } else {
+        fprintf(stderr, "ERROR:  Invalid psearch algorithm:  %s\n", alg);
+        exit(1);
+    }
+}
+
+
+/*===========================================================================*
+ *
+ * PSearchName
+ *
+ *  returns a string containing the name of the search algorithm
+ *
+ * RETURNS: pointer to the string
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+const char *
+PSearchName(void)
+{
+    const char *retval;
+
+    switch(psearchAlg) {
+    case PSEARCH_EXHAUSTIVE:
+        retval = "EXHAUSTIVE";break;
+    case PSEARCH_SUBSAMPLE:
+        retval = "SUBSAMPLE";break;
+    case PSEARCH_LOGARITHMIC:
+        retval = "LOGARITHMIC";break;
+    case PSEARCH_TWOLEVEL:
+        retval = "TWOLEVEL";break;
+    default:
+        fprintf(stderr, "ERROR:  Illegal PSEARCH ALG:  %d\n", psearchAlg);
+        exit(1);
+        break;
+    }
+    return retval;
+}
+
+
+/*===========================================================================*
+ *
+ * SetSearchRange
+ *
+ *  sets the range of the search to the given number of pixels,
+ *  allocate histogram storage
+ *
+ *===========================================================================*/
+void
+SetSearchRange(int const pixelsP, int const pixelsB) {
+
+    searchRangeP = 2*pixelsP;   /* +/- 'pixels' pixels */
+    searchRangeB = 2*pixelsB;
+
+    if ( computeMVHist ) {
+        int const max_search = max(searchRangeP, searchRangeB);
+
+        int index;
+    
+        pmvHistogram = (int **) malloc((2*searchRangeP+3)*sizeof(int *));
+        bbmvHistogram = (int **) malloc((2*searchRangeB+3)*sizeof(int *));
+        bfmvHistogram = (int **) malloc((2*searchRangeB+3)*sizeof(int *));
+        for ( index = 0; index < 2*max_search+3; index++ ) {
+            pmvHistogram[index] = 
+                (int *) calloc(2*searchRangeP+3, sizeof(int));
+            bbmvHistogram[index] = 
+                (int *) calloc(2*searchRangeB+3, sizeof(int));
+            bfmvHistogram[index] = 
+                (int *) calloc(2*searchRangeB+3, sizeof(int));
+        }
+    }
+}
+
+
+/*===========================================================================*
+ *
+ *              USER-MODIFIABLE
+ *
+ * MotionSearchPreComputation
+ *
+ *  do whatever you want here; this is called once per frame, directly
+ *  after reading
+ *
+ * RETURNS: whatever
+ *
+ * SIDE EFFECTS:    whatever
+ *
+ *===========================================================================*/
+void
+MotionSearchPreComputation(MpegFrame * const frameP) {
+    /* do nothing */
+}
+
+
+/*===========================================================================*
+ *
+ * PSubSampleSearch
+ *
+ *  uses the subsampling algorithm to compute the P-frame vector
+ *
+ * RETURNS: motion vector
+ *
+ * SIDE EFFECTS:    none
+ *
+ * REFERENCE:  Liu and Zaccarin:  New Fast Algorithms for the Estimation
+ *      of Block Motion Vectors, IEEE Transactions on Circuits
+ *      and Systems for Video Technology, Vol. 3, No. 2, 1993.
+ *
+ *===========================================================================*/
+int
+PSubSampleSearch(const LumBlock * const currentBlockP,
+                 MpegFrame *      const prev, 
+                 int              const by,
+                 int              const bx,
+                 vector *         const motionP,
+                 int              const searchRange) {
+    
+    int my, mx;
+    int bestBestDiff;
+    int stepSize;
+    int x;
+    int bestMY[4], bestMX[4], bestDiff[4];
+    int leftMY, leftMX;
+    int rightMY, rightMX;
+
+    stepSize = (pixelFullSearch ? 2 : 1);
+
+    COMPUTE_MOTION_BOUNDARY(by,bx,stepSize,leftMY,leftMX,rightMY,rightMX);
+
+    if ( searchRange < rightMY ) {
+        rightMY = searchRange;
+    }
+
+    if ( searchRange < rightMX ) {
+        rightMX = searchRange;
+    }
+
+    for ( x = 0; x < 4; x++ ) {
+        bestMY[x] = 0;
+        bestMX[x] = 0;
+        bestDiff[x] = INT_MAX;
+    }
+
+    /* do A pattern */
+    for (my = -searchRange; my < rightMY; my += 2*stepSize) {
+        if (my >= leftMY) {
+            for ( mx = -searchRange; mx < rightMX; mx += 2*stepSize ) {
+                if (mx >= leftMX) {
+                    int diff;
+                    vector m;
+                    m.y = my; m.x = mx;
+                    diff = LumMotionErrorA(currentBlockP, prev, by, bx, m,
+                                           bestDiff[0]);
+                    
+                    if (diff < bestDiff[0]) {
+                        bestMY[0] = my;
+                        bestMX[0] = mx;
+                        bestDiff[0] = diff;
+                    }
+                }
+            }
+        }
+    }
+
+    /* do B pattern */
+    for (my = stepSize-searchRange; my < rightMY; my += 2*stepSize) {
+        if (my >= leftMY) {
+            for (mx = -searchRange; mx < rightMX; mx += 2*stepSize) {
+                if (mx >= leftMX) {
+                    int diff;
+                    vector m;
+                    m.y = my; m.x = mx;
+                    diff = LumMotionErrorB(currentBlockP, prev, by, bx, m, 
+                                           bestDiff[1]);
+                    
+                    if (diff < bestDiff[1]) {
+                        bestMY[1] = my;
+                        bestMX[1] = mx;
+                        bestDiff[1] = diff;
+                    }
+                }
+            }
+        }
+    }
+
+    /* do C pattern */
+    for (my = stepSize-searchRange; my < rightMY; my += 2*stepSize) {
+        if (my >= leftMY) {
+            for ( mx = stepSize-searchRange; mx < rightMX; mx += 2*stepSize ) {
+                if (mx >= leftMX) {
+                    int diff;
+                    vector m;
+                    m.y = my; m.x = mx;
+                    diff = LumMotionErrorC(currentBlockP, prev, by, bx, m,
+                                           bestDiff[2]);
+                    
+                    if (diff < bestDiff[2]) {
+                        bestMY[2] = my;
+                        bestMX[2] = mx;
+                        bestDiff[2] = diff;
+                    }
+                }
+            }
+        }
+    }
+
+    /* do D pattern */
+    for (my = -searchRange; my < rightMY; my += 2*stepSize) {
+        if (my >= leftMY) {
+            for (mx = stepSize-searchRange; mx < rightMX; mx += 2*stepSize) {
+                if (mx >= leftMX) {
+                    int diff;
+                    vector m;
+                    m.y = my; m.x = mx;
+                    diff = LumMotionErrorD(currentBlockP, prev, by, bx, m,
+                                           bestDiff[3]);
+                    
+                    if (diff < bestDiff[3]) {
+                        bestMY[3] = my;
+                        bestMX[3] = mx;
+                        bestDiff[3] = diff;
+                    }
+                }
+            }
+        }
+    }
+
+    /* first check old motion */
+    if ((motionP->y >= leftMY) && (motionP->y < rightMY) &&
+        (motionP->x >= leftMX) && (motionP->x < rightMX)) {
+        bestBestDiff = LumMotionError(currentBlockP, prev, by, bx, 
+                                      *motionP, INT_MAX);
+    } else
+        bestBestDiff = INT_MAX;
+
+    /* look at Error of 4 different motion vectors */
+    for (x = 0; x < 4; ++x) {
+        vector m;
+        m.y = bestMY[x];
+        m.x = bestMX[x];
+        bestDiff[x] = LumMotionError(currentBlockP, prev, by, bx, m,
+                                     bestBestDiff);
+
+        if (bestDiff[x] < bestBestDiff) {
+            bestBestDiff = bestDiff[x];
+            *motionP     = m;
+        }
+    }
+    return bestBestDiff;
+}
+
+
+
+static void
+findBestSpaced(int              const minMY,
+               int              const minMX,
+               int              const maxMY,
+               int              const maxMX,
+               int              const spacing,
+               const LumBlock * const currentBlockP, 
+               MpegFrame *      const prev,
+               int              const by,
+               int              const bx,
+               int *            const bestDiffP, 
+               vector *         const centerP) {
+/*----------------------------------------------------------------------------
+   Examine every 'spacing'th half-pixel within the rectangle 
+   ('minBoundX', 'minBoundY', 'maxBoundX', 'maxBoundY'), 
+
+   If one of the half-pixels examined has a lower "LumMotionError" value
+   than *bestDiffP, update *bestDiffP to that value and update
+   *centerP to the location of that half-pixel.
+-----------------------------------------------------------------------------*/
+    int const minBoundY = MAX(minMY, centerP->y - spacing);
+    int const minBoundX = MAX(minMX, centerP->x - spacing);
+    int const maxBoundY = MIN(maxMY, centerP->y + spacing + 1);
+    int const maxBoundX = MIN(maxMX, centerP->x + spacing + 1);
+
+    int my;
+
+    for (my = minBoundY; my < maxBoundY; my += spacing) {
+        int mx;
+
+        for (mx = minBoundX; mx < maxBoundX; mx += spacing) {
+            int diff;
+            vector m;
+
+            m.y = my; m.x = mx;
+            
+            diff = LumMotionError(currentBlockP, prev, by, bx, m, *bestDiffP);
+            
+            if (diff < *bestDiffP) {
+                *centerP   = m;
+                *bestDiffP = diff;
+            }
+        }
+    }
+}
+
+
+
+/*===========================================================================*
+ *
+ * PLogarithmicSearch
+ *
+ *  uses logarithmic search to compute the P-frame vector
+ *
+ * RETURNS: motion vector
+ *
+ * SIDE EFFECTS:    none
+ *
+ * REFERENCE:  MPEG-I specification, pages 32-33
+ *
+ *===========================================================================*/
+int
+PLogarithmicSearch(const LumBlock * const currentBlockP,
+                   MpegFrame *      const prev, 
+                   int              const by,
+                   int              const bx, 
+                   vector *         const motionP,
+                   int              const searchRange) {
+
+    int const stepSize = (pixelFullSearch ? 2 : 1);
+
+    int minMY, minMX, maxMY, maxMX;
+    int spacing;  /* grid spacing */
+    vector motion;
+        /* Distance from (bx,by) (in half-pixels) of the block that is most
+           like the current block among those that we have examined so far.
+           (0,0) means we haven't examined any.
+        */
+    int bestDiff;
+        /* The difference between the current block and the block offset
+           'motion' from it.
+        */
+    
+    COMPUTE_MOTION_BOUNDARY(by, bx, stepSize, minMY, minMX, maxMY, maxMX);
+    minMX = max(minMX, - searchRange);
+    minMY = max(minMY, - searchRange);
+    maxMX = min(maxMX, + searchRange);
+    maxMY = min(maxMY, + searchRange);
+
+    /* Note: The clipping to 'searchRange' above may seem superfluous because
+       the basic algorithm would never want to look more than 'searchRange'
+       pixels away, but with rounding error, it can.
+    */
+
+    motion.x = motion.y = 0;
+    bestDiff = INT_MAX;
+
+    for (spacing = searchRange; spacing >= stepSize;) {
+        if (stepSize == 2) {  /* make sure spacing is even */
+            if (spacing == 2) 
+                spacing = 0;
+            else {
+                spacing = (spacing+1)/2;
+                if (spacing % 2 != 0) 
+                    --spacing;
+            }
+        } else {
+            if (spacing == 1) {
+                spacing = 0;
+            } else 
+                spacing = (spacing + 1) / 2;
+        }
+        if (spacing >= stepSize) 
+            findBestSpaced(minMY, minMX, maxMY, maxMX,
+                           spacing, currentBlockP, prev, by, bx,
+                           &bestDiff, &motion);
+    }
+
+    {
+        int diff;
+        /* check old motion -- see if it's better */
+        if ((motionP->y >= minMY) && (motionP->y < maxMY) &&
+            (motionP->x >= minMX) && (motionP->x < maxMX)) {
+            diff = LumMotionError(currentBlockP, prev, by, bx, 
+                                  *motionP, bestDiff);
+        } else 
+            diff = INT_MAX;
+        
+        if (bestDiff < diff)
+            *motionP = motion;
+        else 
+            bestDiff = diff;
+    }
+
+    return bestDiff;
+}
+
+
+
+/*===========================================================================*
+ *
+ * PLocalSearch
+ *
+ *  uses local exhaustive search to compute the P-frame vector
+ *
+ * RETURNS: motion vector
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+int
+PLocalSearch(const LumBlock * const currentBlockP,
+             MpegFrame *      const prev, 
+             int              const by,
+             int              const bx,
+             vector *         const motionP,
+             int              const bestSoFar,
+             int              const searchRange) {
+
+    int mx, my;
+    int bestDiff;
+    int stepSize;
+    int leftMY, leftMX;
+    int rightMY, rightMX;
+    int distance;
+    int tempRightMY, tempRightMX;
+
+    stepSize = (pixelFullSearch ? 2 : 1);
+
+    COMPUTE_MOTION_BOUNDARY(by,bx,stepSize,leftMY,leftMX,rightMY,rightMX);
+
+    /* try old motion vector first */
+    if (VALID_MOTION(*motionP)) {
+        bestDiff = LumMotionError(currentBlockP, prev, by, bx, 
+                                  *motionP, bestSoFar);
+
+        if (bestSoFar < bestDiff)
+            bestDiff = bestSoFar;
+    } else {
+        motionP->y = motionP->x = 0;
+        bestDiff = bestSoFar;
+    }
+
+    /* try a spiral pattern */    
+    for (distance = stepSize; distance <= searchRange; distance += stepSize) {
+        tempRightMY = MIN(distance, rightMY);
+        tempRightMX = MIN(distance, rightMX);
+
+        /* do top, bottom */
+        for (my = -distance; my < tempRightMY;
+             my += max(tempRightMY+distance-stepSize, stepSize)) {
+            if (my >= leftMY) {
+                for ( mx = -distance; mx < tempRightMX; mx += stepSize ) {
+                    if (mx >= leftMX) {
+                        int diff;
+                        vector m;
+                        
+                        m.y = my; m.x = mx;
+                        diff = LumMotionError(currentBlockP, prev, by, bx, m, 
+                                              bestDiff);
+                        
+                        if (diff < bestDiff) {
+                            *motionP = m;
+                            bestDiff = diff;
+                        }
+                    }
+                }
+            }
+        }
+
+        /* do left, right */
+        for (mx = -distance; mx < tempRightMX;
+             mx += max(tempRightMX+distance-stepSize, stepSize)) {
+
+            if (mx >= leftMX) {
+                for (my = -distance+stepSize; my < tempRightMY-stepSize;
+                     my += stepSize) {
+                    if (my >= leftMY) {
+                        int diff;
+                        vector m;
+
+                        m.y = my; m.x = mx;
+                        diff = LumMotionError(currentBlockP, prev, by, bx, m,
+                                              bestDiff);
+                        
+                        if (diff < bestDiff) {
+                            *motionP = m;
+                            bestDiff = diff;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return bestDiff;
+}
+
+
+/*===========================================================================*
+ *
+ * PTwoLevelSearch
+ *
+ *  uses two-level search to compute the P-frame vector
+ *  first does exhaustive full-pixel search, then looks at neighboring
+ *  half-pixel motion vectors
+ *
+ * RETURNS: motion vector
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+int
+PTwoLevelSearch(const LumBlock * const currentBlockP,
+                MpegFrame *      const prev, 
+                int              const by,
+                int              const bx, 
+                vector *         const motionP,
+                int              const bestSoFar,
+                int              const searchRange) {
+    int mx, my;
+    int loopInc;
+    int diff, bestDiff;
+    int leftMY, leftMX;
+    int rightMY, rightMX;
+    int distance;
+    int tempRightMY, tempRightMX;
+    int xOffset, yOffset;
+
+    /* exhaustive full-pixel search first */
+
+    COMPUTE_MOTION_BOUNDARY(by,bx,2,leftMY,leftMX,rightMY,rightMX);
+
+    rightMY--;
+    rightMX--;
+
+    /* convert vector into full-pixel vector */
+    if (motionP->y > 0) {
+        if ((motionP->y % 2) == 1) {
+            --motionP->y;
+        }
+    } else if (((-motionP->y) % 2) == 1)
+        ++motionP->y;
+
+    if (motionP->x > 0) {
+        if ((motionP->x % 2) == 1)
+            --motionP->x;
+    } else if ((-motionP->x % 2) == 1)
+        ++motionP->x;
+
+    /* try old motion vector first */
+    if (VALID_MOTION(*motionP)) {
+        bestDiff = LumMotionError(currentBlockP, prev, by, bx, 
+                                  *motionP, bestSoFar);
+
+        if ( bestSoFar < bestDiff ) {
+            bestDiff = bestSoFar;
+        }
+    } else {
+        motionP->y = motionP->x = 0;
+        bestDiff = bestSoFar;
+    }
+
+    ++rightMY;
+    ++rightMX;
+
+    /* try a spiral pattern */    
+    for ( distance = 2; distance <= searchRange; distance += 2 ) {
+        tempRightMY = MIN(distance, rightMY);
+        tempRightMX = MIN(distance, rightMX);
+
+        /* do top, bottom */
+        loopInc = max(tempRightMY + distance - 2, 2);
+        for (my = -distance; my < tempRightMY; my += loopInc) {
+            if (my >= leftMY) {
+                for (mx = -distance; mx < tempRightMX; mx += 2) {
+                    if (mx >= leftMX) {
+                        vector m;
+                        m.y = my; m.x = mx;
+                        diff = LumMotionError(currentBlockP, prev, by, bx, m, 
+                                              bestDiff);
+                        
+                        if (diff < bestDiff) {
+                            *motionP = m;
+                            bestDiff = diff;
+                        }
+                    }
+                }
+            }
+        }
+
+        /* do left, right */
+        loopInc = max(tempRightMX+distance-2, 2);
+        for (mx = -distance; mx < tempRightMX; mx += loopInc) {
+            if (mx >= leftMX) {
+                for ( my = -distance+2; my < tempRightMY-2; my += 2 ) {
+                    if (my >= leftMY) {
+                        int diff;
+                        vector m;
+                        m.y = my; m.x = mx;
+                        diff = LumMotionError(currentBlockP, prev, by, bx, m, 
+                                              bestDiff);
+                        
+                        if ( diff < bestDiff ) {
+                            *motionP = m;
+                            bestDiff = diff;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /* now look at neighboring half-pixels */
+    my = motionP->y;
+    mx = motionP->x;
+
+    --rightMY;
+    --rightMX;
+
+    for (yOffset = -1; yOffset <= 1; ++yOffset) {
+        for (xOffset = -1; xOffset <= 1; ++xOffset) {
+            if ((yOffset != 0) || (xOffset != 0)) {
+                vector m;
+                m.y = my+yOffset; m.x = mx+xOffset;
+                if (VALID_MOTION(m)) {
+                    int diff;
+                    diff = LumMotionError(currentBlockP, prev, by, bx,
+                                          m, bestDiff);
+                    if (diff < bestDiff) {
+                        *motionP = m;
+                        bestDiff = diff;
+                    }
+                }
+            }
+        }
+    }
+    return bestDiff;
+}
+
+
+
+void
+ShowPMVHistogram(fpointer)
+    FILE *fpointer;
+{
+    register int x, y;
+    int *columnTotals;
+    int rowTotal;
+
+    columnTotals = (int *) calloc(2*searchRangeP+3, sizeof(int));
+
+#ifdef COMPLETE_DISPLAY
+    fprintf(fpointer, "    ");
+    for ( y = 0; y < 2*searchRange+3; y++ ) {
+        fprintf(fpointer, "%3d ", y-searchRangeP-1);
+    }
+    fprintf(fpointer, "\n");
+#endif
+
+    for ( x = 0; x < 2*searchRangeP+3; x++ ) {
+#ifdef COMPLETE_DISPLAY
+        fprintf(fpointer, "%3d ", x-searchRangeP-1);
+#endif
+        rowTotal = 0;
+        for ( y = 0; y < 2*searchRangeP+3; y++ ) {
+            fprintf(fpointer, "%3d ", pmvHistogram[x][y]);
+            rowTotal += pmvHistogram[x][y];
+            columnTotals[y] += pmvHistogram[x][y];
+        }
+#ifdef COMPLETE_DISPLAY
+        fprintf(fpointer, "%4d\n", rowTotal);
+#else
+        fprintf(fpointer, "\n");
+#endif
+    }
+
+#ifdef COMPLETE_DISPLAY
+    fprintf(fpointer, "Tot ");
+    for ( y = 0; y < 2*searchRangeP+3; y++ ) {
+        fprintf(fpointer, "%3d ", columnTotals[y]);
+    }
+#endif
+    fprintf(fpointer, "\n");
+}
+
+
+void
+ShowBBMVHistogram(fpointer)
+    FILE *fpointer;
+{
+    register int x, y;
+    int *columnTotals;
+    int rowTotal;
+
+    fprintf(fpointer, "B-frame Backwards:\n");
+
+    columnTotals = (int *) calloc(2*searchRangeB+3, sizeof(int));
+
+#ifdef COMPLETE_DISPLAY
+    fprintf(fpointer, "    ");
+    for ( y = 0; y < 2*searchRangeB+3; y++ ) {
+        fprintf(fpointer, "%3d ", y-searchRangeB-1);
+    }
+    fprintf(fpointer, "\n");
+#endif
+
+    for ( x = 0; x < 2*searchRangeB+3; x++ ) {
+#ifdef COMPLETE_DISPLAY
+        fprintf(fpointer, "%3d ", x-searchRangeB-1);
+#endif
+        rowTotal = 0;
+        for ( y = 0; y < 2*searchRangeB+3; y++ ) {
+            fprintf(fpointer, "%3d ", bbmvHistogram[x][y]);
+            rowTotal += bbmvHistogram[x][y];
+            columnTotals[y] += bbmvHistogram[x][y];
+        }
+#ifdef COMPLETE_DISPLAY
+        fprintf(fpointer, "%4d\n", rowTotal);
+#else
+        fprintf(fpointer, "\n");
+#endif
+    }
+
+#ifdef COMPLETE_DISPLAY
+    fprintf(fpointer, "Tot ");
+    for ( y = 0; y < 2*searchRangeB+3; y++ ) {
+        fprintf(fpointer, "%3d ", columnTotals[y]);
+    }
+#endif
+    fprintf(fpointer, "\n");
+}
+
+
+void
+ShowBFMVHistogram(fpointer)
+    FILE *fpointer;
+{
+    register int x, y;
+    int *columnTotals;
+    int rowTotal;
+
+    fprintf(fpointer, "B-frame Forwards:\n");
+
+    columnTotals = (int *) calloc(2*searchRangeB+3, sizeof(int));
+
+#ifdef COMPLETE_DISPLAY
+    fprintf(fpointer, "    ");
+    for ( y = 0; y < 2*searchRangeB+3; y++ ) {
+        fprintf(fpointer, "%3d ", y-searchRangeB-1);
+    }
+    fprintf(fpointer, "\n");
+#endif
+
+    for ( x = 0; x < 2*searchRangeB+3; x++ ) {
+#ifdef COMPLETE_DISPLAY
+        fprintf(fpointer, "%3d ", x-searchRangeB-1);
+#endif
+        rowTotal = 0;
+        for ( y = 0; y < 2*searchRangeB+3; y++ ) {
+            fprintf(fpointer, "%3d ", bfmvHistogram[x][y]);
+            rowTotal += bfmvHistogram[x][y];
+            columnTotals[y] += bfmvHistogram[x][y];
+        }
+#ifdef COMPLETE_DISPLAY
+        fprintf(fpointer, "%4d\n", rowTotal);
+#else
+        fprintf(fpointer, "\n");
+#endif
+    }
+
+#ifdef COMPLETE_DISPLAY
+    fprintf(fpointer, "Tot ");
+    for ( y = 0; y < 2*searchRangeB+3; y++ ) {
+        fprintf(fpointer, "%3d ", columnTotals[y]);
+    }
+#endif
+    fprintf(fpointer, "\n");
+}
+
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /u/smoot/md/mpeg_encode/RCS/psearch.c,v 1.9 1995/01/19 23:09:12 eyhung Exp $
+ *  $Log: psearch.c,v $
+ * Revision 1.9  1995/01/19  23:09:12  eyhung
+ * Changed copyrights
+ *
+ * Revision 1.9  1995/01/19  23:09:12  eyhung
+ * Changed copyrights
+ *
+ * Revision 1.8  1994/12/07  00:40:36  smoot
+ * Added seperate P and B search ranges
+ *
+ * Revision 1.7  1994/11/12  02:09:45  eyhung
+ * full pixel bug
+ * fixed on lines 512 and 563
+ *
+ * Revision 1.6  1994/03/15  00:27:11  keving
+ * nothing
+ *
+ * Revision 1.5  1993/12/22  19:19:01  keving
+ * nothing
+ *
+ * Revision 1.4  1993/07/22  22:23:43  keving
+ * nothing
+ *
+ * Revision 1.3  1993/06/30  20:06:09  keving
+ * nothing
+ *
+ * Revision 1.2  1993/06/03  21:08:08  keving
+ * nothing
+ *
+ * Revision 1.1  1993/03/02  18:27:05  keving
+ * nothing
+ *
+ */
+
+
diff --git a/converter/ppm/ppmtompeg/psocket.c b/converter/ppm/ppmtompeg/psocket.c
new file mode 100644
index 00000000..707f1d84
--- /dev/null
+++ b/converter/ppm/ppmtompeg/psocket.c
@@ -0,0 +1,452 @@
+/*===========================================================================*
+                              psocket.c
+==============================================================================
+
+   low level communication facilities for Ppmtompeg parallel operation
+
+   By Bryan Henderson 2004.10.13.  Contributed to the public domain by
+   its author.
+
+============================================================================*/
+
+#define _XOPEN_SOURCE 500 /* Make sure stdio.h contains pclose() */
+/* _ALL_SOURCE is needed on AIX to make the C library include the 
+   socket services (e.g. define struct sockaddr) 
+
+   Note that AIX standards.h actually sets feature declaration macros such
+   as _XOPEN_SOURCE, unless they are already set.
+*/
+#define _ALL_SOURCE
+#define __EXTENSIONS__
+  /* __EXTENSIONS__ is for a broken Sun C library (uname SunOS kosh 5.8 
+     generic_108528-16 sun4u sparc).  When you define _XOPEN_SOURCE,
+     it's vnode.h and resource.h fail to define some data types that they
+     need (e.g. timestruct_t).  But with __EXTENSIONS__, they declare the
+     needed types anyway.  Our #include <sys/socket.h> causes the broken
+     header files to get included.
+  */
+
+/* On AIX, pm_config.h includes standards.h, which expects to be included
+   after feature declaration macros such as _XOPEN_SOURCE.  So we include
+   pm_config.h as late as possible.
+*/
+
+#include "pm_config.h" /* For POSIX_IS_IMPLIED */
+
+#ifdef POSIX_IS_IMPLIED
+/* The OpenBSD C library, at least, is broken in that when _XOPEN_SOURCE
+   is defined, its sys/socket.h refers to types "u_char", etc. but does
+   not define them.  But it is also one of the C libraries where
+   POSIX is implied so that we don't need to define _XOPEN_SOURCE in order
+   to get the POSIX routines such as pclose() defined.  So we circumvent
+   the problem by undefining _XOPEN_SOURCE:
+*/
+#undef _XOPEN_SOURCE
+#endif
+
+#include <stdarg.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "pm.h"
+#include "pm_c_util.h"
+#include "nstring.h"
+
+#include "gethostname.h"
+
+#include "psocket.h"
+
+
+/* We use type socklenx_t where we should use socklen_t from the C
+  library, but older systems don't have socklen_t.  And there doesn't
+  appear to be any way for the preprocessor to know whether it exists
+  or not.  On older systems with no socklen_t, a message length is a
+  signed integer, but on modern systems, socklen_t is an unsigned
+  integer.  Until we have some kind of build-time check for the existence
+  of socklen_t, we just use this socklenx_t, which is an unsigned
+  integer, and accept compiler warnings on older system.
+  -Bryan 2001.04.22.
+*/
+typedef unsigned int socklenx_t;
+
+#ifndef SOMAXCONN
+#define SOMAXCONN 5
+#endif
+
+
+
+static void PM_GNU_PRINTF_ATTR(1,2)
+errorExit(const char format[], ...) {
+
+    const char * const hostname = GetHostName();
+
+    va_list args;
+
+    va_start(args, format);
+
+    fprintf(stderr, "%s: FATAL ERROR.  ", hostname);
+    strfree(hostname);
+    vfprintf(stderr, format, args);
+    fputc('\n', stderr);
+
+    exit(1);
+
+    va_end(args);
+}
+
+
+
+static void
+unmarshallInt(unsigned char const buffer[],
+              int *         const valueP) {
+/*----------------------------------------------------------------------------
+  Interpret a number which is formatted for one of our network packets.
+
+  To wit, 32 bit big-endian pure binary.
+-----------------------------------------------------------------------------*/
+    union {
+        uint32_t value;
+        unsigned char bytes[4];
+    } converter;
+
+    memcpy(&converter.bytes, buffer, 4);
+
+    /* Note that contrary to what the C data types suggest, ntohl() is
+       a 32 bit converter, even if a C "long" is bigger than that.
+    */
+    *valueP = ntohl(converter.value);
+}
+
+
+
+static void
+safeRead(int             const fd, 
+         unsigned char * const buf, 
+         unsigned int    const nbyte) {
+/*----------------------------------------------------------------------------
+    Safely read from file 'fd'.  Keep reading until we get
+    'nbyte' bytes.
+-----------------------------------------------------------------------------*/
+    unsigned int numRead;
+
+    numRead = 0;  /* initial value */
+
+    while (numRead < nbyte) {
+        int const result = read(fd, &buf[numRead], nbyte-numRead);
+
+        if (result == -1)
+            errorExit("read (of %u bytes (total %u) ) returned "
+                      "errno %d (%s)",
+                      nbyte-numRead, nbyte, errno, strerror(errno));
+        else 
+            numRead += result;
+    }
+}
+
+
+
+void
+ReadBytes(int             const fd,
+          unsigned char * const buf,
+          unsigned int    const nbyte) {
+
+    safeRead(fd, buf, nbyte);
+}
+
+
+
+void
+ReadInt(int   const socketFd,
+        int * const valueP) {
+
+    unsigned char buffer[4];
+
+    safeRead(socketFd, buffer, sizeof(buffer));
+
+    unmarshallInt(buffer, valueP);
+}
+
+
+
+static void
+marshallInt(int              const value,
+            unsigned char (* const bufferP)[]) {
+/*----------------------------------------------------------------------------
+   Put the number 'value' into the buffer at *bufferP in the form required
+   for one of our network packets.
+
+   To wit, 32 bit big-endian pure binary.
+-----------------------------------------------------------------------------*/
+    union {
+        uint32_t value;
+        unsigned char bytes[4];
+    } converter;
+
+    unsigned char testbuffer[4];
+
+    /* Note that contrary to what the C data types suggest, htonl() is
+       a 32 bit converter, even if a C "long" is bigger than that.
+    */
+    converter.value = htonl(value);
+
+    (*bufferP)[0] = 7;
+    memcpy(testbuffer, &converter.bytes, 4);
+    memcpy(*bufferP, &converter.bytes, 4);
+}
+
+
+
+static void
+safeWrite(int             const fd, 
+          unsigned char * const buf, 
+          unsigned int    const nbyte) {
+/*----------------------------------------------------------------------------
+  Safely write to file 'fd'.  Keep writing until we write 'nbyte'
+  bytes.
+-----------------------------------------------------------------------------*/
+    unsigned int numWritten;
+
+    numWritten = 0;  /* initial value */
+
+    while (numWritten < nbyte) {
+        int const result = write(fd, &buf[numWritten], nbyte-numWritten);
+
+        if (result == -1) 
+            errorExit("write (of %u bytes (total %u) ) returned "
+                      "errno %d (%s)",
+                      nbyte-numWritten, nbyte, errno, strerror(errno));
+        numWritten += result;
+    }
+}
+
+
+
+void
+WriteBytes(int             const fd,
+           unsigned char * const buf,
+           unsigned int    const nbyte) {
+
+    safeWrite(fd, buf, nbyte);
+}
+
+
+
+void
+WriteInt(int const socketFd,
+         int const value) {
+
+    unsigned char buffer[4];
+
+    marshallInt(value, &buffer);
+
+    safeWrite(socketFd, buffer, sizeof(buffer));
+}
+
+
+
+void
+ConnectToSocket(const char *      const machineName, 
+                int               const portNum, 
+                struct hostent ** const hostEnt,
+                int *             const socketFdP,
+                const char **     const errorP) {
+/*----------------------------------------------------------------------------
+   Create a socket and connect it to the specified TCP endpoint.
+
+   That endpoint is fundamentally defined by 'machineName' and
+   'portNum', but *hostEnt is the address of a host entry that caches
+   the results of the host name lookup.  If *hostEnt is non-null, we
+   use it.  If *hostEnt is NULL, we look up the information and update
+   **hostEnt.
+-----------------------------------------------------------------------------*/
+    int rc;
+    
+    *errorP = NULL;  /* initial value */
+
+    if ((*hostEnt) == NULL) {
+        (*hostEnt) = gethostbyname(machineName);
+        if ((*hostEnt) == NULL)
+            asprintfN(errorP, "Couldn't get host by name (%s)", machineName);
+    }
+    if (!*errorP) {
+        rc = socket(AF_INET, SOCK_STREAM, 0);
+        if (rc < 0)
+            asprintfN(errorP, "socket() failed with errno %d (%s)", 
+                      errno, strerror(errno));
+        else {
+            int const socketFd = rc;
+            
+            int rc;
+            unsigned short tempShort;
+            struct sockaddr_in  nameEntry;
+            
+            nameEntry.sin_family = AF_INET;
+            memset((void *) nameEntry.sin_zero, 0, 8);
+            memcpy((void *) &(nameEntry.sin_addr.s_addr),
+                   (void *) (*hostEnt)->h_addr_list[0],
+                   (size_t) (*hostEnt)->h_length);
+            tempShort = portNum;
+            nameEntry.sin_port = htons(tempShort);
+            
+            rc = connect(socketFd, (struct sockaddr *) &nameEntry,
+                         sizeof(struct sockaddr));
+            
+            if (rc != 0)
+                asprintfN(errorP, 
+                          "connect() to host '%s', port %d failed with "
+                          "errno %d (%s)",
+                          machineName, portNum, errno, strerror(errno));
+            else {
+                *errorP = NULL;
+                *socketFdP = socketFd;
+            }
+            if (*errorP)
+                close(socketFd);
+        }
+    }
+}
+
+
+
+static bool
+portInUseErrno(int const testErrno) {
+/*----------------------------------------------------------------------------
+   Return TRUE iff 'testErrno' is what a bind() would return if one requestd
+   a port number that is unavailable (but other port numbers might be).
+-----------------------------------------------------------------------------*/
+    bool retval;
+
+    switch (testErrno) {
+    case EINVAL:
+    case EADDRINUSE:
+    case EADDRNOTAVAIL:
+        retval = TRUE;
+        break;
+    default:
+        retval = FALSE;
+    }
+    return retval;
+}
+
+
+
+static void
+bindToUnusedPort(int              const socketFd,
+                 unsigned short * const portNumP,
+                 const char **    const errorP) {
+    
+    bool foundPort;
+    unsigned short trialPortNum;
+
+    *errorP = NULL;  /* initial value */
+
+    for (foundPort = FALSE, trialPortNum = 2048; 
+         !foundPort && trialPortNum < 16384 && !*errorP; 
+         ++trialPortNum) {
+        
+        struct sockaddr_in nameEntry;
+        int rc;
+        
+        memset((char *) &nameEntry, 0, sizeof(nameEntry));
+        nameEntry.sin_family = AF_INET;
+        nameEntry.sin_port   = htons(trialPortNum);
+
+        rc = bind(socketFd, (struct sockaddr *) &nameEntry,
+                  sizeof(struct sockaddr));
+
+        if (rc == 0) {
+            foundPort = TRUE;
+            *portNumP = trialPortNum;
+        } else if (!portInUseErrno(errno))
+            asprintfN(errorP, "bind() of TCP port number %hu failed "
+                      "with errno %d (%s)", 
+                      trialPortNum, errno, strerror(errno));
+    }
+    
+    if (!*errorP && !foundPort)
+        asprintfN(errorP, "Unable to find a free port.  Every TCP port "
+                  "in the range 2048-16383 is in use");
+}
+
+
+
+void
+CreateListeningSocket(int *         const socketP,
+                      int *         const portNumP,
+                      const char ** const errorP) {
+/*----------------------------------------------------------------------------
+   Create a TCP socket and bind it to the first unused port number we
+   can find.
+
+   Return as *socketP a file handle for the socket (on which Caller can
+   listen()), and as *portNumP the TCP port number (to which Caller's
+   partner can connect).
+-----------------------------------------------------------------------------*/
+    int rc;
+    
+    rc = socket(AF_INET, SOCK_STREAM, 0);
+    if (rc < 0)
+        asprintfN(errorP,
+                  "Unable to create socket.  "
+                  "socket() failed with errno %d (%s)",
+                  errno, strerror(errno));
+    else {
+        int const socketFd = rc;
+
+        unsigned short portNum;
+
+        *socketP = socketFd;
+
+        bindToUnusedPort(socketFd, &portNum, errorP);
+        if (!*errorP) {
+            int rc;
+
+            *portNumP = portNum;
+
+            /* would really like to wait for 1+numMachines machines,
+              but this is max allowable, unfortunately
+            */
+            rc = listen(socketFd, SOMAXCONN);
+            if (rc != 0)
+                asprintfN(errorP, "Unable to listen on TCP socket.  "
+                          "listen() fails with errno %d (%s)", 
+                          errno, strerror(errno));
+        }
+        if (*errorP)
+            close(socketFd);
+    }
+}
+
+
+
+void
+AcceptConnection(int           const listenSocketFd,
+                 int *         const connectSocketFdP,
+                 const char ** const errorP) {
+
+    struct sockaddr otherSocket;
+    socklenx_t      otherSize;
+        /* This is an ugly dual-meaning variable.  As input to accept(),
+           it is the storage size of 'otherSocket'.  As output, it is the 
+           data length of 'otherSocket'.
+        */
+    int             rc;
+    
+    otherSize = sizeof(otherSocket);
+
+    rc = accept(listenSocketFd, &otherSocket, &otherSize);
+
+    if (rc < 0)
+        asprintfN(errorP, "accept() failed with errno %d (%s).  ",
+                  errno, strerror(errno));
+    else {
+        *connectSocketFdP = rc;
+        *errorP = NULL;
+    }
+}
diff --git a/converter/ppm/ppmtompeg/qtest.c b/converter/ppm/ppmtompeg/qtest.c
new file mode 100644
index 00000000..b3d26593
--- /dev/null
+++ b/converter/ppm/ppmtompeg/qtest.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /n/charlie-brown/project/mm/mpeg/mpeg_dist/mpeg_encode/RCS/qtest.c,v 1.5 1995/01/19 23:09:15 eyhung Exp $
+ *  $Log: qtest.c,v $
+ * Revision 1.5  1995/01/19  23:09:15  eyhung
+ * Changed copyrights
+ *
+ * Revision 1.4  1993/01/18  10:20:02  dwallach
+ * *** empty log message ***
+ *
+ * Revision 1.3  1993/01/18  10:17:29  dwallach
+ * RCS headers installed, code indented uniformly
+ *
+ * Revision 1.3  1993/01/18  10:17:29  dwallach
+ * RCS headers installed, code indented uniformly
+ *
+ */
+
+#include <stdio.h>
+#include "mtypes.h"
+#include "mproto.h"
+
+main()
+{
+    Block a;
+    FlatBlock b;
+    BitBucket *bb;
+    int i, j;
+
+    bb = new_bitbucket();
+
+    for (i = 0; i < 8; i++)
+	for (j = 0; j < 8; j++)
+	    a[i][j] = rand() % 100;
+    mp_quant_zig_block(a, b, 1, 1);
+    for (i = 0; i < 64; i++)
+	printf("%6d ", b[i]);
+    printf("\n");
+
+    mp_rle_huff_block(b, bb);	/* intuititve names, huh? */
+
+    printf("Huffman output is %d bits\n", bb->totalbits);
+}
diff --git a/converter/ppm/ppmtompeg/rate.c b/converter/ppm/ppmtompeg/rate.c
new file mode 100644
index 00000000..3940956c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/rate.c
@@ -0,0 +1,986 @@
+/*============================================================================*
+ * rate.c								      *
+ *									      * 
+ *	Procedures concerned with rate control                                *
+ *									      *
+ * EXPORTED PROCEDURES:							      *
+ *      initRatecontrol()                                                     *
+ *      targetRateControl()                                                   *
+ *      updateRateControl()                                                   *
+ *      MB_RateOut()                                                          *
+ *      needQScaleChange()                                                    *
+ *      incNumBlocks()                                                        *
+ *      incQuant()                                                            *
+ *	incMacroBlockBits()                                                   *
+ *      setPictureRate()                                                      *
+ *      setBitRate()                                                          *
+ *      getBitRate()                                                          *
+ *      setBufferSize()                                                       *
+ *      getBufferSize()                                                       *
+ *                                                                            *
+ * NOTES:                                                                     *
+ *	Naming conventions follow those of MPEG-2 draft algorithm (chap. 10)  *
+ *============================================================================*/
+
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/times.h>
+
+#include "ppm.h"
+#include "nstring.h"
+
+#include "all.h"
+#include "mtypes.h"
+#include "bitio.h"
+#include "frames.h"
+#include "prototypes.h"
+#include "param.h"
+#include "mheaders.h"
+#include "fsize.h"
+#include "postdct.h"
+#include "mpeg.h"
+#include "parallel.h"
+#include "dct.h"
+#include "rate.h"
+
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+#define MAX_BIT_RATE 104857600		/* 18 digit number in units of 400 */
+#define MAX_BUFFER_SIZE 16760832        /* 10 digit number in units of 16k */
+#define DEFAULT_BUFFER_SIZE 327680      /* maximun for "constrained" bitstream */
+#define DEFAULT_VBV_FULLNESS 3          /* wait till 1/3 full */
+#define DEFAULT_PICT_RATE_CODE 5        /* code for 30 Frames/sec */
+#define DEFAULT_PICT_RATE 30            /* 30 frames per second */
+#define MAX_VBV_DELAY 32768             /* 16 digits */
+
+
+/*	  Variables from Parameter File */
+
+static int	RateControlMode = VARIABLE_RATE;
+static int32 buffer_size = DEFAULT_BUFFER_SIZE;
+static int32 bit_rate = -1;
+
+static bool wantVbvUnderflowWarning;
+static bool wantVbvOverflowWarning;
+
+/*   Variables for the VBV buffer defined in MPEG specs */
+static unsigned int VBV_remainingDelay; 
+    /* delay in units of 1/90000 seconds */
+static int32 VBV_buffer = 0;	  /* fullness of the theoretical VBV buffer */
+static int32 bufferFillRate = 0;    /* constant rate at which buffer filled */
+static int32 frameDelayIncrement = 0;	/* number of "delay" units/Frame */
+
+/*  Global complexity measure variables */
+static int Xi, Xp, Xb;  /*  Global complexity measure  */
+
+static int Si, Sp, Sb;  /*  Total # bits for last pict of type (Overhead?) */
+
+static float Qi, Qp, Qb; /* avg quantizaton for last picture of type  */
+     
+/*  Target bit allocations for each type of picture*/
+int Ti, Tp, Tb;
+
+int current_Tx;	/* allocation for current frame */
+
+/*  Count of number of pictures of each type remaining */
+int GOP_X;
+int GOP_I;
+int GOP_P;
+int GOP_B;
+
+int Nx = 0;
+int Ni = 0;
+int Np = 0;
+int Nb = 0;
+
+/*   Counters used while encoding frames   */
+
+int rc_numBlocks = 0;
+int rc_totalQuant = 0;
+int rc_bitsThisMB;
+int rc_totalMBBits;
+int rc_totalFrameBits;
+int rc_totalOverheadBits = 0;
+
+
+/*	Want to print out Macroblock info every Nth MB */
+int RC_MB_SAMPLE_RATE = 0;
+
+static float Ki = .7;
+static float Kp = 1;
+static float Kb = 1.4;
+static int rc_R;
+static int rc_G;
+
+/*   Rate Control variables   */
+
+/*   Virtual buffers for each frame type */
+static int d0_i;   /* Initial fullnesses */
+static int d0_p;
+static int d0_b;
+
+static int lastFrameVirtBuf;   /* fullness after last frame of this type */
+static int currentVirtBuf;     /* fullness during current encoding*/
+
+static int MB_cnt = -1;	       /* Number of MB's in picture */
+
+static int rc_Q;               /* reference quantization parameter */
+
+static int reactionParameter;  /*  Reaction parameter */
+
+/*	Adaptive Quantization variables */
+static int act_j;              /*  spatial activity measure */
+static float N_act;            /*  Normalized spacial activity */
+static int avg_act;	   /*  average activity value in last picture encoded */
+static int total_act_j;	       /*  Sum of activity values in current frame */
+
+static int var_sblk;	       /* sub-block activity */
+static int P_mean;	       /* Mean value of pixels in 8x8 sub-block */
+
+static int mquant;	       /* Raw Quantization value */
+static int Qscale;	       /* Clipped, truncated quantization value */
+
+
+
+/*  Output-related variables */
+#ifdef RC_STATS_FILE
+static FILE *RC_FILE;
+#endif
+
+static const char * const Frame_header1 = 
+"  Fm         #     Bit      GOP                    V                ";
+static const char * const Frame_header2 = 
+"   #  type   MBs   Alloc    left  Ni Np Nb  N_act  buff   Q_rc Qscale";
+static const char * const Frame_header3 = 
+"----     -  ----  ------ -------  -- -- --  -----  ------ ----   ----";
+static const char * const Frame_trailer1 = 
+"                      avg          virt     %    GOP      %     VBV";
+static const char * const Frame_trailer2 = 
+"    Sx    Qx      Xx  act N_act  buffer alloc    left  left     buf  delay";
+static const char * const Frame_trailer3 = 
+"------ --.-- -------  --- --.-- -------   --- -------   --- ------- ------";
+
+static const char * const MB_header1 = 
+"MB#  #bits  Q mqt     Dj  Q_j   actj  N_act  totbits b/MB %alloc %done";
+static const char * const MB_header2 = 
+"---  ----- -- --- ------  ---  -----  --.--   ------ ----    ---   ---";
+
+static char rc_buffer[101];
+
+/*	EXTERNAL Variables  */
+extern char *framePattern;
+extern int framePatternLen;
+
+
+/*===============================*
+ * INTERNAL PROCEDURE prototypes *
+ *===============================*/
+
+int initGOPRateControl _ANSI_ARGS_((void));
+int determineMBCount _ANSI_ARGS_((void));
+void checkBufferFullness _ANSI_ARGS_((int count));
+void checkSpatialActivity _ANSI_ARGS_((Block blk0, Block blk1, Block blk2, Block blk3));
+void incNumBlocks _ANSI_ARGS_((int num));
+void calculateVBVDelay _ANSI_ARGS_((int num));
+int BlockExperiments  _ANSI_ARGS_((int16 *OrigBlock, int16 *NewBlock, int control));
+     
+     
+
+static void
+analyzePattern(const char *  const framePattern,
+               int           const framePatternLen,
+               int *         const gop_xP,
+               int *         const gop_iP,
+               int *         const gop_pP,
+               int *         const gop_bP,
+               const char ** const errorP) {
+
+    unsigned int i;
+
+    /*  Initialize Pattern info */
+    *gop_xP = framePatternLen;    
+
+    for (i = 0, *gop_iP = 0, *gop_pP = 0, *gop_bP = 0, *errorP = NULL;
+         i < framePatternLen && !*errorP; 
+         ++i) {
+        switch(framePattern[i]) {
+        case 'i': ++*gop_iP; break;
+        case 'p': ++*gop_pP; break;
+        case 'b': ++*gop_bP; break;
+        default:
+            asprintfN(errorP, "Bad pattern - not composed of i, p, and b");
+        }
+    }
+    assert(*gop_xP == *gop_iP + *gop_pP + *gop_bP);
+}
+  
+
+
+/*===========================================================================*
+ *
+ * initRateControl
+ *
+ *	initialize the allocation parameters.
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:   many global variables 
+ *
+ * NOTES:  Get rid of the redundant pattern stuff!!
+ *===========================================================================*/
+int
+initRateControl(bool const wantUnderflowWarning,
+                bool const wantOverflowWarning) {
+    int result;
+    const char * error;
+
+    wantVbvUnderflowWarning = wantUnderflowWarning;
+    wantVbvOverflowWarning  = wantOverflowWarning;
+
+    DBG_PRINT(("Initializing Allocation Data\n"));
+  
+#ifdef RC_STATS_FILE
+    RC_FILE = fopen("RC_STATS_FILE", "w");
+    if ( RC_FILE  == NULL) {
+        DBG_PRINT(("Open of RC file failed, using stderr\n"));
+        RC_FILE = stderr;
+        fprintf(RC_FILE, "Open of RC file failed, using stderr\n");
+        fflush(RC_FILE);
+    }
+#endif
+
+    VBV_remainingDelay = 0;
+  
+    analyzePattern(framePattern, framePatternLen,
+                   &GOP_X, &GOP_I, &GOP_P, &GOP_B, &error);
+
+    if (error) {
+        pm_message("Unable to set up rate control.  Switching to variable.  "
+                   "%s", error);
+        strfree(error);
+        RateControlMode = VARIABLE_RATE;
+        return -1;
+    }
+
+
+    /* Initializing GOP bit allocation */	
+    rc_R = 0;
+    rc_G = (bit_rate * GOP_X/frameRateRounded);
+  
+    /*   Initialize the "global complexity measures" */
+    Xi = (160 * bit_rate/115);
+    Xp = (60 * bit_rate/115);
+    Xb = (42 * bit_rate/115);
+  
+    /*   Initialize MB counters */
+    rc_totalMBBits= rc_bitsThisMB= rc_totalFrameBits=rc_totalOverheadBits = 0;
+    rc_numBlocks = rc_totalQuant = 0;
+  
+    /*   init virtual buffers  */
+    reactionParameter = (2 * bit_rate / frameRateRounded);
+    d0_i = (10 * reactionParameter / 31);
+    d0_p = (Kp * d0_i);
+    d0_b = (Kb * d0_i);
+  
+    lastFrameVirtBuf = d0_i;	/*  start with I Frame */
+    rc_Q = lastFrameVirtBuf  * 31 / reactionParameter;
+  
+    /*   init spatial activity measures */
+    avg_act = 400;		/* Suggested initial value */
+    N_act = 1;
+  
+    mquant = rc_Q * N_act;
+  
+    frameDelayIncrement = (90000 / frameRateRounded);
+        /* num of "delay" units per frame */
+    bufferFillRate = bit_rate / frameRateRounded; 
+        /* VBV buf fills at constant rate */
+    VBV_buffer = buffer_size;
+    DBG_PRINT(("VBV- delay: %d, fill rate: %d, delay/Frame: %d units, "
+               "buffer size: %d\n",
+               VBV_remainginDelay, bufferFillRate, frameDelayIncrement, 
+               buffer_size));
+  
+    result = initGOPRateControl();
+  
+    return result;
+}
+
+/*===========================================================================*
+ *
+ * initGOPRateControl
+ *
+ *		(re)-initialize the RC for the a new Group of Pictures.
+ *	New bit allocation, but carry over complexity measures.
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:   many global variables 
+ *
+ *===========================================================================*/
+int
+initGOPRateControl()
+{
+    DBG_PRINT(("Initializing new GOP\n"));
+  
+    Nx = GOP_X;
+    Ni = GOP_I;
+    Np = GOP_P;
+    Nb = GOP_B;
+  
+    rc_R += rc_G;
+  
+    DBG_PRINT(("bufsize: %d, bitrate: %d, pictrate: %d, GOP bits: %d\n",
+               buffer_size, bit_rate, frameRateRounded, rc_R));
+    DBG_PRINT(("Xi: %d, Xp: %d, Xb: %d Nx: %d, Ni: %d, Np: %d, Nb: %d\n",
+               Xi, Xp, Xb, Nx,Ni,Np,Nb));
+    DBG_PRINT(("d0_i: %d, d0_p: %d, d0_b: %d, avg_act: %d, rc_Q: %d, "
+               "mquant: %d\n",
+               d0_i, d0_p, d0_b, avg_act, rc_Q, mquant));
+    return 1;
+}
+
+
+
+/*===========================================================================*
+ *
+ * targetRateControl
+ *
+ *      Determine the target allocation for given picture type, initiates
+ *  variables for rate control process.
+ *
+ * RETURNS:     nothing.
+ *
+ * SIDE EFFECTS:   many global variables
+ *
+ *===========================================================================*/
+void
+targetRateControl(MpegFrame * const frame) {
+
+    float temp1, minimumBits;
+    float tempX, tempY, tempZ;
+    int result;
+    int frameType;
+    const char *strPtr;
+  
+    minimumBits = (bit_rate / (8 * frameRateRounded));
+  
+    /*   Check if new GOP */
+    if (Nx == 0) {
+        initGOPRateControl();
+    }
+  
+    if (MB_cnt < 0) {MB_cnt = determineMBCount();}
+  
+    switch (frame->type) {
+    case TYPE_IFRAME:
+        frameType = 'I';
+    
+        tempX = ( (Np * Ki * Xp) / (Xi * Kp) );
+        tempY = ( (Nb * Ki * Xb) / (Xi*Kb) );
+        tempZ = Ni + tempX + tempY;
+        temp1 = (rc_R / tempZ);
+        result = (int) (temp1 > minimumBits ? temp1 :  minimumBits);
+        current_Tx = Ti = result;
+        lastFrameVirtBuf = d0_i;
+        break;
+    
+    case TYPE_PFRAME:
+        frameType = 'P';
+        tempX =  ( (Ni * Kp * Xi) / (Ki * Xp) );
+        tempY =  ( (Nb * Kp * Xb) / (Kb * Xp) );
+        tempZ = Np + tempX + tempY;
+        temp1 = (rc_R/ tempZ);
+        result = (int) (temp1 > minimumBits ? temp1 :  minimumBits);
+        current_Tx = Tp = result;
+        lastFrameVirtBuf = d0_p;
+        break;
+    
+    case TYPE_BFRAME:
+        frameType = 'B';
+        tempX =  ( (Ni * Kb * Xi) / (Ki * Xb) );
+        tempY =  ( (Np * Kb * Xp) / (Kp * Xb) );
+        tempZ = Nb + tempX + tempY;
+        temp1 = (rc_R/ tempZ);
+        result = (int) (temp1 > minimumBits ? temp1 :  minimumBits);
+        current_Tx = Tb = result;
+        lastFrameVirtBuf = d0_b;
+        break;
+    
+    default:
+        frameType = 'X';
+    }
+  
+    N_act = 1;
+    rc_Q = lastFrameVirtBuf  * 31 / reactionParameter;
+    mquant = rc_Q * N_act;
+    Qscale = (mquant > 31 ? 31 : mquant);
+    Qscale = (Qscale < 1 ? 1 : Qscale);
+  
+    /*   Print headers for Frame info */
+    strPtr = Frame_header1;
+    DBG_PRINT(("%s\n",strPtr));
+    strPtr = Frame_header2;
+    DBG_PRINT(("%s\n",strPtr));
+    strPtr = Frame_header3;
+    DBG_PRINT(("%s\n",strPtr));
+  
+    /*   Print Frame info */
+    sprintf(rc_buffer, "%4d     %1c  %4d  %6d %7d  "
+            "%2d %2d %2d   %2.2f  %6d %4d    %3d",
+            frame->id,frameType,MB_cnt,current_Tx,rc_R,Ni,Np,Nb, 
+            N_act, lastFrameVirtBuf, rc_Q, Qscale);
+  
+#ifdef RC_STATS_FILE
+    fprintf(RC_FILE,"%s\n", rc_buffer);
+    fflush(RC_FILE);
+#endif
+    DBG_PRINT(("%s\n",rc_buffer));
+  
+    /*  Print headers for Macroblock info */
+    if (RC_MB_SAMPLE_RATE) {
+        strPtr = MB_header1;
+        DBG_PRINT(("%s\n",strPtr));
+        strPtr = MB_header2;
+        DBG_PRINT(("%s\n",strPtr));
+    }
+}
+
+
+
+
+static void 
+updateVBVBuffer(int const frameBits) {
+/*----------------------------------------------------------------------------
+  Update the VBV buffer after each frame.  This theoretical buffer is
+  being filled at constant rate 'bufferFillRate' from an mpeg stream.
+  It is emptied as each frame is grabbed by the decoder.  Exception is
+  that the decoder will wait until the "delay" is over.
+
+  We mark the passing of one unit of time, in which 'frameBits' bits of
+  mpeg data were grabbed from the buffer by the decoder.
+-----------------------------------------------------------------------------*/
+    if (VBV_remainingDelay)
+        VBV_remainingDelay -= MIN(frameDelayIncrement, VBV_remainingDelay);
+    else
+        VBV_buffer -= frameBits;
+
+    VBV_buffer += bufferFillRate;
+    if (VBV_buffer < 0 && wantVbvUnderflowWarning)
+        pm_message("WARNING - VBV buffer underflow (%d)", VBV_buffer);
+    if (VBV_buffer > buffer_size && wantVbvOverflowWarning)
+        pm_message("WARNING - VBV buffer overflow (%d > %d)",
+                   VBV_buffer, buffer_size);
+}
+
+
+
+/*===========================================================================*
+ *
+ * updateRateControl
+ *
+ *      Update the statistics kept, after end of frame.  Resets
+ *  various global variables
+ *
+ * RETURNS:     nothing
+ *
+ * SIDE EFFECTS:   many global variables
+ *
+ *===========================================================================*/
+void
+updateRateControl(int const type) {
+    int totalBits, frameComplexity, pctAllocUsed, pctGOPUsed;
+    float avgQuant;
+    const char *strPtr;
+
+    totalBits = rc_totalFrameBits;
+    avgQuant = ((float) rc_totalQuant / (float) rc_numBlocks);
+    frameComplexity = totalBits * avgQuant;
+    pctAllocUsed = (totalBits *100 / current_Tx);
+    rc_R -= totalBits;
+    pctGOPUsed = (rc_R *100/ rc_G);
+  
+    avg_act = (total_act_j / MB_cnt);
+  
+    updateVBVBuffer(totalBits);
+  
+    switch (type) {
+    case TYPE_IFRAME:
+        Ti = current_Tx;
+        d0_i = currentVirtBuf;
+        Ni--;
+        Si = totalBits;
+        Qi = avgQuant;
+        Xi = frameComplexity;
+        break;
+    case TYPE_PFRAME:
+        Tp = current_Tx;
+        d0_p = currentVirtBuf;
+        Np--;
+        Sp = totalBits;
+        Qp = avgQuant;
+        Xp = frameComplexity;
+        break;
+    case TYPE_BFRAME:
+        Tb = current_Tx;
+        d0_b = currentVirtBuf;
+        Nb--;
+        Sb = totalBits;
+        Qb = avgQuant;
+        Xb = frameComplexity;
+        break;
+    }
+  
+  
+    /*  Print Frame info */
+    strPtr = Frame_trailer1;
+    DBG_PRINT(("%s\n",strPtr));
+    strPtr = Frame_trailer2;
+    DBG_PRINT(("%s\n",strPtr));
+    strPtr = Frame_trailer3;
+    DBG_PRINT(("%s\n",strPtr));
+  
+    sprintf(rc_buffer, "%6d  %2.2f  %6d  %3d  %2.2f %7d   "
+            "%3d %7d   %3d  %6d %6d",
+            totalBits, avgQuant, frameComplexity, avg_act, N_act, 
+            currentVirtBuf, pctAllocUsed, rc_R, pctGOPUsed, 
+            VBV_buffer, VBV_remainingDelay);
+#ifdef RC_STATS_FILE
+    fprintf(RC_FILE,"%s\n", rc_buffer);
+    fflush(RC_FILE);
+#endif
+    DBG_PRINT(("%s\n",rc_buffer));
+  
+    Nx--;
+    rc_totalMBBits= rc_bitsThisMB= rc_totalFrameBits=rc_totalOverheadBits = 0;
+    rc_numBlocks = rc_totalQuant = total_act_j = currentVirtBuf = 0;
+  
+    DBG_PRINT(("GOP now has %d bits remaining (%3d%%) for %d frames .. , "
+               "Ni= %d, Np= %d, Nb= %d\n", 
+               rc_R, (rc_R*100/rc_G), (Ni+Np+Nb), Ni, Np, Nb));
+  
+}
+
+
+/*===========================================================================*
+ *
+ * MB_RateOut
+ *
+ *      Prints out sampling of MB rate control data.  Every "nth" block
+ *	stats are printed, with "n" controled by global RC_MB_SAMPLE_RATE
+ *	(NB. "skipped" blocks do not go through this function and thus do not
+ *		show up in the sample )
+ *
+ * RETURNS:     nothing
+ *
+ * SIDE EFFECTS:   none
+ *
+ * NOTES:
+ *
+ *===========================================================================*/
+void
+  MB_RateOut(type)
+int type;
+{
+  int totalBits;
+  int pctUsed, pctDone;
+  int bitsThisMB;
+  int bitsPerMB;
+  
+  bitsThisMB = rc_bitsThisMB;
+  totalBits = rc_totalFrameBits;
+  bitsPerMB = (totalBits / rc_numBlocks); 
+  pctDone = (rc_numBlocks * 100/ MB_cnt); 
+  pctUsed = (totalBits *100/current_Tx);
+  
+  sprintf(rc_buffer, "%3d  %5d %2d %3d %6d  %3d %6d   %2.2f   %6d %4d    %3d   %3d\n",
+	  (rc_numBlocks - 1), bitsThisMB, Qscale, mquant, currentVirtBuf, 
+	  rc_Q, act_j, N_act, totalBits, bitsPerMB, pctUsed, pctDone);
+#ifdef RC_STATS_FILE
+  fprintf(RC_FILE, "%s", rc_buffer);
+  fflush(RC_FILE);
+#endif
+  
+  if ( (RC_MB_SAMPLE_RATE) && ((rc_numBlocks -1) % RC_MB_SAMPLE_RATE)) {
+    DBG_PRINT(("%s\n", rc_buffer));
+  } else {
+    return;
+  }
+}
+
+
+
+/*===========================================================================*
+ *
+ * incNumBlocks()
+ *
+ *
+ * RETURNS:   nothing
+ *
+ * SIDE EFFECTS:  rc_numBlocks
+ *
+ * NOTES:
+ *
+ *===========================================================================*/
+void incNumBlocks(num)
+     int num;
+{
+  rc_numBlocks += num;
+}
+
+
+/*===========================================================================*
+ *
+ * incMacroBlockBits()
+ *
+ *	Increments the number of Macro Block bits and the total of Frame
+ *  bits by the number passed.
+ *
+ * RETURNS:   nothing
+ *
+ * SIDE EFFECTS:  rc_totalMBBits
+ *
+ * NOTES:
+ *
+ *===========================================================================*/
+void incMacroBlockBits(num)
+     int num;
+{
+  rc_bitsThisMB = num;
+  rc_totalMBBits += num;
+  rc_totalFrameBits += num;
+}
+
+
+/*===========================================================================*
+ *
+ *   	needQScaleChange(current Q scale, 4 luminance blocks)
+ *
+ *
+ * RETURNS:     new Qscale
+ *
+ * SIDE EFFECTS:   
+ *
+ *===========================================================================*/
+int needQScaleChange(oldQScale, blk0, blk1, blk2, blk3)
+     int oldQScale;
+     Block blk0;
+     Block blk1;
+     Block blk2;
+     Block blk3;
+{
+  
+  /*   One more MacroBlock seen */
+  rc_numBlocks++;		/* this notes each block num in MB */
+  
+  checkBufferFullness(oldQScale);
+  
+  checkSpatialActivity(blk0, blk1, blk2, blk3);
+  
+  mquant = rc_Q * N_act;
+  Qscale = (mquant > 31 ? 31 : mquant);
+  Qscale = (Qscale < 1 ? 1 : Qscale);
+  rc_totalQuant += Qscale;
+  
+  if (oldQScale == Qscale)
+    return -1;
+  else
+    return Qscale;
+}
+
+
+/*===========================================================================*
+ *
+ * determineMBCount() 
+ *
+ *      Determines number of Macro Blocks in frame from the frame sizes
+ *	passed.
+ *
+ * RETURNS:     nothing
+ *
+ * SIDE EFFECTS:   sets the count passed
+ *
+ *===========================================================================*/
+int
+  determineMBCount ()
+{
+  int y,x;
+  
+  x = (Fsize_x +15)/16;
+  y = (Fsize_y +15)/16;
+  return  (x * y);
+}
+
+
+
+/*===========================================================================*
+ *
+ * void checkBufferFullness ()
+ *
+ *      Calculates the fullness of the virtual buffer for each
+ *  frame type.  Called before encoding each macro block.  Along
+ *  with the normalized spatial activity measure (N_act), it
+ *  determine the quantization factor for the next macroblock.
+ *
+ * RETURNS:     nothing
+ *
+ * SIDE EFFECTS:   the "currentVirtBuf" variable
+ *
+ * NOTES:
+ *
+ *===========================================================================*/
+void checkBufferFullness (oldQScale)
+     int oldQScale;
+{
+  int temp;
+  
+  temp = lastFrameVirtBuf + rc_totalFrameBits;
+  temp -=  (current_Tx * rc_numBlocks / MB_cnt);
+  currentVirtBuf = temp;
+  
+  rc_Q = (currentVirtBuf * 31 / reactionParameter);
+  return;
+}
+
+
+/*===========================================================================*
+ *
+ * void checkSpatialActivity()
+ *
+ *      Calcualtes the spatial activity for the four luminance blocks of the
+ *	macroblock.  Along with the normalized reference quantization parameter 
+ *  (rc_Q) , it determines the quantization factor for the next macroblock.
+ *
+ * RETURNS:     nothing
+ *
+ * SIDE EFFECTS:   the Adaptive quantization variables- act_j, N_act.
+ *
+ * NOTES:
+ *
+ *===========================================================================*/
+void checkSpatialActivity(blk0, blk1, blk2, blk3)
+     Block blk0;
+     Block blk1;
+     Block blk2;
+     Block blk3;
+{
+  int temp;
+  int16 *blkArray[4]; 
+  int16 *curBlock;
+  int16 *blk_ptr;
+  int var[4];
+  int i, j;
+  
+  
+  blkArray[0] = (int16 *) blk0;
+  blkArray[1] = (int16 *) blk1;
+  blkArray[2] = (int16 *) blk2;
+  blkArray[3] = (int16 *) blk3;
+  
+  
+  for (i =0; i < 4; i++) {	/* Compute the activity in each block */
+    curBlock = blkArray[i];
+    blk_ptr = curBlock;
+    P_mean = 0;
+    /*  Find the mean pixel value */
+    for (j=0; j < DCTSIZE_SQ; j ++) {
+      P_mean += *(blk_ptr++);
+      /*			P_mean += curBlock[j]; 
+				if (curBlock[j] != *(blk_ptr++)) {
+				printf("ARRAY ERROR: block %d\n", j);
+				}
+				*/
+    }
+    P_mean /= DCTSIZE_SQ;
+    
+    /*  Now find the variance  */
+    curBlock = blkArray[i];
+    blk_ptr = curBlock;
+    var[i] = 0;
+    for (j=0; j < DCTSIZE_SQ; j++) {
+#ifdef notdef
+      if (curBlock[j] != *(blk_ptr++)) {
+	printf("ARRAY ERROR: block %d\n", j);
+      }
+      temp = curBlock[j] - P_mean;
+#endif      
+      temp = *(blk_ptr++) - P_mean;
+      var[i] += (temp * temp);
+    }
+    var[i] /= DCTSIZE_SQ;
+  }
+  
+  /*  Choose the minimum variance from the 4 blocks and use as the activity */
+  var_sblk  = var[0];
+  for (i=1; i < 4; i++) {
+    var_sblk = (var_sblk < var[i] ? var_sblk : var[i]);
+  }
+  
+  
+  act_j = 1 + var_sblk;
+  total_act_j += act_j;
+  temp = (2 * act_j + avg_act);
+  N_act = ( (float) temp / (float) (act_j + 2*avg_act) );
+  
+  return;
+}
+
+
+
+
+/*============================================================================*
+ *
+ * getRateMode ()
+ *
+ *      Returns the rate mode- interpreted as either Fixed or Variable
+ *
+ * RETURNS:     integer
+ *
+ * SIDE EFFECTS:   none
+ *
+ *
+ *==========================================================================*/
+int getRateMode()
+{
+  return RateControlMode;
+}
+
+
+/*===========================================================================*
+ *
+ * setBitRate ()
+ *
+ *      Checks the string parsed from the parameter file.  Verifies
+ *  number and sets global values. MPEG standard specifies that bit rate
+ *	be rounded up to nearest 400 bits/sec.
+ *
+ * RETURNS:     nothing
+ *
+ * SIDE EFFECTS:   global variables
+ *
+ * NOTES:	Should this be in the 400-bit units used in sequence header?
+ *
+ *===========================================================================*/
+void setBitRate (const char * const charPtr)
+{
+  int rate, rnd;
+  
+  rate = atoi(charPtr);
+  if (rate > 0) {
+    RateControlMode = FIXED_RATE;
+  } else {
+    printf("Parameter File Error:  invalid BIT_RATE: \"%s\", defaults to Variable ratemode\n",
+	   charPtr);
+    RateControlMode = VARIABLE_RATE;
+    bit_rate = -1;
+  }
+  rnd = (rate % 400);
+  rate += (rnd ? 400 -rnd : 0); /* round UP to nearest 400 bps */
+  rate = (rate > MAX_BIT_RATE ? MAX_BIT_RATE : rate);
+  bit_rate = rate;
+  DBG_PRINT(("Bit rate is: %d\n", bit_rate));
+} 
+
+
+
+/*===========================================================================*
+ *
+ * getBitRate ()
+ *
+ *      Returns the bit rate read from the parameter file.  This is the
+ *  real rate in bits per second, not in 400 bit units as is written to
+ *  the sequence header.
+ *
+ * RETURNS:     int (-1 if Variable mode operation)
+ *
+ * SIDE EFFECTS:   none
+ *
+ *===========================================================================*/
+int getBitRate ()
+{
+  return bit_rate;
+}
+
+
+
+
+/*===========================================================================*
+ *
+ * setBufferSize ()
+ *
+ *      Checks the string parsed from the parameter file.  Verifies
+ *  number and sets global values.
+ *
+ * RETURNS:     nothing
+ *
+ * SIDE EFFECTS:   buffer_size global variable.
+ *
+ * NOTES:	The global is in bits, NOT the 16kb units used in sequence header
+ *
+ *===========================================================================*/
+void setBufferSize (const char * const charPtr)
+{
+  int size;
+  
+  size = atoi(charPtr);
+  size = (size > MAX_BUFFER_SIZE ? MAX_BUFFER_SIZE : size);
+  if (size > 0) {
+    size = (16*1024) * ((size + (16*1024 - 1)) / (16*1024));
+    buffer_size = size;
+  } else {
+    buffer_size = DEFAULT_BUFFER_SIZE;
+    printf("Parameter File Error:  invalid BUFFER_SIZE: \"%s\", defaults to : %d\n",
+	   charPtr, buffer_size);
+  }
+  DBG_PRINT(("Buffer size is: %d\n", buffer_size));
+}
+
+
+/*===========================================================================*
+ *
+ * getBufferSize ()
+ *
+ *      returns the buffer size read from the parameter file.  Size is
+ *  in bits- not in units of 16k as written to the sequence header.
+ *
+ * RETURNS:     int (or -1 if invalid)
+ *
+ * SIDE EFFECTS:   none
+ *
+ *===========================================================================*/
+int getBufferSize ()
+{
+  return buffer_size;
+}
+
+
+
diff --git a/converter/ppm/ppmtompeg/readframe.c b/converter/ppm/ppmtompeg/readframe.c
new file mode 100644
index 00000000..d1423c1f
--- /dev/null
+++ b/converter/ppm/ppmtompeg/readframe.c
@@ -0,0 +1,1016 @@
+/*===========================================================================*
+ * readframe.c                    
+ *                                
+ *  procedures to read in frames  
+ *                                
+ * EXPORTED PROCEDURES:           
+ *  ReadFrame                     
+ *  SetFileType                   
+ *  SetFileFormat                 
+ *                                
+ *===========================================================================*/
+
+/* COPYRIGHT INFORMATION IS AT THE END OF THIS FILE */
+
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#define _BSD_SOURCE   /* Make sure popen() is in stdio.h */
+#include "all.h"
+#include <time.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include "ppm.h"
+#include "nstring.h"
+
+#include "mtypes.h"
+#include "frames.h"
+#include "prototypes.h"
+#include "parallel.h"
+#include "param.h"
+#include "input.h"
+#include "fsize.h"
+#include "rgbtoycc.h"
+#include "jpeg.h"
+#include "opts.h"
+#include "readframe.h"
+
+
+/*==================*
+ * STATIC VARIABLES *
+ *==================*/
+
+static int  fileType = BASE_FILE_TYPE;
+struct YuvLine {
+    uint8   data[3072];
+    uint8   y[1024];
+    int8    cr[1024];
+    int8    cb[1024];
+};
+
+
+/*==================*
+ * Portability      *
+ *==================*/
+#ifdef __OS2__
+  #define popen _popen
+#endif
+   
+
+/*==================*
+ * Global VARIABLES *
+ *==================*/
+
+extern boolean GammaCorrection;
+extern float GammaValue;
+extern int outputWidth,outputHeight;
+boolean resizeFrame;
+const char *CurrFile;
+
+/*===============================*
+ * INTERNAL PROCEDURE prototypes *
+ *===============================*/
+
+static void ReadEYUV _ANSI_ARGS_((MpegFrame * mf, FILE *fpointer,
+                 int width, int height));
+static void ReadAYUV _ANSI_ARGS_((MpegFrame * mf, FILE *fpointer,
+                 int width, int height));
+static void SeparateLine _ANSI_ARGS_((FILE *fpointer, struct YuvLine *lineptr,
+                     int width));
+static void ReadY _ANSI_ARGS_((MpegFrame * mf, FILE *fpointer,
+                 int width, int height));
+static void ReadSub4 _ANSI_ARGS_((MpegFrame * mf, FILE *fpointer,
+                  int width, int height));
+static void DoGamma  _ANSI_ARGS_((MpegFrame *mf, int width, int height));
+
+static void DoKillDim _ANSI_ARGS_((MpegFrame *mf, int w, int h));
+
+#define safe_fread(ptr,sz,len,fileptr)                           \
+    if ((safe_read_count=fread(ptr,sz,len,fileptr))!=sz*len) {   \
+      fprintf(stderr,"Input file too small! (%s)\n",CurrFile);   \
+      exit(1);}                                                  \
+
+/*=====================*
+ * EXPORTED PROCEDURES *
+ *=====================*/
+
+
+
+void    
+SetResize(bool const set) {
+    resizeFrame = set;
+}
+
+
+
+static void
+ReadPNM(MpegFrame * const mpegFrameP,
+        FILE *      const fp) {
+/*----------------------------------------------------------------------------
+   Read a PPM file containing one movie frame.
+-----------------------------------------------------------------------------*/
+    int pnmCols, pnmRows;
+    xelval maxval;
+    xel ** xels;
+
+    xels = ppm_readppm(fp, &pnmCols, &pnmRows, &maxval);
+    ERRCHK(mpegFrameP, "ppm_readppm");
+
+    /*
+     * if this is the first frame read, set the global frame size
+     */
+    Fsize_Note(mpegFrameP->id, pnmCols, pnmRows);
+
+    PNMtoYUV(mpegFrameP, xels, Fsize_x, Fsize_y, maxval);
+    ppm_freearray(xels, pnmRows);
+}
+
+
+
+static void
+openFile(struct inputSource * const inputSourceP,
+         unsigned int         const frameNumber,
+         const char *         const conversion, 
+         FILE **              const ifPP) {
+
+    if (inputSourceP->stdinUsed) {
+        if (fileType == ANY_FILE_TYPE)
+            pm_error(
+                "ERROR : You cannot use a converter on frames when "
+                "you supply them as Standard Input.  Either specify "
+                "INPUT_CONVERTER * in the parameter file or supply the "
+                "frames in files by specifying a directory with "
+                "INPUT_DIRECTORY in the parameter file.");
+        
+        *ifPP = stdin;
+    } else {
+        const char * fileName;
+        const char * fullFileName;
+        
+        GetNthInputFileName(inputSourceP, frameNumber, &fileName);
+        
+        asprintfN(&fullFileName, "%s/%s", currentPath, fileName);
+        
+        CurrFile = fullFileName;
+        
+        if (fileType == ANY_FILE_TYPE) {
+            char command[1024];
+            const char * convertPtr;
+            char * commandPtr;
+            const char * charPtr;
+            
+            /* replace every occurrence of '*' with fullFileName */
+            convertPtr = conversion;
+            commandPtr = command;
+            while (*convertPtr != '\0') {
+                while ((*convertPtr != '\0') && (*convertPtr != '*')) {
+                    *commandPtr = *convertPtr;
+                    ++commandPtr;
+                    ++convertPtr;
+                }
+                
+                if (*convertPtr == '*') {
+                    /* copy fullFileName */
+                    charPtr = fullFileName;
+                    while (*charPtr != '\0') {
+                        *commandPtr = *charPtr;
+                        ++commandPtr;
+                        ++charPtr;
+                    }
+                    ++convertPtr;   /* go past '*' */
+                }
+            }
+            *commandPtr = '\0';
+            
+            *ifPP = popen(command, "r");
+            if (*ifPP == NULL) {
+                pm_message(
+                    "ERROR:  Couldn't execute input conversion command "
+                    "'%s'.  errno=%d (%s)",
+                    command, errno, strerror(errno));
+                if (ioServer)
+                    pm_error("IO SERVER:  EXITING!!!");
+                else
+                    pm_error("SLAVE EXITING!!!");
+            }
+        } else {
+            *ifPP = fopen(fullFileName, "rb");
+            if (*ifPP == NULL)
+                pm_error("Couldn't open input file '%s'", fullFileName);
+            
+            if (baseFormat == JMOVIE_FILE_TYPE)
+                unlink(fullFileName);
+        }
+        strfree(fullFileName);
+        strfree(fileName);
+    }
+}
+
+
+
+static void
+closeFile(struct inputSource * const inputSourceP,
+          FILE *               const ifP) {
+
+    if (!inputSourceP->stdinUsed) {
+        if (fileType == ANY_FILE_TYPE) {
+            int rc;
+            rc = pclose(ifP);
+            if (rc != 0)
+                pm_message("WARNING:  pclose() failed with errno %d (%s)",
+                           errno, strerror(errno));
+        } else
+            fclose(ifP);
+    }
+}
+
+
+
+static bool
+fileIsAtEnd(FILE * const ifP) {
+
+    int c;
+    bool eof;
+
+    c = getc(ifP);
+    if (c == EOF) {
+        if (feof(ifP)) 
+            eof = TRUE;
+        else
+            pm_error("File error on getc() to position to image");
+    } else {
+        int rc;
+
+        eof = FALSE;
+
+        rc = ungetc(c, ifP);
+        if (rc == EOF) 
+            pm_error("File error doing ungetc() to position to image.");
+    }
+    return eof;
+}
+
+
+
+void
+ReadFrameFile(MpegFrame *  const frameP,
+              FILE *       const ifP,
+              const char * const conversion,
+              bool *       const eofP) {
+/*----------------------------------------------------------------------------
+   Read a frame from the file 'ifP'.
+
+   Return *eofP == TRUE iff we encounter EOF before we can get the
+   frame.
+-----------------------------------------------------------------------------*/
+    MpegFrame   tempFrame;
+    MpegFrame * framePtr;
+
+    /* To make this code fit Netpbm properly, we should remove handling
+       of all types except PNM and use pm_nextimage() to handle sensing
+       of end of stream.
+    */
+
+    if (fileIsAtEnd(ifP))
+        *eofP = TRUE;
+    else {
+        *eofP = FALSE;
+
+        if (resizeFrame) {
+            tempFrame.inUse = FALSE;
+            tempFrame.orig_y = NULL;
+            tempFrame.y_blocks = NULL;
+            tempFrame.decoded_y = NULL;
+            tempFrame.halfX = NULL;
+            framePtr = &tempFrame;
+        } else
+            framePtr = frameP;
+
+        switch(baseFormat) {
+        case YUV_FILE_TYPE:
+
+            /* Encoder YUV */
+            if ((strncmp (yuvConversion, "EYUV", 4) == 0) ||
+                (strncmp (yuvConversion, "UCB", 3) == 0)) 
+
+                ReadEYUV(framePtr, ifP, realWidth, realHeight);
+
+            else
+                /* Abekas-type (interlaced) YUV */
+                ReadAYUV(framePtr, ifP, realWidth, realHeight);
+
+            break;
+        case Y_FILE_TYPE:
+            ReadY(framePtr, ifP, realWidth, realHeight);
+            break;
+        case PNM_FILE_TYPE:
+            ReadPNM(framePtr, ifP);
+            break;
+        case SUB4_FILE_TYPE:
+            ReadSub4(framePtr, ifP, yuvWidth, yuvHeight);
+            break;
+        case JPEG_FILE_TYPE:
+        case JMOVIE_FILE_TYPE:
+            ReadJPEG(framePtr, ifP);
+            break;
+        default:
+            break;
+        }
+
+        if (resizeFrame)
+            Frame_Resize(frameP, &tempFrame, Fsize_x, Fsize_y, 
+                         outputWidth, outputHeight);
+    
+        if (GammaCorrection)
+            DoGamma(frameP, Fsize_x, Fsize_y);
+
+        if (kill_dim)
+            DoKillDim(frameP, Fsize_x, Fsize_y);
+
+        MotionSearchPreComputation(frameP);
+    }
+}
+
+
+
+void
+ReadFrame(MpegFrame *          const frameP, 
+          struct inputSource * const inputSourceP,
+          unsigned int         const frameNumber,
+          const char *         const conversion,
+          bool *               const endOfStreamP) {
+/*----------------------------------------------------------------------------
+   Read the given frame, performing conversion as necessary.
+-----------------------------------------------------------------------------*/
+    FILE * ifP;
+
+    openFile(inputSourceP, frameNumber, conversion, &ifP);
+
+    ReadFrameFile(frameP, ifP, conversion, endOfStreamP);
+
+    if (*endOfStreamP && !inputSourceP->stdinUsed)
+        pm_error("Premature EOF on file containing Frame %u", frameNumber);
+
+    closeFile(inputSourceP, ifP);
+}
+
+
+
+/*===========================================================================*
+ *
+ * SetFileType
+ *
+ *  set the file type to be either a base type (no conversion), or
+ *  any type (conversion required)
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    fileType
+ *
+ *===========================================================================*/
+void
+SetFileType(const char * const conversion)
+{
+    if ( strcmp(conversion, "*") == 0 ) {
+    fileType = BASE_FILE_TYPE;
+    } else {
+    fileType = ANY_FILE_TYPE;
+    }
+}
+
+
+/*===========================================================================*
+ *
+ * SetFileFormat
+ *
+ *  set the file format (PNM, YUV, JPEG)
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    baseFormat
+ *
+ *===========================================================================*/
+void
+SetFileFormat(const char * const format)
+{
+    if ( strcmp(format, "PPM") == 0 ) {
+    baseFormat = PNM_FILE_TYPE;
+    } else if ( strcmp(format, "YUV") == 0 ) {
+    baseFormat = YUV_FILE_TYPE;
+    } else if ( strcmp(format, "Y") == 0 ) {
+    baseFormat = Y_FILE_TYPE;
+    } else if ( strcmp(format, "PNM") == 0 ) {
+    baseFormat = PNM_FILE_TYPE;
+    } else if (( strcmp(format, "JPEG") == 0 ) || ( strcmp(format, "JPG") == 0 )) {
+    baseFormat = JPEG_FILE_TYPE;
+    } else if ( strcmp(format, "JMOVIE") == 0 ) {
+    baseFormat = JMOVIE_FILE_TYPE;
+    } else if ( strcmp(format, "SUB4") == 0 ) {
+    baseFormat = SUB4_FILE_TYPE;
+    } else {
+    fprintf(stderr, "ERROR:  Invalid file format:  %s\n", format);
+    exit(1);
+    }
+}
+
+
+
+FILE *
+ReadIOConvert(struct inputSource * const inputSourceP,
+              unsigned int         const frameNumber) {
+/*----------------------------------------------------------------------------
+   Do conversion; return handle to the appropriate file.
+-----------------------------------------------------------------------------*/
+    FILE    *ifp;
+    char    command[1024];
+    const char * fullFileName;
+    char *convertPtr, *commandPtr;
+    const char * fileName;
+
+    GetNthInputFileName(inputSourceP, frameNumber, &fileName);
+
+    asprintfN(&fullFileName, "%s/%s", currentPath, fileName);
+
+    if ( strcmp(ioConversion, "*") == 0 ) {
+        char buff[1024];
+        ifp = fopen(fullFileName, "rb");
+        sprintf(buff,"fopen \"%s\"",fullFileName);
+        ERRCHK(ifp, buff);
+        return ifp;
+    }
+
+    /* replace every occurrence of '*' with fullFileName */
+    convertPtr = ioConversion;
+    commandPtr = command;
+    while ( *convertPtr != '\0' ) {
+        while ( (*convertPtr != '\0') && (*convertPtr != '*') ) {
+            *commandPtr = *convertPtr;
+            commandPtr++;
+            convertPtr++;
+        }
+
+        if ( *convertPtr == '*' ) {
+            /* copy fullFileName */
+            const char * charPtr;
+            charPtr = fullFileName;
+            while ( *charPtr != '\0' ) {
+                *commandPtr = *charPtr;
+                commandPtr++;
+                charPtr++;
+            }
+
+            convertPtr++;   /* go past '*' */
+        }
+    }
+    *commandPtr = '\0';
+
+    if ( (ifp = popen(command, "r")) == NULL ) {
+        fprintf(stderr, "ERROR:  "
+                "Couldn't execute input conversion command:\n");
+        fprintf(stderr, "\t%s\n", command);
+        fprintf(stderr, "errno = %d\n", errno);
+        if ( ioServer ) {
+            fprintf(stderr, "IO SERVER:  EXITING!!!\n");
+        } else {
+            fprintf(stderr, "SLAVE EXITING!!!\n");
+        }
+        exit(1);
+    }
+
+    strfree(fullFileName);
+    strfree(fileName);
+
+    return ifp;
+}
+
+
+
+/*===========================================================================*
+ *
+ * ReadEYUV
+ *
+ *  read a Encoder-YUV file (concatenated Y, U, and V)
+ *
+ * RETURNS: mf modified
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+ReadEYUV(mf, fpointer, width, height)
+    MpegFrame *mf;
+    FILE *fpointer;
+    int width;
+    int height;
+{
+    register int y;
+    uint8   junk[4096];
+    int     safe_read_count;
+
+    Fsize_Note(mf->id, width, height);
+
+    Frame_AllocYCC(mf);
+
+    for (y = 0; y < Fsize_y; y++) {         /* Y */
+    safe_fread(mf->orig_y[y], 1, Fsize_x, fpointer);
+
+    /* read the leftover stuff on the right side */
+    if ( width != Fsize_x ) {
+        safe_fread(junk, 1, width-Fsize_x, fpointer);
+    }
+    }
+
+    /* read the leftover stuff on the bottom */
+    for (y = Fsize_y; y < height; y++) {
+    safe_fread(junk, 1, width, fpointer);
+    }
+
+    for (y = 0; y < (Fsize_y >> 1); y++) {          /* U */
+    safe_fread(mf->orig_cb[y], 1, Fsize_x >> 1, fpointer);
+
+    /* read the leftover stuff on the right side */
+    if ( width != Fsize_x ) {
+        safe_fread(junk, 1, (width-Fsize_x)>>1, fpointer);
+    }
+    }
+
+    /* read the leftover stuff on the bottom */
+    for (y = (Fsize_y >> 1); y < (height >> 1); y++) {
+    safe_fread(junk, 1, width>>1, fpointer);
+    }
+
+    for (y = 0; y < (Fsize_y >> 1); y++) {          /* V */
+    safe_fread(mf->orig_cr[y], 1, Fsize_x >> 1, fpointer);
+
+    /* read the leftover stuff on the right side */
+    if ( width != Fsize_x ) {
+        safe_fread(junk, 1, (width-Fsize_x)>>1, fpointer);
+    }
+    }
+
+    /* ignore leftover stuff on the bottom */
+}
+
+/*===========================================================================*
+ *
+ * ReadAYUV
+ *
+ *  read an Abekas-YUV file
+ *
+ * RETURNS: mf modified
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+ReadAYUV(mf, fpointer, width, height)
+    MpegFrame *mf;
+    FILE *fpointer;
+    int width;
+    int height;
+{
+    register int x, y;
+    struct  YuvLine line1, line2;
+    uint8   junk[4096];
+    uint8    *cbptr, *crptr;
+    int     safe_read_count;
+
+    Fsize_Note(mf->id, width, height);
+
+    Frame_AllocYCC(mf);
+
+    for (y = 0; y < Fsize_y; y += 2) {
+    SeparateLine(fpointer, &line1, width);
+    SeparateLine(fpointer, &line2, width);
+
+    /* Copy the Y values for each line to the frame */
+    for (x = 0; x < Fsize_x; x++) {
+        mf->orig_y[y][x]   = line1.y[x];
+        mf->orig_y[y+1][x] = line2.y[x];
+    }
+
+    cbptr = &(mf->orig_cb[y>>1][0]);
+    crptr = &(mf->orig_cr[y>>1][0]);
+
+    /* One U and one V for each two pixels horizontal as well */
+    /* Toss the second line of Cr/Cb info, averaging was worse,
+       so just subsample */
+    for (x = 0; x < (Fsize_x >> 1); x ++) {
+        cbptr[x] =  line1.cb[x];
+        crptr[x] =  line1.cr[x];
+
+    }
+    }
+
+    /* read the leftover stuff on the bottom */
+    for (y = Fsize_y; y < height; y++) {
+    safe_fread(junk, 1, width<<1, fpointer);
+    }
+
+}
+
+/*===========================================================================*
+ *
+ * SeparateLine
+ *
+ *  Separates one line of pixels into Y, U, and V components
+ *
+ * RETURNS: lineptr modified
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+SeparateLine(fpointer, lineptr, width)
+    FILE *fpointer;
+    struct YuvLine *lineptr;
+    int width;
+{
+    uint8   junk[4096];
+    int8    *crptr, *cbptr;
+    uint8   *yptr;
+    int     num, length;
+    int     safe_read_count;
+
+
+    /* Sets the deinterlacing pattern */
+
+    /* shorthand for UYVY */
+    if (strncmp(yuvConversion, "ABEKAS", 6) == 0) {
+    strcpy(yuvConversion, "UYVY");
+
+    /* shorthand for YUYV */
+    } else if (strncmp(yuvConversion, "PHILLIPS", 8) == 0) {
+    strcpy(yuvConversion, "YUYV");
+    }
+
+    length = strlen (yuvConversion);
+
+    if ((length % 2) != 0) {
+    fprintf (stderr, "ERROR : YUV_FORMAT must represent two pixels, hence must be even in length.\n");
+    exit(1);
+    }
+
+    /* each line in 4:2:2 chroma format takes 2X bytes to represent X pixels.
+     * each line in 4:4:4 chroma format takes 3X bytes to represent X pixels.
+     * Therefore, half of the length of the YUV_FORMAT represents 1 pixel.
+     */
+    safe_fread(lineptr->data, 1, Fsize_x*(length>>1), fpointer);
+
+    /* read the leftover stuff on the right side */
+    if ( width != Fsize_x ) {
+    safe_fread(junk, 1, (width-Fsize_x)*(length>>1), fpointer);
+    }
+
+    crptr = &(lineptr->cr[0]);
+    cbptr = &(lineptr->cb[0]);
+    yptr = &(lineptr->y[0]);
+
+    for (num = 0; num < (Fsize_x*(length>>1)); num++) {
+    switch (yuvConversion[num % length]) {
+    case 'U':
+    case 'u':
+        *(cbptr++) = (lineptr->data[num]);
+        break;
+    case 'V':
+    case 'v':
+        *(crptr++) = (lineptr->data[num]);
+        break;
+    case 'Y':
+    case 'y':
+        *(yptr++) = (lineptr->data[num]);
+        break;
+    default:
+            fprintf(stderr, "ERROR: YUV_FORMAT must be one of the following:\n");
+            fprintf(stderr, "       ABEKAS\n");
+            fprintf(stderr, "       EYUV\n");
+            fprintf(stderr, "       PHILLIPS\n");
+            fprintf(stderr, "       UCB\n");
+        fprintf(stderr, "       or any even-length string consisting of the letters U, V, and Y.\n");
+            exit(1);
+        }
+    
+    }
+
+}
+
+
+/*===========================================================================*
+ *
+ * ReadY
+ *
+ *  read a Y file
+ *
+ * RETURNS: mf modified
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+ReadY(mf, fpointer, width, height)
+    MpegFrame *mf;
+    FILE *fpointer;
+    int width;
+    int height;
+{
+    register int y;
+    uint8   junk[4096];
+    int     safe_read_count;
+
+    Fsize_Note(mf->id, width, height);
+
+    Frame_AllocYCC(mf);
+
+    for (y = 0; y < Fsize_y; y++) {         /* Y */
+    safe_fread(mf->orig_y[y], 1, Fsize_x, fpointer);
+
+    /* read the leftover stuff on the right side */
+    if ( width != Fsize_x ) {
+        safe_fread(junk, 1, width-Fsize_x, fpointer);
+    }
+    }
+
+    /* read the leftover stuff on the bottom */
+    for (y = Fsize_y; y < height; y++) {
+    safe_fread(junk, 1, width, fpointer);
+    }
+    
+    for (y = 0 ; y < (Fsize_y >> 1); y++) {
+      memset(mf->orig_cb[y], 128, (Fsize_x>>1));
+      memset(mf->orig_cr[y], 128, (Fsize_x>>1));
+    }
+}
+
+
+/*===========================================================================*
+ *
+ * ReadSub4
+ *
+ *  read a YUV file (subsampled even further by 4:1 ratio)
+ *
+ * RETURNS: mf modified
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+ReadSub4(mf, fpointer, width, height)
+    MpegFrame *mf;
+    FILE *fpointer;
+    int width;
+    int height;
+{
+    register int y;
+    register int x;
+    uint8   buffer[1024];
+    int     safe_read_count;
+
+    Fsize_Note(mf->id, width, height);
+
+    Frame_AllocYCC(mf);
+
+    for (y = 0; y < (height>>1); y++) {         /* Y */
+    safe_fread(buffer, 1, width>>1, fpointer);
+    for ( x = 0; x < (width>>1); x++ ) {
+        mf->orig_y[2*y][2*x] = buffer[x];
+        mf->orig_y[2*y][2*x+1] = buffer[x];
+        mf->orig_y[2*y+1][2*x] = buffer[x];
+        mf->orig_y[2*y+1][2*x+1] = buffer[x];
+    }
+    }
+
+    for (y = 0; y < (height >> 2); y++) {           /* U */
+    safe_fread(buffer, 1, width>>2, fpointer);
+    for ( x = 0; x < (width>>2); x++ ) {
+        mf->orig_cb[2*y][2*x] = buffer[x];
+        mf->orig_cb[2*y][2*x+1] = buffer[x];
+        mf->orig_cb[2*y+1][2*x] = buffer[x];
+        mf->orig_cb[2*y+1][2*x+1] = buffer[x];
+    }
+    }
+
+    for (y = 0; y < (height >> 2); y++) {           /* V */
+    safe_fread(buffer, 1, width>>2, fpointer);
+    for ( x = 0; x < (width>>2); x++ ) {
+        mf->orig_cr[2*y][2*x] = buffer[x];
+        mf->orig_cr[2*y][2*x+1] = buffer[x];
+        mf->orig_cr[2*y+1][2*x] = buffer[x];
+        mf->orig_cr[2*y+1][2*x+1] = buffer[x];
+    }
+    }
+}
+
+
+/*=====================*
+ * INTERNAL PROCEDURES *
+ *=====================*/
+
+/*===========================================================================*
+ *
+ * DoGamma
+ *
+ *  Gamma Correct the Lum values
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    Raises Y values to power gamma.
+ *
+ *===========================================================================*/
+static void
+DoGamma(mf, w, h)
+MpegFrame *mf;
+int w,h;
+{
+  static int GammaVal[256];
+  static boolean init_done=FALSE;
+  int i,j;
+
+  if (!init_done) {
+    for(i=0; i<256; i++) 
+      GammaVal[i]=(unsigned char) (pow(((double) i)/255.0,GammaValue)*255.0+0.5);
+    init_done=TRUE;
+  }
+
+  for (i=0; i< h; i++) {  /* For each line */
+    for (j=0; j<w; j++) { /* For each Y value */
+      mf->orig_y[i][j] = GammaVal[mf->orig_y[i][j]];
+    }}
+}
+
+
+
+
+/*===========================================================================*
+ *
+ * DoKillDim
+ *
+ *  Applies an input filter to small Y values.
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    Changes Y values:
+ *
+ *  Output    |                 /
+              |                /
+              |               /
+              |              !
+              |             /
+              |            !
+              |           /
+              |          -
+              |        /
+              |      --
+              |     /
+              |   --
+              | /
+              ------------------------
+                        ^ kill_dim_break
+                             ^kill_dim_end
+              kill_dim_slope gives the slope (y = kill_dim_slope * x +0)
+              from 0 to kill_dim_break                      
+ *
+ *===========================================================================*/
+
+static void
+DoKillDim(mf, w, h)
+MpegFrame *mf;
+int w,h;
+{
+  static boolean init_done=FALSE;
+  static unsigned char mapper[256];
+  register int i,j;
+  double slope, intercept;
+
+  slope = (kill_dim_end - kill_dim_break*kill_dim_slope)*1.0 /
+    (kill_dim_end - kill_dim_break);
+  intercept = kill_dim_end * (1.0-slope);
+
+  if (!init_done) {
+    for(i=0; i<256; i++) {
+      if (i >= kill_dim_end) {
+        mapper[i] = (char) i;
+      } else if (i >= kill_dim_break) {
+        mapper[i] = (char) (slope*i + intercept);
+      } else { /* i <= kill_dim_break */
+        mapper[i] = (char) floor(i*kill_dim_slope + 0.49999);
+      }
+    }
+    init_done = TRUE;
+  }
+
+  for (i=0;  i < h;  i++) {  /* For each line */
+    for (j=0;   j < w;   j++) { /* For each Y value */
+      mf->orig_y[i][j] = mapper[mf->orig_y[i][j]];
+    }}
+}
+
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*  
+ *  $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/RCS/readframe.c,v 1.27 1995/08/14 22:31:40 smoot Exp $
+ *  $Log: readframe.c,v $
+ *  Revision 1.27  1995/08/14 22:31:40  smoot
+ *  reads training info from PPms now (needed for piping reads)
+ *
+ *  Revision 1.26  1995/08/07 21:48:36  smoot
+ *  better error reporting, JPG == JPEG now
+ *
+ *  Revision 1.25  1995/06/12 20:30:12  smoot
+ *  added popen for OS2
+ *
+ * Revision 1.24  1995/06/08  20:34:36  smoot
+ * added "b"'s to fopen calls to make MSDOS happy
+ *
+ * Revision 1.23  1995/05/03  10:16:01  smoot
+ * minor compile bug with static f
+ *
+ * Revision 1.22  1995/05/02  22:00:12  smoot
+ * added TUNEing, setting near-black values to black
+ *
+ * Revision 1.21  1995/03/27  21:00:01  eyhung
+ * fixed bug with some long jpeg names
+ *
+ * Revision 1.20  1995/02/02  01:05:54  eyhung
+ * Fixed aAdded error checking for stdin
+ *
+ * Revision 1.19  1995/02/01  05:01:12  eyhung
+ * Removed troubleshooting printf
+ *
+ * Revision 1.18  1995/01/31  21:08:16  eyhung
+ * Improved YUV_FORMAT strings with better algorithm
+ *
+ * Revision 1.17  1995/01/27  23:34:09  eyhung
+ * Removed temporary JPEG files created by JMOVIE input
+ *
+ * Revision 1.16  1995/01/27  21:57:43  eyhung
+ * Added case for reading original JMOVIES
+ *
+ * Revision 1.14  1995/01/24  23:47:51  eyhung
+ * Confusion with Abekas format fixed : all other YUV revisions are wrong
+ *
+ * Revision 1.13  1995/01/20  00:02:30  smoot
+ * added gamma correction
+ *
+ * Revision 1.12  1995/01/19  23:09:21  eyhung
+ * Changed copyrights
+ *
+ * Revision 1.11  1995/01/17  22:23:07  aswan
+ * AbekasYUV chrominance implementation fixed
+ *
+ * Revision 1.10  1995/01/17  21:26:25  smoot
+ * Tore our average on Abekus/Phillips reconstruct
+ *
+ * Revision 1.9  1995/01/17  08:22:34  eyhung
+ * Debugging of ReadAYUV
+ *
+ * Revision 1.8  1995/01/16  13:18:24  eyhung
+ * Interlaced YUV format (e.g. Abekas) capability added (slightly buggy)
+ *
+ * Revision 1.7  1995/01/16  06:58:23  eyhung
+ * Added skeleton of ReadAYUV (for Abekas YUV files)
+ *
+ * Revision 1.6  1995/01/13  23:22:23  smoot
+ * Added ReadY, so we can make black&white movies (how artsy!)
+ *
+ * Revision 1.5  1994/12/16  00:20:40  smoot
+ * Now errors out on too small an input file
+ *
+ * Revision 1.4  1994/11/12  02:11:59  keving
+ * nothing
+ *
+ * Revision 1.3  1994/03/15  00:27:11  keving
+ * nothing
+ *
+ * Revision 1.2  1993/12/22  19:19:01  keving
+ * nothing
+ *
+ * Revision 1.1  1993/07/22  22:23:43  keving
+ * nothing
+ *
+ */
diff --git a/converter/ppm/ppmtompeg/rgbtoycc.c b/converter/ppm/ppmtompeg/rgbtoycc.c
new file mode 100644
index 00000000..766b8902
--- /dev/null
+++ b/converter/ppm/ppmtompeg/rgbtoycc.c
@@ -0,0 +1,204 @@
+/*===========================================================================*
+ * rgbtoycc.c                                    *
+ *                                       *
+ *  Procedures to convert from RGB space to YUV space            *
+ *                                       *
+ * EXPORTED PROCEDURES:                              *
+ *  PNMtoYUV                                 *
+ *  PPMtoYUV                                 *
+ *                                       *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "pnm.h"
+#include "all.h"
+#include "frame.h"
+#include "rgbtoycc.h"
+
+
+static float *mult299, *mult587, *mult114, *mult16874, *mult33126,
+    *mult5, *mult41869, *mult08131;  /* malloc'ed */
+    /* These are tables we use for fast arithmetic */
+static pixval table_maxval = 0;
+    /* The maxval used to compute the above arrays.  Zero means
+       the above arrays don't exist yet
+    */
+
+static void
+compute_mult_tables(const pixval maxval) {
+
+    /* For speed, we do the arithmetic with eight tables that reduce a
+       bunch of multiplications and divisions to a simple table lookup.
+       
+       Because a large maxval could require a significant amount of
+       table space, we allocate the space dynamically.
+
+       If we had to compute the tables for every frame, it wouldn't be 
+       fast at all, but since all the frames normally have the same
+       maxval, we only need to compute them once.  But just in case,
+       we check each frame to see if it has a different maxval and
+       recompute the tables if so.
+    */
+    
+    if (table_maxval != maxval) {
+        /* We need to compute or re-compute the multiplication tables */
+        if (table_maxval != 0) {
+            free(mult299); free(mult587); free(mult114); free(mult16874);
+            free(mult33126); free(mult5); free(mult41869); free(mult08131);  
+        } 
+        table_maxval = maxval;
+
+        mult299   = malloc((table_maxval+1)*sizeof(float));
+        mult587   = malloc((table_maxval+1)*sizeof(float));
+        mult114   = malloc((table_maxval+1)*sizeof(float));
+        mult16874 = malloc((table_maxval+1)*sizeof(float));
+        mult33126 = malloc((table_maxval+1)*sizeof(float));
+        mult5     = malloc((table_maxval+1)*sizeof(float));
+        mult41869 = malloc((table_maxval+1)*sizeof(float));
+        mult08131 = malloc((table_maxval+1)*sizeof(float));
+
+        if (mult299 == NULL || mult587 == NULL || mult114 == NULL ||
+            mult16874 == NULL || mult33126 == NULL || mult5 == NULL ||
+            mult41869 == NULL || mult08131 == NULL) 
+            pm_error("Unable to allocate storage for arithmetic tables.\n"
+                     "We need %d bytes, which is the maxval of the input "
+                     "image, plus 1,\n"
+                     "times the storage size of a floating point value.", 
+                     8 * (table_maxval+1)*sizeof(float));
+
+        {
+            int index;
+
+            for (index = 0; index <= table_maxval; index++ ) {
+                mult299[index]   = index*0.29900  * 255 / table_maxval; 
+                mult587[index]   = index*0.58700  * 255 / table_maxval;
+                mult114[index]   = index*0.11400  * 255 / table_maxval;
+                mult16874[index] = -0.16874*index * 255 / table_maxval;
+                mult33126[index] = -0.33126*index * 255 / table_maxval;
+                mult5[index]     = index*0.50000  * 255 / table_maxval;
+                mult41869[index] = -0.41869*index * 255 / table_maxval;
+                mult08131[index] = -0.08131*index * 255 / table_maxval;
+            }
+        }
+    }
+}
+
+
+/*=====================*
+ * EXPORTED PROCEDURES *
+ *=====================*/
+
+
+void
+PNMtoYUV(MpegFrame *  const frameP,
+         xel **       const xels,
+         unsigned int const cols,
+         unsigned int const rows,
+         xelval       const maxval) {
+/*----------------------------------------------------------------------------
+   Set the raster of the MPEG frame *frameP from the libnetpbm input
+   'xels'.  Note that the raster information in a MpegFrame is in YUV
+   form.
+-----------------------------------------------------------------------------*/
+    int x, y;
+    uint8 *dy0, *dy1;
+    uint8 *dcr, *dcb;
+    pixel *src0, *src1;
+
+    compute_mult_tables(maxval);  /* This sets up mult299[], etc. */
+
+    Frame_AllocYCC(frameP);
+
+    /*
+     * okay.  Now, convert everything into YCbCr space. (the specific
+     * numbers come from the JPEG source, jccolor.c) The conversion
+     * equations to be implemented are therefore
+     *
+     * Y  =  0.29900 * R + 0.58700 * G + 0.11400 * B
+     * Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B
+     * Cr =  0.50000 * R - 0.41869 * G - 0.08131 * B
+     *
+     * With Y, Cb, and Cr then normalized to the range 0 - 255.
+     */
+
+    for (y = 0; y < rows; y += 2) {
+        for (x = 0, src0 = xels[y], src1 = xels[y + 1],
+                 dy0 = frameP->orig_y[y], dy1 = frameP->orig_y[y + 1],
+                 dcr = frameP->orig_cr[y >> 1], dcb = frameP->orig_cb[y >> 1];
+             x < cols;
+             x += 2, dy0 += 2, dy1 += 2, dcr++,
+                 dcb++, src0 += 2, src1 += 2) {
+
+            *dy0 = (mult299[PPM_GETR(*src0)] +
+                    mult587[PPM_GETG(*src0)] +
+                    mult114[PPM_GETB(*src0)]);
+
+            *dy1 = (mult299[PPM_GETR(*src1)] +
+                    mult587[PPM_GETG(*src1)] +
+                    mult114[PPM_GETB(*src1)]);
+
+            dy0[1] = (mult299[PPM_GETR(src0[1])] +
+                      mult587[PPM_GETG(src0[1])] +
+                      mult114[PPM_GETB(src0[1])]);
+
+            dy1[1] = (mult299[PPM_GETR(src1[1])] +
+                      mult587[PPM_GETG(src1[1])] +
+                      mult114[PPM_GETB(src1[1])]);
+
+            *dcb = ((mult16874[PPM_GETR(*src0)] +
+                     mult33126[PPM_GETG(*src0)] +
+                     mult5[PPM_GETB(*src0)] +
+                     mult16874[PPM_GETR(*src1)] +
+                     mult33126[PPM_GETG(*src1)] +
+                     mult5[PPM_GETB(*src1)] +
+                     mult16874[PPM_GETR(src0[1])] +
+                     mult33126[PPM_GETG(src0[1])] +
+                     mult5[PPM_GETB(src0[1])] +
+                     mult16874[PPM_GETR(src1[1])] +
+                     mult33126[PPM_GETG(src1[1])] +
+                     mult5[PPM_GETB(src1[1])]) / 4) + 128;
+
+            *dcr = ((mult5[PPM_GETR(*src0)] +
+                     mult41869[PPM_GETG(*src0)] +
+                     mult08131[PPM_GETB(*src0)] +
+                     mult5[PPM_GETR(*src1)] +
+                     mult41869[PPM_GETG(*src1)] +
+                     mult08131[PPM_GETB(*src1)] +
+                     mult5[PPM_GETR(src0[1])] +
+                     mult41869[PPM_GETG(src0[1])] +
+                     mult08131[PPM_GETB(src0[1])] +
+                     mult5[PPM_GETR(src1[1])] +
+                     mult41869[PPM_GETG(src1[1])] +
+                     mult08131[PPM_GETB(src1[1])]) / 4) + 128;
+
+            DBG_PRINT(("%3d,%3d: (%3d,%3d,%3d) --> (%3d,%3d,%3d)\n", 
+                       x, y, 
+                       PPM_GETR(*src0), PPM_GETG(*src0), PPM_GETB(*src0), 
+                       *dy0, *dcb, *dcr));
+        }
+    }
+}
diff --git a/converter/ppm/ppmtompeg/specifics.c b/converter/ppm/ppmtompeg/specifics.c
new file mode 100644
index 00000000..c7bbfb5b
--- /dev/null
+++ b/converter/ppm/ppmtompeg/specifics.c
@@ -0,0 +1,688 @@
+/*===========================================================================*
+ * specifics.c								     *
+ *									     *
+ *	basic procedures to deal with the specifics file                     *
+ *									     *
+ * EXPORTED PROCEDURES:							     *
+ *	Specifics_Init							     *
+ *      Spec_Lookup                                                          *
+ *      SpecTypeLookup                                                       *
+ *									     *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "all.h"
+#include "mtypes.h"
+#include "frames.h"
+#include "frame.h"
+#include "fsize.h"
+#include "dct.h"
+#include "specifics.h"
+#include <stdio.h>
+#include <string.h>
+#include "prototypes.h"
+
+/*====================*
+ * System Information *
+ *====================*/
+
+#define CPP_LOC "/lib/cpp"
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+extern boolean specificsOn;
+extern char specificsFile[];
+extern char specificsDefines[];
+FrameSpecList *fsl;
+
+/*=====================*
+ * Internal procedures *
+ *=====================*/
+
+void Parse_Specifics_File _ANSI_ARGS_((FILE *fp));
+void Parse_Specifics_File_v1 _ANSI_ARGS_((FILE *fp));
+void Parse_Specifics_File_v2 _ANSI_ARGS_((FILE *fp));
+FrameSpecList *MakeFslEntry _ANSI_ARGS_((void));
+void AddSlc _ANSI_ARGS_((FrameSpecList *c,int snum, int qs));
+Block_Specifics *AddBs _ANSI_ARGS_((FrameSpecList *c,int bnum, 
+				    boolean rel, int qs));
+FrameSpecList *MakeFslEntry _ANSI_ARGS_((void));
+#define my_upper(c) (((c>='a') && (c<='z')) ? (c-'a'+'A') : c)
+#define CvtType(x) ReallyCvt(my_upper(x))
+#define ReallyCvt(x) (x=='I' ? 1 : (x=='P')?2: ((x=='B')?3:-1))
+#define SkipToSpace(lp) while ((*lp != ' ') && (*lp != '\n') && (*lp != '\0')) lp++
+#define EndString(lp)  ((*lp == '\n') || (*lp == '\0'))
+
+/*=============================================================
+ * SPEC FILE FORMAT (version 1):
+
+Specs files are processed with the c preprecoessor, so use C style comments
+and #defines if you wish.
+
+frames, blocks, and slices are numbered from 0.
+(sorry)
+
+In order by frame number, slice number, block number
+(if you skip slices it's fine).
+Can have specifics for any frame, block, or slice.
+Format:
+version N
+  Specify the version of the specifics file format (this is 1)
+frame N T M
+  Sets frame number N to type T and Qscale M
+  (type T is I,B,P,other, other means unspec.  I recomend - )
+slice M Q
+  Sets slice M (in frame N as defined by a previous frame command)
+  to qscale Q
+block M Q
+  Sets block M to qscale Q, in frame N previously specified.
+
+Unspecified frame types are set via the last I frame set, which is forced
+to act as the first I of the GOP.
+FORCE_ENCODE_LAST_FRAME overrides specifics on the final frame type.
+Note that Qscale changes in skipped blocks will be lost!
+
+Version 2:
+frames and slices are the same as above, but Q in blocks can be relative, i.e.
++N or -N.  Clipping to 1..31 is done but sequences like
+block 1 2
+block 2 -3
+block 3 +3
+
+has undefined results (as present block 3 would be Qscale 2).
+
+In addition motion vectors can be specified:
+block M Q skip
+  Says to skip the block  (not really an MV, but....)
+block M Q bi fx fy bx by
+  Sets block M to quality Q.  It will be a bidirectional block.
+  fx/fy is the forward (like a P frame) vector, bx/y is the back
+block M Q forw fx fy
+block M Q back bx by
+  Single directional.
+
+All vectors are specified in HALF PIXEL fixed point units, i.e.
+3.5 pixels is 7
+To specify a vector but not touch the q factor, set Q to 0
+
+*=============================================================*/
+
+
+/*=============*
+ * Local State *
+ *=============*/
+
+static char version = -1;
+
+/*================================================================
+ *
+ *   Specifics_Init
+ *
+ *   Cpp's and reads in the specifics file.  Creates fsl data structure.
+ *
+ *   Returns: nothing
+ * 
+ *   Modifies: fsl, file specificsFile".cpp"
+ *
+ *================================================================
+ */
+void Specifics_Init()
+{
+  char command[1100];
+  FILE *specificsFP;
+  
+  sprintf(command, "/bin/rm -f %s.cpp", specificsFile);
+  system(command);
+  sprintf(command, "%s -P %s %s %s.cpp",
+	  CPP_LOC, specificsDefines, specificsFile, specificsFile);
+  system(command);
+  strcat(specificsFile, ".cpp");
+  if ((specificsFP = fopen(specificsFile, "r")) == NULL) {
+    fprintf(stderr, "Error with specifics file, cannot open %s\n", specificsFile);
+    exit(1);
+  }
+  printf("Specifics file: %s\n", specificsFile);
+  Parse_Specifics_File(specificsFP);
+  sprintf(command, "/bin/rm -f %s.cpp", specificsFile);
+  system(command);
+
+}
+
+
+
+
+/*================================================================
+ *
+ *   Parse_Specifics_File
+ *
+ *   Read through the file passed in creating the fsl data structure
+ *   There is a primary routine, and helpers for the specific versions.
+ *
+ *   Returns: Nothing
+ *
+ *   Modifies: fsl
+ *
+ *================================================================
+ */
+void Parse_Specifics_File(fp)
+FILE *fp;
+{
+  char line[1024], *lp;
+  int vers;
+
+  while ((fgets(line, 1023, fp)) != NULL) {
+    lp = &line[0];
+    while ((*lp == ' ') || (*lp == '\t')) lp++;
+    if (( *lp == '#' ) || (*lp=='\n')) {
+      continue;
+    }
+
+    switch (my_upper(*lp)) {
+    case 'F': case 'S': case 'B':
+      fprintf(stderr, "Must specify version at beginning of specifics file\n");
+      exit(0);
+      break;
+    case 'V':
+      lp += 7;
+      if (1 != sscanf(lp, "%d", &vers)) {
+	fprintf(stderr," Improper version line in specs file: %s\n", line);
+      } else {
+	switch (vers) {
+	case 1:
+	  version = vers;
+	  Parse_Specifics_File_v1(fp);
+	  break;
+	case 2:
+	  version = vers;
+	  Parse_Specifics_File_v2(fp);
+	  break;
+	default:
+	  fprintf(stderr, "Improper version line in specs file: %s\n", line);
+	  fprintf(stderr, "\tSpecifics file will be IGNORED.\n");
+	  specificsOn = FALSE;
+	  return;
+	  break;
+	}}
+      break;
+    default:
+      fprintf(stderr, "Specifics file: What? *%s*\n", line);
+      break;
+    }}
+  
+}
+
+/* Version 1 */
+void Parse_Specifics_File_v1(fp)
+FILE *fp;
+{
+  char line[1024],*lp;
+  FrameSpecList *current, *new;
+  char typ; 
+  int fnum,snum, bnum, qs, newqs;
+  int num_scanned;
+
+  fsl = MakeFslEntry();
+  current = fsl;
+
+  while ((fgets(line,1023, fp)) != NULL) {
+    lp = &line[0];
+    while ((*lp == ' ') || (*lp == '\t')) lp++;
+    if (( *lp == '#' ) || (*lp=='\n')) {
+      continue;
+    }
+
+    switch (my_upper(*lp)) {
+    case 'F':
+      lp += 6;
+      sscanf(lp, "%d %c %d", &fnum, &typ, &qs);
+      if (current->framenum != -1) {
+	new=MakeFslEntry();
+	current->next = new;
+	current = new;
+      }
+      current->framenum = fnum;
+      current->frametype = CvtType(typ);
+      if (qs <= 0) qs = -1;
+      current->qscale = qs;
+      break;
+    case 'S':
+      lp += 6;
+      sscanf(lp, "%d %d", &snum, &newqs);
+      if (qs == newqs) break;
+      qs = newqs;
+      AddSlc(current, snum, qs);
+      break;
+    case 'B':
+      lp += 6;
+      num_scanned = sscanf(lp, "%d %d", &bnum, &newqs);
+      if (qs == newqs) break;
+      qs = newqs;
+      AddBs(current, bnum, FALSE, qs);
+      break;
+    case 'V':
+      fprintf(stderr, "Cannot specify version twice!  Taking first (%d)\n", version);
+      break;
+    default:
+      fprintf(stderr," What? *%s*\n", line);
+      break;
+    }}
+  
+}
+
+/* Version 2 */
+void Parse_Specifics_File_v2(fp)
+FILE *fp;
+{
+  char line[1024], *lp;
+  FrameSpecList *current, *new;
+  char typ;
+  int fnum, snum, bnum, qs, newqs;
+  int num_scanned, fx=0, fy=0, sx=0, sy=0;
+  char kind[100];
+  Block_Specifics *new_blk;
+  boolean relative;
+
+  fsl = MakeFslEntry();
+  current = fsl;
+
+  while ((fgets(line,1023,fp))!=NULL) {
+    lp = &line[0];
+    while ((*lp == ' ') || (*lp == '\t')) lp++;
+    if (( *lp == '#' ) || (*lp=='\n')) {
+      continue;
+    }
+
+    switch (my_upper(*lp)) {
+    case 'F':
+      lp += 6;
+      sscanf(lp,"%d %c %d", &fnum, &typ, &qs);
+      new = MakeFslEntry();
+      if (current->framenum != -1) {
+	current->next = new;
+	current = new;
+      }
+      current->framenum = fnum;
+      current->frametype = CvtType(typ);
+      if (qs <= 0) qs = -1;
+      current->qscale = qs;
+      break;
+    case 'S':
+      lp += 6;
+      sscanf(lp,"%d %d", &snum, &newqs);
+      if (qs == newqs) break;
+      qs = newqs;
+      AddSlc(current, snum, qs);
+      break;
+    case 'B':
+      lp += 6;
+      num_scanned = 0;
+      bnum = atoi(lp);
+      SkipToSpace(lp);
+      while ((*lp != '-') && (*lp != '+') &&
+	     ((*lp < '0') || (*lp > '9'))) lp++;
+      relative = ((*lp == '-') || (*lp == '+'));
+      newqs = atoi(lp);
+      SkipToSpace(lp);
+      if (EndString(lp)) {
+	num_scanned = 2;
+      } else {
+	num_scanned = 2+sscanf(lp, "%s %d %d %d %d", kind, &fx, &fy, &sx, &sy); 
+      }
+
+      qs = newqs;
+      new_blk = AddBs(current, bnum, relative, qs);
+      if (num_scanned > 2) {
+	BlockMV *tmp;
+	tmp = (BlockMV *) malloc(sizeof(BlockMV));
+	switch (num_scanned) {
+	case 7:
+	  tmp->typ = TYP_BOTH;
+	  tmp->fx = fx;
+	  tmp->fy = fy;
+	  tmp->bx = sx;
+	  tmp->by = sy;
+	  new_blk->mv = tmp;
+	  break;
+	case 3:
+	  tmp->typ = TYP_SKIP;
+	  new_blk->mv = tmp;
+	  break;
+	case 5:
+	  if (my_upper(kind[0]) == 'B') {
+	    tmp->typ = TYP_BACK;
+	    tmp->bx = fx;
+	    tmp->by = fy;
+	  } else {
+	    tmp->typ = TYP_FORW;
+	    tmp->fx = fx;
+	    tmp->fy = fy;
+	  }
+	  new_blk->mv = tmp;
+	  break;
+	default:
+	  fprintf(stderr,
+		  "Bug in specifics file!  Skipping short/long entry: %s\n",line);
+	  break;
+	}
+      } else {
+	new_blk->mv = (BlockMV *) NULL;
+      }
+
+      break;
+    case 'V':
+      fprintf(stderr,
+	      "Cannot specify version twice!  Taking first (%d).\n",
+	      version);
+      break;
+    default:
+      printf("What? *%s*\n",line);
+      break;
+    }}
+  
+}
+
+
+
+
+/*=================================================================
+ *
+ *     MakeFslEntry
+ *
+ *     Makes a single entry in for the fsl linked list (makes a frame)
+ *
+ *     Returns: the new entry
+ *
+ *     Modifies: nothing
+ *
+ *=================================================================
+ */
+FrameSpecList *MakeFslEntry()
+{
+  FrameSpecList *fslp;
+  fslp = (FrameSpecList *) malloc(sizeof(FrameSpecList));
+  fslp->framenum = -1;
+  fslp->slc = (Slice_Specifics *) NULL;
+  fslp->bs = (Block_Specifics *) NULL;
+  return fslp;
+}
+
+
+
+
+
+/*================================================================
+ *
+ *   AddSlc
+ *
+ *   Adds a slice to framespeclist c with values snum and qs
+ *
+ *   Returns: nothing
+ *
+ *   Modifies: fsl's structure
+ *
+ *================================================================
+ */
+void AddSlc(c, snum, qs)
+FrameSpecList *c;
+int snum,qs;
+{
+  Slice_Specifics *new;
+  static Slice_Specifics *last;
+
+  new = (Slice_Specifics *) malloc(sizeof(Slice_Specifics));
+  new->num = snum;
+  new->qscale = qs;
+  new->next = (Slice_Specifics *)NULL;
+  if (c->slc == (Slice_Specifics *)NULL) {
+    last = new;
+    c->slc = new;
+  } else {
+    last->next = new;
+    last = new;
+  }
+}
+
+
+
+
+
+/*================================================================
+ *
+ *   AddBs
+ *
+ *   Adds a sliceblock to framespeclist c with values bnum and qs
+ *
+ *   Returns: pointer to the new block spec
+ *
+ *   Modifies: fsl's structure
+ *
+ *================================================================
+ */
+Block_Specifics *AddBs(c,bnum,rel,qs)
+FrameSpecList *c;
+boolean rel;
+int bnum,qs;
+{
+  Block_Specifics *new;
+  static Block_Specifics *last;
+
+  new = (Block_Specifics *) malloc(sizeof(Block_Specifics));
+  new->num = bnum;
+  if (qs == 0) rel = TRUE;
+  new->relative = rel;
+  new->qscale = qs;
+  new->next = (Block_Specifics *)NULL;
+  new->mv = (BlockMV *) NULL;
+  if (c->bs == (Block_Specifics *)NULL) {
+    last = new;
+    c->bs = new;
+  } else {
+    last->next = new;
+    last = new;
+  }
+  return new;
+}
+
+
+
+
+
+
+/*================================================================
+ *
+ *  SpecLookup
+ *
+ *  Find out if there is any changes to be made for the qscale
+ *  at entry fn.num (which is of type typ).  Sets info to point to
+ *  motion vector info (if any), else NULL.
+ *
+ *  Returns: new qscale or -1
+ *
+ *  Modifies: *info (well, internal cache can change)
+ *
+ *================================================================
+ */
+
+int SpecLookup(fn,typ,num,info,start_qs)
+int fn,typ,num;
+BlockMV **info;
+int start_qs;
+{
+  static FrameSpecList *last = (FrameSpecList *) NULL;
+  Slice_Specifics *sptr=(Slice_Specifics *) NULL;
+  Block_Specifics *bptr=(Block_Specifics *) NULL;
+  FrameSpecList *tmp;
+  boolean found_it;
+  static int leftovers = 0;  /* Used in case of forced movement into 1..31 range */
+  
+  *info = (BlockMV * )NULL;
+  if (last == (FrameSpecList *) NULL){
+    /* No cache, try to find number fn */
+    tmp = fsl;
+    found_it = FALSE;
+    while (tmp != (FrameSpecList *) NULL) {
+      if (tmp->framenum == fn) {
+	found_it = TRUE;
+	break;
+      } else tmp = tmp->next;
+    }
+    if (!found_it) return -1;
+    last=tmp;
+  } else {
+    if (last->framenum != fn) { /* cache miss! */
+      /* first check if it is next */
+      if ((last->next != (FrameSpecList *) NULL) && 
+	  (last->next->framenum == fn)) {
+	last = last->next;
+      } else {
+	/* if not next, check from the start.
+	   (this allows people to put frames out of order,even
+	   though the spec doesnt allow it.) */
+	tmp = fsl;
+	found_it = FALSE;
+	while (tmp != (FrameSpecList *) NULL) {
+	  if (tmp->framenum==fn) {found_it = TRUE; break;}
+	  tmp = tmp->next;
+	}
+	if (!found_it) return -1;
+	last = tmp;
+      }
+    }
+  }
+  /* neither of these should ever be true, unless there is a bug above */
+  if (last == (FrameSpecList *) NULL) {
+    fprintf(stderr, "PROGRAMMER ERROR: last is null!\n");
+    return -1;
+  }
+  if (last->framenum!=fn) {
+    fprintf(stderr, "PROGRAMMER ERROR: last has wrong number!\n");
+    return -1; /* no data on it */
+  }
+  
+  switch(typ) {
+  case 0: /* Frame: num is ignored */
+    leftovers = 0;
+#ifdef BLEAH
+    printf("QSchange frame %d to %d\n", fn, last->qscale);
+#endif 
+    return last->qscale;
+    break;
+
+  case 1: /* Slice */
+    leftovers = 0;
+    /* So, any data on slices? */
+    if (last->slc == (Slice_Specifics *) NULL) return -1;
+    for (sptr = last->slc; sptr != (Slice_Specifics *) NULL; sptr = sptr->next) {
+      if (sptr->num == num) {
+#ifdef BLEAH
+	printf("QSchange Slice %d.%d to %d\n", fn, num, sptr->qscale);
+#endif
+	if (sptr->qscale == 0) return -1;
+	return sptr->qscale;
+      }
+    }
+    break;
+
+  case 2:  /* block */
+    /* So, any data on blocks? */
+    if (last->bs == (Block_Specifics *) NULL) {
+      return -1;
+    }
+    for (bptr=last->bs; bptr != (Block_Specifics *) NULL; bptr=bptr->next) {
+      if (bptr->num == num) {
+	int new_one;
+#ifdef BLEAH
+	printf("QSchange Block %d.%d to %d\n", fn, num, bptr->qscale);
+#endif
+	*info = bptr->mv;
+	if (bptr->relative) {
+	  if (bptr->qscale == 0) {
+	    /* Do nothing! */
+	    new_one = start_qs;
+	  } else {
+	    new_one = start_qs + bptr->qscale + leftovers;
+	    if (new_one < 1) {
+	      leftovers = new_one - 1;
+	      new_one = 1;
+	    } else if (new_one > 31) {
+	      leftovers = new_one - 31;
+	      new_one = 31;
+	    } else leftovers = 0;
+	  }}
+	else {
+	  new_one = bptr->qscale;
+	  leftovers = 0;
+	}
+	return new_one;
+      }
+    }
+    break;
+  default:
+    fprintf(stderr, "PROGRAMMER ERROR:  reached unreachable code in SpecLookup\n");
+    break;
+  }
+  /* no luck */
+  return -1;
+}
+     
+    
+/*================================================================
+ *
+ *  SpecTypeLookup
+ *
+ *  Find out if there is any changes to be made for the type of frame
+ *  at frame fn.
+ *
+ *  Returns: new type or -1 (unspecified)
+ *
+ *================================================================
+ */
+int SpecTypeLookup(fn)
+int fn;
+{
+  FrameSpecList *tmp;
+
+  /* try to find number fn */
+  tmp = fsl;
+  do {
+    if (tmp->framenum == fn) break;
+    else tmp = tmp->next;
+  } while (tmp != (FrameSpecList *) NULL);
+  if (tmp == (FrameSpecList *) NULL) {
+#ifdef BLEAH
+    printf("Frame %d type not specified\n", fn);
+#endif
+    return -1;
+  }
+#ifdef BLEAH
+  printf("Frame %d type set to %d\n", fn, tmp->frametype);
+#endif
+  return tmp->frametype;
+}
diff --git a/converter/ppm/ppmtompeg/subsample.c b/converter/ppm/ppmtompeg/subsample.c
new file mode 100644
index 00000000..5ec71814
--- /dev/null
+++ b/converter/ppm/ppmtompeg/subsample.c
@@ -0,0 +1,280 @@
+/*===========================================================================*
+ * subsample.c                                   *
+ *                                       *
+ *  Procedures concerned with subsampling                    *
+ *                                       *
+ * EXPORTED PROCEDURES:                              *
+ *  LumMotionErrorA                              *
+ *  LumMotionErrorB                              *
+ *  LumMotionErrorC                              *
+ *  LumMotionErrorD                              *
+ *                                       *
+ *===========================================================================*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*==============*
+ * HEADER FILES *
+ *==============*/
+
+#include "pm_c_util.h"
+#include "all.h"
+#include "mtypes.h"
+#include "frames.h"
+#include "bitio.h"
+#include "prototypes.h"
+
+
+
+static void
+computePrevFyFx(MpegFrame * const prevFrame,
+                int         const by,
+                int         const bx,
+                vector      const m,
+                uint8 ***   const prevP,
+                int *       const fyP,
+                int *       const fxP) {
+
+    boolean const xHalf = (ABS(m.x) % 2 == 1);
+    boolean const yHalf = (ABS(m.y) % 2 == 1);
+
+    MotionToFrameCoord(by, bx, m.y/2, m.x/2, fyP, fxP);
+
+    if (xHalf) {
+        if (m.x < 0)
+            --*fxP;
+
+        if (yHalf) {
+            if (m.y < 0)
+                --*fyP;
+        
+            *prevP = prevFrame->halfBoth;
+        } else
+            *prevP = prevFrame->halfX;
+    } else if (yHalf) {
+        if (m.y < 0)
+            --*fyP;
+        
+        *prevP = prevFrame->halfY;
+    } else
+        *prevP = prevFrame->ref_y;
+}
+
+
+
+static int32
+evenColDiff(const uint8 * const macross,
+            const int32 * const currentRow) {
+
+    return 0
+        + ABS(macross[ 0] - currentRow[ 0])
+        + ABS(macross[ 2] - currentRow[ 2])
+        + ABS(macross[ 4] - currentRow[ 4])
+        + ABS(macross[ 6] - currentRow[ 6])
+        + ABS(macross[ 8] - currentRow[ 8])
+        + ABS(macross[10] - currentRow[10])
+        + ABS(macross[12] - currentRow[12])
+        + ABS(macross[14] - currentRow[14]);
+}
+
+
+
+static int32
+oddColDiff(const uint8 * const macross,
+           const int32 * const currentRow) {
+
+    return 0
+        + ABS(macross[ 1] - currentRow[ 1])
+        + ABS(macross[ 3] - currentRow[ 3])
+        + ABS(macross[ 5] - currentRow[ 5])
+        + ABS(macross[ 7] - currentRow[ 7])
+        + ABS(macross[ 9] - currentRow[ 9])
+        + ABS(macross[11] - currentRow[11])
+        + ABS(macross[13] - currentRow[13])
+        + ABS(macross[15] - currentRow[15]);
+}
+
+
+
+/*===========================================================================*
+ *
+ * LumMotionErrorA
+ *
+ *  compute the motion error for the A subsampling pattern
+ *
+ * RETURNS: the error, or some number greater if it is worse
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+int32
+LumMotionErrorA(const LumBlock * const currentBlockP,
+                MpegFrame *      const prevFrame,
+                int              const by,
+                int              const bx,
+                vector           const m,
+                int32            const bestSoFar) {
+
+    int32 diff; /* max value of diff is 255*256 = 65280 */
+    uint8 ** prev;
+    int fy, fx;
+    unsigned int rowNumber;
+
+    computePrevFyFx(prevFrame, by, bx, m, &prev, &fy, &fx);
+
+    diff = 0;  /* initial value */
+
+    for (rowNumber = 0; rowNumber < 16; rowNumber +=2) {
+        uint8 *       const macross    = &(prev[fy + rowNumber][fx]);
+        const int32 * const currentRow = currentBlockP->l[rowNumber];
+
+        diff += evenColDiff(macross, currentRow);
+
+        if (diff > bestSoFar)
+            return diff;
+    }
+    return diff;
+}
+
+
+
+/*===========================================================================*
+ *
+ * LumMotionErrorB
+ *
+ *  compute the motion error for the B subsampling pattern
+ *
+ * RETURNS: the error, or some number greater if it is worse
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+int32
+LumMotionErrorB(const LumBlock * const currentBlockP,
+                MpegFrame *      const prevFrame,
+                int              const by,
+                int              const bx,
+                vector           const m,
+                int32            const bestSoFar) {
+
+    int32 diff;  /* max value of diff is 255*256 = 65280 */
+    uint8 **prev;
+    int fy, fx;
+    unsigned int rowNumber;
+
+    computePrevFyFx(prevFrame, by, bx, m, &prev, &fy, &fx);
+
+    diff = 0;  /* initial value */
+    
+    for (rowNumber = 0; rowNumber < 16; rowNumber +=2) {
+        uint8 *       const macross    = &(prev[fy + rowNumber][fx]);
+        const int32 * const currentRow = currentBlockP->l[rowNumber];
+
+        diff += oddColDiff(macross, currentRow);
+
+        if (diff > bestSoFar)
+            return diff;
+    }
+    return diff;
+}
+
+
+/*===========================================================================*
+ *
+ * LumMotionErrorC
+ *
+ *  compute the motion error for the C subsampling pattern
+ *
+ * RETURNS: the error, or some number greater if it is worse
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+int32
+LumMotionErrorC(const LumBlock * const currentBlockP,
+                MpegFrame *      const prevFrame,
+                int              const by,
+                int              const bx,
+                vector           const m,
+                int32            const bestSoFar) {
+
+    int32 diff;        /* max value of diff is 255*256 = 65280 */
+    uint8 **prev;
+    int fy, fx;
+    unsigned int rowNumber;
+
+    computePrevFyFx(prevFrame, by, bx, m, &prev, &fy, &fx);
+
+    diff = 0;  /* initial value */
+    
+    for (rowNumber = 1; rowNumber < 16; rowNumber +=2) {
+        uint8 *       const macross    = &(prev[fy + rowNumber][fx]);
+        const int32 * const currentRow = currentBlockP->l[rowNumber];
+
+        diff += evenColDiff(macross, currentRow);
+
+        if (diff > bestSoFar)
+            return diff;
+    }
+    return diff;
+}
+
+
+/*===========================================================================*
+ *
+ * LumMotionErrorD
+ *
+ *  compute the motion error for the D subsampling pattern
+ *
+ * RETURNS: the error, or some number greater if it is worse
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+int32
+LumMotionErrorD(const LumBlock * const currentBlockP,
+                MpegFrame *      const prevFrame,
+                int              const by,
+                int              const bx,
+                vector           const m,
+                int32            const bestSoFar) {
+
+    int32 diff;     /* max value of diff is 255*256 = 65280 */
+    uint8 ** prev;
+    int fy, fx;
+    unsigned int rowNumber;
+
+    computePrevFyFx(prevFrame, by, bx, m, &prev, &fy, &fx);
+
+    diff = 0;  /* initial value */
+
+    for (rowNumber = 1; rowNumber < 16; rowNumber +=2) {
+        uint8 *       const macross    = &(prev[fy + rowNumber][fx]);
+        const int32 * const currentRow = currentBlockP->l[rowNumber];
+
+        diff += oddColDiff(macross, currentRow);
+
+        if (diff > bestSoFar)
+            return diff;
+    }
+    return diff;
+}
diff --git a/converter/ppm/ppmtompeg/tst/README b/converter/ppm/ppmtompeg/tst/README
new file mode 100644
index 00000000..4e35f6ba
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/README
@@ -0,0 +1,30 @@
+The test in this directory is derived from that in the Berkeley mpeg
+package, but we have trimmed it down for the purposes of the Netpbm
+package to save space.
+
+First of all, the Berkeley package had Berkeley YUV files for sample input
+(in the 'ts' directory).  But we converted them to JPEG to save a great 
+deal of space (even when the package is gzipped).
+
+We modified the ts.param file to work with the JPEG files and updated the
+expected results file to correspond to the JPEG input (the expected results
+are different after the JPEG conversion because JPEG conversion is lossy).
+
+We kept some of the other parameter files from the Berkeley package in
+case someone can get some use out of them, but did not update them to
+use the JPEG files.  You'll have to do that yourself.  And we removed
+the expected results files for them, since you can't generate those
+results with the inputs we have supplied.
+
+Note that JPEG input doesn't really exercise the most standard function
+of ppmtompeg.  As its name suggests, it's main purpose is to take PPM
+frames as input.  You can use jpegtoppm to create PPM input and then
+modify the parameter files if you want to test that.  We have not, however
+supplied an expected results file for that.
+
+The .mpg files are what ppmtojpeg output as the mpeg movie when we
+ran it on the jpeg files in the 'ts' directory.
+
+The .stat files are what ppmtojpeg output when we used the -stat
+option in generating the expected results.  As part of your test, use
+the -stat option and compare yours to the expected results.
diff --git a/converter/ppm/ppmtompeg/tst/gop.param b/converter/ppm/ppmtompeg/tst/gop.param
new file mode 100644
index 00000000..96900a3d
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/gop.param
@@ -0,0 +1,30 @@
+# test suite parameter file
+
+PATTERN		IBPBIBPBPB
+OUTPUT		/tmp/ts.mpg
+
+YUV_SIZE	352x240
+
+BASE_FILE_FORMAT	YUV
+INPUT_CONVERT	*
+GOP_SIZE	10
+SLICES_PER_FRAME  1
+
+INPUT_DIR	./tst/ts
+
+INPUT
+stennis.*.yuv	[30-39]
+stennis.*.yuv	[30-39]
+END_INPUT
+
+PIXEL		HALF
+RANGE		8
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	SIMPLE
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+REFERENCE_FRAME	ORIGINAL
diff --git a/converter/ppm/ppmtompeg/tst/par3.param b/converter/ppm/ppmtompeg/tst/par3.param
new file mode 100644
index 00000000..6ee6586e
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/par3.param
@@ -0,0 +1,43 @@
+# speed test parameter file
+
+PATTERN		IBBPBBPBBPBBPBB
+
+OUTPUT		/n/picasso/users/keving/encode/output/flowgard.mpg
+GOP_SIZE	30
+SLICES_PER_FRAME	15
+
+BASE_FILE_FORMAT	YUV
+YUV_SIZE	352x240
+
+INPUT_CONVERT	*
+
+INPUT_DIR	/n/picasso/users/keving/encode/input/flowg
+
+INPUT
+sflowg.*.yuv	[0-39]
+END_INPUT
+
+# quality parameters
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+# motion vector search parameters
+
+PIXEL		HALF
+
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+REFERENCE_FRAME	ORIGINAL
+
+PARALLEL_TEST_FRAMES	3
+PARALLEL_TIME_CHUNKS	30
+
+PARALLEL
+bugs-bunny	keving	~keving/encode/bin/sun/mpeg_encode
+linus		keving	~keving/encode/bin/sun/mpeg_encode
+END_PARALLEL
diff --git a/converter/ppm/ppmtompeg/tst/short.param b/converter/ppm/ppmtompeg/tst/short.param
new file mode 100644
index 00000000..9189e5d6
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/short.param
@@ -0,0 +1,31 @@
+# test suite parameter file
+
+PATTERN		IBBBPBBB
+OUTPUT		/tmp/ts.mpg
+
+YUV_SIZE	352x240
+
+BASE_FILE_FORMAT	YUV
+INPUT_CONVERT	*
+GOP_SIZE	30
+SLICES_PER_FRAME  1
+
+INPUT_DIR	../test/ts
+
+INPUT
+stennis.*.yuv	[30-37]
+END_INPUT
+
+PIXEL		HALF
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+REFERENCE_FRAME	ORIGINAL
+
+# FORCE_ENCODE_LAST_FRAME
diff --git a/converter/ppm/ppmtompeg/tst/test_all b/converter/ppm/ppmtompeg/tst/test_all
new file mode 100755
index 00000000..67b56c9c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/test_all
@@ -0,0 +1,19 @@
+#!/bin/csh -f
+cd ..
+echo "First we encode three mpegs... (note requires 5MB on /tmp)"
+rm -f /tmp/ts{,2,d}.{mpg,stat}
+./mpeg_encode -stat /tmp/ts.stat ./tst/ts.param
+./mpeg_encode -stat /tmp/ts2.stat ./tst/ts2.param
+./mpeg_encode -stat /tmp/tsd.stat ./tst/tsd.param
+
+cd tst
+
+echo "Test one - tst/ts.param"
+csh diffscript /tmp/ts.stat ts.stat /tmp/ts.mpg ts.mpg
+
+echo "Test two - tst/ts2.param (different pattern)"
+csh diffscript /tmp/ts2.stat ts2.stat /tmp/ts2.mpg ts2.mpg
+
+echo "Test three - tst/tsd.param (uses decoded frames)"
+csh diffscript /tmp/tsd.stat tsd.stat /tmp/tsd.mpg tsd.mpg
+
diff --git a/converter/ppm/ppmtompeg/tst/ts.mpg b/converter/ppm/ppmtompeg/tst/ts.mpg
new file mode 100644
index 00000000..2bc6ae6e
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/ts.mpg
Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts.param b/converter/ppm/ppmtompeg/tst/ts.param
new file mode 100644
index 00000000..23e1092c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/ts.param
@@ -0,0 +1,29 @@
+# test suite parameter file
+
+PATTERN		IBBPBBPBBP
+OUTPUT		/tmp/ts.mpg
+
+BASE_FILE_FORMAT JPEG
+INPUT_CONVERT	*
+GOP_SIZE	30
+SLICES_PER_FRAME  1
+
+INPUT_DIR	./tst/ts
+
+INPUT
+stennis.*.jpg	[30-39]
+END_INPUT
+
+PIXEL		HALF
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+REFERENCE_FRAME	ORIGINAL
+# Default string has the date in it, bad for tests!
+USER_DATA  /dev/null
diff --git a/converter/ppm/ppmtompeg/tst/ts.stat b/converter/ppm/ppmtompeg/tst/ts.stat
new file mode 100644
index 00000000..e13be8a3
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/ts.stat
@@ -0,0 +1,52 @@
+MPEG ENCODER STATS (1.5b)
+------------------------
+TIME STARTED:  Thu Mar 30 19:50:43 2000
+MACHINE:  unknown
+FIRST FILE:  ./tst/ts/stennis.30.jpg
+LAST FILE:  ./tst/ts/stennis.39.jpg
+PATTERN:  ibbpbbpbbp
+GOP_SIZE:  30
+SLICES PER FRAME:  1
+RANGE:  +/-10
+PIXEL SEARCH:  HALF
+PSEARCH:  LOGARITHMIC
+BSEARCH:  CROSS2
+QSCALE:  8 10 25
+REFERENCE FRAME:  ORIGINAL
+TIME COMPLETED:  Thu Mar 30 19:50:50 2000
+Total time:  7 seconds
+
+-------------------------
+*****I FRAME SUMMARY*****
+-------------------------
+  Blocks:      330     (105385 bits)     (  319 bpb)
+  Frames:        1     (105488 bits)     (105488 bpf)     (32.2% of total)
+  Compression:   19:1     (   1.2487 bpp)
+  Seconds:          0     (   7.6923 fps)  (   649846 pps)  (     2538 mps)
+-------------------------
+*****P FRAME SUMMARY*****
+-------------------------
+  I Blocks:    110     ( 27261 bits)     (  247 bpb)
+  P Blocks:    872     (147211 bits)     (  168 bpb)
+  Skipped:       8
+  Frames:        3     (174816 bits)     (58272 bpf)     (53.3% of total)
+  Compression:   34:1     (   0.6898 bpp)
+  Seconds:          0     (   3.2967 fps)  (   278505 pps)  (     1087 mps)
+-------------------------
+*****B FRAME SUMMARY*****
+-------------------------
+  I Blocks:      1     (   156 bits)     (  156 bpb)
+  B Blocks:   1979     ( 46497 bits)     (   23 bpb)
+  B types:     227     (  21 bpb) forw    484 (  16 bpb) back    1268 (  26 bpb) bi
+  Skipped:       0
+  Frames:        6     ( 47328 bits)     ( 7888 bpf)     (14.4% of total)
+  Compression:  257:1     (   0.0934 bpp)
+  Seconds:          5     (   1.1952 fps)  (   100972 pps)  (      394 mps)
+---------------------------------------------
+Total Compression:   61:1     (   0.3880 bpp)
+Total Frames Per Second:  1.428571 (471 mps)
+CPU Time:  1.650165 fps     (544 mps)
+Total Output Bit Rate (30 fps):  983472 bits/sec
+MPEG file created in :  /tmp/ts.mpg
+
+
diff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.30.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.30.jpg
new file mode 100644
index 00000000..f70e014e
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/ts/stennis.30.jpg
Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.31.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.31.jpg
new file mode 100644
index 00000000..cc88a285
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/ts/stennis.31.jpg
Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.32.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.32.jpg
new file mode 100644
index 00000000..221e22c9
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/ts/stennis.32.jpg
Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.33.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.33.jpg
new file mode 100644
index 00000000..c702a620
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/ts/stennis.33.jpg
Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.34.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.34.jpg
new file mode 100644
index 00000000..47c86806
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/ts/stennis.34.jpg
Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.35.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.35.jpg
new file mode 100644
index 00000000..ed21c16b
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/ts/stennis.35.jpg
Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.36.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.36.jpg
new file mode 100644
index 00000000..6fb7506b
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/ts/stennis.36.jpg
Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.37.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.37.jpg
new file mode 100644
index 00000000..561bd3b2
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/ts/stennis.37.jpg
Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.38.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.38.jpg
new file mode 100644
index 00000000..25ba097e
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/ts/stennis.38.jpg
Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.39.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.39.jpg
new file mode 100644
index 00000000..02b15aff
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/ts/stennis.39.jpg
Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts2.param b/converter/ppm/ppmtompeg/tst/ts2.param
new file mode 100644
index 00000000..f72ba150
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/ts2.param
@@ -0,0 +1,33 @@
+# test suite parameter file
+
+PATTERN		IBBPBBPBB
+
+OUTPUT		/tmp/ts2.mpg
+
+YUV_SIZE	352x240
+
+BASE_FILE_FORMAT	YUV
+YUV_FORMAT	UCB
+INPUT_CONVERT	*
+GOP_SIZE	30
+SLICES_PER_FRAME  1
+
+INPUT_DIR	./tst/ts
+
+INPUT
+stennis.*.yuv	[30-39]
+END_INPUT
+
+PIXEL		HALF
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+REFERENCE_FRAME	ORIGINAL
+# Default string has the date in it, bad for tests!
+USER_DATA  /dev/null
diff --git a/converter/ppm/ppmtompeg/tst/ts2.stat b/converter/ppm/ppmtompeg/tst/ts2.stat
new file mode 100644
index 00000000..5a14ef79
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/ts2.stat
@@ -0,0 +1,52 @@
+MPEG ENCODER STATS (1.5)
+------------------------
+TIME STARTED:  Mon Jun 26 14:22:56 1995
+MACHINE:  odie.CS.Berkeley.EDU
+FIRST FILE:  ./tst/ts/stennis.30.yuv
+LAST FILE:  ./tst/ts/stennis.39.yuv
+PATTERN:  ibbpbbpbb
+GOP_SIZE:  30
+SLICES PER FRAME:  1
+RANGE:  +/-10
+FULL SEARCH:  0
+PSEARCH:  LOGARITHMIC
+BSEARCH:  CROSS2
+QSCALE:  8 10 25
+REFERENCE FRAME:  ORIGINAL
+TIME COMPLETED:  Mon Jun 26 14:23:11 1995
+Total time:  15 seconds
+
+-------------------------
+*****I FRAME SUMMARY*****
+-------------------------
+  Blocks:      660     (171792 bits)     (  260 bpb)
+  Frames:        2     (172008 bits)     (86004 bpf)     (60.0% of total)
+  Compression:   23:1     (   1.0180 bpp)
+  Seconds:          0     (   2.9851 fps)  (   252179 pps)  (      985 mps)
+-------------------------
+*****P FRAME SUMMARY*****
+-------------------------
+  I Blocks:     54     ( 13161 bits)     (  243 bpb)
+  P Blocks:    598     ( 69956 bits)     (  116 bpb)
+  Skipped:       8
+  Frames:        2     ( 83344 bits)     (41672 bpf)     (29.1% of total)
+  Compression:   48:1     (   0.4933 bpp)
+  Seconds:          1     (   1.2821 fps)  (   108307 pps)  (      423 mps)
+-------------------------
+*****B FRAME SUMMARY*****
+-------------------------
+  I Blocks:      5     (   391 bits)     (   78 bpb)
+  B Blocks:   1733     ( 29897 bits)     (   17 bpb)
+  B types:     250     (  11 bpb) forw    483 (  12 bpb) back    1000 (  20 bpb) bi
+  Skipped:     491
+  Frames:        6     ( 30960 bits)     ( 5160 bpf)     (10.8% of total)
+  Compression:  392:1     (   0.0611 bpp)
+  Seconds:         11     (   0.5029 fps)  (    42487 pps)  (      165 mps)
+---------------------------------------------
+Total Compression:   70:1     (   0.3392 bpp)
+Total Frames Per Second:  0.666667 (220 mps)
+CPU Time:  0.706215 fps     (233 mps)
+Total Output Bit Rate (30 fps):  859608 bits/sec
+MPEG file created in :  /tmp/ts2.mpg
+
+
diff --git a/converter/ppm/ppmtompeg/tst/ts3.param b/converter/ppm/ppmtompeg/tst/ts3.param
new file mode 100644
index 00000000..5f7b9efa
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/ts3.param
@@ -0,0 +1,32 @@
+# test suite parameter file
+
+PATTERN		IBPB
+OUTPUT		/tmp/ts.mpg
+
+YUV_SIZE	352x240
+
+BASE_FILE_FORMAT	YUV
+YUV_FORMAT	UCB
+INPUT_CONVERT	*
+GOP_SIZE	3
+SLICES_PER_FRAME  1
+
+INPUT_DIR	./tst/ts
+
+INPUT
+stennis.*.yuv	[30-39]
+END_INPUT
+
+PIXEL		HALF
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+REFERENCE_FRAME	ORIGINAL
+# Default string has the date in it, bad for tests!
+USER_DATA  /dev/null
diff --git a/converter/ppm/ppmtompeg/tst/ts4.param b/converter/ppm/ppmtompeg/tst/ts4.param
new file mode 100644
index 00000000..c8ba161c
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/ts4.param
@@ -0,0 +1,56 @@
+# test suite parameter file
+
+PATTERN		IBBBPBBBBP
+OUTPUT		/tmp/ts.mpg
+
+YUV_SIZE	352x240
+
+BASE_FILE_FORMAT	YUV
+YUV_FORMAT	UCB
+INPUT_CONVERT	*
+GOP_SIZE	30
+SLICES_PER_FRAME  1
+
+INPUT_DIR	./tst/ts
+
+INPUT
+stennis.*.yuv	[30-39]
+END_INPUT
+
+PIXEL		HALF
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+FRAME_RATE	25.000
+
+ASPECT_RATIO	1.0
+
+REFERENCE_FRAME	ORIGINAL
+
+IQTABLE
+	8	16	19	22	26	27	29	34
+	9	10	11	12	13	14	15	16
+	17	18	19	20	21	22	23	24
+	25	26	27	28	29	30	31	32
+	33	34	35	36	37	38	39	40
+	41	42	43	44	45	46	47	48
+	49	50	51	52	53	54	55	56
+	57	58	59	60	61	62	63	64
+
+NIQTABLE
+	8	16	19	22	26	27	29	34
+	9	10	11	12	13	14	15	16
+	17	18	19	20	21	22	23	24
+	25	26	27	28	29	30	31	32
+	33	34	35	36	37	38	39	40
+	41	42	43	44	45	46	47	48
+	49	50	51	52	53	54	55	56
+	57	58	59	60	61	62	63	64
+# Default string has the date in it, bad for tests!
+USER_DATA  /dev/null
diff --git a/converter/ppm/ppmtompeg/tst/tsd.param b/converter/ppm/ppmtompeg/tst/tsd.param
new file mode 100644
index 00000000..330a7a2a
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/tsd.param
@@ -0,0 +1,32 @@
+# test suite parameter file
+
+PATTERN		IBBBPBBBBP
+OUTPUT		/tmp/tsd.mpg
+
+YUV_SIZE	352x240
+
+BASE_FILE_FORMAT	YUV
+YUV_FORMAT	UCB
+INPUT_CONVERT	*
+GOP_SIZE	30
+SLICES_PER_FRAME  1
+
+INPUT_DIR	./tst/ts
+
+INPUT
+stennis.*.yuv	[30-39]
+END_INPUT
+
+PIXEL		HALF
+RANGE		10
+
+PSEARCH_ALG	LOGARITHMIC
+BSEARCH_ALG	CROSS2
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+REFERENCE_FRAME	DECODED
+# Default string has the date in it, bad for tests!
+USER_DATA  /dev/null
diff --git a/converter/ppm/ppmtompeg/tst/tsd.stat b/converter/ppm/ppmtompeg/tst/tsd.stat
new file mode 100644
index 00000000..b130e089
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/tsd.stat
@@ -0,0 +1,52 @@
+MPEG ENCODER STATS (1.5b)
+------------------------
+TIME STARTED:  Tue Aug 15 12:31:11 1995
+MACHINE:  odie.CS.Berkeley.EDU
+FIRST FILE:  ./tst/ts/stennis.30.yuv
+LAST FILE:  ./tst/ts/stennis.39.yuv
+PATTERN:  ibbbpbbbbp
+GOP_SIZE:  30
+SLICES PER FRAME:  1
+RANGE:  +/-10
+PIXEL SEARCH:  HALF
+PSEARCH:  LOGARITHMIC
+BSEARCH:  CROSS2
+QSCALE:  8 10 25
+REFERENCE FRAME:  DECODED
+TIME COMPLETED:  Tue Aug 15 12:31:31 1995
+Total time:  20 seconds
+
+-------------------------
+*****I FRAME SUMMARY*****
+-------------------------
+  Blocks:      330     ( 94083 bits)     (  285 bpb)
+  Frames:        1     ( 94192 bits)     (94192 bpf)     (37.0% of total)
+  Compression:   21:1     (   1.1150 bpp)
+  Seconds:          0     (   2.4390 fps)  (   206048 pps)  (      804 mps)
+-------------------------
+*****P FRAME SUMMARY*****
+-------------------------
+  I Blocks:     97     ( 20894 bits)     (  215 bpb)
+  P Blocks:    560     ( 83390 bits)     (  148 bpb)
+  Skipped:       3
+  Frames:        2     (104512 bits)     (52256 bpf)     (41.1% of total)
+  Compression:   38:1     (   0.6186 bpp)
+  Seconds:          1     (   1.0811 fps)  (    91329 pps)  (      356 mps)
+-------------------------
+*****B FRAME SUMMARY*****
+-------------------------
+  I Blocks:      4     (   588 bits)     (  147 bpb)
+  B Blocks:   2306     ( 54124 bits)     (   23 bpb)
+  B types:     283     (  14 bpb) forw    419 (  16 bpb) back    1604 (  26 bpb) bi
+  Skipped:       0
+  Frames:        7     ( 55504 bits)     ( 7929 bpf)     (21.8% of total)
+  Compression:  255:1     (   0.0939 bpp)
+  Seconds:         16     (   0.4182 fps)  (    35326 pps)  (      137 mps)
+---------------------------------------------
+Total Compression:   79:1     (   0.3011 bpp)
+Total Frames Per Second:  0.500000 (165 mps)
+CPU Time:  0.526316 fps     (173 mps)
+Total Output Bit Rate (30 fps):  763200 bits/sec
+MPEG file created in :  /tmp/tsd.mpg
+
+
diff --git a/converter/ppm/ppmtompeg/tst/tstl.param b/converter/ppm/ppmtompeg/tst/tstl.param
new file mode 100644
index 00000000..2b9c1718
--- /dev/null
+++ b/converter/ppm/ppmtompeg/tst/tstl.param
@@ -0,0 +1,31 @@
+# test suite parameter file
+
+PATTERN		IBBBPBBBBP
+OUTPUT		/tmp/ts.mpg
+
+YUV_SIZE	352x240
+
+BASE_FILE_FORMAT	YUV
+INPUT_CONVERT	*
+GOP_SIZE	30
+SLICES_PER_FRAME  1
+
+INPUT_DIR	../test/ts
+
+INPUT
+stennis.*.yuv	[30-39]
+END_INPUT
+
+PIXEL		HALF
+RANGE		10
+
+PSEARCH_ALG	TWOLEVEL
+BSEARCH_ALG	CROSS2
+
+IQSCALE		8
+PQSCALE		10
+BQSCALE		25
+
+REFERENCE_FRAME	ORIGINAL
+# Default string has the date in it, bad for tests!
+USER_DATA  /dev/null
diff --git a/converter/ppm/ppmtoneo.c b/converter/ppm/ppmtoneo.c
new file mode 100644
index 00000000..5703c12a
--- /dev/null
+++ b/converter/ppm/ppmtoneo.c
@@ -0,0 +1,123 @@
+/* ppmtoneo.c - read a portable pixmap and write a Neochrome NEO file
+**
+** Copyright (C) 2001 by Teemu Hukkanen <tjhukkan@iki.fi>
+**  based on ppmtopi1 by Steve Belczyk and Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "ppm.h"
+
+#define COLS 320
+#define ROWS 200
+#define MAXVAL 7
+#define MAXCOLORS 16
+
+static short screen[ROWS*COLS/4];  /* simulate the ST's video RAM */
+
+int
+main(int argc, char *argv[] ) {
+
+    FILE* ifp;
+    pixel** pixels;
+    colorhist_vector chv;
+    colorhash_table cht;
+    int rows, cols;
+    int colors;
+    int i;
+    int row;
+    pixval maxval;
+
+    ppm_init( &argc, argv );
+
+    if ( argc > 2 )
+        pm_usage( "[ppmfile]" );
+
+    if ( argc == 2 )
+        ifp = pm_openr( argv[1] );
+    else
+        ifp = stdin;
+
+    pixels = ppm_readppm( ifp, &cols, &rows, &maxval );
+    pm_close( ifp );
+    if ( (cols > COLS) || (rows > ROWS) )
+        pm_error( "image is larger than %dx%d - sorry", COLS, ROWS );
+
+    pm_message( "computing colormap..." );
+    chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &colors );
+    if ( chv == (colorhist_vector) 0 ) {
+        pm_error(
+            "too many colors - try doing a 'pnmquant %d'", MAXCOLORS );
+    }
+    pm_message( "%d colors found", colors );
+
+    /* Write NEO header */
+    /* Flag */
+    pm_writebigshort( stdout, (short) 0);
+
+    /* resolution */
+    pm_writebigshort( stdout, (short) 0 );       /* low resolution */
+
+    /* palette */
+    for ( i = 0; i < 16; ++i ) {
+        short w;
+
+        if ( i < colors ) {
+            pixel p;
+
+            p = chv[i].color;
+            if ( maxval != MAXVAL )
+                PPM_DEPTH( p, p, maxval, MAXVAL );
+            w  = ( (int) PPM_GETR( p ) ) << 8;
+            w |= ( (int) PPM_GETG( p ) ) << 4;
+            w |= ( (int) PPM_GETB( p ) );
+        } else
+            w = 0;
+        pm_writebigshort( stdout, w );
+    }
+    if ( maxval > MAXVAL )
+        pm_message(
+            "maxval is not %d - automatically rescaling colors", MAXVAL );
+
+    /* Convert color vector to color hash table, for fast lookup. */
+    cht = ppm_colorhisttocolorhash( chv, colors );
+    ppm_freecolorhist( chv );
+
+    /* Skip rest of header */
+    fseek(stdout, 128, SEEK_SET);
+
+    /* Clear the screen buffer. */
+    for ( i = 0; i < ROWS*COLS/4; ++i )
+        screen[i] = 0;
+
+    /* Convert. */
+    for ( row = 0; row < rows; ++row ) {
+        int col;
+        for ( col = 0; col < cols; ++col) {
+            int color, ind, b, plane;
+            pixel const p = pixels[row][col];
+
+            color = ppm_lookupcolor( cht, &p );
+            if ( color == -1 )
+                pm_error(
+                    "color not found?!?  row=%d col=%d  r=%d g=%d b=%d",
+                    row, col, PPM_GETR(p), PPM_GETG(p), PPM_GETB(p) );
+            ind = 80 * row + ( ( col >> 4 ) << 2 );
+            b = 0x8000 >> (col & 0xf);
+            for ( plane = 0; plane < 4; ++plane )
+                if ( color & (1 << plane) )
+                    screen[ind+plane] |= b;
+            }
+        }
+
+    /* And write out the screen buffer. */
+    for ( i = 0; i < ROWS*COLS/4; ++i )
+        (void) pm_writebigshort( stdout, screen[i] );
+
+    exit( 0 );
+}
diff --git a/converter/ppm/ppmtopcx.c b/converter/ppm/ppmtopcx.c
new file mode 100644
index 00000000..bdcfc5c7
--- /dev/null
+++ b/converter/ppm/ppmtopcx.c
@@ -0,0 +1,906 @@
+/* ppmtopcx.c - convert a portable pixmap to PCX
+**
+** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
+** based on ppmtopcx.c by Michael Davidson
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+**
+** 11/Dec/94: first version
+** 12/Dec/94: added handling of "packed" format (16 colors or less)
+*/
+#include <assert.h>
+
+#include "ppm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+
+#define MAXCOLORS       256
+
+#define PCX_MAGIC       0x0a            /* PCX magic number             */
+#define PCX_256_COLORS  0x0c            /* magic number for 256 colors  */
+#define PCX_MAXVAL      (pixval)255
+
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *inputFilespec;  /* '-' if stdin */
+    unsigned int truecolor;   /* -24bit option */
+    unsigned int use_8_bit; /* -8bit option */
+    unsigned int planes;    /* zero means minimum */
+    unsigned int packed;
+    unsigned int verbose;
+    unsigned int stdpalette;
+    const char * palette;   /* NULL means none */
+    int xpos;
+    int ypos;
+};
+
+
+
+struct pcxCmapEntry {
+    unsigned char r;
+    unsigned char g;
+    unsigned char b;
+};
+
+static struct pcxCmapEntry
+pcxCmapEntryFromPixel(pixel const colorPixel) {
+
+    struct pcxCmapEntry retval;
+
+    retval.r = PPM_GETR(colorPixel);
+    retval.g = PPM_GETG(colorPixel);
+    retval.b = PPM_GETB(colorPixel);
+
+    return retval;
+}
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int planesSpec, xposSpec, yposSpec, paletteSpec;
+
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "24bit",     OPT_FLAG,   NULL,                  
+            &cmdlineP->truecolor,    0);
+    OPTENT3(0, "8bit",      OPT_FLAG,   NULL,    
+            &cmdlineP->use_8_bit,    0);
+    OPTENT3(0, "planes",    OPT_UINT,   &cmdlineP->planes, 
+            &planesSpec,             0);
+    OPTENT3(0, "packed",    OPT_FLAG,   NULL,                  
+            &cmdlineP->packed,       0);
+    OPTENT3(0, "verbose",   OPT_FLAG,   NULL,                  
+            &cmdlineP->verbose,      0);
+    OPTENT3(0, "stdpalette", OPT_FLAG,  NULL,
+            &cmdlineP->stdpalette,   0);
+    OPTENT3(0, "palette",    OPT_STRING, &cmdlineP->palette,
+            &paletteSpec,   0);
+    OPTENT3(0, "xpos",  OPT_INT, &cmdlineP->xpos, &xposSpec,   0);
+    OPTENT3(0, "ypos",  OPT_INT, &cmdlineP->ypos, &yposSpec,   0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+        /* Uses and sets argc, argv, and some of *cmdline_p and others. */
+
+    if (!xposSpec)
+        cmdlineP->xpos = 0;
+    else if (cmdlineP->xpos < -32767 || cmdlineP->xpos > 32768)
+        pm_error("-xpos value (%d) is outside acceptable range "
+                 "(-32767, 32768)", cmdlineP->xpos);
+
+    if (!yposSpec)
+        cmdlineP->ypos = 0;
+    else if (cmdlineP->ypos < -32767 || cmdlineP->ypos > 32768)
+        pm_error("-ypos value (%d) is outside acceptable range "
+                 "(-32767, 32768)", cmdlineP->ypos);
+
+    if (!planesSpec)
+        cmdlineP->planes = 0;  /* 0 means minimum */
+
+    if (planesSpec) {
+        if (cmdlineP->planes > 4 || cmdlineP->planes < 1)
+            pm_error("The only possible numbers of planes are 1-4.  "
+                     "You specified %u", cmdlineP->planes);
+        if (cmdlineP->packed)
+            pm_error("-planes is meaningless with -packed.");
+        if (cmdlineP->truecolor)
+            pm_error("-planes is meaningless with -24bit");
+        if (cmdlineP->use_8_bit)
+            pm_error("-planes is meaningless with -8bit");
+    }
+    
+    if (paletteSpec && cmdlineP->stdpalette)
+        pm_error("You can't specify both -palette and -stdpalette");
+
+    if (!paletteSpec)
+        cmdlineP->palette = NULL;
+
+    if (cmdlineP->use_8_bit && cmdlineP->truecolor) 
+        pm_error("You cannot specify both -8bit and -truecolor");
+
+    if (argc-1 < 1)
+        cmdlineP->inputFilespec = "-";
+    else if (argc-1 == 1)
+        cmdlineP->inputFilespec = argv[1];
+    else
+        pm_error("Program takes at most one argument "
+                 "(input file specification).  You specified %d",
+                 argc-1);
+}
+
+
+
+/*
+ * Write out a two-byte little-endian word to the PCX file
+ */
+static void
+Putword(int    const w, 
+        FILE * const fp) {
+
+    int rc;
+    
+    rc = pm_writelittleshort(fp, w);
+
+    if (rc != 0)
+        pm_error("Error writing integer to output file");
+}
+
+
+
+/*
+ * Write out a byte to the PCX file
+ */
+static void
+Putbyte(int    const b, 
+        FILE * const fp) {
+
+    int rc;
+
+    rc = fputc(b & 0xff, fp);
+    if (rc == EOF)
+        pm_error("Error writing byte to output file.");
+}
+
+
+
+static const unsigned char bitmask[] = {1, 2, 4, 8, 16, 32, 64, 128};
+
+
+static void
+extractPlane(unsigned char * const rawrow, 
+             int             const cols, 
+             unsigned char * const buf, 
+             int             const plane) {
+/*----------------------------------------------------------------------------
+   From the image row 'rawrow', which is an array of 'cols' palette indices
+   (as unsigned 8 bit integers), extract plane number 'plane' and return
+   it at 'buf'.
+
+   E.g. Plane 2 is all the 2nd bits from the palette indices, packed so
+   that each byte represents 8 columns.
+-----------------------------------------------------------------------------*/
+    unsigned int const planeMask = 1 << plane;
+
+    unsigned int col;
+    int cbit;  /* Significance of bit representing current column in output */
+    unsigned char *cp;  /* Ptr to next output byte to fill */
+    unsigned char byteUnderConstruction;
+    
+    cp = buf;  /* initial value */
+
+    cbit = 7;
+    byteUnderConstruction = 0x00;
+    for (col = 0; col < cols; ++col) {
+        if (rawrow[col] & planeMask)
+            byteUnderConstruction |= (1 << cbit);
+
+        --cbit;
+        if (cbit < 0) {
+            /* We've filled a byte.  Output it and start the next */
+            *cp++ = byteUnderConstruction;
+            cbit = 7;
+            byteUnderConstruction = 0x00;
+        }
+    }
+    if (cbit < 7)
+        /* A byte was partially built when we ran out of columns (the number
+           of columns is not a multiple of 8.  Output the partial byte.
+        */
+        *cp++ = byteUnderConstruction;
+}
+
+
+
+static void
+PackBits(unsigned char * const rawrow, 
+         int             const width, 
+         unsigned char * const buf, 
+         int             const bits) {
+
+    int x, i, shift;
+
+    shift = i = -1;
+
+    for (x = 0; x < width; ++x) {
+        if (shift < 0) {
+            shift = 8 - bits;
+            buf[++i] = 0;
+        }
+
+        buf[i] |= (rawrow[x] << shift);
+        shift -= bits;
+    }
+}
+
+
+
+static void
+write_header(FILE *              const fp, 
+             int                 const cols, 
+             int                 const rows, 
+             int                 const BitsPerPixel, 
+             int                 const Planes, 
+             struct pcxCmapEntry const cmap16[],
+             unsigned int        const xPos, 
+             unsigned int        const yPos) {
+
+    int i, BytesPerLine;
+
+    Putbyte(PCX_MAGIC, fp);        /* .PCX magic number            */
+    Putbyte(0x05, fp);             /* PC Paintbrush version        */
+    Putbyte(0x01, fp);             /* .PCX run length encoding     */
+    Putbyte(BitsPerPixel, fp);     /* bits per pixel               */
+    
+    Putword(xPos, fp);             /* x1   - image left            */
+    Putword(yPos, fp);             /* y1   - image top             */
+    Putword(xPos+cols-1, fp);      /* x2   - image right           */
+    Putword(yPos+rows-1, fp);      /* y2   - image bottom          */
+
+    Putword(cols, fp);             /* horizontal resolution        */
+    Putword(rows, fp);             /* vertical resolution          */
+
+    /* Write out the Color Map for images with 16 colors or less */
+    if (cmap16)
+        for (i = 0; i < 16; ++i) {
+            Putbyte(cmap16[i].r, fp);
+            Putbyte(cmap16[i].g, fp);
+            Putbyte(cmap16[i].b, fp);
+        }
+    else {
+        unsigned int i;
+        for (i = 0; i < 16; ++i) {
+            Putbyte(0, fp);
+            Putbyte(0, fp);
+            Putbyte(0, fp);
+        }
+    }
+    Putbyte(0, fp);                /* reserved byte                */
+    Putbyte(Planes, fp);           /* number of color planes       */
+
+    BytesPerLine = ((cols * BitsPerPixel) + 7) / 8;
+    Putword(BytesPerLine, fp);    /* number of bytes per scanline */
+
+    Putword(1, fp);                /* palette info                 */
+
+    {
+        unsigned int i;
+        for (i = 0; i < 58; ++i)        /* fill to end of header        */
+            Putbyte(0, fp);
+    }
+}
+
+
+
+static void
+PCXEncode(FILE *                const fp, 
+          const unsigned char * const buf, 
+          int                   const Size) {
+
+    const unsigned char * const end = buf + Size;
+
+    const unsigned char * currentP;
+    int previous, count;
+
+    currentP = buf;
+    previous = *currentP++;
+    count    = 1;
+
+    while (currentP < end) {
+        unsigned char const c = *currentP++;
+        if (c == previous && count < 63)
+            ++count;
+        else {
+            if (count > 1 || (previous & 0xc0) == 0xc0) {
+                count |= 0xc0;
+                Putbyte ( count , fp );
+            }
+            Putbyte(previous, fp);
+            previous = c;
+            count   = 1;
+        }
+    }
+
+    if (count > 1 || (previous & 0xc0) == 0xc0) {
+        count |= 0xc0;
+        Putbyte ( count , fp );
+    }
+    Putbyte(previous, fp);
+}
+
+
+
+static unsigned int
+indexOfColor(colorhash_table const cht,
+             pixel           const color) {
+/*----------------------------------------------------------------------------
+   Return the index in the palette described by 'cht' of the color 'color'.
+
+   Abort program with error message if the color is not in the palette.
+-----------------------------------------------------------------------------*/
+
+    int const rc = ppm_lookupcolor(cht, &color);
+            
+    if (rc < 0)
+        pm_error("Image contains color which is not "
+                 "in the palette: %u/%u/%u", 
+                 PPM_GETR(color), PPM_GETG(color), PPM_GETB(color));
+
+    return rc;
+}
+
+
+
+static void
+ppmTo16ColorPcx(pixel **            const pixels, 
+                int                 const cols, 
+                int                 const rows, 
+                struct pcxCmapEntry const pcxcmap[], 
+                int                 const colors, 
+                colorhash_table     const cht, 
+                bool                const packbits,
+                unsigned int        const planesRequested,
+                unsigned int        const xPos,
+                unsigned int        const yPos) {
+
+    int Planes, BytesPerLine, BitsPerPixel;
+    unsigned char *indexRow;  /* malloc'ed */
+        /* indexRow[x] is the palette index of the pixel at column x of
+           the row currently being processed
+        */
+    unsigned char *planesrow; /* malloc'ed */
+        /* This is the input for a single row to the compressor */
+    int row;
+
+    if (packbits) {
+        Planes = 1;
+        if (colors > 4)        BitsPerPixel = 4;
+        else if (colors > 2)   BitsPerPixel = 2;
+        else                   BitsPerPixel = 1;
+    } else {
+        BitsPerPixel = 1;
+        if (planesRequested)
+            Planes = planesRequested;
+        else {
+            if (colors > 8)        Planes = 4;
+            else if (colors > 4)   Planes = 3;
+            else if (colors > 2)   Planes = 2;
+            else                   Planes = 1;
+        }
+    }
+    BytesPerLine = ((cols * BitsPerPixel) + 7) / 8;
+    MALLOCARRAY_NOFAIL(indexRow, cols);
+    MALLOCARRAY_NOFAIL(planesrow, BytesPerLine);
+
+    write_header(stdout, cols, rows, BitsPerPixel, Planes, pcxcmap, 
+                 xPos, yPos);
+    for (row = 0; row < rows; ++row) {
+        int col;
+        for (col = 0; col < cols; ++col)
+            indexRow[col] = indexOfColor(cht, pixels[row][col]);
+
+        if (packbits) {
+            PackBits(indexRow, cols, planesrow, BitsPerPixel);
+            PCXEncode(stdout, planesrow, BytesPerLine);
+        } else {
+            unsigned int plane;
+            for (plane = 0; plane < Planes; ++plane) {
+                extractPlane(indexRow, cols, planesrow, plane);
+                PCXEncode(stdout, planesrow, BytesPerLine);
+            }
+        }
+    }
+    free(planesrow);
+    free(indexRow);
+}
+
+
+
+static void
+ppmTo256ColorPcx(pixel **            const pixels, 
+                 int                 const cols, 
+                 int                 const rows, 
+                 struct pcxCmapEntry const pcxcmap[], 
+                 int                 const colors, 
+                 colorhash_table     const cht,
+                 unsigned int        const xPos, 
+                 unsigned int        const yPos) {
+
+    int row;
+    unsigned int i;
+    unsigned char *rawrow;
+
+    rawrow = (unsigned char *)pm_allocrow(cols, sizeof(unsigned char));
+
+    /* 8 bits per pixel, 1 plane */
+    write_header(stdout, cols, rows, 8, 1, NULL, xPos, yPos);
+    for (row = 0; row < rows; ++row) {
+        int col;
+        for (col = 0; col < cols; ++col)
+            rawrow[col] = indexOfColor(cht, pixels[row][col]);
+        PCXEncode(stdout, rawrow, cols);
+    }
+    Putbyte(PCX_256_COLORS, stdout);
+    for (i = 0; i < MAXCOLORS; ++i) {
+        Putbyte(pcxcmap[i].r, stdout);
+        Putbyte(pcxcmap[i].g, stdout);
+        Putbyte(pcxcmap[i].b, stdout);
+    }
+    pm_freerow((void*)rawrow);
+}
+
+
+
+static void
+ppmToTruecolorPcx(pixel **     const pixels, 
+                  int          const cols, 
+                  int          const rows, 
+                  pixval       const maxval,
+                  unsigned int const xPos, 
+                  unsigned int const yPos) {
+
+    unsigned char *redrow, *greenrow, *bluerow;
+    int col, row;
+
+    redrow   = (unsigned char *)pm_allocrow(cols, sizeof(unsigned char));
+    greenrow = (unsigned char *)pm_allocrow(cols, sizeof(unsigned char));
+    bluerow  = (unsigned char *)pm_allocrow(cols, sizeof(unsigned char));
+
+    /* 8 bits per pixel, 3 planes */
+    write_header(stdout, cols, rows, 8, 3, NULL, xPos, yPos);
+    for( row = 0; row < rows; row++ ) {
+        register pixel *pP = pixels[row];
+        for( col = 0; col < cols; col++, pP++ ) {
+            if( maxval != PCX_MAXVAL ) {
+                redrow[col]   = (long)PPM_GETR(*pP) * PCX_MAXVAL / maxval;
+                greenrow[col] = (long)PPM_GETG(*pP) * PCX_MAXVAL / maxval;
+                bluerow[col]  = (long)PPM_GETB(*pP) * PCX_MAXVAL / maxval;
+            }
+            else {
+                redrow[col]   = PPM_GETR(*pP);
+                greenrow[col] = PPM_GETG(*pP);
+                bluerow[col]  = PPM_GETB(*pP);
+            }
+        }
+        PCXEncode(stdout, redrow, cols);
+        PCXEncode(stdout, greenrow, cols);
+        PCXEncode(stdout, bluerow, cols);
+    }
+    pm_freerow((void*)bluerow);
+    pm_freerow((void*)greenrow);
+    pm_freerow((void*)redrow);
+}
+
+
+
+static const struct pcxCmapEntry 
+stdPalette[] = {
+    {   0,   0,   0 },
+    {   0,   0, 170 },
+    {   0, 170,   0 },
+    {   0, 170, 170 },
+    { 170,   0,   0 },
+    { 170,   0, 170 },
+    { 170, 170,   0 },
+    { 170, 170, 170 },
+    {  85,  85,  85 },
+    {  85,  85, 255 },
+    {  85, 255,  85 },
+    {  85, 255, 255 },
+    { 255,  85,  85 },
+    { 255,  85, 255 },
+    { 255, 255,  85 },
+    { 255, 255, 255 }
+};
+
+
+
+static void
+putPcxColorInHash(colorhash_table const cht,
+                  pixel           const newPcxColor,
+                  unsigned int    const newColorIndex,
+                  pixval          const maxval) {
+
+    pixel ppmColor;
+        /* Same color as 'newPcxColor', but at the PPM image's color
+           resolution: 'maxval'
+        */
+    int rc;
+
+    PPM_DEPTH(ppmColor, newPcxColor, PCX_MAXVAL, maxval);
+        
+    rc = ppm_lookupcolor(cht, &ppmColor);
+
+    if (rc == -1)
+        /* This color is not in the hash yet, so we just add it */
+        ppm_addtocolorhash(cht, &ppmColor, newColorIndex);
+    else {
+        /* This color is already in the hash.  That's because the
+           subject image has less color resolution than PCX (i.e.
+           'maxval' is less than PCX_MAXVAL), and two distinct
+           colors in the standard palette are indistinguishable at
+           subject image color resolution.
+           
+           So we have to figure out wether color 'newPcxColor' or
+           'existingPcxColor' is a better match for 'ppmColor'.
+        */
+
+        unsigned int const existingColorIndex = rc;
+
+        pixel idealPcxColor;
+        pixel existingPcxColor;
+
+        PPM_DEPTH(idealPcxColor, ppmColor, maxval, PCX_MAXVAL);
+        
+        PPM_ASSIGN(existingPcxColor, 
+                   stdPalette[existingColorIndex].r,
+                   stdPalette[existingColorIndex].g,
+                   stdPalette[existingColorIndex].b);
+
+        if (PPM_DISTANCE(newPcxColor, idealPcxColor) <
+            PPM_DISTANCE(existingPcxColor, idealPcxColor)) {
+            /* The new PCX color is a better match.  Make it the new
+               translation of image color 'ppmColor'.
+            */
+            ppm_delfromcolorhash(cht, &ppmColor);
+            ppm_addtocolorhash(cht, &ppmColor, newColorIndex);
+        }
+    }
+}
+
+
+
+static void
+generateStandardPalette(struct pcxCmapEntry ** const pcxcmapP,
+                        pixval                 const maxval,
+                        colorhash_table *      const chtP,
+                        int *                  const colorsP) {
+
+    unsigned int const stdPaletteSize = 16;
+    unsigned int colorIndex;
+    struct pcxCmapEntry * pcxcmap;
+    colorhash_table cht;
+
+    MALLOCARRAY_NOFAIL(pcxcmap, MAXCOLORS);
+    
+    *pcxcmapP = pcxcmap;
+
+    cht = ppm_alloccolorhash();
+
+    for (colorIndex = 0; colorIndex < stdPaletteSize; ++colorIndex) {
+        pixel pcxColor;
+            /* The color of this colormap entry, in PCX resolution */
+
+        pcxcmap[colorIndex] = stdPalette[colorIndex];
+        
+        PPM_ASSIGN(pcxColor, 
+                   stdPalette[colorIndex].r,
+                   stdPalette[colorIndex].g,
+                   stdPalette[colorIndex].b);
+
+        putPcxColorInHash(cht, pcxColor, colorIndex, maxval);
+    }
+
+    *chtP = cht;
+    *colorsP = stdPaletteSize;
+}
+    
+
+
+static void
+readPpmPalette(const char *   const paletteFileName,
+               pixel       (* const ppmPaletteP)[], 
+               unsigned int * const paletteSizeP) {
+
+    FILE * pfP;
+    pixel ** pixels;
+    int cols, rows;
+    pixval maxval;
+    
+    pfP = pm_openr(paletteFileName);
+
+    pixels = ppm_readppm(pfP, &cols, &rows, &maxval);
+
+    pm_close(pfP);
+    
+    *paletteSizeP = rows * cols;
+    if (*paletteSizeP > MAXCOLORS) 
+        pm_error("ordered palette image contains %d pixels.  Maximum is %d",
+                 *paletteSizeP, MAXCOLORS);
+
+    {
+        int j;
+        int row;
+        j = 0;  /* initial value */
+        for (row = 0; row < rows; ++row) {
+            int col;
+            for (col = 0; col < cols; ++col) 
+                (*ppmPaletteP)[j++] = pixels[row][col];
+        }
+    }
+    ppm_freearray(pixels, rows);
+}        
+
+
+
+static void
+readPaletteFromFile(struct pcxCmapEntry ** const pcxcmapP,
+                    const char *           const paletteFileName,
+                    pixval                 const maxval,
+                    colorhash_table *      const chtP,
+                    int *                  const colorsP) {
+
+    unsigned int colorIndex;
+    pixel ppmPalette[MAXCOLORS];
+    unsigned int paletteSize;
+    struct pcxCmapEntry * pcxcmap;
+    colorhash_table cht;
+
+    readPpmPalette(paletteFileName, &ppmPalette, &paletteSize);
+
+    MALLOCARRAY_NOFAIL(pcxcmap, MAXCOLORS);
+    
+    *pcxcmapP = pcxcmap;
+
+    cht = ppm_alloccolorhash();
+
+    for (colorIndex = 0; colorIndex < paletteSize; ++colorIndex) {
+        pixel pcxColor;
+            /* The color of this colormap entry, in PCX resolution */
+
+        pcxcmap[colorIndex] = pcxCmapEntryFromPixel(ppmPalette[colorIndex]);
+        
+        PPM_ASSIGN(pcxColor, 
+                   ppmPalette[colorIndex].r,
+                   ppmPalette[colorIndex].g,
+                   ppmPalette[colorIndex].b);
+
+        putPcxColorInHash(cht, pcxColor, colorIndex, maxval);
+    }
+
+    *chtP = cht;
+    *colorsP = paletteSize;
+}
+    
+
+
+static void
+moveBlackToIndex0(colorhist_vector const chv,
+                  int              const colors) {
+/*----------------------------------------------------------------------------
+   If black is in the palette, make it at Index 0.
+-----------------------------------------------------------------------------*/
+    pixel blackPixel;
+    unsigned int i;
+    bool blackPresent;
+
+    PPM_ASSIGN(blackPixel, 0, 0, 0);
+
+    blackPresent = FALSE;  /* initial assumption */
+
+    for (i = 0; i < colors; ++i)
+        if (PPM_EQUAL(chv[i].color, blackPixel))
+            blackPresent = TRUE;
+            
+    if (blackPresent) {
+        /* We use a trick here.  ppm_addtocolorhist() always adds to the
+           beginning of the table and if the color is already elsewhere in
+           the table, removes it.
+        */
+        int colors2;
+        colors2 = colors;
+        ppm_addtocolorhist(chv, &colors2, MAXCOLORS, &blackPixel, 0, 0);
+        assert(colors2 == colors);
+    }
+}
+
+
+
+static void
+makePcxColormapFromImage(pixel **               const pixels,
+                         int                    const cols,
+                         int                    const rows,
+                         pixval                 const maxval,
+                         struct pcxCmapEntry ** const pcxcmapP,
+                         colorhash_table *      const chtP,
+                         int *                  const colorsP,
+                         bool *                 const tooManyColorsP) {
+/*----------------------------------------------------------------------------
+   Make a colormap (palette) for the PCX header that can be used
+   for the image described by 'pixels', 'cols', 'rows', and 'maxval'.
+
+   Return it in newly malloc'ed storage and return its address as
+   *pcxcmapP.
+
+   Also return a lookup hash to relate a color in the image to the
+   appropriate index in *pcxcmapP.  Return that in newly malloc'ed 
+   storage as *chtP.
+
+   Iff there are too many colors to do that (i.e. more than 256), 
+   return *tooManyColorsP == TRUE.
+-----------------------------------------------------------------------------*/
+    int colors;
+    colorhist_vector chv;
+
+    pm_message("computing colormap...");
+
+    chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors);
+    if (chv == NULL)
+        *tooManyColorsP = TRUE;
+    else {
+        int i;
+        struct pcxCmapEntry * pcxcmap;
+
+        *tooManyColorsP = FALSE;
+
+        pm_message("%d colors found", colors);
+        
+        moveBlackToIndex0(chv, colors);
+
+        MALLOCARRAY_NOFAIL(pcxcmap, MAXCOLORS);
+
+        *pcxcmapP = pcxcmap;
+
+        for (i = 0; i < colors; ++i) {
+            pixel p;
+
+            PPM_DEPTH(p, chv[i].color, maxval, PCX_MAXVAL);
+
+            pcxcmap[i].r = PPM_GETR(p);
+            pcxcmap[i].g = PPM_GETG(p);
+            pcxcmap[i].b = PPM_GETB(p);
+        }
+
+        /* Fill it out with black */
+        for ( ; i < MAXCOLORS; ++i) {
+            pcxcmap[i].r = 0;
+            pcxcmap[i].g = 0;
+            pcxcmap[i].b = 0;
+        }
+
+        *chtP = ppm_colorhisttocolorhash(chv, colors);
+
+        *colorsP = colors;
+
+        ppm_freecolorhist(chv);
+    }
+}
+
+
+
+static void 
+ppmToPalettePcx(pixel **            const pixels, 
+                int                 const cols, 
+                int                 const rows,
+                pixval              const maxval,
+                unsigned int        const xPos, 
+                unsigned int        const yPos,
+                struct pcxCmapEntry const pcxcmap[],
+                colorhash_table     const cht,
+                int                 const colors,
+                bool                const packbits,
+                unsigned int        const planes,
+                bool                const use_8_bit) {
+    
+    /* convert image */
+    if( colors <= 16 && !use_8_bit )
+        ppmTo16ColorPcx(pixels, cols, rows, pcxcmap, colors, cht, 
+                        packbits, planes, xPos, yPos);
+    else
+        ppmTo256ColorPcx(pixels, cols, rows, pcxcmap, colors, cht,
+                         xPos, yPos);
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE* ifP;
+    int rows, cols;
+    pixval maxval;
+    pixel **pixels;
+    struct pcxCmapEntry * pcxcmap;
+    colorhash_table cht;
+    bool truecolor;
+    int colors;
+
+    ppm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+    pixels = ppm_readppm(ifP, &cols, &rows, &maxval);
+    pm_close(ifP);
+
+    if (cmdline.truecolor)
+        truecolor = TRUE;
+    else {
+        if (cmdline.stdpalette) {
+            truecolor = FALSE;
+            generateStandardPalette(&pcxcmap, maxval, &cht, &colors);
+        } else if (cmdline.palette) {
+            truecolor = FALSE;
+            readPaletteFromFile(&pcxcmap, cmdline.palette, maxval, 
+                                &cht, &colors);
+        } else {
+            bool tooManyColors;
+            makePcxColormapFromImage(pixels, cols, rows, maxval,
+                                     &pcxcmap, &cht, &colors,
+                                     &tooManyColors);
+            
+            if (tooManyColors) {
+                pm_message("too many colors - writing a 24bit PCX file");
+                pm_message("if you want a non-24bit file, "
+                           " a 'pnmquant %d'", MAXCOLORS);
+                truecolor = TRUE;
+            } else
+                truecolor = FALSE;
+        }
+    }
+
+    if (truecolor)
+        ppmToTruecolorPcx(pixels, cols, rows, maxval, 
+                          cmdline.xpos, cmdline.ypos);
+    else {
+        ppmToPalettePcx(pixels, cols, rows, maxval, 
+                        cmdline.xpos, cmdline.ypos,
+                        pcxcmap, cht, colors, cmdline.packed, 
+                        cmdline.planes, cmdline.use_8_bit);
+        
+        ppm_freecolorhash(cht);
+        free(pcxcmap);
+    }
+    return 0;
+}
diff --git a/converter/ppm/ppmtopi1.c b/converter/ppm/ppmtopi1.c
new file mode 100644
index 00000000..64f836c7
--- /dev/null
+++ b/converter/ppm/ppmtopi1.c
@@ -0,0 +1,120 @@
+/* ppmtopi1.c - read a portable pixmap and write a Degas PI1 file
+**
+** Copyright (C) 1991 by Steve Belczyk and Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "ppm.h"
+
+#define COLS 320
+#define ROWS 200
+#define MAXVAL 7
+#define MAXCOLORS 16
+
+static short screen[ROWS*COLS/4];  /* simulate the ST's video RAM */
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    pixel** pixels;
+    register pixel *pP;
+    colorhist_vector chv;
+    colorhash_table cht;
+    int rows, cols, row, colors, i;
+    register int col;
+    pixval maxval;
+
+
+    ppm_init( &argc, argv );
+
+    if ( argc > 2 )
+        pm_usage( "[ppmfile]" );
+
+    if ( argc == 2 )
+        ifp = pm_openr( argv[1] );
+    else
+        ifp = stdin;
+
+    pixels = ppm_readppm( ifp, &cols, &rows, &maxval );
+    pm_close( ifp );
+    if ( (cols > COLS) || (rows > ROWS) )
+        pm_error( "image is larger than %dx%d - sorry", COLS, ROWS );
+
+    pm_message( "computing colormap..." );
+    chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &colors );
+    if ( chv == (colorhist_vector) 0 )
+        {
+        pm_message(
+            "too many colors - try doing a 'pnmquant %d'", MAXCOLORS );
+        exit( 1 );
+        }
+    pm_message( "%d colors found", colors );
+
+    /* Write PI1 header - resolution and palette. */
+    (void) pm_writebigshort( stdout, (short) 0 );       /* low resolution */
+    for ( i = 0; i < 16; ++i )
+        {
+        short w;
+
+        if ( i < colors )
+            {
+            pixel p;
+
+            p = chv[i].color;
+            if ( maxval != MAXVAL )
+                PPM_DEPTH( p, p, maxval, MAXVAL );
+            w  = ( (int) PPM_GETR( p ) ) << 8;
+            w |= ( (int) PPM_GETG( p ) ) << 4;
+            w |= ( (int) PPM_GETB( p ) );
+            }
+        else
+            w = 0;
+        (void) pm_writebigshort( stdout, w );
+        }
+    if ( maxval > MAXVAL )
+        pm_message(
+            "maxval is not %d - automatically rescaling colors", MAXVAL );
+
+    /* Convert color vector to color hash table, for fast lookup. */
+    cht = ppm_colorhisttocolorhash( chv, colors );
+    ppm_freecolorhist( chv );
+
+    /* Clear the screen buffer. */
+    for ( i = 0; i < ROWS*COLS/4; ++i )
+        screen[i] = 0;
+
+    /* Convert. */
+    for ( row = 0; row < rows; ++row )
+        {
+        for ( col = 0, pP = pixels[row]; col < cols; ++col, ++pP )
+            {
+            register int color, ind, b, plane;
+
+            color = ppm_lookupcolor( cht, pP );
+            if ( color == -1 )
+                pm_error(
+                    "color not found?!?  row=%d col=%d  r=%d g=%d b=%d",
+                    row, col, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP) );
+            ind = 80 * row + ( ( col >> 4 ) << 2 );
+            b = 0x8000 >> (col & 0xf);
+            for ( plane = 0; plane < 4; ++plane )
+                if ( color & (1 << plane) )
+                    screen[ind+plane] |= b;
+            }
+        }
+
+    /* And write out the screen buffer. */
+    for ( i = 0; i < ROWS*COLS/4; ++i )
+        (void) pm_writebigshort( stdout, screen[i] );
+
+    exit( 0 );
+    }
diff --git a/converter/ppm/ppmtopict.c b/converter/ppm/ppmtopict.c
new file mode 100644
index 00000000..e2428fb6
--- /dev/null
+++ b/converter/ppm/ppmtopict.c
@@ -0,0 +1,481 @@
+/*
+** ppmtopict.c - read a portable pixmap and produce a Macintosh PICT2 file.
+**
+** Copyright (C) 1990 by Ken Yap <ken@cs.rochester.edu>.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "ppm.h"
+
+#define HEADER_SIZE		512
+
+#define	RUN_THRESH		3
+#define	MAX_RUN			128		/* 0xff = 2, 0xfe = 3, etc */
+#define	MAX_COUNT		128		/* 0x00 = 1, 0x01 = 2, etc */
+
+/* Opcodes */
+#define PICT_NOP		0x00
+#define PICT_clipRgn		0x01
+#define PICT_bkPat		0x02
+#define PICT_txFont		0x03
+#define PICT_txFace		0x04
+#define PICT_txMode		0x05
+#define PICT_spExtra		0x06
+#define PICT_pnSize		0x07
+#define PICT_pnMode		0x08
+#define PICT_pnPat		0x09
+#define PICT_thePat		0x0A
+#define PICT_ovSize		0x0B
+#define PICT_origin		0x0C
+#define PICT_txSize		0x0D
+#define PICT_fgColor		0x0E
+#define PICT_bkColor		0x0F
+#define PICT_txRatio		0x10
+#define PICT_picVersion		0x11
+#define	PICT_blPixPat		0x12
+#define	PICT_pnPixPat		0x13
+#define	PICT_fillPixPat		0x14
+#define	PICT_pnLocHFrac		0x15
+#define	PICT_chExtra		0x16
+#define	PICT_rgbFgCol		0x1A
+#define	PICT_rgbBkCol		0x1B
+#define	PICT_hiliteMode		0x1C
+#define	PICT_hiliteColor	0x1D
+#define	PICT_defHilite		0x1E
+#define	PICT_opColor		0x1F
+#define PICT_line		0x20
+#define PICT_line_from		0x21
+#define PICT_short_line		0x22
+#define PICT_short_line_from	0x23
+#define PICT_long_text		0x28
+#define PICT_DH_text		0x29
+#define PICT_DV_text		0x2A
+#define PICT_DHDV_text		0x2B
+#define PICT_frameRect		0x30
+#define PICT_paintRect		0x31
+#define PICT_eraseRect		0x32
+#define PICT_invertRect		0x33
+#define PICT_fillRect		0x34
+#define PICT_frameSameRect	0x38
+#define PICT_paintSameRect	0x39
+#define PICT_eraseSameRect	0x3A
+#define PICT_invertSameRect	0x3B
+#define PICT_fillSameRect	0x3C
+#define PICT_frameRRect		0x40
+#define PICT_paintRRect		0x41
+#define PICT_eraseRRect		0x42
+#define PICT_invertRRect	0x43
+#define PICT_fillRRect		0x44
+#define PICT_frameSameRRect	0x48
+#define PICT_paintSameRRect	0x49
+#define PICT_eraseSameRRect	0x4A
+#define PICT_invertSameRRect	0x4B
+#define PICT_fillSameRRect	0x4C
+#define PICT_frameOval		0x50
+#define PICT_paintOval		0x51
+#define PICT_eraseOval		0x52
+#define PICT_invertOval		0x53
+#define PICT_fillOval		0x54
+#define PICT_frameSameOval	0x58
+#define PICT_paintSameOval	0x59
+#define PICT_eraseSameOval	0x5A
+#define PICT_invertSameOval	0x5B
+#define PICT_fillSameOval	0x5C
+#define PICT_frameArc		0x60
+#define PICT_paintArc		0x61
+#define PICT_eraseArc		0x62
+#define PICT_invertArc		0x63
+#define PICT_fillArc		0x64
+#define PICT_frameSameArc	0x68
+#define PICT_paintSameArc	0x69
+#define PICT_eraseSameArc	0x6A
+#define PICT_invertSameArc	0x6B
+#define PICT_fillSameArc	0x6C
+#define PICT_framePoly		0x70
+#define PICT_paintPoly		0x71
+#define PICT_erasePoly		0x72
+#define PICT_invertPoly		0x73
+#define PICT_fillPoly		0x74
+#define PICT_frameSamePoly	0x78
+#define PICT_paintSamePoly	0x79
+#define PICT_eraseSamePoly	0x7A
+#define PICT_invertSamePoly	0x7B
+#define PICT_fillSamePoly	0x7C
+#define PICT_frameRgn		0x80
+#define PICT_paintRgn		0x81
+#define PICT_eraseRgn		0x82
+#define PICT_invertRgn		0x83
+#define PICT_fillRgn		0x84
+#define PICT_frameSameRgn	0x88
+#define PICT_paintSameRgn	0x89
+#define PICT_eraseSameRgn	0x8A
+#define PICT_invertSameRgn	0x8B
+#define PICT_fillSameRgn	0x8C
+#define PICT_BitsRect		0x90
+#define PICT_BitsRgn		0x91
+#define PICT_PackBitsRect	0x98
+#define PICT_PackBitsRgn	0x99
+#define PICT_shortComment	0xA0
+#define PICT_longComment	0xA1
+#define PICT_EndOfPicture	0xFF
+#define	PICT_headerOp		0x0C00
+
+static void putFill ARGS(( FILE *fd, int n ));
+static void putShort ARGS(( FILE *fd, int i ));
+static void putLong ARGS(( FILE *fd, long i ));
+static void putFixed ARGS(( FILE *fd, int in, int frac ));
+static void putRect ARGS(( FILE *fd, int x1, int x2, int y1, int y2 ));
+static int putRow ARGS(( FILE *fd, int row, int cols, pixel *rowpixels, char *packed ));
+
+#define MAXCOLORS 256
+static colorhash_table cht;
+
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{
+	FILE *ifp;
+	int argn, rows, cols, colors, i, row, oc;
+	register pixel **pixels;
+	char *packed;
+	pixval maxval;
+	long lmaxval, rval, gval, bval;
+	colorhist_vector chv;
+
+
+	ppm_init( &argc, argv );
+
+	argn = 1;
+	if (argn < argc)
+	{
+		ifp = pm_openr(argv[1]);
+		argn++;
+	}
+	else
+		ifp = stdin;
+	if (argn != argc)
+		pm_usage("[ppmfile]");
+
+	pixels = ppm_readppm(ifp, &cols, &rows, &maxval);
+	if (cols < 8)
+		pm_error("ppm input too narrow, must be >= 8 pixels wide" );
+	lmaxval = (long)maxval;
+	pm_close(ifp);
+
+	/* Figure out the colormap. */
+	pm_message("computing colormap..." );
+	chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors);
+	if (chv == (colorhist_vector) 0)
+		pm_error("too many colors - try doing a 'pnmquant %d'", MAXCOLORS);
+	pm_message("%d colors found", colors );
+
+	/* Make a hash table for fast color lookup. */
+	cht = ppm_colorhisttocolorhash(chv, colors);
+
+	/* write the header */
+	putFill(stdout, HEADER_SIZE);
+
+	/* write picSize and picFrame */
+	putShort(stdout, 0);
+	putRect(stdout, 0, 0, rows, cols);
+
+	/* write version op and version */
+	putShort(stdout, PICT_picVersion);
+	putShort(stdout, 0x02FF);
+	putShort(stdout, PICT_headerOp);
+	putLong(stdout, -1L);
+	putFixed(stdout, 0, 0);
+	putFixed(stdout, 0, 0);
+	putFixed(stdout, cols, 0);
+	putFixed(stdout, rows, 0);
+	putFill(stdout, 4);
+
+	/* seems to be needed by many PICT2 programs */
+	putShort(stdout, PICT_clipRgn);
+	putShort(stdout, 10);
+	putRect(stdout, 0, 0, rows, cols);
+
+	/* write picture */
+	putShort(stdout, PICT_PackBitsRect);
+	putShort(stdout, cols | 0x8000);
+	putRect(stdout, 0, 0, rows, cols);
+	putShort(stdout, 0);	/* pmVersion */
+	putShort(stdout, 0);	/* packType */
+	putLong(stdout, 0L);	/* packSize */
+	putFixed(stdout, 72, 0);	/* hRes */
+	putFixed(stdout, 72, 0);	/* vRes */
+	putShort(stdout, 0);	/* pixelType */
+	putShort(stdout, 8);	/* pixelSize */
+	putShort(stdout, 1);	/* cmpCount */
+	putShort(stdout, 8);	/* cmpSize */
+	putLong(stdout, 0L);	/* planeBytes */
+	putLong(stdout, 0L);	/* pmTable */
+	putLong(stdout, 0L);	/* pmReserved */
+	putLong(stdout, 0L);	/* ctSeed */
+	putShort(stdout, 0);	/* ctFlags */
+	putShort(stdout, colors-1);	/* ctSize */
+
+	/* Write out the colormap. */
+	for (i = 0; i < colors; i++)
+	{
+		putShort(stdout, i);
+		rval = PPM_GETR(chv[i].color);
+		gval = PPM_GETG(chv[i].color);
+		bval = PPM_GETB(chv[i].color);
+		if (lmaxval != 65535L)
+		{
+			rval = rval * 65535L / lmaxval;
+			gval = gval * 65535L / lmaxval;
+			bval = bval * 65535L / lmaxval;
+		}
+		putShort(stdout, (short)rval);
+		putShort(stdout, (short)gval);
+		putShort(stdout, (short)bval);
+	}
+
+	putRect(stdout, 0, 0, rows, cols);	/* srcRect */
+	putRect(stdout, 0, 0, rows, cols);	/* dstRect */
+	putShort(stdout, 0);			/* mode */
+
+	/* Finally, write out the data. */
+	packed = (char*) malloc((unsigned)(cols+cols/MAX_COUNT+1));
+	oc = 0;
+	for (row = 0; row < rows; row++)
+		oc += putRow(stdout, row, cols, pixels[row], packed);
+
+	/* if we wrote an odd number of pixdata bytes, pad */
+	if (oc & 1)
+		(void) putc(0, stdout);
+	putShort(stdout, PICT_EndOfPicture);
+
+	lmaxval = ftell(stdout) - HEADER_SIZE;
+	if (fseek(stdout, (long)HEADER_SIZE, 0) >= 0)
+		putShort(stdout, (short)(lmaxval & 0xffff));
+
+	exit(0);
+}
+
+static void
+putFill(fd, n)
+FILE *fd;
+int n;
+{
+	register int i;
+
+	for (i = 0; i < n; i++)
+		(void) putc(0, fd);
+}
+
+static void
+putShort(fd, i)
+FILE *fd;
+int i;
+{
+	(void) putc((i >> 8) & 0xff, fd);
+	(void) putc(i & 0xff, fd);
+}
+
+#if __STDC__
+static void
+putLong( FILE *fd, long i )
+#else /*__STDC__*/
+static void
+putLong(fd, i)
+FILE *fd;
+long i;
+#endif /*__STDC__*/
+{
+	(void) putc((int)((i >> 24) & 0xff), fd);
+	(void) putc(((int)(i >> 16) & 0xff), fd);
+	(void) putc(((int)(i >> 8) & 0xff), fd);
+	(void) putc((int)(i & 0xff), fd);
+}
+
+static void
+putFixed(fd, in, frac)
+FILE *fd;
+int in, frac;
+{
+	putShort(fd, in);
+	putShort(fd, frac);
+}
+
+static void
+putRect(fd, x1, x2, y1, y2)
+FILE *fd;
+int x1, x2, y1, y2;
+{
+	putShort(fd, x1);
+	putShort(fd, x2);
+	putShort(fd, y1);
+	putShort(fd, y2);
+}
+
+#define	RUNLENGTH
+#ifdef	RUNLENGTH
+
+#define		runtochar(c)	(257-(c))
+#define		counttochar(c)	((c)-1)
+
+static int
+putRow(fd, row, cols, rowpixels, packed)
+FILE *fd;
+int row, cols;
+pixel *rowpixels;
+char *packed;
+{
+	register int i;
+	int packcols, count, run, rep, oc;
+	register pixel *pP;
+	pixel lastp;
+	register char *p;
+
+	run = count = 0;
+	for (cols--, i = cols, pP = rowpixels + cols, p = packed, lastp = *pP;
+		i >= 0; i--, lastp = *pP, pP--)
+	{
+		if (PPM_EQUAL(lastp, *pP))
+			run++;
+		else if (run < RUN_THRESH)
+		{
+			while (run > 0)
+			{
+				*p++ = ppm_lookupcolor(cht, &lastp);
+				run--;
+				count++;
+				if (count == MAX_COUNT)
+				{
+					*p++ = counttochar(MAX_COUNT);
+					count -= MAX_COUNT;
+				}
+			}
+			run = 1;
+		}
+		else
+		{
+			if (count > 0)
+				*p++ = counttochar(count);
+			count = 0;
+			while (run > 0)
+			{
+				rep = run > MAX_RUN ? MAX_RUN : run;
+				*p++ = ppm_lookupcolor(cht, &lastp);
+				*p++ = runtochar(rep);
+				run -= rep;
+			}
+			run = 1;
+		}
+	}
+	if (run < RUN_THRESH)
+	{
+		while (run > 0)
+		{
+			*p++ = ppm_lookupcolor(cht, &lastp);
+			run--;
+			count++;
+			if (count == MAX_COUNT)
+			{
+				*p++ = counttochar(MAX_COUNT);
+				count -= MAX_COUNT;
+			}
+		}
+	}
+	else
+	{
+		if (count > 0)
+			*p++ = counttochar(count);
+		count = 0;
+		while (run > 0)
+		{
+			rep = run > MAX_RUN ? MAX_RUN : run;
+			*p++ = ppm_lookupcolor(cht, &lastp);
+			*p++ = runtochar(rep);
+				run -= rep;
+		}
+		run = 1;
+	}
+	if (count > 0)
+		*p++ = counttochar(count);
+
+	packcols = p - packed;		/* how many did we write? */
+	if (cols > 250)
+	{
+		putShort(fd, packcols);
+		oc = packcols + 2;
+	}
+	else
+	{
+		(void) putc(packcols, fd);
+		oc = packcols + 1;
+	}
+
+	/* now write out the packed row */
+	while(p != packed)
+	{
+		--p;
+		(void) putc(*p, fd);
+	}
+
+	return (oc);
+}
+
+#else	/* RUNLENGTH */
+
+/* real dumb putRow with no compression */
+static int
+putRow(fd, row, cols, rowpixels, packed)
+FILE *fd;
+int row, cols;
+pixel *rowpixels;
+char *packed;
+{
+	register int i, j, bc, oc;
+	register pixel *pP;
+
+#if notdef
+	bzero(aux, cols); /* aux?? */
+#endif /*notdef*/
+	bc = cols + (cols + MAX_COUNT - 1) / MAX_COUNT;
+	if (bc > 250)
+	{
+		putShort(fd, bc);
+		oc = bc + 2;
+	}
+	else
+	{
+		(void) putc(bc, fd);
+		oc = bc + 1;
+	}
+	for (i = 0, pP = rowpixels; i < cols;)
+	{
+		if (cols - i > MAX_COUNT)
+		{
+			(void) putc(MAX_COUNT - 1, fd);
+			for (j = 0; j < MAX_COUNT; j++)
+			{
+				(void) putc(ppm_lookupcolor(cht, pP), fd);
+				pP++;
+			}
+			i += MAX_COUNT;
+		}
+		else
+		{
+			(void) putc(cols - i - 1, fd);
+			for (j = 0; j < cols - i; j++)
+			{
+				(void) putc(ppm_lookupcolor(cht, pP), fd);
+				pP++;
+			}
+			i = cols;
+		}
+	}
+	return (oc);
+}
+#endif	/* RUNLENGTH */
diff --git a/converter/ppm/ppmtopj.c b/converter/ppm/ppmtopj.c
new file mode 100644
index 00000000..5d449f7a
--- /dev/null
+++ b/converter/ppm/ppmtopj.c
@@ -0,0 +1,256 @@
+/* ppmtopj.c - convert a portable pixmap to an HP PainJetXL image
+**
+** Copyright (C) 1990 by Christos Zoulas (christos@ee.cornell.edu)
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <string.h>
+
+#include "nstring.h"
+#include "ppm.h"
+
+static int compress_row ARGS((unsigned char *op, unsigned char *oe, unsigned char *cp));
+/*
+ * XXX: Only 8.5 x 11 paper
+ */
+#define WIDTH	  8.5
+#define HEIGHT	  11.0
+#define DPI	  180
+#define XPIX	  ((int) ((DPI * WIDTH + 7) / 8) << 3)
+#define YPIX	  ((int) ((DPI * HEIGHT + 7) / 8) << 3)
+
+#define C_RESET 			"\033E"
+#define C_RENDER 			"\033*t%dJ"
+# define C_RENDER_NONE			0
+# define C_RENDER_SNAP			1
+# define C_RENDER_BW			2
+# define C_RENDER_DITHER		3
+# define C_RENDER_DIFFUSE		4
+# define C_RENDER_MONODITHER		5
+# define C_RENDER_MONODIFFUSE		6
+# define C_RENDER_MONO_CL_DITHER	5
+# define C_RENDER_MONO_CL_DIFFUSE	6
+#define C_BACK_SCALE			"\033*t%dK"
+# define C_BACK_SCALE_LIGHT		0
+# define C_BACK_SCALE_DARK		1
+#define C_GAMMA				"\033*t%dI"
+#define C_IMAGE_WIDTH			"\033*r%dS"
+#define C_IMAGE_HEIGHT			"\033*r%dT"
+#define C_DATA_PLANES			"\033*r%dU"
+#define C_TRANS_MODE			"\033*b%dM"
+# define C_TRANS_MODE_STD		0
+# define C_TRANS_MODE_RLE		1
+# define C_TRANS_MODE_TIFF		2
+#define C_SEND_PLANE			"\033*b%dV"
+#define C_LAST_PLANE			"\033*b%dW"
+#define C_BEGIN_RASTER			"\033*r%dA"
+# define C_BEGIN_RASTER_MARGIN		0
+# define C_BEGIN_RASTER_ACTIVE		1
+# define C_BEGIN_RASTER_NOSCALE		0
+# define C_BEGIN_RASTER_SCALE		2
+#define C_END_RASTER			"\033*r%dC"
+# define C_END_RASTER_UNUSED		0
+#define C_RESOLUTION			"\033*t%dR"
+# define C_RESOLUTION_90DPI		90
+# define C_RESOLUTION_180DPI		180
+#define C_MOVE_X			"\033*p+%dX"
+#define C_MOVE_Y			"\033*p+%dY"
+
+static const char * const rmode[] = { 
+    "none", "snap", "bw", "dither", "diffuse", 
+    "monodither", "monodiffuse", "clusterdither", 
+    "monoclusterdither", NULL 
+};
+
+/*
+ * Run-length encoding for the PaintJet. We have pairs of <instances>
+ * <value>, where instances goes from 0 (meaning one instance) to 255
+ * If we are unlucky we can double the size of the image.
+ */
+static int
+compress_row(op, oe, cp)
+unsigned char *op, *oe, *cp;
+{
+    unsigned char *ce = cp;
+    while ( op < oe ) {	
+	unsigned char px = *op++;
+	unsigned char *pr = op;
+	while ( op < oe && *op == px && op - pr < 255) op++;
+	*ce++ = op - pr;
+	*ce++ = px;
+    }
+    return ce - cp;
+}
+
+int main(argc, argv)
+int argc;
+char *argv[];
+{
+	pixel **pixels;
+	FILE *ifp;
+	int argn, rows, cols, r, c, k, p;
+	pixval maxval;
+	unsigned char *obuf, *op, *cbuf;
+	int render_mode = C_RENDER_NONE;
+	int back_scale = C_BACK_SCALE_DARK;
+	int gamma = 0;
+	int mode = C_TRANS_MODE_STD;
+	int center = 0;
+	int xoff = 0, yoff = 0;
+	/*
+	 * XXX: Someday we could make this command line options.
+	 */
+	int posscale = C_BEGIN_RASTER_MARGIN | C_BEGIN_RASTER_NOSCALE;
+	int resolution = C_RESOLUTION_180DPI;
+
+	const char * const usage = "[-center] [-xpos <pos>] [-ypos <pos>] [-gamma <val>] [-back <dark|lite>] [-rle] [-render <none|snap|bw|dither|diffuse|monodither|monodiffuse|clusterdither|monoclusterdither>] [ppmfile]";
+
+
+	ppm_init( &argc, argv );
+
+	argn = 1;
+	while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
+	    {
+	    if ( pm_keymatch(argv[argn],"-render",2) && argn + 1 < argc )
+		{
+		++argn;
+		for (r = 0; rmode[r] != NULL; r++)
+		     if (STREQ(rmode[r], argv[argn]))
+			 break;
+		if (rmode[r] != NULL)
+		    render_mode = r;
+		else
+		    pm_usage(usage);
+		}
+	    else if ( pm_keymatch(argv[argn],"-back",2) && argn + 1 < argc )
+		{
+		++argn;
+		if (STREQ(argv[argn], "dark"))
+		    back_scale = C_BACK_SCALE_DARK;
+		else if (STREQ(argv[argn], "lite"))
+		    back_scale = C_BACK_SCALE_LIGHT;
+		else
+		    pm_usage(usage);
+		}
+	    else if ( pm_keymatch(argv[argn],"-gamma",2) && argn + 1 < argc )
+		{
+		++argn;
+		if ( sscanf( argv[argn], "%d",&gamma ) != 1 )
+		    pm_usage( usage );
+		}
+	    else if ( pm_keymatch(argv[argn],"-xpos",2) && argn + 1 < argc )
+		{
+		++argn;
+		if ( sscanf( argv[argn], "%d",&xoff ) != 1 )
+		    pm_usage( usage );
+		}
+	    else if ( pm_keymatch(argv[argn],"-ypos",2) && argn + 1 < argc )
+		{
+		++argn;
+		if ( sscanf( argv[argn], "%d",&yoff ) != 1 )
+		    pm_usage( usage );
+		}
+	    else if (pm_keymatch(argv[argn],"-rle",2))
+		mode = C_TRANS_MODE_RLE;
+	    else if (pm_keymatch(argv[argn],"-center",2))
+		center = 1;
+	    else
+		pm_usage( usage );
+	    ++argn;
+	    }
+
+	if ( argn < argc )
+	    {
+	    ifp = pm_openr( argv[argn] );
+	    ++argn;
+	    }
+	else
+	    ifp = stdin;
+
+	if ( argn != argc )
+	    pm_usage( usage );
+
+	pixels = ppm_readppm( ifp, &cols, &rows, &maxval );
+
+	pm_close( ifp );
+	obuf = (unsigned char *) pm_allocrow(cols, sizeof(unsigned char));
+	cbuf = (unsigned char *) pm_allocrow(cols * 2, sizeof(unsigned char));
+
+        if (cols > XPIX || rows > YPIX)
+	    pm_message("image too large for page");
+        if (center) {
+	    if (xoff || yoff)
+		pm_error("cannot specify both center and position");
+	    xoff = (XPIX - cols) / 2;
+	    yoff = (YPIX - rows) / 2;
+	}
+
+	(void) printf(C_RESET);
+	/*
+	 * Set the resolution before begin raster otherwise it
+	 * does not work.
+	 */
+	(void) printf(C_RESOLUTION, resolution);
+	(void) printf(C_BEGIN_RASTER, posscale);
+	if (xoff)
+	    (void) printf(C_MOVE_X, xoff);
+	if (yoff)
+	    (void) printf(C_MOVE_Y, yoff);
+	(void) printf(C_TRANS_MODE, mode);
+	(void) printf(C_RENDER, render_mode);
+	(void) printf(C_BACK_SCALE, back_scale);
+	(void) printf(C_GAMMA,	 gamma);
+	(void) printf(C_IMAGE_WIDTH, cols);
+	(void) printf(C_IMAGE_HEIGHT, rows);
+	(void) printf(C_DATA_PLANES, 3);
+
+        for (r = 0; r < rows; r++)
+	    /* for each primary */
+	    for (p = 0; p < 3; p++) {
+		switch (p) {
+		case 0:
+		    for (c = 0, op = &obuf[-1]; c < cols; c++) {
+			if ((k = (c & 7)) == 0)
+			    *++op = 0;
+			if (PPM_GETR(pixels[r][c]) > maxval / 2)
+			    *op |= 1 << (7 - k);
+		    }
+		    break;
+		case 1:
+		    for (c = 0, op = &obuf[-1]; c < cols; c++) {
+			if ((k = (c & 7)) == 0)
+			    *++op = 0;
+			if (PPM_GETG(pixels[r][c]) > maxval / 2)
+			    *op |= 1 << (7 - k);
+		    }
+		    break;
+		case 2:
+		    for (c = 0, op = &obuf[-1]; c < cols; c++) {
+			if ((k = (c & 7)) == 0)
+			    *++op = 0;
+			if (PPM_GETB(pixels[r][c]) > maxval / 2)
+			    *op |= 1 << (7 - k);
+		    }
+		    break;
+		}
+		++op;
+		if (mode == C_TRANS_MODE_RLE) {
+		    k = compress_row(obuf, op, cbuf);
+		    op = cbuf;
+		}
+		else {
+		    k = op - obuf;
+		    op = obuf;
+		}
+		(void) printf(p == 2 ? C_LAST_PLANE : C_SEND_PLANE, k);
+		(void) fwrite(op, 1, k, stdout);
+	    }
+	(void) printf(C_END_RASTER, C_END_RASTER_UNUSED);
+	exit(0);
+}
diff --git a/converter/ppm/ppmtopjxl.c b/converter/ppm/ppmtopjxl.c
new file mode 100644
index 00000000..72d299fd
--- /dev/null
+++ b/converter/ppm/ppmtopjxl.c
@@ -0,0 +1,423 @@
+/* ppmtopcl.c - convert portable pixmap into PCL language for HP PaintJet and
+ *              PaintJet XL color printers
+ * AJCD 12/3/91
+ * 
+ * usage:
+ *       ppmtopcl [-nopack] [-gamma <n>] [-presentation] [-dark]
+ *          [-diffuse] [-cluster] [-dither]
+ *          [-xshift <s>] [-yshift <s>]
+ *          [-xshift <s>] [-yshift <s>]
+ *          [-xsize|-width|-xscale <s>] [-ysize|-height|-yscale <s>]
+ *          [ppmfile]
+ *
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "ppm.h"
+#include "nstring.h"
+
+#define MAXCOLORS 1024
+
+const char * const usage="[-nopack] [-gamma <n>] [-presentation] [-dark]\n\
+            [-diffuse] [-cluster] [-dither]\n\
+            [-xshift <s>] [-yshift <s>]\n\
+            [-xshift <s>] [-yshift <s>]\n\
+            [-xsize|-width|-xscale <s>] [-ysize|-height|-yscale <s>]\n\
+            [ppmfile]";
+
+#define PCL_MAXWIDTH 2048
+#define PCL_MAXHEIGHT 32767
+#define PCL_MAXVAL 255
+
+static int nopack = 0;
+static int dark = 0;
+static int diffuse = 0;
+static int dither = 0;
+static int cluster = 0;
+static int xsize = 0;
+static int ysize = 0;
+static int xshift = 0;
+static int yshift = 0;
+static int quality = 0;
+static double xscale = 0.0;
+static double yscale = 0.0;
+static double gamma_val = 0.0;
+
+/* argument types */
+#define DIM 0
+#define REAL 1
+#define BOOL 2
+static const struct options {
+    const char *name;
+    int type;
+    void *value;
+} options[] = {
+   {"-gamma",        REAL, &gamma_val },
+   {"-presentation", BOOL, &quality },
+   {"-width",        DIM,  &xsize },
+   {"-xsize",        DIM,  &xsize },
+   {"-height",       DIM,  &ysize },
+   {"-ysize",        DIM,  &ysize },
+   {"-xscale",       REAL, &xscale },
+   {"-yscale",       REAL, &yscale },
+   {"-xshift",       DIM,  &xshift },
+   {"-yshift",       DIM,  &yshift },
+   {"-dark",         BOOL, &dark },
+   {"-diffuse",      BOOL, &diffuse },
+   {"-dither",       BOOL, &dither },
+   {"-cluster",      BOOL, &cluster },
+   {"-nopack",       BOOL, &nopack },
+};
+
+#define putword(w) (putchar(((w)>>8) & 255), putchar((w) & 255))
+
+static int 
+bitsperpixel(int v) {
+   int bpp = 0;
+   while (v > 0) {  /* calculate # bits for value */
+      ++bpp;
+      v>>=1;
+   }
+   return (bpp);
+}
+
+
+
+static char *inrow = NULL;
+static char *outrow = NULL;
+/* "signed" was commented out below, but it caused warnings on an SGI 
+   compiler, which defaulted to unsigned character.  2001.03.30 BJH */
+static signed char *runcnt = NULL;
+
+static void 
+putbits(b, n) {
+    /* put #n bits in b out, packing into bytes; n=0 flushes bits */
+    /* n should never be > 8 */
+
+   static int out = 0;
+   static int cnt = 0;
+   static int num = 0;
+   static int pack = 0;
+   if (n) {
+      int xo = 0;
+      int xc = 0;
+      if (cnt+n > 8) {  /* overflowing current byte? */
+	 xc = cnt + n - 8;
+	 xo = (b & ~(-1 << xc)) << (8-xc);
+	 n -= xc;
+	 b >>= xc;
+      }
+      cnt += n;
+      out |= (b & ~(-1 << n)) << (8-cnt);
+      if (cnt >= 8) {
+	 inrow[num++] = out;
+	 out = xo;
+	 cnt = xc;
+      }
+   } else { /* flush row */
+      int i;
+      if (cnt) {
+	 inrow[num++] = out;
+	 out = cnt = 0;
+      }
+      for (; num > 0 && inrow[num-1] == 0; num--); /* remove trailing zeros */
+      printf("\033*b"); 
+      if (num && !nopack) {            /* TIFF 4.0 packbits encoding */
+	 int start = 0;
+	 int next;
+	 runcnt[start] = 0;
+	 for (i = 1; i < num; i++) {
+	    if (inrow[i] == inrow[i-1]) {
+	       if (runcnt[start] <= 0 && runcnt[start] > -127)
+		  runcnt[start]--;
+	       else
+		  runcnt[start = i] = 0;
+	    } else {
+	       if (runcnt[start] >= 0 && runcnt[start] < 127)
+		  runcnt[start]++;
+	       else
+		  runcnt[start = i] = 0;
+	    }
+	 }
+	 start = 0;
+	 for (i = 0; i < num; i = next) {
+	    int count = runcnt[i];
+	    int from = i;
+	    if (count >= 0) { /* merge two-byte runs */
+	       for (;;) {
+		  next = i+1+runcnt[i];
+		  if(next >= num || runcnt[next] < 0 ||
+		     count+runcnt[next]+1 > 127)
+		     break;
+		  count += runcnt[next]+1;
+		  i = next;
+	       }
+	    }
+	    next =  i + 1 + ((runcnt[i] < 0) ? -runcnt[i] : runcnt[i]);
+	    if (next < num && count > 0 &&
+		runcnt[next] < 0 && runcnt[next] > -127) {
+	       count--;
+	       next--;
+	       runcnt[next] = runcnt[next+1]-1;
+	    }
+	    outrow[start++] = count;
+	    if (count >= 0) {
+	       while (count-- >= 0)
+		  outrow[start++] = inrow[from++];
+	    } else
+	       outrow[start++] = inrow[from];
+	 }
+	 if (start < num) {
+	    num = start;
+	    if (!pack) {
+	       printf("2m");
+	       pack = 1;
+	    }
+	 } else {
+	    if (pack) {
+	       printf("0m");
+	       pack = 0;
+	    }
+	 }
+      }
+      printf("%dW", num);
+      for (i = 0; i < num; i++)
+	 putchar(pack ? outrow[i] : inrow[i]);
+      num = 0; /* new row */
+   }
+}
+
+int
+main(argc, argv)
+     int argc;
+     char *argv[];
+{
+   FILE *ifd;
+   register pixel **pixels, *pixrow;
+   register int  row, col, bpp, i;
+   int rows, cols;
+   pixval maxval;
+   int bpr, bpg, bpb;
+   int render;
+   int colors, pclindex;
+   colorhist_vector chv;
+   colorhash_table cht;
+   
+   ppm_init( &argc, argv );
+
+   while (argc > 1 && argv[1][0] == '-') {
+      char *c;
+      for (i = 0; i < sizeof(options)/sizeof(struct options); i++) {
+	 if (pm_keymatch(argv[1], options[i].name,
+			 MIN(strlen(argv[1]), strlen(options[i].name)))) {
+	    switch (options[i].type) {
+	    case DIM:
+	       if (++argv, --argc == 1)
+		  pm_usage(usage);
+	       for (c = argv[1]; ISDIGIT(*c); c++);
+	       if (c[0] == 'p' && c[1] == 't') /* points */
+		  *(int *)(options[i].value) = atoi(argv[1])*10;
+	       else if (c[0] == 'd' && c[1] == 'p') /* decipoints */
+		  *(int *)(options[i].value) = atoi(argv[1]);
+	       else if (c[0] == 'i' && c[1] == 'n') /* inches */
+		  *(int *)(options[i].value) = atoi(argv[1])*720;
+	       else if (c[0] == 'c' && c[1] == 'm') /* centimetres */
+		  *(int *)(options[i].value) = atoi(argv[1])*283.46457;
+	       else if (!c[0]) /* dots */
+		  *(int *)(options[i].value) = atoi(argv[1])*4;
+	       else
+		  pm_error("illegal unit of measure %s", c);
+	       break;
+	    case REAL:
+	       if (++argv, --argc == 1)
+		  pm_usage(usage);
+	       *(double *)(options[i].value) = atof(argv[1]);
+	       break;
+	    case BOOL:
+	       *(int *)(options[i].value) = 1;
+	       break;
+	    }
+	    break;
+	 }
+      }
+      if (i >= sizeof(options)/sizeof(struct options))
+	 pm_usage(usage);
+      argv++; argc--;
+   }
+   if (argc > 2)
+      pm_usage(usage);
+   else if (argc == 2)
+      ifd = pm_openr(argv[1]);
+   else
+      ifd = stdin ;
+
+   /* validate arguments */
+   if (diffuse+cluster+dither > 1)
+      pm_error("only one of -diffuse, -dither and -cluster may be used");
+   render = diffuse ? 4 : dither ? 3 : cluster ? 7 : 0;
+
+   if (xsize != 0.0 && xscale != 0.0)
+      pm_error("only one of -xsize and -xscale may be used");
+
+   if (ysize != 0.0 && yscale != 0.0)
+      pm_error("only one of -ysize and -yscale may be used");
+
+   pixels = ppm_readppm( ifd, &cols, &rows, &maxval );
+   pm_close( ifd );
+
+   /* limit checks */
+   if (cols > PCL_MAXWIDTH || rows > PCL_MAXHEIGHT)
+      pm_error("image too large; reduce with ppmscale");
+   if (maxval > PCL_MAXVAL)
+      pm_error("color range too large; reduce with ppmcscale");
+
+   /* Figure out the colormap. */
+   fprintf( stderr, "(Computing colormap..." ); fflush( stderr );
+   chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &colors );
+   if ( chv == (colorhist_vector) 0 )
+      pm_error("too many colors; reduce with pnmquant");
+   fprintf( stderr, "  Done.  %d colors found.)\n", colors );
+
+   /* And make a hash table for fast lookup. */
+   cht = ppm_colorhisttocolorhash( chv, colors );
+
+   /* work out color downloading mode */
+   pclindex = bitsperpixel(colors);
+   if (pclindex > 8) /* can't use indexed mode */
+      pclindex = 0;
+   else
+      switch (pclindex) { /* round up to 1,2,4,8 */
+      case 0: /* direct mode (no palette) */
+	 bpp = bitsperpixel(maxval); /* bits per pixel */
+	 bpg = bpp; bpb = bpp;
+	 bpp = (bpp*3+7)>>3;     /* bytes per pixel now */
+	 bpr = (bpp<<3)-bpg-bpb; 
+	 bpp *= cols;            /* bytes per row now */
+	 break;
+      case 5:         pclindex++;
+      case 6:         pclindex++;
+      case 3: case 7: pclindex++;
+      default:
+	 bpp = 8/pclindex;
+	 bpp = (cols+bpp-1)/bpp;      /* bytes per row */
+      }
+
+   if ((inrow = (char *)malloc((unsigned)bpp)) == NULL ||
+       (outrow = (char *)malloc((unsigned)bpp*2)) == NULL ||
+       (runcnt = (signed char *)malloc((unsigned)bpp)) == NULL)
+      pm_error("can't allocate space for row");
+
+   /* set up image details */
+   if (xscale != 0.0)
+      xsize = cols * xscale * 4;
+   if (yscale != 0.0)
+      ysize = rows * yscale * 4;
+
+#ifdef DEBUG
+   fprintf(stderr, "dark    =%d\n", dark);
+   fprintf(stderr, "diffuse =%d\n", diffuse);
+   fprintf(stderr, "dither  =%d\n", dither);
+   fprintf(stderr, "cluster =%d\n", cluster);
+   fprintf(stderr, "quality =%d\n", quality);
+   fprintf(stderr, "xsize   =%d\n", xsize);
+   fprintf(stderr, "ysize   =%d\n", ysize);
+   fprintf(stderr, "xshift  =%d\n", xshift);
+   fprintf(stderr, "yshift  =%d\n", yshift);
+   fprintf(stderr, "xscale  =%lf\n", xscale);
+   fprintf(stderr, "yscale  =%lf\n", yscale);
+   fprintf(stderr, "gamma   =%lf\n", gamma_val);
+   fprintf(stderr, "pclindex   =%d\n", pclindex);
+   fprintf(stderr, "nopack  =%d\n", nopack);
+#endif
+
+   /* write PCL header */
+#if 0
+   printf("\033&l26A");                         /* paper size */
+#endif
+   printf("\033*r%ds%dT", cols, rows);          /* source width, height */
+   if (xshift != 0 || yshift != 0)
+      printf("\033&a%+dh%+dV", xshift, yshift); /* xshift, yshift */
+   if (quality)
+      printf("\033*o%dQ", quality);             /* print quality */
+   printf("\033*t");
+   if (xsize == 0 && ysize == 0)
+      printf("180r");                   /* resolution */
+   else {                               /* destination width, height */
+      if (xsize != 0)
+	 printf("%dh", xsize);
+      if (ysize != 0)
+	 printf("%dv", ysize);
+   }
+   if (gamma_val != 0)
+      printf("%.3fi", gamma_val);                    /* gamma correction */
+   if (dark)
+      printf("%dk", dark);              /* scaling algorithms */
+   printf("%dJ", render);               /* rendering algorithm */
+   printf("\033*v18W");                           /* configure image data */
+      putchar(0); /* relative colors */
+      putchar(pclindex ? 1 : 3); /* index/direct pixel mode */
+      putchar(pclindex); /* ignored in direct pixel mode */
+      if (pclindex) {
+	 putchar(0);
+	 putchar(0);
+	 putchar(0);
+      } else {
+	 putchar(bpr); /* bits per red */
+	 putchar(bpg); /* bits per green */
+	 putchar(bpb); /* bits per blue */
+      }
+      putword(maxval); /* max red reference */
+      putword(maxval); /* max green reference */
+      putword(maxval); /* max blue reference */
+      putword(0); /* min red reference */
+      putword(0); /* min green reference */
+      putword(0); /* min blue reference */
+   if (pclindex) {                        /* set palette */
+      for (i = 0; i < colors; i++) {
+	 int r, g, b;
+	 r = PPM_GETR( chv[i].color );
+	 g = PPM_GETG( chv[i].color );
+	 b = PPM_GETB( chv[i].color );
+	 if (i == 0)
+	    printf("\033*v");
+	 if (r)
+	    printf("%da", r);
+	 if (g)
+	    printf("%db", g);
+	 if (b)
+	    printf("%dc", b);
+	 if (i == colors-1)
+	    printf("%dI", i);    /* assign color index */
+	 else
+	    printf("%di", i);    /* assign color index */
+      }
+   }
+   ppm_freecolorhist( chv );
+
+   /* start raster graphics at CAP */
+   printf("\033*r%dA", (xsize != 0 || ysize != 0) ? 3 : 1);
+
+   for (row = 0; row < rows; row++) {
+      if (pclindex) { /* indexed color mode */
+	 int out, cnt;
+	 out = cnt = 0;
+	 for (col = 0, pixrow=pixels[row]; col < cols; col++, pixrow++) {
+	    putbits(ppm_lookupcolor( cht, pixrow ), pclindex);
+	 }
+	 putbits(0, 0); /* flush row */
+      } else { /* direct color mode */
+	 for (col = 0, pixrow=pixels[row]; col < cols; col++, pixrow++) {
+	    putbits(PPM_GETR( *pixrow ), bpr);
+	    putbits(PPM_GETG( *pixrow ), bpg);
+	    putbits(PPM_GETB( *pixrow ), bpb); /* don't need to flush */
+	 }
+	 putbits(0, 0); /* flush row */
+      }
+   }
+   printf("\033*rC"); /* end raster graphics */
+   exit(0);
+}
diff --git a/converter/ppm/ppmtoppm.c b/converter/ppm/ppmtoppm.c
new file mode 100644
index 00000000..500c9856
--- /dev/null
+++ b/converter/ppm/ppmtoppm.c
@@ -0,0 +1,44 @@
+/*----------------------------------------------------------------------------
+                               ppmtoppm
+------------------------------------------------------------------------------
+  Part of the Netpbm package.
+
+  Copy PPM image from Standard Input to Standard Output
+
+
+  By Bryan Henderson, San Jose CA 2002.09.07
+
+  Contributed to the public domain by its author 2002.09.07
+-----------------------------------------------------------------------------*/
+
+#include "ppm.h"
+
+int
+main(int argc, char *argv[]) {
+    int format;
+    int rows, cols;
+    pixval maxval;
+    int row;
+    pixel* pixelrow;
+    
+    ppm_init(&argc, argv);
+
+    if (argc-1 != 0)
+        pm_error("Program takes no arguments.  Input is from Standard Input");
+
+    ppm_readppminit(stdin, &cols, &rows, &maxval, &format);
+
+    ppm_writeppminit(stdout, cols, rows, maxval, 0);
+
+    pixelrow = ppm_allocrow(cols);
+
+    for (row = 0; row < rows; row++) {
+        ppm_readppmrow(stdin, pixelrow, cols, maxval, format);
+        ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0);
+    }
+    ppm_freerow(pixelrow);
+
+    pm_close(stdin);
+
+    exit(0);
+}
diff --git a/converter/ppm/ppmtopuzz.c b/converter/ppm/ppmtopuzz.c
new file mode 100644
index 00000000..1277cc20
--- /dev/null
+++ b/converter/ppm/ppmtopuzz.c
@@ -0,0 +1,96 @@
+/* ppmtopuzz.c - read a portable pixmap and write an X11 "puzzle" file
+**
+** Copyright (C) 1991 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "ppm.h"
+
+#define MAXVAL 255
+#define MAXCOLORS 256
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    pixel** pixels;
+    register pixel* pP;
+    colorhist_vector chv;
+    colorhash_table cht;
+    int rows, cols, row, colors, i;
+    register int col;
+    pixval maxval;
+
+
+    ppm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[ppmfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    pixels = ppm_readppm( ifp, &cols, &rows, &maxval );
+    pm_close( ifp );
+
+    pm_message( "computing colormap..." );
+    chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &colors );
+    if ( chv == (colorhist_vector) 0 )
+	{
+	pm_message(
+	    "too many colors - try doing a 'pnmquant %d'", MAXCOLORS );
+	exit( 1 );
+	}
+    pm_message( "%d colors found", colors );
+
+    /* Write puzzle header. */
+    (void) pm_writebiglong( stdout, cols );
+    (void) pm_writebiglong( stdout, rows );
+    (void) putchar( (unsigned char) colors );
+    if ( maxval > MAXVAL )
+	pm_message(
+	    "maxval is not %d - automatically rescaling colors", MAXVAL );
+    for ( i = 0; i < colors; ++i )
+	{
+	pixel p;
+
+	p = chv[i].color;
+	if ( maxval != MAXVAL )
+	    PPM_DEPTH( p, p, maxval, MAXVAL );
+	(void) putchar( (unsigned char) PPM_GETR( p ) );
+	(void) putchar( (unsigned char) PPM_GETG( p ) );
+	(void) putchar( (unsigned char) PPM_GETB( p ) );
+	}
+
+    /* Convert color vector to color hash table, for fast lookup. */
+    cht = ppm_colorhisttocolorhash( chv, colors );
+    ppm_freecolorhist( chv );
+
+    /* And write out the data. */
+    for ( row = 0; row < rows; ++row )
+	{
+	for ( col = 0, pP = pixels[row]; col < cols; ++col, ++pP )
+	    {
+	    register int color;
+
+	    color = ppm_lookupcolor( cht, pP );
+	    if ( color == -1 )
+		pm_error(
+		    "color not found?!?  row=%d col=%d  r=%d g=%d b=%d",
+		    row, col, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP) );
+	    (void) putchar( (unsigned char) color );
+	    }
+	}
+
+    exit( 0 );
+    }
diff --git a/converter/ppm/ppmtorgb3.c b/converter/ppm/ppmtorgb3.c
new file mode 100644
index 00000000..21d3346a
--- /dev/null
+++ b/converter/ppm/ppmtorgb3.c
@@ -0,0 +1,99 @@
+/* ppmtorgb3.c - separate a portable pixmap into three portable graymaps
+**
+** Copyright (C) 1991 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <string.h>
+#include "ppm.h"
+#include "pgm.h"
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    FILE* redfile;
+    FILE* grnfile;
+    FILE* blufile;
+    const char* basename;
+    char filename[100];
+    char* cp;
+    pixel* pixelrow;
+    register pixel* pP;
+    gray* grayrow;
+    register gray* gP;
+    int rows, cols, format, row;
+    register int col;
+    pixval maxval;
+
+
+    ppm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[ppmfile]" );
+
+    if ( argc == 2 )
+	{
+	ifp = pm_openr( argv[1] );
+	basename = argv[1];
+	cp = strrchr( basename, '.' );
+	if ( cp != NULL )
+	    *cp = '\0';
+	}
+    else
+	{
+	ifp = stdin;
+	basename = "noname";
+	}
+
+    ppm_readppminit( ifp, &cols, &rows, &maxval, &format );
+    pixelrow = ppm_allocrow( cols );
+    (void) strcpy( filename, basename );
+    (void) strcat( filename, ".red" );
+    redfile = pm_openw( filename );
+    pgm_writepgminit( redfile, cols, rows, (gray) maxval, 0 );
+    (void) strcpy( filename, basename );
+    (void) strcat( filename, ".grn" );
+    grnfile = pm_openw( filename );
+    pgm_writepgminit( grnfile, cols, rows, (gray) maxval, 0 );
+    (void) strcpy( filename, basename );
+    (void) strcat( filename, ".blu" );
+    blufile = pm_openw( filename );
+    pgm_writepgminit( blufile, cols, rows, (gray) maxval, 0 );
+    grayrow = pgm_allocrow( cols );
+
+    for ( row = 0; row < rows; ++row )
+	{
+	ppm_readppmrow( ifp, pixelrow, cols, maxval, format );
+
+	for ( col = 0, pP = pixelrow, gP = grayrow; col < cols;
+	      ++col, ++pP, ++gP )
+	    *gP = (gray) PPM_GETR( *pP );
+	pgm_writepgmrow( redfile, grayrow, cols, maxval, 0 );
+
+	for ( col = 0, pP = pixelrow, gP = grayrow; col < cols;
+	      ++col, ++pP, ++gP )
+	    *gP = (gray) PPM_GETG( *pP );
+	pgm_writepgmrow( grnfile, grayrow, cols, maxval, 0 );
+
+	for ( col = 0, pP = pixelrow, gP = grayrow; col < cols;
+	      ++col, ++pP, ++gP )
+	    *gP = (gray) PPM_GETB( *pP );
+	pgm_writepgmrow( blufile, grayrow, cols, maxval, 0 );
+	}
+
+    pm_close( ifp );
+    pm_close( redfile );
+    pm_close( blufile );
+    pm_close( grnfile );
+
+    exit( 0 );
+    }
diff --git a/converter/ppm/ppmtosixel.c b/converter/ppm/ppmtosixel.c
new file mode 100644
index 00000000..5f361e45
--- /dev/null
+++ b/converter/ppm/ppmtosixel.c
@@ -0,0 +1,215 @@
+/* ppmtosix.c - read a portable pixmap and produce a color sixel file
+**
+** Copyright (C) 1991 by Rick Vinci.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "ppm.h"
+
+static void WriteHeader ARGS((void));
+static void WriteColorMap 
+  ARGS((colorhist_vector chv, int colors, pixval maxval));
+static void WriteRawImage ARGS((colorhash_table cht, int rows, int cols));
+static void WritePackedImage ARGS((colorhash_table cht, int rows, int cols));
+static void WriteEnd ARGS((void));
+#define MAXVAL 100
+#define MAXCOLORS 256
+
+#define DCS '\220'   /* Device Control String */
+#define ST  '\234'   /* String Terminator */
+#define CSI '\233'   /* Control String Introducer */
+#define ESC '\033'   /* Escape character */
+
+static pixel** pixels;   /* stored ppm pixmap input */
+static colorhash_table cht;
+int margin;
+
+
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+{
+    FILE* ifp;
+    int argn, rows, cols, colors;
+    int raw;
+    pixval maxval;
+    colorhist_vector chv;
+    const char* const usage = "[-raw] [-margin] [ppmfile]";
+
+
+    ppm_init( &argc, argv );
+
+    argn = 1;
+    raw = 0;
+    margin = 0;
+
+    /* Parse args. */
+    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
+	{
+	if ( pm_keymatch( argv[argn], "-raw", 2 ) )
+	    raw = 1;
+	else if ( pm_keymatch( argv[argn], "-margin", 2 ) )
+	    margin = 1;
+	else
+	    pm_usage( usage );
+	++argn;
+	}
+
+    if ( argn < argc )
+	{
+	ifp = pm_openr( argv[argn] );
+	++argn;
+	}
+    else
+	ifp = stdin;
+
+    if ( argn != argc )
+	pm_usage( usage );
+
+    /* Read in the whole ppmfile. */
+    pixels = ppm_readppm( ifp, &cols, &rows, &maxval );
+    pm_close( ifp );
+
+    /* Print a warning if we could lose accuracy when rescaling colors. */
+    if ( maxval > MAXVAL )
+	pm_message(
+	    "maxval is not %d - automatically rescaling colors", MAXVAL );
+
+    /* Figure out the colormap. */
+    pm_message( "computing colormap..." );
+    chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &colors );
+    if ( chv == (colorhist_vector) 0 )
+	pm_error( "too many colors - try doing a 'pnmquant %d'", MAXCOLORS );
+    pm_message( "%d colors found", colors );
+
+    /* Make a hash table for fast color lookup. */
+    cht = ppm_colorhisttocolorhash( chv, colors );
+
+    WriteHeader();
+    WriteColorMap( chv, colors, maxval );
+    if ( raw == 1 )
+	WriteRawImage( cht, rows, cols );
+    else
+	WritePackedImage( cht, rows, cols );
+    WriteEnd();
+
+    /* If the program failed, it previously aborted with nonzero completion
+       code, via various function calls.
+    */
+    return 0;
+}
+
+
+
+static void
+WriteHeader()
+    {
+    if ( margin == 1 )
+	printf( "%c%d;%ds", CSI, 14, 72 );
+    printf( "%c", DCS );  /* start with Device Control String */
+    printf( "0;0;8q" );   /* Horizontal Grid Size at 1/90" and graphics On */
+    printf( "\"1;1\n" );  /* set aspect ratio 1:1 */
+    }
+
+
+
+static void
+WriteColorMap( colorhist_vector chv, int colors, pixval maxval )
+    {
+    register int colornum;
+    pixel p;
+
+    for ( colornum = 0; colornum < colors ; ++colornum )
+	{
+	p = chv[colornum].color;
+	if ( maxval != MAXVAL )
+	    PPM_DEPTH( p, p, maxval, MAXVAL );
+	printf( "#%d;2;%d;%d;%d", colornum,
+	    (int) PPM_GETR( p ), (int) PPM_GETG( p ), (int) PPM_GETB( p ) );
+	}
+    printf( "\n" );
+    }
+
+
+
+static void
+WriteRawImage( cht, rows, cols )
+    colorhash_table cht;
+    int rows, cols;
+    {
+    int rownum, colnum, b;
+    const char* sixel = "@ACGO_";
+    register pixel* pP;
+
+    for ( rownum = 0; rownum < rows; ++rownum )
+	{
+	b = rownum % 6;
+	for ( colnum = 0, pP = pixels[rownum]; colnum < cols; ++colnum, ++pP )
+	    printf( "#%d%c", ppm_lookupcolor(cht, pP), sixel[b] );
+	printf( "$\n" );   /* Carriage Return */
+	if ( b == 5 )
+	    printf( "-\n" );   /* Line Feed (one sixel height) */
+	}
+    }
+
+
+
+static void
+WritePackedImage( cht, rows, cols )
+    colorhash_table cht;
+    int rows, cols;
+    {
+    int rownum, colnum, b, repeat, thiscolor, nextcolor;
+    const char* sixel = "@ACGO_";
+    register pixel* pP;
+
+    for ( rownum = 0; rownum < rows; ++rownum )
+	{
+	b = rownum % 6;
+	repeat = 1;
+	for ( colnum = 0, pP = pixels[rownum]; colnum < cols; ++colnum, ++pP )
+	    {
+	    thiscolor = ppm_lookupcolor(cht, pP);
+	    if ( colnum == cols -1 )   /* last pixel in row */
+		if ( repeat == 1 )
+		    printf( "#%d%c", thiscolor, sixel[b] );
+		else
+		    printf( "#%d!%d%c", thiscolor, repeat, sixel[b] );
+	    else   /* not last pixel in row */
+		{
+		nextcolor =  ppm_lookupcolor(cht, pP+1);
+		if ( thiscolor == nextcolor )
+		    ++repeat;
+		else
+		    if ( repeat == 1 )
+			printf( "#%d%c", thiscolor, sixel[b] );
+		    else
+		    {
+		    printf( "#%d!%d%c", thiscolor, repeat, sixel[b] );
+		    repeat = 1;
+		    }
+		}
+	    }   /* end column loop */
+	printf( "$\n" );   /* Carriage Return */
+	if ( b == 5 )
+	    printf( "-\n" );   /* Line Feed (one sixel height) */
+	}
+    }
+
+
+
+static void
+WriteEnd()
+    {
+    if ( margin == 1 )
+	printf ( "%c%d;%ds", CSI, 1, 80 );
+    printf( "%c\n", ST );
+    }
diff --git a/converter/ppm/ppmtoterm.c b/converter/ppm/ppmtoterm.c
new file mode 100644
index 00000000..72105705
--- /dev/null
+++ b/converter/ppm/ppmtoterm.c
@@ -0,0 +1,173 @@
+/* ppmtoterm.c - convert a portable pixmap into an ISO 6429 (ANSI) color ascii
+** text.
+**
+**  Copyright (C) 2002 by Ero Carrera (ecarrera@lightforge.com)
+**  Partially based on,
+**      ppmtoyuv.c by Marc Boucher,
+**      ppmtolj.c by Jonathan Melvin and
+**      ppmtogif.c by Jef Poskanzer
+**
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+**
+** 14/Aug/2002: First version.
+**
+*/
+
+#include <string.h>
+
+#include "ppm.h"
+#include "shhopt.h"
+
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *inputFilespec;  /* Filespec of input file */
+    unsigned int verbose;
+};
+
+
+
+static void
+parseCommandLine(int argc, char **argv,
+                 struct cmdlineInfo *cmdlineP) {
+    optEntry *option_def = malloc(100*sizeof(optEntry));
+        /* Instructions to OptParseOptions3 on how to parse our options */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    switch (argc-1) {
+    case 0:
+        cmdlineP->inputFilespec = "-";
+        break;
+    case 1:
+        cmdlineP->inputFilespec = argv[1];
+        break;
+    case 2:
+        break;
+    }
+}
+
+
+#define ESC         "\x1B\x5B"
+#define NUM_COLORS      128
+#define MAX_ANSI_STR_LEN    16
+
+
+static int __inline__ sqr(const int x) {
+    return x*x;
+}
+
+/*
+    Generates some sort of color palette mixing the available
+    colors as different values of background, foreground & brightness.
+*/
+static int 
+generate_palette(unsigned char rgb[NUM_COLORS][3], 
+                 char ansi_code[NUM_COLORS][MAX_ANSI_STR_LEN]) {
+    int code, col=0, cd2=0;
+    
+    memset((void *)rgb, 0, NUM_COLORS*3);
+    memset((void *)ansi_code, 0, NUM_COLORS*MAX_ANSI_STR_LEN);
+
+    for(col=cd2=0; cd2<8; cd2++) {
+        unsigned int b;
+        for(b=0;b<2;b++) {
+            for(code=0; code<8; code++) {
+                unsigned int c;
+                for(c=0;c<3;c++) {
+                    if(code&(1<<c)) {
+                        rgb[col][c]=(192|(b?63:0));
+                    }
+                    if(cd2&(1<<c)) {
+                        rgb[col][c]|=(128);
+                    }
+                }
+                sprintf(ansi_code[col],
+                        ESC"%dm"ESC"3%dm"ESC"4%dm",
+                        b, code, cd2);
+                col++;
+            }
+        }
+    }
+    return col;
+}
+
+
+
+int main(int argc, char **argv)
+{
+    FILE            *ifp;
+    pixel           **pixels;
+    int             rows, row, cols, col,
+                    pal_len, i;
+    pixval          maxval;
+    struct cmdlineInfo
+                    cmdline;
+    unsigned char   rgb[NUM_COLORS][3];
+    char            ansi_code[NUM_COLORS][MAX_ANSI_STR_LEN];
+
+    
+    ppm_init(&argc, argv);    
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifp = pm_openr(cmdline.inputFilespec);
+    
+    pixels = ppm_readppm(ifp, &cols, &rows, &maxval);
+
+    pm_close(ifp);
+        
+    pal_len=generate_palette(rgb, ansi_code);
+    
+    for (row = 0; row < rows; ++row) {
+        for (col = 0; col < cols; col++) {
+            pixval const r=(int)PPM_GETR(pixels[row][col])*255/maxval;
+            pixval const g=(int)PPM_GETG(pixels[row][col])*255/maxval;
+            pixval const b=(int)PPM_GETB(pixels[row][col])*255/maxval;
+            int val, dist;
+            
+            /*
+            The following loop calculates the index that
+            corresponds to the minimum color distance
+            between the given RGB values and the values
+            available in the palette.
+            */
+            for(i=0, dist=sqr(255)*3, val=0; i<pal_len; i++) {
+                pixval const pr=rgb[i][0];
+                pixval const pg=rgb[i][1];
+                pixval const pb=rgb[i][2];
+                unsigned int j;
+                if( (j=sqr(r-pr)+sqr(b-pb)+sqr(g-pg))<dist ) {
+                    dist=j;
+                    val=i;
+                }
+            }
+            printf("%s%c", ansi_code[val],0xB1);
+        }
+        printf(ESC"\x30m\n");
+    }
+    printf(ESC"\x30m");
+
+    ppm_freearray(pixels, rows);
+    
+    exit(0);
+}
diff --git a/converter/ppm/ppmtowinicon.c b/converter/ppm/ppmtowinicon.c
new file mode 100644
index 00000000..bcaa0e08
--- /dev/null
+++ b/converter/ppm/ppmtowinicon.c
@@ -0,0 +1,881 @@
+/* ppmtowinicon.c - read portable pixmap file(s) and write a MS Windows .ico
+**
+** Copyright (C) 2000 by Lee Benfield - lee@benf.org
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <math.h>
+#include <string.h>
+
+#include "winico.h"
+#include "ppm.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "nstring.h"
+
+#define MAJVERSION 0
+#define MINVERSION 3
+
+#define MAXCOLORS 256
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    unsigned int iconCount;
+    const char **inputFilespec;  /* '-' if stdin; malloc'ed array */
+    const char **andpgmFilespec;    /* NULL if unspecified; malloc'ed array */
+    const char *output;     /* '-' if unspecified */
+    unsigned int truetransparent;
+    unsigned int verbose;
+};
+
+
+static bool verbose;
+
+static int      file_offset = 0; /* not actually used, but useful for debug. */
+static FILE *   ofp;
+
+static void
+parseCommandLine(int                 argc, 
+                 char **             argv,
+                 struct cmdlineInfo *cmdlineP ) {
+/*----------------------------------------------------------------------------
+   Parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int outputSpec, andpgms;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "output",     OPT_STRING, &cmdlineP->output,
+            &outputSpec,                   0);
+    OPTENT3(0, "andpgms",    OPT_FLAG,   NULL,
+            &andpgms,                      0);
+    OPTENT3(0, "truetransparent", OPT_FLAG,   NULL,
+            &cmdlineP->truetransparent,    0);
+    OPTENT3(0, "verbose",    OPT_STRING, NULL,
+            &cmdlineP->verbose,            0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!outputSpec)
+        cmdlineP->output = "-";
+
+
+    if (!andpgms) {
+        if (argc-1 == 0) {
+            cmdlineP->iconCount = 1;
+            MALLOCARRAY_NOFAIL(cmdlineP->inputFilespec, cmdlineP->iconCount);
+            cmdlineP->inputFilespec[0] = "-";
+        } else {
+            unsigned int iconIndex;
+
+            cmdlineP->iconCount = argc-1;
+            MALLOCARRAY_NOFAIL(cmdlineP->inputFilespec, cmdlineP->iconCount);
+            for (iconIndex = 0; iconIndex < cmdlineP->iconCount; ++iconIndex)
+                cmdlineP->inputFilespec[iconIndex] = argv[iconIndex+1];
+        }
+        {
+            unsigned int iconIndex;
+            MALLOCARRAY_NOFAIL(cmdlineP->andpgmFilespec, cmdlineP->iconCount);
+            for (iconIndex = 0; iconIndex < cmdlineP->iconCount; ++iconIndex)
+                cmdlineP->andpgmFilespec[iconIndex] = NULL;
+        }
+    } else {
+        if (argc-1 < 2)
+            pm_error("with -andpgms, you must specify at least two "
+                     "arguments: image file name and and mask file name.  "
+                     "You specified %d", argc-1);
+        else if ((argc-1)/2*2 != (argc-1))
+            pm_error("with -andpgms, you must specify an even number of "
+                     "arguments.  You specified %d", argc-1);
+        else {
+            unsigned int iconIndex;
+            cmdlineP->iconCount = (argc-1)/2;
+            MALLOCARRAY_NOFAIL(cmdlineP->inputFilespec, cmdlineP->iconCount);
+            MALLOCARRAY_NOFAIL(cmdlineP->andpgmFilespec, cmdlineP->iconCount);
+            for (iconIndex = 0; iconIndex < cmdlineP->iconCount; ++iconIndex) {
+                cmdlineP->inputFilespec[iconIndex] = argv[1 + iconIndex*2];
+                cmdlineP->andpgmFilespec[iconIndex] = argv[2 + iconIndex*2];
+            }
+        }
+    }
+
+}
+
+
+
+static void 
+PutByte(int const v) {
+   
+   if (putc(v, ofp) == EOF)
+       pm_error("Unable to write byte to output file.");
+}
+   
+
+
+static void 
+PutShort(short const v) {
+   
+   if (pm_writelittleshort(ofp, v) == -1)
+       pm_error("Unable to write short integer to output file");
+}
+   
+
+
+static void 
+PutLong(long const v) {
+   
+   if (pm_writelittlelong(ofp, v) == -1)
+       pm_error("Unable to write long integer to output file");
+}
+
+
+   
+/*
+ * These have no purpose but to wrapper the Byte, Short & Long 
+ * functions.
+ */
+static void 
+writeU1 (u1 const v) {
+   file_offset++;
+   PutByte(v);
+}
+
+static  void 
+writeU2 (u2 const v) {
+   file_offset +=2;
+   PutShort(v);
+}
+
+static void 
+writeU4 (u4 const v) {
+   file_offset += 4;
+   PutLong(v);
+}
+
+static MS_Ico 
+createIconFile (void) {
+   MS_Ico MSIconData;
+   
+   MALLOCVAR_NOFAIL(MSIconData);
+
+   MSIconData->reserved     = 0;
+   MSIconData->type         = 1;
+   MSIconData->count        = 0;
+   MSIconData->entries      = NULL;
+   return MSIconData;
+}
+
+
+/* create andBitmap from pgm */
+
+static ICON_bmp 
+createAndBitmap (gray ** const ba, int const cols, int const rows,
+                 gray const maxval) {
+   /*
+    * How wide should the u1 string for each row be?
+    * each byte is 8 pixels, but must be a multiple of 4 bytes.
+    */
+   ICON_bmp icBitmap;
+   int xBytes,y,x;
+   int wt = cols;
+   u1 ** rowData;
+
+   MALLOCVAR_NOFAIL(icBitmap);
+
+   wt >>= 3;
+   if (wt & 3) {
+      wt = (wt & ~3) + 4;
+   }
+   xBytes = wt;
+   MALLOCARRAY_NOFAIL(rowData, rows);
+   icBitmap->xBytes = xBytes;
+   icBitmap->data   = rowData;
+   icBitmap->size   = xBytes * rows;
+   for (y=0;y<rows;y++) {
+      u1 * row;
+      int byteOn = 0;
+      int bitOn = 128;
+
+      MALLOCARRAY_NOFAIL(row, xBytes);
+
+      memset (row, 0, xBytes);
+      rowData[rows-y-1] = row;
+      /* 
+       * Check there's a bit array, otherwise we're just faking this...
+       */
+      if (ba) {
+     for (x=0;x<cols;x++) {
+            /* Black (bit clear) is transparent in PGM alpha maps,
+             * in ICO bit *set* is transparent.
+             */
+            if (ba[y][x] <= maxval/2) row[byteOn] |= bitOn;
+
+        if (bitOn == 1) {
+           byteOn++;
+           bitOn = 128;
+        } else {
+           bitOn >>= 1;
+        }
+     }
+      }
+   }
+   return icBitmap;
+}
+
+
+/*
+ * Depending on if the image is stored as 1bpp, 4bpp or 8bpp, the 
+ * encoding mechanism is different.
+ * 
+ * I didn't re-use the code from ppmtobmp since I need to keep the
+ * bitmaps in memory till I've loaded all ppms.
+ * 
+ * 8bpp => 1 byte/palette index.
+ * 4bpp => High Nibble, Low Nibble
+ * 1bpp => 1 palette value per bit, high bit 1st.
+ */
+static ICON_bmp 
+create1Bitmap (pixel ** const pa, int const cols, int const rows, 
+               colorhash_table const cht) {
+   /*
+    * How wide should the u1 string for each row be?
+    * each byte is 8 pixels, but must be a multiple of 4 bytes.
+    */
+   ICON_bmp icBitmap;
+   int xBytes,y,x;
+   int wt = cols;
+   u1 ** rowData;
+
+   MALLOCVAR_NOFAIL(icBitmap);
+   
+   wt >>= 3;
+   if (wt & 3) {
+      wt = (wt & ~3) + 4;
+   }
+   xBytes = wt;
+   MALLOCARRAY_NOFAIL(rowData, rows);
+   icBitmap->xBytes = xBytes;
+   icBitmap->data   = rowData;
+   icBitmap->size   = xBytes * rows;
+   for (y=0;y<rows;y++) {
+      u1 * row;
+      int byteOn = 0;
+      int bitOn = 128;
+      int value;
+      
+      MALLOCARRAY_NOFAIL(row, xBytes);
+      memset (row, 0, xBytes);
+      rowData[rows-y-1] = row;
+      /* 
+       * Check there's a pixel array, otherwise we're just faking this...
+       */
+      if (pa) {
+     for (x=0;x<cols;x++) {
+        /*
+         * So we've got a colorhash_table with two colors in it.
+         * Which is black?!
+         * 
+         * Unless the hashing function changes, 0's black.
+         */
+        value = ppm_lookupcolor(cht, &pa[y][x]);
+        if (!value) {
+           /* leave black. */
+        } else {
+           row[byteOn] |= bitOn;
+        }
+        if (bitOn == 1) {
+           byteOn++;
+           bitOn = 128;
+        } else {
+           bitOn >>= 1;
+        }
+     }
+      }
+   }
+   return icBitmap;
+}
+
+
+static ICON_bmp 
+create4Bitmap (pixel ** const pa, int const cols, int const rows,
+               colorhash_table const cht) {
+   /*
+    * How wide should the u1 string for each row be?
+    * each byte is 8 pixels, but must be a multiple of 4 bytes.
+    */
+   ICON_bmp icBitmap;
+   int xBytes,y,x;
+   int wt = cols;
+   u1 ** rowData;
+
+   MALLOCVAR_NOFAIL(icBitmap);
+
+   wt >>= 1;
+   if (wt & 3) {
+      wt = (wt & ~3) + 4;
+   }
+   xBytes = wt;
+   MALLOCARRAY_NOFAIL(rowData, rows);
+   icBitmap->xBytes = xBytes;
+   icBitmap->data   = rowData;
+   icBitmap->size   = xBytes * rows;
+
+   for (y=0;y<rows;y++) {
+      u1 * row;
+      int byteOn = 0;
+      int nibble = 1;   /* high nibble = 1, low nibble = 0; */
+      int value;
+
+      MALLOCARRAY_NOFAIL(row, xBytes);
+
+      memset (row, 0, xBytes);
+      rowData[rows-y-1] = row;
+      /* 
+       * Check there's a pixel array, otherwise we're just faking this...
+       */
+      if (pa) {
+     for (x=0;x<cols;x++) {
+        value = ppm_lookupcolor(cht, &pa[y][x]);
+        /*
+         * Shift it, if we're putting it in the high nibble.
+         */
+        if (nibble) {
+           value <<= 4;
+        }
+        row[byteOn] |= value;
+        if (nibble) {
+           nibble = 0;
+        } else {
+           nibble = 1;
+           byteOn++;
+        }
+     }
+      }
+   }
+   return icBitmap;
+}
+
+
+
+static ICON_bmp 
+create8Bitmap (pixel ** const pa, int const cols, int const rows,
+               colorhash_table const cht) {
+   /*
+    * How wide should the u1 string for each row be?
+    * each byte is 8 pixels, but must be a multiple of 4 bytes.
+    */
+   ICON_bmp icBitmap;
+   int xBytes,y,x;
+   int wt = cols;
+   u1 ** rowData;
+
+   MALLOCVAR_NOFAIL(icBitmap);
+
+   if (wt & 3) {
+      wt = (wt & ~3) + 4;
+   }
+   xBytes = wt;
+   MALLOCARRAY_NOFAIL(rowData, rows);
+   icBitmap->xBytes = xBytes;
+   icBitmap->data   = rowData;
+   icBitmap->size   = xBytes * rows;
+
+   for (y=0;y<rows;y++) {
+      u1 * row;
+
+      MALLOCARRAY_NOFAIL(row, xBytes);
+      memset (row, 0, xBytes);
+      rowData[rows-y-1] = row;
+      /* 
+       * Check there's a pixel array, otherwise we're just faking this...
+       */
+      if (pa) {
+     for (x=0;x<cols;x++) {
+        row[x] = ppm_lookupcolor(cht, &pa[y][x]);
+     }
+      }
+   }
+   return icBitmap;
+}
+
+
+
+static IC_InfoHeader 
+createInfoHeader(IC_Entry const entry, ICON_bmp const xbmp,
+                 ICON_bmp const abmp) {
+   IC_InfoHeader ih;
+   
+   MALLOCVAR_NOFAIL(ih);
+
+   ih->size          = 40;
+   ih->width         = entry->width;
+   ih->height        = entry->height * 2;  
+   ih->planes        = 1;  
+   ih->bitcount      = entry->bitcount;
+   ih->compression   = 0;
+   ih->imagesize     = entry->width * entry->height * 8 / entry->bitcount;
+   ih->x_pixels_per_m= 0;
+   ih->y_pixels_per_m= 0;
+   ih->colors_used   = 1 << entry->bitcount;
+   ih->colors_important = 0;
+   return ih;
+}
+
+
+
+static IC_Palette 
+createCleanPalette(void) {
+   IC_Palette palette;
+   int x;
+   
+   MALLOCVAR_NOFAIL(palette);
+
+   MALLOCARRAY_NOFAIL(palette->colors, MAXCOLORS);
+   for (x=0;x<MAXCOLORS;x++ ){
+      palette->colors[x] = NULL;
+   }
+   return palette;
+}
+
+
+
+static void 
+addColorToPalette(IC_Palette const palette, int const i,
+                  int const r, int const g, int const b) {
+
+    MALLOCVAR_NOFAIL(palette->colors[i]);
+
+    palette->colors[i]->red      = r;
+    palette->colors[i]->green    = g;
+    palette->colors[i]->blue     = b;
+    palette->colors[i]->reserved = 0;
+}
+
+
+
+static ICON_bmp 
+createBitmap (int const bpp, pixel ** const pa, 
+              int const cols, int const rows, 
+              colorhash_table const cht) {
+    
+    ICON_bmp retval;
+    const int assumed_bpp = (pa == NULL) ? 1 : bpp;
+
+    switch (assumed_bpp) {
+    case 1:
+        retval = create1Bitmap (pa,cols,rows,cht);
+        break;
+    case 4:
+        retval = create4Bitmap (pa,cols,rows,cht);
+        break;
+    case 8:
+    default:
+        retval = create8Bitmap (pa,cols,rows,cht);
+        break;
+    }
+    return retval;
+}
+
+
+
+static void
+makePalette(pixel **          const xorPPMarray,
+            int               const xorCols,
+            int               const xorRows,
+            pixval            const xorMaxval,
+            IC_Palette *      const paletteP,
+            colorhash_table * const xorChtP,
+            int *             const colorsP,
+            const char **     const errorP) {
+   /*
+    * Figure out the colormap and turn it into the appropriate GIF
+    * colormap - this code's pretty much straight from ppmtobpm
+    */
+    colorhist_vector xorChv;
+    unsigned int i;
+    int colors;
+    IC_Palette palette = createCleanPalette();
+
+    if (verbose) pm_message("computing colormap...");
+    xorChv = ppm_computecolorhist(xorPPMarray, xorCols, xorRows, MAXCOLORS, 
+                                  &colors);
+    if (xorChv == NULL)
+        asprintfN(errorP,
+                  "image has too many colors - try doing a 'pnmquant %d'",
+                  MAXCOLORS);
+    else {
+        *errorP = NULL;
+
+        if (verbose) pm_message("%d colors found", colors);
+        
+        if (verbose && (xorMaxval > 255))
+            pm_message("maxval is not 255 - automatically rescaling colors");
+        for (i = 0; i < colors; ++i) {
+            if (xorMaxval == 255) {
+                addColorToPalette(palette,i,
+                                  PPM_GETR(xorChv[i].color),
+                                  PPM_GETG(xorChv[i].color),
+                                  PPM_GETB(xorChv[i].color));
+            } else {
+                addColorToPalette(palette,i,
+                                  PPM_GETR(xorChv[i].color) * 255 / xorMaxval,
+                                  PPM_GETG(xorChv[i].color) * 255 / xorMaxval,
+                                  PPM_GETB(xorChv[i].color) * 255 / xorMaxval);
+            }
+        }
+        
+        /* And make a hash table for fast lookup. */
+        *xorChtP = ppm_colorhisttocolorhash(xorChv, colors);
+        
+        ppm_freecolorhist(xorChv);
+        
+        *paletteP = palette;
+        *colorsP = colors;
+    }
+}
+
+
+
+static void
+getOrFakeAndMap(const char *      const andPgmFname,
+                int               const xorCols,
+                int               const xorRows,
+                gray ***          const andPGMarrayP,
+                pixval *          const andMaxvalP,
+                colorhash_table * const andChtP,
+                const char **     const errorP) {
+
+    int andRows, andCols;
+    
+    if (!andPgmFname) {
+        /* He's not supplying a bitmap for 'and'.  Fake the bitmap. */
+        *andPGMarrayP = NULL;
+        *andMaxvalP   = 1;
+        *andChtP      = NULL;
+        *errorP       = NULL;
+    } else {
+        FILE * andfile;
+        andfile = pm_openr(andPgmFname);
+        *andPGMarrayP = pgm_readpgm(andfile, &andCols, &andRows, andMaxvalP);
+        pm_close(andfile);
+
+        if ((andCols != xorCols) || (andRows != xorRows)) {
+            asprintfN(errorP,
+                      "And mask and image have different dimensions "
+                     "(%d x %d vs %d x %d).  Aborting.",
+                     andCols, xorCols, andRows, xorRows);
+        } else
+            *errorP = NULL;
+    }
+}
+
+
+
+static void
+blackenTransparentAreas(pixel ** const xorPPMarray,
+                        int      const cols,
+                        int      const rows,
+                        gray **  const andPGMarray,
+                        pixval   const andMaxval) {
+
+    unsigned int row;
+
+    if (verbose) pm_message("Setting transparent pixels to black");
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        for (col = 0; col < cols; ++col) {
+            if (andPGMarray[row][col] < andMaxval)
+                /* It's not opaque here; make it black */
+                PPM_ASSIGN(xorPPMarray[row][col], 0, 0, 0);
+        }
+    }
+}
+
+
+
+static void 
+addEntryToIcon(MS_Ico       const MSIconData, 
+               const char * const xorPpmFname,
+               const char * const andPgmFname,
+               bool         const trueTransparent) {
+
+    IC_Entry entry;
+    FILE * xorfile;
+    pixel ** xorPPMarray;
+    gray ** andPGMarray;
+    ICON_bmp xorBitmap;
+    ICON_bmp andBitmap;
+    int rows, cols;
+    int bpp, colors;
+    int entry_cols;
+    IC_Palette palette;
+    colorhash_table  xorCht;
+    colorhash_table  andCht; 
+    const char * error;
+   
+    pixval xorMaxval;
+    gray andMaxval;
+
+    MALLOCVAR_NOFAIL(entry);
+
+   /*
+    * Read the xor PPM.
+    */
+    xorfile = pm_openr(xorPpmFname);
+    xorPPMarray = ppm_readppm(xorfile, &cols, &rows, &xorMaxval);
+    pm_close(xorfile);
+    /*
+    * Since the entry uses 1 byte to hold the width and height of the icon, the
+    * image can't be more than 256 x 256.
+    */
+    if (rows > 255 || cols > 255) {
+        pm_error("Max size for a icon is 255 x 255 (1 byte fields).  "
+                 "%s is %d x %d", xorPpmFname, cols, rows);
+    }
+   
+    if (verbose) pm_message("read PPM: %dw x %dh, maxval = %d", 
+                            cols, rows, xorMaxval);
+
+    makePalette(xorPPMarray, cols, rows, xorMaxval, 
+                &palette, &xorCht, &colors, &error);
+
+    if (error)
+        pm_error("Unable to make palette for '%s'.  %s", xorPpmFname, error);
+   /*
+    * All the icons I found seemed to pad the palette to the max entries
+    * for that bitdepth.
+    * 
+    * The spec indicates this isn't neccessary, but I'll follow this behaviour
+    * just in case.
+    */
+    if (colors < 3) {
+        bpp = 1;
+        entry_cols = 2;
+    } else if (colors < 17) {
+        bpp = 4;
+        entry_cols = 16;
+    } else {
+        bpp = 8;
+        entry_cols = 256;
+    }
+
+    getOrFakeAndMap(andPgmFname, cols, rows,
+                    &andPGMarray, &andMaxval, &andCht, &error);
+    if (error)
+        pm_error("Error in and map for '%s'.  %s", xorPpmFname, error);
+
+    if (andPGMarray && trueTransparent)
+        blackenTransparentAreas(xorPPMarray, cols, rows, 
+                                andPGMarray, andMaxval);
+
+    xorBitmap = createBitmap(bpp, xorPPMarray, cols, rows, xorCht);
+    andBitmap = createAndBitmap(andPGMarray, cols, rows, andMaxval);
+    /*
+     * Fill in the entry data fields.
+    */
+    entry->width         = cols;
+    entry->height        = rows;
+    entry->color_count   = entry_cols;
+    entry->reserved      = 0;
+    entry->planes        = 1;
+    /* 
+    * all the icons I looked at ignored this value...
+    */
+    entry->bitcount      = bpp;
+    entry->ih            = createInfoHeader(entry, xorBitmap, andBitmap);
+    entry->colors        = palette->colors;
+    entry->size_in_bytes = 
+        xorBitmap->size + andBitmap->size + 40 + (4 * entry->color_count);
+    if (verbose) 
+        pm_message("entry->size_in_bytes = %d + %d + %d = %d",
+                   xorBitmap->size ,andBitmap->size, 
+                   40, entry->size_in_bytes );
+    /*
+    * We don't know the offset ATM, set to 0 for now.
+    * Have to calculate this at the end.
+    */
+    entry->file_offset   = 0;
+    entry->xorBitmapOut  = xorBitmap->data;
+    entry->andBitmapOut  = andBitmap->data;
+    entry->xBytesXor     = xorBitmap->xBytes;
+    entry->xBytesAnd     = andBitmap->xBytes;  
+    /*
+    * Add the entry to the entries array.
+    */
+    ++MSIconData->count;
+    /* Perhaps I should allocate ahead, and take fewer trips to the well. */
+    REALLOCARRAY(MSIconData->entries, MSIconData->count);
+    MSIconData->entries[MSIconData->count-1] = entry;
+}
+
+
+
+static void 
+writeIC_Entry (IC_Entry const entry) {
+   writeU1(entry->width);
+   writeU1(entry->height);
+   writeU1(entry->color_count); /* chops 256->0 on its own.. */
+   writeU1(entry->reserved);
+   writeU2(entry->planes);
+   writeU2(entry->bitcount);
+   writeU4(entry->size_in_bytes);
+   writeU4(entry->file_offset);
+}
+
+
+
+static void 
+writeIC_InfoHeader (IC_InfoHeader const ih) {
+   writeU4(ih->size);
+   writeU4(ih->width);
+   writeU4(ih->height);
+   writeU2(ih->planes);
+   writeU2(ih->bitcount);
+   writeU4(ih->compression);
+   writeU4(ih->imagesize);
+   writeU4(ih->x_pixels_per_m);
+   writeU4(ih->y_pixels_per_m);
+   writeU4(ih->colors_used);
+   writeU4(ih->colors_important);
+}
+
+
+
+static void 
+writeIC_Color (IC_Color const col) {
+   /* Since the ppm might not have as many colors in it as we'd like,
+    * (2, 16, 256), stick 0 in the gaps.
+    * 
+    * This means that we lose palette information, but that can't be
+    * helped.  
+    */
+   if (col == NULL) {
+      writeU1(0);
+      writeU1(0);
+      writeU1(0);
+      writeU1(0);
+   } else {
+      writeU1(col->blue);
+      writeU1(col->green);
+      writeU1(col->red);
+      writeU1(col->reserved);
+   }
+}
+
+
+
+static void
+writeBitmap(u1 ** const bitmap, int const xBytes, int const height) {
+   int y;
+   for (y = 0;y<height;y++) {
+      fwrite (bitmap[y],1,xBytes,ofp);
+      file_offset += xBytes;
+   }
+}
+
+
+
+static void 
+writeMS_Ico(MS_Ico       const MSIconData, 
+            const char * const outFname) {
+    int x,y;
+   
+    ofp = pm_openw(outFname);
+
+    writeU2(MSIconData->reserved);
+    writeU2(MSIconData->type);
+    writeU2(MSIconData->count);
+    for (x=0;x<MSIconData->count;x++) writeIC_Entry(MSIconData->entries[x]);
+    for (x=0;x<MSIconData->count;x++) {
+        writeIC_InfoHeader(MSIconData->entries[x]->ih);
+        for (y=0;y<(MSIconData->entries[x]->color_count);y++) {
+            writeIC_Color(MSIconData->entries[x]->colors[y]);
+        }
+        if (verbose) pm_message("writing xor bitmap");
+        writeBitmap(MSIconData->entries[x]->xorBitmapOut,
+                    MSIconData->entries[x]->xBytesXor,
+                    MSIconData->entries[x]->height);
+        if (verbose) pm_message("writing and bitmap");
+        writeBitmap(MSIconData->entries[x]->andBitmapOut,
+                    MSIconData->entries[x]->xBytesAnd,
+                    MSIconData->entries[x]->height);
+    }
+    fclose(ofp);
+}
+
+
+
+int 
+main(int argc, char ** argv) {
+
+    struct cmdlineInfo cmdline;
+
+    MS_Ico const MSIconDataP = createIconFile();
+    unsigned int iconIndex;
+    unsigned int offset;
+   
+    ppm_init (&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    verbose = cmdline.verbose;
+
+    for (iconIndex = 0; iconIndex < cmdline.iconCount; ++iconIndex) {
+        addEntryToIcon(MSIconDataP, cmdline.inputFilespec[iconIndex],
+                       cmdline.andpgmFilespec[iconIndex], 
+                       cmdline.truetransparent);
+    }
+    /*
+     * Now we have to go through and calculate the offsets.
+     * The first infoheader starts at 6 + count*16 bytes.
+     */
+    offset = (MSIconDataP->count * 16) + 6;
+    for (iconIndex = 0; iconIndex < MSIconDataP->count; ++iconIndex) {
+        IC_Entry entry = MSIconDataP->entries[iconIndex];
+        entry->file_offset = offset;
+        /* 
+         * Increase the offset by the size of this offset & data.
+         * this includes the size of the color data.
+         */
+        offset += entry->size_in_bytes;
+    }
+    /*
+     * And now, we have to actually SAVE the .ico!
+     */
+    writeMS_Ico(MSIconDataP, cmdline.output);
+
+    free(cmdline.inputFilespec);
+    free(cmdline.andpgmFilespec);
+
+    return 0;
+}
+
diff --git a/converter/ppm/ppmtoxpm.c b/converter/ppm/ppmtoxpm.c
new file mode 100644
index 00000000..853ae711
--- /dev/null
+++ b/converter/ppm/ppmtoxpm.c
@@ -0,0 +1,659 @@
+/* ppmtoxpm.c - read a portable pixmap and produce a (version 3) X11 pixmap
+**
+** Copyright (C) 1990 by Mark W. Snitily
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+**
+** This tool was developed for Schlumberger Technologies, ATE Division, and
+** with their permission is being made available to the public with the above
+** copyright notice and permission notice.
+**
+** Upgraded to XPM2 by
+**   Paul Breslaw, Mecasoft SA, Zurich, Switzerland (paul@mecazh.uu.ch)
+**   Thu Nov  8 16:01:17 1990
+**
+** Upgraded to XPM version 3 by
+**   Arnaud Le Hors (lehors@mirsa.inria.fr)
+**   Tue Apr 9 1991
+**
+** Rainer Sinkwitz sinkwitz@ifi.unizh.ch - 21 Nov 91:
+**  - Bug fix, should should malloc space for rgbn[j].name+1 in line 441
+**    caused segmentation faults
+**    
+**  - lowercase conversion of RGB names def'ed out,
+**    considered harmful.
+**
+** Michael Pall (pall@rz.uni-karlsruhe.de) - 29 Nov 93:
+**  - Use the algorithm from xpm-lib for pixel encoding
+**    (base 93 not base 28 -> saves a lot of space for colorful xpms)
+*/
+
+#define _BSD_SOURCE   /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "ppm.h"
+#include "shhopt.h"
+#include "nstring.h"
+#include "mallocvar.h"
+
+/* Max number of entries we will put in the XPM color map 
+   Don't forget the one entry for transparency.
+
+   We don't use this anymore.  Ppmtoxpm now has no arbitrary limit on
+   the number of colors.
+
+   We're assuming this isn't in fact an XPM format requirement, because
+   we've seen it work with 257, and 257 seems to be common, because it's
+   the classic 256 colors, plus transparency.  The value was 256 for
+   ages before we added transparency capability to this program in May
+   2001.  At that time, we started failing with 256 color images.
+   Some limit was also necessary before then because ppm_computecolorhash()
+   required us to specify a maximum number of colors.  It doesn't anymore.
+
+   If we find out that some XPM processing programs choke on more than
+   256 colors, we'll have to readdress this issue.  - Bryan.  2001.05.13.
+*/
+#define MAXCOLORS    256
+
+#define MAXPRINTABLE 92         /* number of printable ascii chars
+                                 * minus \ and " for string compat
+                                 * and ? to avoid ANSI trigraphs. */
+
+static const char * const printable =
+" .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjklzxcvbnmMNBVCZ\
+ASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
+
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *inputFilename;
+    const char *name;
+    const char *rgb;
+    const char *alpha_filename;
+    unsigned int hexonly;
+    unsigned int verbose;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+    const char * nameOpt;
+    unsigned int nameSpec;
+
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0,   "alphamask",   OPT_STRING, &cmdlineP->alpha_filename, 
+            NULL, 0);
+    OPTENT3(0,   "name",        OPT_STRING, &nameOpt,                   
+            &nameSpec, 0);
+    OPTENT3(0,   "rgb",         OPT_STRING, &cmdlineP->rgb,            
+            NULL, 0);
+    OPTENT3(0,   "hexonly",     OPT_FLAG, NULL,
+            &cmdlineP->hexonly, 0);
+    OPTENT3(0,   "verbose",     OPT_FLAG, NULL,
+            &cmdlineP->verbose, 0);
+
+    /* Set the defaults */
+    cmdlineP->alpha_filename = NULL;  /* no transparency */
+    cmdlineP->rgb = NULL;      /* no rgb file specified */
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 == 0) 
+        cmdlineP->inputFilename = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
+    else
+        cmdlineP->inputFilename = argv[1];
+
+    /* If output filename not specified, use input filename as default. */
+    if (nameSpec)
+        cmdlineP->name = nameOpt;
+    else if (STREQ(cmdlineP->inputFilename, "-"))
+        cmdlineP->name = "noname";
+    else {
+        static char name[80+1];
+        char *cp;
+
+        STRSCPY(name, cmdlineP->inputFilename);
+        cp = strchr(name, '.');
+        if (cp)
+            *cp = '\0';     /* remove extension */
+        cmdlineP->name = name;
+    }
+}
+
+
+typedef struct {            /* rgb values and ascii names (from
+                             * rgb text file) */
+    int r, g, b;            /* rgb values, range of 0 -> 65535 */
+    char *name;             /* color mnemonic of rgb value */
+} rgb_names;
+
+typedef struct {            
+    /* Information for an XPM color map entry */
+    char *cixel;    
+       /* XPM color number, as might appear in the XPM raster */
+    const char *rgbname;  
+       /* color name, e.g. "blue" or "#01FF23" */
+} cixel_map;
+
+
+
+static char *
+genNumstr(unsigned int const input, int const digits) {
+/*---------------------------------------------------------------------------
+   Given a number and a base (MAXPRINTABLE), this routine prints the
+   number into a malloc'ed string and returns it.  The length of the
+   string is specified by "digits".  It is not printed in decimal or
+   any other number system you're used to.  Rather, each digit is from
+   the set printable[], which contains MAXPRINTABLE characters; the
+   character printable[n] has value n.
+
+   The string is printable[0] filled with high order zeroes.
+
+   Example:
+     Assume: 
+       printable[0] == 'q'
+       printable[1] == '%'
+       MAXPRINTABLE == 2 
+       digits == 5
+       input == 3 
+     Result is the malloc'ed string "qqq%%"
+---------------------------------------------------------------------------*/
+    char *str, *p;
+    int d;
+    unsigned int i;
+
+    /* Allocate memory for printed number.  Abort if error. */
+    if (!(str = (char *) malloc(digits + 1)))
+        pm_error("out of memory");
+
+    i = input;
+    /* Generate characters starting with least significant digit. */
+    p = str + digits;
+    *p-- = '\0';            /* nul terminate string */
+    while (p >= str) {
+        d = i % MAXPRINTABLE;
+        i /= MAXPRINTABLE;
+        *p-- = printable[d];
+    }
+
+    if (i != 0)
+        pm_error("Overflow converting %d to %d digits in base %d",
+                 input, digits, MAXPRINTABLE);
+
+    return str;
+}
+
+
+
+static unsigned int
+xpmMaxvalFromMaxval(pixval const maxval) {
+
+    unsigned int retval;
+
+    /*
+     * Determine how many hex digits we'll be normalizing to if the rgb
+     * value doesn't match a color mnemonic. 
+     */
+    if (maxval <= 0x000F)
+        retval = 0x000F;
+    else if (maxval <= 0x00FF)
+        retval = 0x00FF;
+    else if (maxval <= 0x0FFF)
+        retval = 0x0FFF;
+    else if (maxval <= 0xFFFF)
+        retval = 0xFFFF;
+    else
+        pm_error("Internal error - impossible maxval %x", maxval);
+
+    return retval;
+}
+    
+
+
+static unsigned int
+charsPerPixelForSize(unsigned int const cmapSize) { 
+/*----------------------------------------------------------------------------
+   Return the number of characters it will take to represent a pixel in
+   an XPM that has a colormap of size 'cmapSize'.  Each pixel in an XPM
+   represents an index into the colormap with a base-92 scheme where each
+   character is one of 92 printable characters.  Ergo, if the colormap
+   has at most 92 characters, each pixel will be represented by a single
+   character.  If it has more than 92, but at most 92*92, it will take 2, 
+   etc.
+
+   If cmapSize is zero, there's no such thing as an XPM pixel, so we
+   return an undefined value.
+-----------------------------------------------------------------------------*/
+    unsigned int charsPerPixel;
+
+    if (cmapSize > 0) {
+        unsigned int j;
+
+        for (charsPerPixel = 0, j = cmapSize-1; j > 0; ++charsPerPixel)
+            j /= MAXPRINTABLE;
+    }
+    return charsPerPixel;
+} 
+
+
+
+static void
+genCmap(colorhist_vector const chv, 
+        int              const ncolors, 
+        pixval           const maxval, 
+        colorhash_table  const colornameHash,
+        const char *     const colornames[],
+        bool             const includeTransparent,
+        cixel_map **     const cmapP, 
+        unsigned int *   const transIndexP,
+        unsigned int *   const cmapSizeP,
+        unsigned int *   const charsPerPixelP) {
+/*----------------------------------------------------------------------------
+   Generate the XPM color map in cixel_map format (which is just a step
+   away from the actual text that needs to be written the XPM file).  The
+   color map is defined by 'chv', which contains 'ncolors' colors which
+   have maxval 'maxval'.
+
+   Output is in newly malloc'ed storage, with its address returned as
+   *cmapP.  We return the number of entries in it as *cmapSizeP.
+
+   This map includes an entry for transparency, whether the raster uses
+   it or not.  We return its index as *transIndexP.
+
+   In the map, identify colors by the names given by 'colornameHash' and
+   colornames[].  'colornameHash' maps a color in 'pixel' form to an
+   index into colornames[]; colornames[] contains the text to identify the
+   color in the XPM format.  The colors in 'colornameHash' have maxval 255.
+   If a color is not in 'colornameHash', use hexadecimal notation in the
+   output colormap.
+
+   But if 'colornameHash' is null, don't use color names at all.  Just use
+   hexadecimal notation.
+
+   Return as *charsPerPixel the number of characters, or digits, that
+   will be needed in the XPM raster to form an index into this color map.
+-----------------------------------------------------------------------------*/
+    unsigned int const cmapSize = ncolors + (includeTransparent ? 1 : 0);
+
+    cixel_map * cmap;  /* malloc'ed */
+    unsigned int cmapIndex;
+    unsigned int charsPerPixel;
+    unsigned int xpmMaxval;
+    
+    MALLOCARRAY(cmap, cmapSize);
+    if (cmapP == NULL)
+        pm_error("Out of memory allocating %u bytes for a color map.",
+                 sizeof(cixel_map) * (ncolors+1));
+
+    xpmMaxval = xpmMaxvalFromMaxval(maxval);
+
+    charsPerPixel = charsPerPixelForSize(cmapSize);
+
+    /*
+     * Generate the character-pixel string and the rgb name for each
+     * colormap entry. 
+     */
+    for (cmapIndex = 0; cmapIndex < ncolors; ++cmapIndex) {
+        pixel const color = chv[cmapIndex].color;
+
+        pixel color255;
+            /* The color, scaled to maxval 255 */
+        const char * colorname;  /* malloc'ed */
+        /*
+         * The character-pixel string is simply a printed number in base
+         * MAXPRINTABLE where the digits of the number range from
+         * printable[0] .. printable[MAXPRINTABLE-1] and the printed length
+         * of the number is 'charsPerPixel'. 
+         */
+        cmap[cmapIndex].cixel = genNumstr(cmapIndex, charsPerPixel);
+        
+        PPM_DEPTH(color255, color, maxval, 255);
+
+        if (colornameHash == NULL)
+            colorname = NULL;
+        else {
+            int colornameIndex;
+            colornameIndex = ppm_lookupcolor(colornameHash, &color255);
+            if (colornameIndex >= 0)
+                colorname = strdup(colornames[colornameIndex]);
+            else
+                colorname = NULL;
+        }
+        if (colorname)
+            cmap[cmapIndex].rgbname = colorname;
+        else {
+            /* Color has no name; represent it in hexadecimal */
+
+            pixel scaledColor;
+            const char * hexString;  /* malloc'ed */
+
+            PPM_DEPTH(scaledColor, color, maxval, xpmMaxval);
+
+            asprintfN(&hexString, xpmMaxval == 0x000F ? "#%X%X%X" :
+                      xpmMaxval == 0x00FF ? "#%02X%02X%02X" :
+                      xpmMaxval == 0x0FFF ? "#%03X%03X%03X" :
+                      "#%04X%04X%04X", 
+                      PPM_GETR(scaledColor),
+                      PPM_GETG(scaledColor),
+                      PPM_GETB(scaledColor)
+                );
+
+            if (hexString == NULL)
+                pm_error("Unable to allocate storage for hex string");
+            cmap[cmapIndex].rgbname = hexString;
+        }
+    }
+
+    if (includeTransparent) {
+        /* Add the special transparency entry to the colormap */
+        unsigned int const transIndex = ncolors;
+        cmap[transIndex].rgbname = strdup("None");
+        cmap[transIndex].cixel = genNumstr(transIndex, charsPerPixel);
+        *transIndexP = transIndex;
+    }
+    *cmapP          = cmap;
+    *cmapSizeP      = cmapSize;
+    *charsPerPixelP = charsPerPixel;
+}
+
+
+
+static void
+destroyCmap(cixel_map *  const cmap, 
+            unsigned int const cmapSize) {
+
+    int i;
+    /* Free the real color entries */
+    for (i = 0; i < cmapSize; i++) {
+        strfree(cmap[i].rgbname);
+        free(cmap[i].cixel);
+    }
+    free(cmap);
+}
+
+
+
+static void
+writeXpmFile(FILE *          const outfile, 
+             pixel **        const pixels, 
+             gray **         const alpha, 
+             pixval          const alphamaxval,
+             char            const name[], 
+             int             const cols, 
+             int             const rows, 
+             unsigned int    const cmapSize,
+             unsigned int    const charsPerPixel, 
+             cixel_map       const cmap[],
+             colorhash_table const cht,
+             unsigned int    const transIndex) {
+/*----------------------------------------------------------------------------
+   Write the whole XPM file to the open stream 'outfile'.
+
+   'cmap' is the colormap to be placed in the XPM.  'cmapSize' is the
+   number of entries in it.  'cht' is a hash table that gives you an 
+   index into 'cmap' given a color.  'transIndex' is the index into cmap
+   of the transparent color, and is valid only if 'alpha' is non-null
+   (otherwise, cmap might not contain a transparent color).
+-----------------------------------------------------------------------------*/
+    /* First the header */
+    printf("/* XPM */\n");
+    fprintf(outfile, "static char *%s[] = {\n", name);
+    fprintf(outfile, "/* width height ncolors chars_per_pixel */\n");
+    fprintf(outfile, "\"%d %d %d %d\",\n", cols, rows, 
+            cmapSize, charsPerPixel);
+
+    {
+        int i;
+        /* Write out the colormap (part of header) */
+        fprintf(outfile, "/* colors */\n");
+        for (i = 0; i < cmapSize; i++) { 
+            fprintf(outfile, "\"%s c %s\",\n", cmap[i].cixel, cmap[i].rgbname);
+        }
+    }
+    {
+        int row;
+
+        /* And now the raster */
+        fprintf(outfile, "/* pixels */\n");
+        for (row = 0; row < rows; row++) {
+            int col;
+            fprintf(outfile, "\"");
+            for (col = 0; col < cols; col++) {
+                if (alpha && alpha[row][col] <= alphamaxval/2)
+                    /* It's a transparent pixel */
+                    fprintf(outfile, "%s", cmap[transIndex].cixel);
+                else 
+                    fprintf(outfile, "%s", 
+                            cmap[ppm_lookupcolor(cht, 
+                                                 &pixels[row][col])].cixel);
+            }
+            fprintf(outfile, "\"%s\n", (row == (rows - 1) ? "" : ","));
+        }
+    }
+    /* And close up */
+    fprintf(outfile, "};\n");
+}
+
+
+
+static void
+readAlpha(const char filespec[], gray *** const alphaP,
+          int const cols, int const rows, pixval * const alphamaxvalP) {
+
+    FILE * alpha_file;
+    int alphacols, alpharows;
+        
+    alpha_file = pm_openr(filespec);
+    *alphaP = pgm_readpgm(alpha_file, &alphacols, &alpharows, alphamaxvalP);
+    pm_close(alpha_file);
+    
+    if (cols != alphacols || rows != alpharows)
+        pm_error("Alpha mask is not the same dimensions as the "
+                 "image.  Image is %d by %d, while mask is %d x %d.",
+                 cols, rows, alphacols, alpharows);
+}
+    
+
+
+static void
+computecolorhash(pixel **          const pixels,
+                 gray **           const alpha,
+                 int               const cols,
+                 int               const rows,
+                 gray              const alphaMaxval,
+                 colorhash_table * const chtP,
+                 unsigned int *    const ncolorsP,
+                 bool *            const transparentSomewhereP) {
+/*----------------------------------------------------------------------------
+   Compute a colorhash_table with one entry for each color in 'pixels' that
+   is not mostly transparent according to alpha mask 'alpha' (which has
+   maxval 'alphaMaxval').  alpha == NULL means all pixels are opaque.
+
+   The value associated with the color in the hash we build is meaningless.
+
+   Return the colorhash_table as *chtP, and the number of colors in it
+   as *ncolorsP.  Return *transparentSomewhereP == TRUE iff the image has
+   at least one pixel that is mostly transparent.
+-----------------------------------------------------------------------------*/
+    colorhash_table cht;
+    int row;
+    
+    cht = ppm_alloccolorhash( );
+    *ncolorsP = 0;   /* initial value */
+    *transparentSomewhereP = FALSE;  /* initial assumption */
+
+    /* Go through the entire image, building a hash table of colors. */
+    for (row = 0; row < rows; ++row) {
+        int col;
+
+        for (col = 0; col < cols; ++col) {
+            if (!alpha || alpha[row][col] > alphaMaxval/2) {
+                /* It's mostly opaque, so add this color to the hash
+                   if it's not already there.  
+                */
+                pixel const color = pixels[row][col];
+                int const lookupRc = ppm_lookupcolor(cht, &color);
+ 
+                if (lookupRc < 0) {
+                    /* It's not in the hash yet, so add it */
+                    ppm_addtocolorhash(cht, &color, 0);
+                    ++(*ncolorsP);
+                }
+            } else
+                *transparentSomewhereP = TRUE;
+        }
+    }
+    *chtP = cht;
+}
+
+
+
+static void
+computeColormap(pixel **           const pixels, 
+                gray **            const alpha,
+                int                const cols, 
+                int                const rows,
+                gray               const alphaMaxval,
+                colorhist_vector * const chvP, 
+                colorhash_table *  const chtP,
+                unsigned int *     const ncolorsP,
+                bool *             const transparentSomewhereP) {
+/*----------------------------------------------------------------------------
+   Compute the color map for the image 'pixels', which is 'cols' by 'rows',
+   in Netpbm data structures (a colorhist_vector for index-to-color lookups
+   and a colorhash_table for color-to-index lookups).
+
+   Exclude pixels that alpha mask 'alpha' (which has maxval
+   'alphaMaxval') says are mostly transparent.  alpha == NULL means all
+   pixels are opaque.
+
+   We return as *chvP an array of the colors present in 'pixels',
+   excluding those that are mostly transparent.  We return as
+   *ncolorsP the number of such colors.  We return
+   *transparentSomewhereP == TRUE iff the image has at least one
+   pixel that is mostly transparent.
+-----------------------------------------------------------------------------*/
+    colorhash_table histCht;
+
+    pm_message("(Computing colormap...");
+    computecolorhash(pixels, alpha, cols, rows, alphaMaxval, 
+                     &histCht, ncolorsP, transparentSomewhereP);
+    pm_message("...Done.  %d colors found.)", *ncolorsP);
+    
+    *chvP = ppm_colorhashtocolorhist(histCht, *ncolorsP);
+    ppm_freecolorhash(histCht);
+    /* Despite the name, the following generates an index on *chvP,
+       with which given a color you can quickly find the entry number
+       in *chvP that contains that color.
+    */
+    *chtP = ppm_colorhisttocolorhash(*chvP, *ncolorsP);
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    FILE *ifp;
+    int rows, cols;
+    unsigned int ncolors;
+    bool transparentSomewhere;
+    pixval maxval, alphaMaxval;
+    colorhash_table cht;
+    colorhist_vector chv;
+
+    colorhash_table colornameHash;
+        /* Hash table to map colors to their names */
+    const char ** colornames;
+        /* Table of color names; 'colornameHash' yields an index into this
+           array.
+        */
+
+    pixel **pixels;
+    gray **alpha;
+
+    /* Used for rgb value -> character-pixel string mapping */
+    cixel_map *cmap;  /* malloc'ed */
+        /* The XPM colormap */
+    unsigned int cmapSize;
+        /* Number of entries in 'cmap' */
+    unsigned int transIndex;
+        /* Index into 'cmap' of the transparent color, if there is one */
+
+    unsigned int charsPerPixel;  
+
+    struct cmdlineInfo cmdline;
+
+    ppm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifp = pm_openr(cmdline.inputFilename);
+    pixels = ppm_readppm(ifp, &cols, &rows, &maxval);
+    pm_close(ifp);
+
+    if (cmdline.alpha_filename) 
+        readAlpha(cmdline.alpha_filename, &alpha, cols, rows, &alphaMaxval);
+    else
+        alpha = NULL;
+
+    computeColormap(pixels, alpha, cols, rows, alphaMaxval,
+                    &chv, &cht, &ncolors, &transparentSomewhere);
+
+    if (cmdline.hexonly)
+        colornameHash = NULL;
+    else
+        ppm_readcolornamefile(cmdline.rgb, FALSE, &colornameHash, &colornames);
+
+    /* Now generate the character-pixel colormap table. */
+    genCmap(chv, ncolors, maxval, 
+            colornameHash, colornames, transparentSomewhere, 
+            &cmap, &transIndex, &cmapSize, &charsPerPixel);
+
+    writeXpmFile(stdout, pixels, alpha, alphaMaxval,
+                 cmdline.name, cols, rows, cmapSize,
+                 charsPerPixel, cmap, cht, transIndex);
+    
+    if (colornameHash) {
+        ppm_freecolorhash(colornameHash);
+        ppm_freecolornames(colornames);
+    }
+    destroyCmap(cmap, cmapSize);
+    ppm_freearray(pixels, rows);
+    if (alpha) pgm_freearray(alpha, rows);
+
+    return 0;
+}
+
diff --git a/converter/ppm/ppmtoyuv.c b/converter/ppm/ppmtoyuv.c
new file mode 100644
index 00000000..7d843cc0
--- /dev/null
+++ b/converter/ppm/ppmtoyuv.c
@@ -0,0 +1,97 @@
+/* ppmtoyuv.c - convert a portable pixmap into an Abekas YUV file
+**
+** by Marc Boucher
+** Internet: marc@PostImage.COM
+** 
+** Based on Example Conversion Program, A60/A64 Digital Video Interface
+** Manual, page 69.
+**
+** Copyright (C) 1991 by DHD PostImage Inc.
+** Copyright (C) 1987 by Abekas Video Systems Inc.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "ppm.h"
+
+int
+main(argc, argv)
+char **argv;
+{
+	FILE *ifp;
+	pixel          *pixelrow;
+	register pixel *pP;
+	int             rows, cols, format, row;
+	register int    col;
+	pixval          maxval;
+	unsigned long   y1, y2=0, u=0, v=0, u0=0, u1, u2, v0=0, v1, v2;
+	unsigned char  *yuvbuf;
+
+
+	ppm_init(&argc, argv);
+
+	if (argc > 2) pm_usage("[ppmfile]");
+
+	if (argc == 2) ifp = pm_openr(argv[1]);
+	else ifp = stdin;
+
+	ppm_readppminit(ifp, &cols, &rows, &maxval, &format);
+
+    if (cols % 2 != 0)
+        pm_error("Image must have even number of columns.\n"
+                 "This image is %d columns wide.  Try Pnmcut.", cols);
+
+	pixelrow = ((pixel*) pm_allocrow( cols, sizeof(pixel) ));
+	yuvbuf = (unsigned char *) pm_allocrow( cols, 2 );
+
+	for (row = 0; row < rows; ++row) {
+		unsigned char *yuvptr;
+
+		ppm_readppmrow(ifp, pixelrow, cols, maxval, format);
+
+		for (col = 0, pP = pixelrow, yuvptr=yuvbuf; col < cols; col += 2, ++pP) {
+			pixval r, g, b;
+
+			/* first pixel gives Y and 0.5 of chroma */
+			r = PPM_GETR(*pP);
+			g = PPM_GETG(*pP);
+			b = PPM_GETB(*pP);
+
+			y1 = 16829 * r + 33039 * g + 6416 * b + (0xffff & y2);
+			u1 = -4853 * r - 9530 * g + 14383 * b;
+			v1 = 14386 * r - 12046 * g - 2340 * b;
+
+			pP++;
+			/* second pixel just yields a Y and 0.25 U, 0.25 V */
+			r = PPM_GETR(*pP);
+			g = PPM_GETG(*pP);
+			b = PPM_GETB(*pP);
+
+			y2 = 16829 * r + 33039 * g + 6416 * b + (0xffff & y1);
+			u2 = -2426 * r - 4765 * g + 7191 * b;
+			v2 = 7193 * r - 6023 * g - 1170 * b;
+
+			/* filter the chroma */
+			u = u0 + u1 + u2 + (0xffff & u);
+			v = v0 + v1 + v2 + (0xffff & v);
+
+			u0 = u2;
+			v0 = v2;
+
+			*yuvptr++ = (u >> 16) + 128;
+			*yuvptr++ = (y1 >> 16) + 16;
+			*yuvptr++ = (v >> 16) + 128;
+			*yuvptr++ = (y2 >> 16) + 16;
+		}
+		fwrite(yuvbuf, cols*2, 1, stdout);
+	}
+
+	pm_close(ifp);
+
+	exit(0);
+}
diff --git a/converter/ppm/ppmtoyuvsplit.c b/converter/ppm/ppmtoyuvsplit.c
new file mode 100644
index 00000000..2dddebfc
--- /dev/null
+++ b/converter/ppm/ppmtoyuvsplit.c
@@ -0,0 +1,197 @@
+/* ppmtoyuvsplit.c - convert a portable pixmap into 3 raw files:
+** - basename.Y : The Luminance chunk at the size of the Image
+** - basename.U : The Chrominance chunk U at 1/4
+** - basename.V : The Chrominance chunk V at 1/4
+** The subsampled U and V values are made by arithmetic mean.
+**
+** If CCIR601 is defined, the produced YUV triples are scaled again
+** to fit into the smaller range of values for this standard.
+**
+** by A.Beck
+** Internet: Andre_Beck@IRS.Inf.TU-Dresden.de
+**
+** Based on ppmtoyuv.c
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+/* Wether to create YUV in JFIF(JPEG) or CCIR.601(MPEG) scale */
+#define CCIR601
+
+/* Wether to use pm_close() or fake it -- don't ask me why */
+/* #define ORIGINAL */
+
+/* ALPHA Kludge by Franky */
+
+#ifdef __alpha
+#define myLONG int
+#else
+#define myLONG long
+#endif
+
+#include <string.h>
+#include "ppm.h"
+
+int
+main(argc, argv)
+char **argv;
+{
+        FILE *ifp,*vf,*uf,*yf;
+        pixel          *pixelrow1,*pixelrow2;
+        register pixel *pP1,*pP2;
+        int             rows, cols, format, row;
+        register int    col;
+        pixval          maxval;
+        myLONG u,v,y0,y1,y2,y3,u0,u1,u2,u3,v0,v1,v2,v3;
+        unsigned char  *y1buf,*y2buf,*ubuf,*vbuf;
+        char            ufname[256],vfname[256],yfname[256];
+
+
+        ppm_init(&argc, argv);
+
+        if ((argc>3)||(argc<2)) pm_usage("basename [ppmfile]");
+
+        if (argc == 3) ifp = pm_openr(argv[2]);
+        else ifp = stdin;
+
+        strcpy(ufname,argv[1]);
+        strcpy(vfname,argv[1]);
+        strcpy(yfname,argv[1]);
+
+        strcat(ufname,".U");
+        strcat(vfname,".V");
+        strcat(yfname,".Y");
+
+        uf = fopen(ufname,"wb");
+        vf = fopen(vfname,"wb");
+        yf = fopen(yfname,"wb");
+
+        if(!(uf && vf && yf)) {
+         perror("error opening output files");
+         exit(0);
+        }
+        ppm_readppminit(ifp, &cols, &rows, &maxval, &format);
+
+        if(cols & 1) fprintf(stderr,
+                             "%s: Warning: odd columns count, exceed ignored\n",
+                             argv[0]);
+        if(rows & 1) fprintf(stderr,
+                             "%s: Warning: odd rows count, exceed ignored\n",
+                             argv[0]);
+
+        pixelrow1 = ((pixel*) pm_allocrow( cols, sizeof(pixel) ));
+        pixelrow2 = ((pixel*) pm_allocrow( cols, sizeof(pixel) ));
+
+        y1buf = (unsigned char *) pm_allocrow( cols, 1 );
+        y2buf = (unsigned char *) pm_allocrow( cols, 1 );
+        ubuf = (unsigned char *) pm_allocrow( cols, 1 );
+        vbuf = (unsigned char *) pm_allocrow( cols, 1 );
+
+        for (row = 0; row < (rows & ~1); row += 2) {
+                unsigned char *y1ptr,*y2ptr,*uptr,*vptr;
+
+                ppm_readppmrow(ifp, pixelrow1, cols, maxval, format);
+                ppm_readppmrow(ifp, pixelrow2, cols, maxval, format);
+
+                pP1 = pixelrow1; pP2 = pixelrow2;
+                y1ptr = y1buf; y2ptr = y2buf; vptr = vbuf; uptr = ubuf;
+
+                for (col = 0 ; col < (cols & ~1); col += 2) {
+                        pixval r0,g0,b0,r1,g1,b1,r2,g2,b2,r3,g3,b3;
+
+                        /* first pixel */
+                        r0 = PPM_GETR(*pP1);
+                        g0 = PPM_GETG(*pP1);
+                        b0 = PPM_GETB(*pP1);
+                        pP1++;
+                        /* 2nd pixel */
+                        r1 = PPM_GETR(*pP1);
+                        g1 = PPM_GETG(*pP1);
+                        b1 = PPM_GETB(*pP1);
+                        pP1++;
+                        /* 3rd pixel */
+                        r2 = PPM_GETR(*pP2);
+                        g2 = PPM_GETG(*pP2);
+                        b2 = PPM_GETB(*pP2);
+                        pP2++;
+                        /* 4th pixel */
+                        r3 = PPM_GETR(*pP2);
+                        g3 = PPM_GETG(*pP2);
+                        b3 = PPM_GETB(*pP2);
+                        pP2++;
+
+
+/* The JFIF RGB to YUV Matrix for $00010000 = 1.0
+
+[Y]   [19595   38469    7471][R]
+[U] = [-11056  -21712  32768][G]
+[V]   [32768   -27440  -5328][B]
+
+*/
+
+                        y0 =  19595 * r0 + 38469 * g0 +  7471 * b0;
+                        u0 = -11056 * r0 - 21712 * g0 + 32768 * b0;
+                        v0 =  32768 * r0 - 27440 * g0 -  5328 * b0;
+
+                        y1 =  19595 * r1 + 38469 * g1 +  7471 * b1;
+                        u1 = -11056 * r1 - 21712 * g1 + 32768 * b1;
+                        v1 =  32768 * r1 - 27440 * g1 -  5328 * b1;
+
+                        y2 =  19595 * r2 + 38469 * g2 +  7471 * b2;
+                        u2 = -11056 * r2 - 21712 * g2 + 32768 * b2;
+                        v2 =  32768 * r2 - 27440 * g2 -  5328 * b2;
+
+                        y3 =  19595 * r3 + 38469 * g3 +  7471 * b3;
+                        u3 = -11056 * r3 - 21712 * g3 + 32768 * b3;
+                        v3 =  32768 * r3 - 27440 * g3 -  5328 * b3;
+
+                        /* mean the chroma for subsampling */
+
+                        u  = (u0+u1+u2+u3)>>2;
+                        v  = (v0+v1+v2+v3)>>2;
+
+#ifdef CCIR601
+
+                        y0 = (y0 * 219)/255 + 1048576;
+                        y1 = (y1 * 219)/255 + 1048576;
+                        y2 = (y2 * 219)/255 + 1048576;
+                        y3 = (y3 * 219)/255 + 1048576;
+
+                        u  = (u * 224)/255 ;
+                        v  = (v * 224)/255 ;
+#endif
+
+
+                        *y1ptr++  = (y0 >> 16) ;
+                        *y1ptr++  = (y1 >> 16) ;
+                        *y2ptr++  = (y2 >> 16) ;
+                        *y2ptr++  = (y3 >> 16) ;
+
+
+                        *uptr++   = (u >> 16)+128 ;
+                        *vptr++   = (v >> 16)+128 ;
+
+                }
+                fwrite(y1buf, (cols & ~1), 1, yf);
+                fwrite(y2buf, (cols & ~1), 1, yf);
+                fwrite(ubuf, cols/2, 1, uf);
+                fwrite(vbuf, cols/2, 1, vf);
+        }
+
+/* I dunno why pm_close sees an error... get rid of it */
+
+#ifdef ORIGINAL
+        pm_close(ifp);
+#else
+        if(ifp != stdin) fclose(ifp);
+#endif
+        fclose(yf);
+        fclose(uf);
+        fclose(vf);
+        exit(0);
+}
diff --git a/converter/ppm/qrttoppm.c b/converter/ppm/qrttoppm.c
new file mode 100644
index 00000000..935463e7
--- /dev/null
+++ b/converter/ppm/qrttoppm.c
@@ -0,0 +1,69 @@
+/* qrttoppm.c - read a QRT ray-tracer output file and produce a portable pixmap
+**
+** Copyright (C) 1989 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "ppm.h"
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    register pixel* pixelrow;
+    int rows, cols, row, col;
+    pixval maxval;
+    unsigned char* buf;
+
+
+    ppm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[qrtfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    /* Read in the QRT file.  First the header. */
+    cols = getc( ifp );
+    cols += getc( ifp ) << 8;
+    rows = getc( ifp );
+    rows += getc( ifp ) << 8;
+
+    if ( cols <= 0 || rows <= 0 )
+	pm_error( "invalid size: %d %d", cols, rows );
+    maxval = 255;
+
+    ppm_writeppminit( stdout, cols, rows, maxval, 0 );
+    pixelrow = ppm_allocrow( cols );
+    buf = (unsigned char *) malloc( 3 * cols );
+    if ( buf == (unsigned char *) 0 )
+	pm_error( "out of memory" );
+
+    for ( row = 0; row < rows; row++ )
+	{
+        (void) getc( ifp );	/* discard */
+        (void) getc( ifp );	/* linenum */
+	if ( fread( buf, 3 * cols, 1, ifp ) != 1 )
+	    pm_error( "EOF / read error" );
+	for ( col = 0; col < cols; col++ )
+	    PPM_ASSIGN(
+		pixelrow[col], buf[col], buf[cols + col], buf[2 * cols + col] );
+	ppm_writeppmrow( stdout, pixelrow, cols, maxval, 0 );
+	}
+
+    pm_close( ifp );
+    pm_close( stdout );
+
+    exit( 0 );
+    }
diff --git a/converter/ppm/rawtoppm.c b/converter/ppm/rawtoppm.c
new file mode 100644
index 00000000..8d6c3b2c
--- /dev/null
+++ b/converter/ppm/rawtoppm.c
@@ -0,0 +1,244 @@
+/* rawtoppm.c - convert raw RGB bytes into a portable pixmap
+**
+** Copyright (C) 1991 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <math.h>
+#include "ppm.h"
+
+static void dorowskip ARGS(( FILE* ifp, int rowskip ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    pixel* pixrow;
+    register pixel* pP;
+    int argn, headerskip, rowskip, rows, cols, row, i;
+    register int col;
+    int order;
+#define ORD_RGB 1
+#define ORD_RBG 2
+#define ORD_GRB 3
+#define ORD_GBR 4
+#define ORD_BRG 5
+#define ORD_BGR 6
+int interleave;
+#define INT_PIX 1
+#define INT_ROW 2
+    int val1, val2, val3;
+    gray* grow1;
+    gray* grow2;
+    gray* grow3;
+    register gray* g1P;
+    register gray* g2P;
+    register gray* g3P;
+    const char* const usage = "[-headerskip N] [-rowskip N] [-rgb|-rbg|-grb|-gbr|-brg|-bgr] [-interpixel|-interrow] <width> <height> [rawfile]";
+
+
+    ppm_init( &argc, argv );
+
+    argn = 1;
+    headerskip = 0;
+    rowskip = 0;
+    order = ORD_RGB;
+    interleave = INT_PIX;
+
+    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
+	{
+	if ( pm_keymatch( argv[argn], "-headerskip", 2 ) )
+	    {
+	    ++argn;
+	    if ( argn >= argc )
+		pm_usage( usage );
+	    headerskip = atoi( argv[argn] );
+	    }
+	else if ( pm_keymatch( argv[argn], "-rowskip", 3 ) )
+	    {
+	    ++argn;
+	    if ( argn >= argc )
+		pm_usage( usage );
+	    rowskip = atoi( argv[argn] );
+	    }
+	else if ( pm_keymatch( argv[argn], "-rgb", 3 ) )
+	    order = ORD_RGB;
+	else if ( pm_keymatch( argv[argn], "-rbg", 3 ) )
+	    order = ORD_RBG;
+	else if ( pm_keymatch( argv[argn], "-grb", 3 ) )
+	    order = ORD_GRB;
+	else if ( pm_keymatch( argv[argn], "-gbr", 3 ) )
+	    order = ORD_GBR;
+	else if ( pm_keymatch( argv[argn], "-brg", 3 ) )
+	    order = ORD_BRG;
+	else if ( pm_keymatch( argv[argn], "-bgr", 3 ) )
+	    order = ORD_BGR;
+	else if ( pm_keymatch( argv[argn], "-interpixel", 7 ) )
+	    interleave = INT_PIX;
+	else if ( pm_keymatch( argv[argn], "-interrow", 7 ) )
+	    interleave = INT_ROW;
+	else
+	    pm_usage( usage );
+	++argn;
+	}
+
+    if ( argn + 2 > argc )
+	pm_usage( usage );
+
+    cols = atoi( argv[argn++] );
+    rows = atoi( argv[argn++] );
+    if ( cols <= 0 || rows <= 0 )
+	pm_usage( usage );
+
+    if ( argn < argc )
+	{
+	ifp = pm_openr( argv[argn] );
+	++argn;
+	}
+    else
+	ifp = stdin;
+
+    if ( argn != argc )
+	pm_usage( usage );
+
+    ppm_writeppminit( stdout, cols, rows, (pixval) 255, 0 );
+    pixrow = ppm_allocrow( cols );
+
+    if ( interleave == INT_ROW )
+	{
+	grow1 = pgm_allocrow( cols );
+	grow2 = pgm_allocrow( cols );
+	grow3 = pgm_allocrow( cols );
+	}
+
+    for ( i = 0; i < headerskip; ++i )
+	{
+	val1 = getc( ifp );
+	if ( val1 == EOF )
+	    pm_error( "EOF / read error" );
+	}
+
+    for ( row = 0; row < rows; ++row)
+	{
+	switch ( interleave )
+	    {
+	    case INT_PIX:
+	    for ( col = 0, pP = pixrow; col < cols; ++col, ++pP )
+		{
+		val1 = getc( ifp );
+		if ( val1 == EOF )
+		    pm_error( "EOF / read error" );
+		val2 = getc( ifp );
+		if ( val2 == EOF )
+		    pm_error( "EOF / read error" );
+		val3 = getc( ifp );
+		if ( val3 == EOF )
+		    pm_error( "EOF / read error" );
+		switch ( order )
+		    {
+		    case ORD_RGB:
+		    PPM_ASSIGN( *pP, val1, val2, val3 );
+		    break;
+		    case ORD_RBG:
+		    PPM_ASSIGN( *pP, val1, val3, val2 );
+		    break;
+		    case ORD_GRB:
+		    PPM_ASSIGN( *pP, val2, val1, val3 );
+		    break;
+		    case ORD_GBR:
+		    PPM_ASSIGN( *pP, val3, val1, val2 );
+		    break;
+		    case ORD_BRG:
+		    PPM_ASSIGN( *pP, val2, val3, val1 );
+		    break;
+		    case ORD_BGR:
+		    PPM_ASSIGN( *pP, val3, val2, val1 );
+		    break;
+		    }
+		}
+	    dorowskip( ifp, rowskip );
+	    break;
+
+	    case INT_ROW:
+	    for ( col = 0, g1P = grow1; col < cols; ++col, ++g1P )
+		{
+		val1 = getc( ifp );
+		if ( val1 == EOF )
+		    pm_error( "EOF / read error" );
+		*g1P = val1;
+		}
+	    dorowskip( ifp, rowskip );
+	    for ( col = 0, g2P = grow2; col < cols; ++col, ++g2P )
+		{
+		val2 = getc( ifp );
+		if ( val2 == EOF )
+		    pm_error( "EOF / read error" );
+		*g2P = val2;
+		}
+	    dorowskip( ifp, rowskip );
+	    for ( col = 0, g3P = grow3; col < cols; ++col, ++g3P )
+		{
+		val3 = getc( ifp );
+		if ( val3 == EOF )
+		    pm_error( "EOF / read error" );
+		*g3P = val3;
+		}
+	    dorowskip( ifp, rowskip );
+	    for ( col = 0, pP = pixrow, g1P = grow1, g2P = grow2, g3P = grow3;
+		  col < cols; ++col, ++pP, ++g1P, ++g2P, ++g3P )
+		{
+		switch ( order )
+		    {
+		    case ORD_RGB:
+		    PPM_ASSIGN( *pP, *g1P, *g2P, *g3P );
+		    break;
+		    case ORD_RBG:
+		    PPM_ASSIGN( *pP, *g1P, *g3P, *g2P );
+		    break;
+		    case ORD_GRB:
+		    PPM_ASSIGN( *pP, *g2P, *g1P, *g3P );
+		    break;
+		    case ORD_GBR:
+		    PPM_ASSIGN( *pP, *g3P, *g1P, *g2P );
+		    break;
+		    case ORD_BRG:
+		    PPM_ASSIGN( *pP, *g2P, *g3P, *g1P );
+		    break;
+		    case ORD_BGR:
+		    PPM_ASSIGN( *pP, *g3P, *g2P, *g1P );
+		    break;
+		    }
+		}
+	    break;
+	    }
+	ppm_writeppmrow( stdout, pixrow, cols, (pixval) 255, 0 );
+	}
+
+    pm_close( ifp );
+    pm_close( stdout );
+
+    exit( 0 );
+    }
+
+static void
+dorowskip( ifp, rowskip )
+    FILE* ifp;
+    int rowskip;
+    {
+    int i, val;
+
+    for ( i = 0; i < rowskip; ++i )
+	{
+	val = getc( ifp );
+	if ( val == EOF )
+	    pm_error( "EOF / read error" );
+	}
+    }
diff --git a/converter/ppm/rgb3toppm.c b/converter/ppm/rgb3toppm.c
new file mode 100644
index 00000000..a666553c
--- /dev/null
+++ b/converter/ppm/rgb3toppm.c
@@ -0,0 +1,88 @@
+/* rgb3toppm - combine three portable graymaps into one portable pixmap
+**
+** Copyright (C) 1991 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "ppm.h"
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* rfd;
+    FILE* gfd;
+    FILE* bfd;
+    gray* rrow;
+    gray* rP;
+    gray* grow;
+    gray* gP;
+    gray* brow;
+    gray* bP;
+    pixel* pixelrow;
+    register pixel* pP;
+    int rows, cols, trows, tcols, row, col;
+    gray rmaxval, gmaxval, bmaxval;
+    int rformat, gformat, bformat;
+    pixval pmaxval;
+
+
+    ppm_init( &argc, argv );
+
+    if ( argc != 4 )
+	pm_usage( "<red pgmfile> <green pgmfile> <blue pgmfile> " );
+
+    rfd = pm_openr( argv[1] );
+    gfd = pm_openr( argv[2] );
+    bfd = pm_openr( argv[3] );
+
+    pgm_readpgminit( rfd, &cols, &rows, &rmaxval, &rformat );
+    pgm_readpgminit( gfd, &tcols, &trows, &gmaxval, &gformat );
+    if ( tcols != cols || trows != rows )
+	pm_error( "all three graymaps must be the same size" );
+    pgm_readpgminit( bfd, &tcols, &trows, &bmaxval, &bformat );
+    if ( tcols != cols || trows != rows )
+	pm_error( "all three graymaps must be the same size" );
+
+    pmaxval = rmaxval;
+    if ( gmaxval > pmaxval ) pmaxval = gmaxval;
+    if ( bmaxval > pmaxval ) pmaxval = bmaxval;
+    rrow = pgm_allocrow( cols );
+    grow = pgm_allocrow( cols );
+    brow = pgm_allocrow( cols );
+
+    ppm_writeppminit( stdout, cols, rows, pmaxval, 0 );
+    pixelrow = ppm_allocrow( cols );
+
+    for ( row = 0; row < rows; row++ )
+	{
+	pgm_readpgmrow( rfd, rrow, cols, rmaxval, rformat );
+	pgm_readpgmrow( gfd, grow, cols, gmaxval, gformat );
+	pgm_readpgmrow( bfd, brow, cols, bmaxval, bformat );
+
+	for ( col = 0, rP = rrow, gP = grow, bP = brow, pP = pixelrow;
+	      col < cols;
+	      ++col, ++rP, ++gP, ++bP, ++pP )
+	    {
+	    if ( rmaxval != pmaxval ) *rP = (int) *rP * pmaxval / rmaxval;
+	    if ( gmaxval != pmaxval ) *gP = (int) *gP * pmaxval / gmaxval;
+	    if ( bmaxval != pmaxval ) *bP = (int) *bP * pmaxval / bmaxval;
+	    PPM_ASSIGN( *pP, *rP, *gP, *bP );
+	    }
+	ppm_writeppmrow( stdout, pixelrow, cols, pmaxval, 0 );
+	}
+
+    pm_close( rfd );
+    pm_close( gfd );
+    pm_close( bfd );
+    pm_close( stdout );
+
+    exit( 0 );
+    }
diff --git a/converter/ppm/sldtoppm.c b/converter/ppm/sldtoppm.c
new file mode 100644
index 00000000..7da6d592
--- /dev/null
+++ b/converter/ppm/sldtoppm.c
@@ -0,0 +1,650 @@
+/*
+
+      Convert an AutoCAD slide (.sld) file to PPM format
+
+    An AutoCAD slide is a compressed sequence of  vectors  and  filled
+    polygons.   The  ppmdraw  package  is  used  to scan convert these
+    geometrical objects into a portable pixmap.
+
+    Author:
+        John Walker
+        Autodesk SA
+        Avenue des Champs-Montants 14b
+        CH-2074 MARIN
+        Switzerland
+        Usenet: kelvin@Autodesk.com
+        Fax:    038/33 88 15
+        Voice:  038/33 76 33
+
+    Permission  to  use, copy, modify, and distribute this software and
+    its documentation  for  any  purpose  and  without  fee  is  hereby
+    granted,  without any conditions or restrictions.  This software is
+    provided "as is" without express or implied warranty.
+
+*/
+
+#include <string.h>
+#include <math.h>
+
+#include "ppm.h"
+#include "ppmdraw.h"
+#include "nstring.h"
+#ifdef DEBUG
+#include <assert.h>
+#else
+#define assert(x)
+#endif
+
+/*  Define a variable type accepting numbers -127 <= n <= 127.  But note
+    that we still expect it to act UNSIGNED. */
+
+#define smallint unsigned char        /* Small integers */
+
+#define TRUE     1
+#define FALSE    0
+
+#define EOS     '\0'
+
+/* Screen point */
+
+struct spoint {
+    int x, y;
+};
+
+/* Screen polygon */
+
+struct spolygon { 
+    int npoints,              /* Number of points in polygon */
+          fill;           /* Fill type */
+    struct spoint pt[11];         /* Actual points */
+};
+
+/* Screen vector */
+
+struct svector {
+    struct spoint f;          /* From point */
+    struct spoint t;          /* To point */
+};
+
+static int extend ARGS((smallint ch));
+static int sli ARGS((void));
+static int slib ARGS((void));
+static void vscale ARGS((int *px, int *py));
+static void slider ARGS((void (*slvec) ARGS((struct svector *vec, int color)),
+                    void (*slflood) ARGS((struct spolygon *poly, int color)) ));
+static void slidefind ARGS((char *sname, int dironly, int ucasen));
+static void draw ARGS((struct svector *vec, int color));
+static void flood ARGS((struct spolygon *poly, int color));
+
+static int ixdots, iydots;        /* Screen size in dots */
+static FILE *slfile;              /* Slide file descriptor */
+static int blither = FALSE;       /* Dump slide file information ? */
+static int info = FALSE;          /* Print header information */
+static pixel **pixels;            /* Pixel map */
+static int pixcols, pixrows;          /* Pixel map size */
+#define pixmaxval 255             /* Largest pixel value */
+static double uscale = -1;        /* Uniform scale factor */
+static int sxsize = -1, sysize = -1;  /* Scale to X, Y size ? */
+
+#include "autocad.h"                  /* AutoCAD standard color assignments */
+
+/*  Local variables  */
+
+struct slhead {
+    char slh[17];             /* Primate-readable header */
+    char sntype;              /* Machine type (for number compat) */
+    char slevel;              /* Format type */
+    short sxdots, sydots;         /* Display X, Y dots */
+    double sdsar;             /* Display aspect ratio */
+    short shwfill;            /* Display hardware fill type */
+    char spad;                /* Pad to even byte length */
+};
+
+static int adjust = FALSE;        /* Adjust to correct aspect ratio ? */
+static struct slhead slfrof;          /* Slide file header */
+static long xfac, yfac;           /* Aspect ratio scale factors */
+
+static int sdrawkcab;             /* Slide drawing kinematic conversion of
+                     ass-backwards data flag */
+
+/*  EXTEND  --  Turn a smallint into an int with sign extension, whether
+        or not that happens automatically.  */
+
+static int extend(smallint ch)
+{
+    return ((int) ((ch & 0x80) ? (ch | ~0xFF) : ch));
+}
+
+/*  SLI  --  Input word from slide file  */
+
+static int sli()
+{
+    short wd;
+
+    if (fread(&wd, sizeof wd, 1, slfile) != 1) {
+        pm_error("error reading slide file");
+    } else {
+    if (sdrawkcab) {
+        wd = ((wd >> 8) & 0xFF) | (wd << 8);
+    }
+    }
+    return wd;
+}
+
+/*  SLIB  --  Input byte from slide file  */
+
+static int slib()
+{
+    smallint ch = 0;
+
+    if (fread(&ch, sizeof ch, 1, slfile) != 1) {
+        pm_error("error reading slide file");
+    }
+    return extend(ch);
+}
+
+/*  VSCALE -- scale screen coordinates for mismatched display.  */
+
+static void vscale(px, py)
+  int *px, *py;
+{
+    *px = (((unsigned) *px) * xfac) >> 16;
+    *py = (((unsigned) *py) * yfac) >> 16;
+}
+
+/*  SLIDER  --  Read slide file.  This is called with the name of the
+        file to be read and function pointers to the routines
+        which process vectors and polygon fill requests
+        respectively.
+*/
+
+static void slider(slvec, slflood)
+  void (*slvec) ARGS((struct svector *vec, int color));
+  void (*slflood) ARGS((struct spolygon *poly, int color));
+{
+    int i, rescale;
+    unsigned char ubfr[4];        /* Utility character buffer */
+    int lx, ly;               /* Last x and y point */
+    int slx, sly;             /* Last x and y scaled screen point */
+    struct svector vec;           /* Screen vector */
+    struct spolygon poly;         /* Screen polygon */
+    unsigned short cw;            /* Control word */
+    double dsar;              /* Screen aspect ratio */
+    long ldsar;               /* Scaled long DSAR */
+    short rtest;              /* Value to test byte reversal */
+    short btest = 0x1234;         /* Value to test byte-reversal */
+    static struct slhead slhi =       /* Master slide header sample */
+        {"AutoCAD Slide\r\n\32", 86,2, 0,0, 0.0, 0};
+    int curcolor = 7;             /* Current vector color */
+    pixel rgbcolor;           /* Pixel used to clear pixmap */
+
+    lx = ly = 32000;
+
+    /* Process the header of the slide file.  */
+
+    sdrawkcab = FALSE;            /* Initially guess byte order is OK */
+    fread(slfrof.slh, 17, 1, slfile);
+    fread(&slfrof.sntype, sizeof(char), 1, slfile);
+    fread(&slfrof.slevel, sizeof(char), 1, slfile);
+    fread(&slfrof.sxdots, sizeof(short), 1, slfile);
+    fread(&slfrof.sydots, sizeof(short), 1, slfile);
+    fread(ubfr, 4, 1, slfile);
+    fread(&slfrof.shwfill, sizeof(short), 1, slfile);
+    fread(&rtest, sizeof rtest, 1, slfile);
+
+    /* Verify that slide format is compatible with this program. */
+
+    if (STREQ(slfrof.slh, slhi.slh)) {
+        pm_error("this is not an AutoCAD slide file.");
+    }
+
+    /* Verify that the number format and file level in the header  are
+       compatible.  All slides written by versions of AutoCAD released
+       since September of 1987 are compatible with this format.  */
+
+    if ((slfrof.sntype != slhi.sntype) || (slfrof.slevel != slhi.slevel)) {
+        pm_error("incompatible slide file format");
+    }
+
+    /* Build SDSAR value from long scaled version. */
+
+    ldsar = 0L;
+    for (i = 3; i >= 0; i--) {
+    ldsar = (ldsar << 8) | ubfr[i];
+    }
+    slfrof.sdsar = ((double) ldsar) / 1E7;
+
+    /* Examine the byte order test value.   If it's backwards, set the
+       byte-reversal flag and correct all of the values we've read  in
+       so far. */
+
+    if (btest != rtest) {
+    sdrawkcab = TRUE;
+#define rshort(x) x = ((x >> 8) & 0xFF) | (x << 8)
+    rshort(slfrof.sxdots);
+    rshort(slfrof.sydots);
+    rshort(slfrof.shwfill);
+#undef rshort
+    }
+
+    /* Dump the header if we're blithering. */
+
+    if (blither || info) {
+        pm_message("Slide file type %d, level %d, hwfill type %d.",
+        slfrof.sntype, slfrof.slevel, slfrof.shwfill);
+        pm_message("Original screen size %dx%d, aspect ratio %.3f.",
+        slfrof.sxdots + 1, slfrof.sydots + 1, slfrof.sdsar);
+        pm_message("Byte order is %s.",
+            sdrawkcab ? "being reversed" : "the same");
+    }
+
+    /* If the display aspect ratio indicates that the  pixels  on  the
+       sending  screen  were  not  square,  adjust  the  size  of  the
+       generated bitmap to correct the  aspect  ratio  to  square  the
+       pixels.
+
+       We  always  correct  the aspect ratio by adjusting the width of
+       the image.  This guarantees that output from the SHADE command,
+       which  is  essentially  scan-line  data written in vector form,
+       will not be corrupted. */
+
+    dsar = ((double) slfrof.sxdots) / slfrof.sydots;
+    if (fabs(slfrof.sdsar - dsar) > 0.0001) {
+    if (adjust) {
+        ixdots = slfrof.sxdots * (slfrof.sdsar / dsar) + 0.5;
+        iydots = slfrof.sydots;
+        dsar = ((double) ixdots) / iydots;
+    } else {
+            pm_message("Warning - pixels on source screen were non-square.");
+            pm_message("          Specifying -adjust will correct image width to compensate.");
+        ixdots = slfrof.sxdots;
+        iydots = slfrof.sydots;
+        dsar = slfrof.sdsar;
+    }
+    } else {
+    /* Source pixels were square. */
+    ixdots = slfrof.sxdots;
+    iydots = slfrof.sydots;
+    dsar = slfrof.sdsar;
+    adjust = FALSE;           /* Mark no adjustment needed */
+    }
+
+    /* If there's a uniform scale factor specified, apply it. */
+
+    if (uscale > 0) {
+    ixdots = (ixdots * uscale) + 0.5;
+    iydots = (iydots * uscale) + 0.5;
+    }
+
+    /* If the image is to be stretched  to  a  given  width,  set  the
+       output  image  sizes accordingly.  If only a height or width is
+       given, scale the other direction proportionally to preserve the
+       aspect ratio. */
+
+    if (sxsize > 0) {
+    if (sysize > 0) {
+        iydots = sysize - 1;
+    } else {
+        iydots = ((((long) iydots) * (sxsize - 1)) +
+              (iydots / 2)) / ixdots;
+    }
+    ixdots = sxsize - 1;
+    } else if (sysize > 0) {
+    if (sxsize > 0) {
+        ixdots = sxsize - 1;
+    } else {
+        ixdots = ((((long) ixdots) * (sysize - 1)) +
+              (ixdots / 2)) / iydots;
+    }
+    iydots = sysize - 1;
+    }
+
+    if (adjust) {
+    pm_message(
+            "Resized from %dx%d to %dx%d to correct pixel aspect ratio.",
+        slfrof.sxdots + 1, slfrof.sydots + 1, ixdots + 1, iydots + 1);
+    }
+
+    /* Allocate image buffer and clear it to black. */
+
+    pixels = ppm_allocarray(pixcols = ixdots + 1, pixrows = iydots + 1);
+    PPM_ASSIGN(rgbcolor, 0, 0, 0);
+    ppmd_filledrectangle(pixels, pixcols, pixrows, pixmaxval, 0, 0,
+                         pixcols, pixrows, PPMD_NULLDRAWPROC,
+                         (char *) &rgbcolor);
+
+    if ((rescale = slfrof.sxdots != ixdots ||
+    slfrof.sydots != iydots ||
+    slfrof.sdsar != dsar) != 0) {
+
+        /* Rescale all coords. so they'll look (more or less)
+       right on this display.  */
+
+    xfac = (ixdots + 1) * 0x10000L;
+    xfac /= (long) (slfrof.sxdots + 1);
+    yfac = (iydots + 1) * 0x10000L;
+    yfac /= (long) (slfrof.sydots + 1);
+    if (dsar < slfrof.sdsar) {
+        yfac = yfac * dsar / slfrof.sdsar;
+       } else {
+        xfac = xfac * slfrof.sdsar / dsar;
+    }
+    }
+
+    poly.npoints = 0;             /* No flood in progress. */
+
+    while ((cw = sli()) != 0xFC00) {
+    switch (cw & 0xFF00) {
+        case 0xFB00:          /*  Short vector compressed  */
+        vec.f.x = lx + extend(cw & 0xFF);
+        vec.f.y = ly + slib();
+        vec.t.x = lx + slib();
+        vec.t.y = ly + slib();
+        lx = vec.f.x;
+        ly = vec.f.y;
+        if (rescale) {
+            vscale(&vec.f.x, &vec.f.y);
+            vscale(&vec.t.x, &vec.t.y);
+        }
+        (*slvec)(&vec, curcolor);/* Draw vector on screen */
+        slx = vec.f.x;        /* Save scaled point */
+        sly = vec.f.y;
+        break;
+
+        case 0xFC00:          /*  End of file  */
+        break;
+
+        case 0xFD00:          /*  Flood command  */
+        vec.f.x = sli();
+        vec.f.y = sli();
+        if ((int) vec.f.y < 0) { /* start or end */
+            if (poly.npoints != 0) { /* end?  */
+            if (poly.npoints > 2 && poly.npoints < 11) {
+                (*slflood)(&poly, curcolor);
+            } else {
+                            pm_error("Bad polygon vertex count (%d)",
+                   poly.npoints);
+            }
+            poly.npoints = 0;
+            } else {
+            poly.fill = -vec.f.y;  /* Start */
+            }
+        } else {          /* Polygon vertex */
+            if (poly.npoints < 10) {
+            if (rescale) {
+                vscale(&vec.f.x, &vec.f.y);
+            }
+            poly.pt[poly.npoints].x = vec.f.x;
+            poly.pt[poly.npoints].y = vec.f.y;
+            }
+            poly.npoints++;
+        }
+        break;
+
+        case 0xFE00:          /*  Common endpoint compressed  */
+        vec.f.x = lx + extend(cw & 0xFF);
+        vec.f.y = ly + slib();
+        lx = vec.f.x;
+        ly = vec.f.y;
+        vec.t.x = slx;
+        vec.t.y = sly;
+        if (rescale) {
+            vscale(&vec.f.x, &vec.f.y);
+        }
+        (*slvec)(&vec, curcolor);/* Draw vector */
+        slx = vec.f.x;        /* Save scaled point */
+        sly = vec.f.y;
+        break;
+
+        case 0xFF00:          /*  Change color  */
+        curcolor = cw & 0xFF;
+        break;
+
+        default:              /*  Co-ordinates  */
+        lx = vec.f.x = cw;
+        ly = vec.f.y = sli();
+        vec.t.x = sli();
+        vec.t.y = sli();
+        if (rescale) {
+           vscale(&vec.f.x, &vec.f.y);
+           vscale(&vec.t.x, &vec.t.y);
+        }
+        (*slvec)(&vec, curcolor);
+        slx = vec.f.x;        /* Save scaled point */
+        sly = vec.f.y;
+        break;
+    }
+    }
+}
+
+/*  SLIDEFIND  --  Find  a  slide  in  a  library  or,  if  DIRONLY is
+           nonzero, print a directory listing of the  library.
+           If  UCASEN  is nonzero, the requested slide name is
+           converted to upper case. */
+
+static void slidefind(sname, dironly, ucasen)
+  char *sname;
+  int dironly, ucasen;
+{
+    char uname[32];
+    unsigned char libent[36];
+    long pos;
+
+    if (dironly) {
+        pm_message("Slides in library:");
+    } else {
+    int i;
+    char *ip = sname;
+
+    for (i = 0; i < 31; i++) {
+        char ch = *ip++;
+        if (ch == EOS) {
+        break;
+        }
+        if (ucasen && ISLOWER(ch)) {
+        ch = TOUPPER(ch);
+        }
+        uname[i] = ch;
+    }
+    uname[i] = EOS;
+    }
+
+    /* Read slide library header and verify. */
+
+    if ((fread(libent, 32, 1, slfile) != 1) ||
+        (!STREQ((char *)libent, "AutoCAD Slide Library 1.0\015\012\32"))) {
+        pm_error("not an AutoCAD slide library file.");
+    }
+    pos = 32;
+
+    /* Search for a slide with the requested name. */
+
+    while (TRUE) {
+    if ((fread(libent, 36, 1, slfile) != 1) ||
+        (strlen((char *)libent) == 0)) {
+        if (dironly) {
+        return;
+        }
+            pm_error("slide %s not in library.", sname);
+    }
+    pos += 36;
+    if (dironly) {
+            pm_message("  %s", libent);
+    } else if (STREQ((char *)libent, uname)) {
+        long dpos = (((((libent[35] << 8) | libent[34]) << 8) |
+                 libent[33]) << 8) | libent[32];
+        if ((slfile == stdin) || (fseek(slfile, dpos, 0) == -1)) {
+        dpos -= pos;
+
+        while (dpos-- > 0) {
+            (void) getc(slfile);
+        }
+        }
+        break;
+    }
+    }
+}
+
+/*  DRAW  --  Draw a vector in the given AutoCAD color.  */
+
+static void draw(vec, color)
+  struct svector *vec;
+  int color;
+{
+    pixel rgbcolor;
+
+    if (blither) {
+        pm_message("Vector (%d, %d) - (%d, %d)  Color %d",
+           vec->f.x, vec->f.y, vec->t.x, vec->t.y, color);
+    }
+    assert(vec->f.x >= 0 && vec->f.x < pixcols);
+    assert(vec->f.y >= 0 && vec->f.y < pixrows);
+    assert(vec->t.x >= 0 && vec->t.x < pixcols);
+    assert(vec->t.y >= 0 && vec->t.y < pixrows);
+    PPM_ASSIGN(rgbcolor,
+           acadcol[color][0], acadcol[color][1], acadcol[color][2]);
+    ppmd_line(pixels, pixcols, pixrows, pixmaxval,
+          vec->f.x, iydots - vec->f.y, vec->t.x, iydots - vec->t.y,
+          PPMD_NULLDRAWPROC,
+          (char *) &rgbcolor);
+}
+
+/*  FLOOD  --  Draw a filled polygon.  */
+
+static void
+flood(struct spolygon * const poly,
+      int               const color) {
+
+    int i;
+    struct fillobj * handle;
+    pixel rgbcolor;
+
+    handle = ppmd_fill_create();
+
+    if (blither) {
+        pm_message("Polygon: %d points, fill type %d, color %d",
+                   poly->npoints, poly->fill, color);
+        for (i = 0; i < poly->npoints; i++) {
+            pm_message("   Point %d:  (%d, %d)", i + 1,
+                       poly->pt[i].x, poly->pt[i].y);
+        }
+    }
+
+    PPM_ASSIGN(rgbcolor,
+               acadcol[color][0], acadcol[color][1], acadcol[color][2]);
+    for (i = 0; i < poly->npoints; i++) {
+        assert(poly->pt[i].x >= 0 && poly->pt[i].x < pixcols);
+        assert(poly->pt[i].y >= 0 && poly->pt[i].y < pixrows);
+        ppmd_line(pixels, pixcols, pixrows, pixmaxval,
+                  poly->pt[i].x, iydots - poly->pt[i].y, 
+                  poly->pt[(i + 1) % poly->npoints].x,
+                  iydots - poly->pt[(i + 1) % poly->npoints].y,
+                  ppmd_fill_drawproc, handle);
+    }
+    ppmd_fill(pixels, pixcols, pixrows, pixmaxval,
+              handle, PPMD_NULLDRAWPROC, (char *) &rgbcolor);
+
+    ppmd_fill_destroy(handle);
+}
+
+/*  Main program. */
+
+int main(argc, argv)
+  int argc;
+  char *argv[];
+{
+    int argn;
+    const char * const usage = "[-verbose] [-info] [-adjust] [-scale <s>]\n\
+                 [-dir] [-lib|-Lib <name>]\n\
+                 [-xsize|-width <x>] [-ysize|-height <y>] [sldfile]";
+    int scalespec = FALSE, widspec = FALSE, hgtspec = FALSE, dironly = FALSE,
+    ucasen;
+    char *slobber = (char *) 0;       /* Slide library item */
+
+
+    ppm_init(&argc, argv);
+    argn = 1;
+
+    while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') {
+        if (pm_keymatch(argv[argn], "-verbose", 2)) {
+        blither = TRUE;
+        } else if (pm_keymatch(argv[argn], "-adjust", 2)) {
+        adjust = TRUE;
+        } else if (pm_keymatch(argv[argn], "-dir", 2)) {
+        dironly = TRUE;
+        } else if (pm_keymatch(argv[argn], "-info", 2)) {
+        info = TRUE;
+        } else if (pm_keymatch(argv[argn], "-lib", 2)) {
+        if (slobber != (char *) 0) {
+                pm_error("already specified a library item");
+        }
+            ucasen = argv[argn][1] != 'L';
+        argn++;
+        if (argn == argc) {
+        pm_usage(usage);
+        }
+        slobber = argv[argn];
+        } else if (pm_keymatch(argv[argn], "-scale", 2)) {
+        if (scalespec) {
+                pm_error("already specified a scale factor");
+        }
+        argn++;
+            if ((argn == argc) || (sscanf(argv[argn], "%lf", &uscale) != 1))
+        pm_usage(usage);
+        if (uscale <= 0.0) {
+                pm_error("scale factor must be greater than 0");
+        }
+        scalespec = TRUE;
+        } else if (pm_keymatch(argv[argn], "-xsize", 2) ||
+                   pm_keymatch(argv[argn], "-width", 2)) {
+        if (widspec) {
+                pm_error("already specified a width/xsize");
+        }
+        argn++;
+            if ((argn == argc) || (sscanf(argv[argn], "%d", &sxsize) != 1))
+        pm_usage(usage);
+        widspec = TRUE;
+        } else if (pm_keymatch(argv[argn], "-ysize", 2) ||
+                   pm_keymatch(argv[argn], "-height", 2)) {
+        if (hgtspec) {
+                pm_error("already specified a height/ysize");
+        }
+        argn++;
+            if ((argn == argc) || (sscanf(argv[argn], "%d", &sysize) != 1))
+        pm_usage(usage);
+        hgtspec = TRUE;
+    } else {
+        pm_usage(usage);
+    }
+    argn++;
+    }
+
+    /* If a file name is specified, open it.  Otherwise read from
+       standard input. */
+
+    if (argn < argc) {
+    slfile = pm_openr(argv[argn]);
+    argn++;
+    } else {
+    slfile = stdin;
+    }
+
+    if (argn != argc) {           /* Extra bogus arguments ? */
+    pm_usage(usage);
+    }
+
+    /* If we're extracting an item from a slide library, position the
+       input stream to the start of the chosen slide. */
+
+    if (dironly || (slobber != (char *) 0)) {
+    slidefind(slobber, dironly, ucasen);
+    }
+
+    if (!dironly) {
+    slider(draw, flood);
+    ppm_writeppm(stdout, pixels, pixcols, pixrows, pixmaxval, FALSE);
+    }
+    pm_close(slfile);
+    pm_close(stdout);
+    exit(0);
+}
diff --git a/converter/ppm/spctoppm.c b/converter/ppm/spctoppm.c
new file mode 100644
index 00000000..3eea7821
--- /dev/null
+++ b/converter/ppm/spctoppm.c
@@ -0,0 +1,212 @@
+/* spctoppm.c - read a compressed Spectrum file and produce a portable pixmap
+**
+** Copyright (C) 1991 by Steve Belczyk and Jef Poskanzer
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "ppm.h"
+
+#define ROWS 200
+#define COLS 320
+#define MAXVAL 7
+
+static void DoBitmap ARGS(( FILE* ifp ));
+static void DoChar ARGS(( int n, char c ));
+static void DoColormap ARGS(( FILE* ifp ));
+
+static char screen[ROWS*COLS/2];
+static short sscreen[ROWS*COLS/4];
+static pixel pal[ROWS][48];
+static long colormap_length, bitmap_length;
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    char c1, c2;
+    pixel* pixelrow;
+    register pixel* pP;
+    int row, col;
+
+
+    ppm_init( &argc, argv );
+
+    /* Check args. */
+    if ( argc > 2 )
+	pm_usage( "[spcfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    /* Check SPC file header. */
+    c1 = getc( ifp );
+    c2 = getc( ifp );
+
+    if ( ( c1 != 'S' ) || ( c2 != 'P' ) )
+	pm_error( "not a Spectrum picture" );
+
+    /* Skip reserved bytes. */
+    getc( ifp );
+    getc( ifp );
+
+    /* Get length of bitmap data. */
+    (void) pm_readbiglong( ifp, &bitmap_length );
+
+    /* and colormap */
+    (void) pm_readbiglong( ifp, &colormap_length );
+
+    /* Process bitmap. */
+    DoBitmap( ifp );
+
+    /* Process colormap. */
+    DoColormap( ifp );
+
+    pm_close( ifp );
+
+    /* Write the PPM file. */
+    ppm_writeppminit( stdout, COLS, ROWS, (pixval) MAXVAL, 0 );
+    pixelrow = ppm_allocrow( COLS );
+
+    for ( row = 0; row < ROWS; ++row )
+	{
+	for ( col = 0, pP = pixelrow; col < COLS; ++col, ++pP )
+	    {
+	    int c, ind, b, plane, x1;
+
+	    /* Compute pixel value. */
+	    ind = ( 80 * row ) + ( ( col >> 4 ) << 2 );
+	    b = 0x8000 >> (col & 0xf);
+	    c = 0;
+	    for ( plane = 0; plane < 4; ++plane )
+		if ( b & sscreen[ind+plane] )
+		    c |= (1 << plane);
+
+	    /* Compute palette index. */
+	    x1 = 10 * c;
+	    if ( c & 1 )
+		x1 -= 5;
+	    else
+		++x1;
+	    if ( ( col >= x1 ) && ( col < ( x1 + 160 ) ) )
+		c += 16;
+	    if ( col >= ( x1 + 160 ) )
+		c += 32;
+
+	    /* Store the proper color. */
+	    *pP = pal[row][c];
+	    }
+	ppm_writeppmrow( stdout, pixelrow, COLS, (pixval) MAXVAL, 0 );
+	}
+
+    pm_close( stdout );
+
+    exit( 0 );
+    }
+
+static void
+DoBitmap( ifp )
+    FILE* ifp;
+    {
+    int i;
+    long count, data;
+    signed char h, c;
+
+    /* Zero out first scan line. */
+    for ( i = 0; i < 160; ++i )
+	screen[i] = 0;
+
+    /* 'count' counts number of input bytes. */
+    count = 0;
+
+    /* 'data' counts just data bytes. */
+    data = 0;
+
+    while ( count < bitmap_length )
+	{
+	/* Get next record header. */
+	h = getc( ifp );
+	++count;
+
+	if ( ( h >= 0 ) && ( count < bitmap_length ) )
+	    {
+	    for ( i = 0; i <= h; ++i )
+		{
+		c = getc( ifp );
+		++count;
+		DoChar( data, c );
+		++data;
+		}
+	    }
+	else if ( ( h < 0 ) && ( count < bitmap_length ) )
+	    {
+	    c = getc( ifp );
+	    ++count;
+
+	    for ( i = 0; i < ( 2 - h ); ++i )
+		{
+		DoChar( data, c );
+		++data;
+		}
+	    }
+    }
+
+    /* Convert the char version of the screen to short. */
+    for ( i = 0; i < ROWS*COLS/4; ++i )
+	sscreen[i] = ( screen[i<<1] << 8 ) + ( 0xff & screen[(i<<1)+1] );
+    }
+
+#if __STDC__
+static void
+DoChar( int n, char c )
+#else /*__STDC__*/
+static void
+DoChar( n, c )
+    int n;
+    char c;
+#endif /*__STDC__*/
+    {
+    int i;
+
+    /* Compute screen index. */
+    i = 160 + 2 * ( n / 7960 ) + 8 * ( ( n % 7960 ) / 2 ) + ( n & 1 );
+    screen[i] = c;
+    }
+
+static void
+DoColormap( ifp )
+    FILE* ifp;
+    {
+    int i, j, b;
+    short mask;
+
+    /* Clear first three palettes. */
+    for ( j = 0; j < 48; ++j )
+	PPM_ASSIGN( pal[0][j], 0, 0, 0 );
+
+    /* Read the palettes. */
+    for ( i = 1; i < ROWS; ++i )
+	for ( j = 0; j < 3; ++j )
+	    {
+	    (void) pm_readbigshort( ifp, &mask );
+	    for ( b = 0; b < 15; ++b )
+		if ( mask & ( 1 << b ) )
+		    {
+		    short k;
+		    (void) pm_readbigshort( ifp, &k );
+		    PPM_ASSIGN( pal[i][(j*16)+b],
+			( k & 0x700 ) >> 8,
+			( k & 0x070 ) >> 4,
+			( k & 0x007 ) );
+		    }
+	    }
+    }
diff --git a/converter/ppm/sputoppm.c b/converter/ppm/sputoppm.c
new file mode 100644
index 00000000..2671bffa
--- /dev/null
+++ b/converter/ppm/sputoppm.c
@@ -0,0 +1,108 @@
+/* sputoppm.c - read an uncompressed Spectrum file and produce a portable pixmap
+**
+** Copyright (C) 1991 by Steve Belczyk and Jef Poskanzer
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "ppm.h"
+
+#define ROWS 200
+#define COLS 320
+#define MAXVAL 7
+
+static pixel pal[ROWS][48];                /* Spectrum palettes, three per row */
+static short screen[ROWS*COLS/4];          /* simulates the Atari's video RAM */
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    int i, j;
+    pixel* pixelrow;
+    register pixel* pP;
+    int row, col;
+
+
+    ppm_init( &argc, argv );
+
+    /* Check args. */
+    if ( argc > 2 )
+        pm_usage( "[spufile]" );
+
+    if ( argc == 2 )
+        ifp = pm_openr( argv[1] );
+    else
+        ifp = stdin;
+
+    /* Read the SPU file */
+
+    /* Read the screen data. */
+    for ( i = 0; i < ROWS*COLS/4; ++i )
+        (void) pm_readbigshort( ifp, &screen[i] );
+
+    /* Clear the first palette line. */
+    for ( j = 0; j < 48; ++j )
+        PPM_ASSIGN( pal[0][j], 0, 0, 0 );
+
+    /* Read the palettes. */
+    for ( i = 1; i < ROWS; ++i )
+        for ( j = 0; j < 48; ++j )
+            {
+            short k;
+            (void) pm_readbigshort( ifp, &k );
+            PPM_ASSIGN( pal[i][j],
+                ( k & 0x700 ) >> 8,
+                ( k & 0x070 ) >> 4,
+                ( k & 0x007 ) );
+            }
+
+    pm_close( ifp );
+
+    /* Ok, get set for writing PPM. */
+    ppm_writeppminit( stdout, COLS, ROWS, (pixval) MAXVAL, 0 );
+    pixelrow = ppm_allocrow( COLS );
+
+    /* Now do the conversion. */
+    for ( row = 0; row < ROWS; ++row )
+        {
+        for ( col = 0, pP = pixelrow; col < COLS; ++col, ++pP )
+            {
+            int c, ind, b, plane, x1;
+
+            /* Compute pixel value. */
+            ind = 80 * row + ( ( col >> 4 ) << 2 );
+            b = 0x8000 >> (col & 0xf);
+            c = 0;
+            for ( plane = 0; plane < 4; ++plane )
+                if ( b & screen[ind+plane] )
+                    c |= (1 << plane);
+
+            /* Compute palette index. */
+            x1 = 10 * c;
+            if ( c & 1 )
+                x1 -= 5;
+            else
+                ++x1;
+            if ( ( col >= x1 ) && ( col < ( x1 + 160 ) ) )
+                c += 16;
+            if ( col >= ( x1 + 160 ) )
+                c += 32;
+
+            /* Store the proper color. */
+            *pP = pal[row][c];
+            }
+        ppm_writeppmrow( stdout, pixelrow, COLS, (pixval) MAXVAL, 0 );
+        }
+
+    pm_close( stdout );
+
+    exit( 0 );
+    }
diff --git a/converter/ppm/tgatoppm.c b/converter/ppm/tgatoppm.c
new file mode 100644
index 00000000..9f2bc4c0
--- /dev/null
+++ b/converter/ppm/tgatoppm.c
@@ -0,0 +1,462 @@
+/* tgatoppm.c - read a TrueVision Targa file and write a portable pixmap
+**
+** Partially based on tga2rast, version 1.0, by Ian MacPhedran.
+**
+** Copyright (C) 1989 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include <string.h>
+#include "ppm.h"
+#include "tga.h"
+#include "shhopt.h"
+#include "nstring.h"
+
+#define MAXCOLORS 16384
+
+static int mapped, rlencoded;
+
+static pixel ColorMap[MAXCOLORS];
+static gray AlphaMap[MAXCOLORS];
+static int RLE_count = 0, RLE_flag = 0;
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *input_filename;
+    unsigned int headerdump;
+    const char *alpha_filename;
+    unsigned int alpha_stdout;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                   struct cmdlineInfo *cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that many of the strings that this function returns in the
+   *cmdlineP structure are actually in the supplied argv array.  And
+   sometimes, one of these strings is actually just a suffix of an entry
+   in argv!
+-----------------------------------------------------------------------------*/
+    optEntry *option_def = malloc(100*sizeof(optEntry));
+        /* Instructions to OptParseOptions2 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int alpha_spec;
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "headerdump", OPT_FLAG,   NULL, &cmdlineP->headerdump,   0);
+    OPTENT3(0, "debug",      OPT_FLAG,   NULL, &cmdlineP->headerdump,   0);
+    OPTENT3(0, "alphaout",   OPT_STRING, &cmdlineP->alpha_filename,
+            &alpha_spec, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc - 1 == 0)
+        cmdlineP->input_filename = "=";  /* he wants stdin */
+    else if (argc - 1 == 1)
+        cmdlineP->input_filename = strdup(argv[1]);
+    else 
+        pm_error("Too many arguments.  The only argument accepted "
+                 "is the input file specification");
+
+    if (alpha_spec &&
+        STREQ(cmdlineP->alpha_filename, "-"))
+        cmdlineP->alpha_stdout = 1;
+    else 
+        cmdlineP->alpha_stdout = 0;
+
+    if (!alpha_spec)
+        cmdlineP->alpha_filename = NULL;
+}
+
+
+
+
+static unsigned char
+getbyte(FILE * const ifP) {
+
+    unsigned char c;
+
+    if ( fread( (char*) &c, 1, 1, ifP ) != 1 )
+        pm_error( "EOF / read error" );
+    
+    return c;
+}
+
+
+
+static void
+get_pixel(FILE * const ifP, pixel * dest, int Size, gray *alpha_p) {
+
+    static pixval Red, Grn, Blu;
+    static pixval Alpha;
+    unsigned char j, k;
+    static unsigned int l;
+
+    /* Check if run length encoded. */
+    if ( rlencoded )
+	{
+	if ( RLE_count == 0 )
+	    { /* Have to restart run. */
+	    unsigned char i;
+	    i = getbyte( ifP );
+	    RLE_flag = ( i & 0x80 );
+	    if ( RLE_flag == 0 )
+		/* Stream of unencoded pixels. */
+		RLE_count = i + 1;
+	    else
+		/* Single pixel replicated. */
+		RLE_count = i - 127;
+	    /* Decrement count & get pixel. */
+	    --RLE_count;
+	    }
+	else
+	    { /* Have already read count & (at least) first pixel. */
+	    --RLE_count;
+	    if ( RLE_flag != 0 )
+		/* Replicated pixels. */
+		goto PixEncode;
+	    }
+	}
+    /* Read appropriate number of bytes, break into RGB. */
+    switch ( Size )
+	{
+	case 8:				/* Grayscale, read and triplicate. */
+	Red = Grn = Blu = l = getbyte( ifP );
+    Alpha = 0;
+	break;
+
+	case 16:			/* 5 bits each of red green and blue. */
+	case 15:			/* Watch byte order. */
+	j = getbyte( ifP );
+	k = getbyte( ifP );
+	l = ( (unsigned int) k << 8 ) + j;
+	Red = ( k & 0x7C ) >> 2;
+	Grn = ( ( k & 0x03 ) << 3 ) + ( ( j & 0xE0 ) >> 5 );
+	Blu = j & 0x1F;
+    Alpha = 0;
+	break;
+
+	case 32:            /* 8 bits each of blue, green, red, and alpha */
+	case 24:			/* 8 bits each of blue, green, and red. */
+	Blu = getbyte( ifP );
+	Grn = getbyte( ifP );
+	Red = getbyte( ifP );
+	if ( Size == 32 )
+	    Alpha = getbyte( ifP );
+    else
+        Alpha = 0;
+	l = 0;
+	break;
+
+	default:
+	pm_error( "unknown pixel size (#2) - %d", Size );
+	}
+
+PixEncode:
+    if ( mapped ) {
+        *dest = ColorMap[l];
+        *alpha_p = AlphaMap[l];
+    } else {
+        PPM_ASSIGN( *dest, Red, Grn, Blu );
+        *alpha_p = Alpha;
+    }
+    }
+
+
+
+static void
+readtga(FILE * const ifP, struct ImageHeader * tgaP) {
+
+    unsigned char flags;
+    ImageIDField junk;
+
+    tgaP->IdLength = getbyte( ifP );
+    tgaP->CoMapType = getbyte( ifP );
+    tgaP->ImgType = getbyte( ifP );
+    tgaP->Index_lo = getbyte( ifP );
+    tgaP->Index_hi = getbyte( ifP );
+    tgaP->Length_lo = getbyte( ifP );
+    tgaP->Length_hi = getbyte( ifP );
+    tgaP->CoSize = getbyte( ifP );
+    tgaP->X_org_lo = getbyte( ifP );
+    tgaP->X_org_hi = getbyte( ifP );
+    tgaP->Y_org_lo = getbyte( ifP );
+    tgaP->Y_org_hi = getbyte( ifP );
+    tgaP->Width_lo = getbyte( ifP );
+    tgaP->Width_hi = getbyte( ifP );
+    tgaP->Height_lo = getbyte( ifP );
+    tgaP->Height_hi = getbyte( ifP );
+    tgaP->PixelSize = getbyte( ifP );
+    flags = getbyte( ifP );
+    tgaP->AttBits = flags & 0xf;
+    tgaP->Rsrvd = ( flags & 0x10 ) >> 4;
+    tgaP->OrgBit = ( flags & 0x20 ) >> 5;
+    tgaP->IntrLve = ( flags & 0xc0 ) >> 6;
+    
+    if ( tgaP->IdLength != 0 )
+        fread( junk, 1, (int) tgaP->IdLength, ifP );
+}
+
+
+
+static void
+get_map_entry(FILE * const ifP, pixel * Value, int Size, gray * Alpha) {
+
+    unsigned char j, k, r, g, b, a;
+
+    /* Read appropriate number of bytes, break into rgb & put in map. */
+    switch ( Size )
+	{
+	case 8:				/* Grayscale, read and triplicate. */
+        r = g = b = getbyte( ifP );
+        a = 0;
+        break;
+        
+	case 16:			/* 5 bits each of red green and blue. */
+	case 15:			/* Watch for byte order. */
+        j = getbyte( ifP );
+        k = getbyte( ifP );
+        r = ( k & 0x7C ) >> 2;
+        g = ( ( k & 0x03 ) << 3 ) + ( ( j & 0xE0 ) >> 5 );
+        b = j & 0x1F;
+        a = 0;
+        break;
+        
+	case 32:            /* 8 bits each of blue, green, red, and alpha */
+	case 24:			/* 8 bits each of blue green and red. */
+        b = getbyte( ifP );
+        g = getbyte( ifP );
+        r = getbyte( ifP );
+        if ( Size == 32 )
+            a = getbyte( ifP );
+        else 
+            a = 0;
+        break;
+        
+	default:
+        pm_error( "unknown colormap pixel size (#2) - %d", Size );
+	}
+    PPM_ASSIGN( *Value, r, g, b );
+    *Alpha = a;
+}
+
+
+
+static void
+dumpHeader(struct ImageHeader const tga_head) {
+    const char * imgTypeName;
+    switch(tga_head.ImgType) {
+    case TGA_Map:      imgTypeName = "TGA_Map";      break;
+    case TGA_RGB:      imgTypeName = "TGA_RGB";      break;
+    case TGA_Mono:     imgTypeName = "TGA_Mono";     break;
+    case TGA_RLEMap:   imgTypeName = "TGA_RLEMap";   break;
+    case TGA_RLERGB:   imgTypeName = "TGA_RLERGB";   break;
+    case TGA_RLEMono:  imgTypeName = "TGA_RLEMono";  break;
+    case TGA_CompMap:  imgTypeName = "TGA_CompMap";  break;
+    case TGA_CompMap4: imgTypeName = "TGA_CompMap4"; break;
+    default:           imgTypeName = "unknown";
+    }
+    pm_message( "IdLength = %d", (int) tga_head.IdLength );
+    pm_message( "CoMapType = %d", (int) tga_head.CoMapType );
+    pm_message( "ImgType = %d (%s)", (int) tga_head.ImgType, imgTypeName );
+    pm_message( "Index_lo = %d", (int) tga_head.Index_lo );
+    pm_message( "Index_hi = %d", (int) tga_head.Index_hi );
+    pm_message( "Length_lo = %d", (int) tga_head.Length_lo );
+    pm_message( "Length_hi = %d", (int) tga_head.Length_hi );
+    pm_message( "CoSize = %d", (int) tga_head.CoSize );
+    pm_message( "X_org_lo = %d", (int) tga_head.X_org_lo );
+    pm_message( "X_org_hi = %d", (int) tga_head.X_org_hi );
+    pm_message( "Y_org_lo = %d", (int) tga_head.Y_org_lo );
+    pm_message( "Y_org_hi = %d", (int) tga_head.Y_org_hi );
+    pm_message( "Width_lo = %d", (int) tga_head.Width_lo );
+    pm_message( "Width_hi = %d", (int) tga_head.Width_hi );
+    pm_message( "Height_lo = %d", (int) tga_head.Height_lo );
+    pm_message( "Height_hi = %d", (int) tga_head.Height_hi );
+    pm_message( "PixelSize = %d", (int) tga_head.PixelSize );
+    pm_message( "AttBits = %d", (int) tga_head.AttBits );
+    pm_message( "Rsrvd = %d", (int) tga_head.Rsrvd );
+    pm_message( "OrgBit = %d", (int) tga_head.OrgBit );
+    pm_message( "IntrLve = %d", (int) tga_head.IntrLve );
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    struct cmdlineInfo cmdline;
+    struct ImageHeader tga_head;
+    FILE* ifP;
+    FILE *imageout_file, *alpha_file;
+    int rows, cols, row, realrow, truerow, baserow;
+    int maxval;
+    pixel** pixels;   /* The image array in ppm format */
+    gray** alpha;     /* The alpha channel array in pgm format */
+
+    ppm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.input_filename);
+
+    if (cmdline.alpha_stdout)
+        alpha_file = stdout;
+    else if (cmdline.alpha_filename == NULL) 
+        alpha_file = NULL;
+    else
+        alpha_file = pm_openw(cmdline.alpha_filename);
+    
+    if (cmdline.alpha_stdout) 
+        imageout_file = NULL;
+    else
+        imageout_file = stdout;
+    
+    /* Read the Targa file header. */
+    readtga(ifP, &tga_head);
+
+    if (cmdline.headerdump) 
+        dumpHeader(tga_head);
+
+    rows = ((int) tga_head.Height_lo) + ((int) tga_head.Height_hi) * 256;
+    cols = ((int) tga_head.Width_lo)  + ((int) tga_head.Width_hi)  * 256;
+
+    switch (tga_head.ImgType) {
+	case TGA_Map:
+	case TGA_RGB:
+	case TGA_Mono:
+	case TGA_RLEMap:
+	case TGA_RLERGB:
+	case TGA_RLEMono:
+        break;
+	default:
+        pm_error("unknown Targa image type %d", tga_head.ImgType);
+	}
+    
+    if (tga_head.ImgType == TGA_Map ||
+        tga_head.ImgType == TGA_RLEMap ||
+        tga_head.ImgType == TGA_CompMap ||
+        tga_head.ImgType == TGA_CompMap4)
+	{ /* Color-mapped image */
+        if (tga_head.CoMapType != 1)
+            pm_error( 
+                "mapped image (type %d) with color map type != 1",
+                tga_head.ImgType );
+        mapped = true;
+        /* Figure maxval from CoSize. */
+        switch (tga_head.CoSize) {
+	    case 8:
+	    case 24:
+	    case 32:
+            maxval = 255;
+            break;
+
+	    case 15:
+	    case 16:
+            maxval = 31;
+            break;
+
+	    default:
+	    pm_error(
+		"unknown colormap pixel size - %d", tga_head.CoSize );
+	    }
+	} else { 
+        /* Not colormap, so figure maxval from PixelSize. */
+        mapped = false;
+        switch ( tga_head.PixelSize ) {
+	    case 8:
+	    case 24:
+	    case 32:
+            maxval = 255;
+            break;
+            
+	    case 15:
+	    case 16:
+            maxval = 31;
+            break;
+            
+	    default:
+            pm_error("unknown pixel size - %d", tga_head.PixelSize);
+	    }
+	}
+    
+    /* If required, read the color map information. */
+    if ( tga_head.CoMapType != 0 ) {
+        unsigned int i;
+        unsigned int temp1, temp2;
+
+        temp1 = tga_head.Index_lo + tga_head.Index_hi * 256;
+        temp2 = tga_head.Length_lo + tga_head.Length_hi * 256;
+        if ((temp1 + temp2 + 1) >= MAXCOLORS)
+            pm_error("too many colors - %d", (temp1 + temp2 + 1));
+        for (i = temp1; i < (temp1 + temp2); ++i)
+            get_map_entry(ifP, &ColorMap[i], (int) tga_head.CoSize,
+                          &AlphaMap[i]);
+	}
+
+    /* Check run-length encoding. */
+    if (tga_head.ImgType == TGA_RLEMap ||
+        tga_head.ImgType == TGA_RLERGB ||
+        tga_head.ImgType == TGA_RLEMono)
+        rlencoded = 1;
+    else
+        rlencoded = 0;
+    
+    /* Read the Targa file body and convert to portable format. */
+    pixels = ppm_allocarray( cols, rows );
+    alpha = pgm_allocarray( cols, rows );
+    truerow = 0;
+    baserow = 0;
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        realrow = truerow;
+        if (tga_head.OrgBit == 0)
+            realrow = rows - realrow - 1;
+        
+        for (col = 0; col < cols; ++col)
+            get_pixel(ifP, &(pixels[realrow][col]), (int) tga_head.PixelSize,
+                      &(alpha[realrow][col]));
+        if (tga_head.IntrLve == TGA_IL_Four)
+            truerow += 4;
+        else if (tga_head.IntrLve == TGA_IL_Two)
+            truerow += 2;
+        else
+            ++truerow;
+        if (truerow >= rows)
+            truerow = ++baserow;
+	}
+    pm_close(ifP);
+    
+    if (imageout_file) 
+        ppm_writeppm(imageout_file, pixels, cols, rows, (pixval) maxval, 0);
+    if (alpha_file)
+        pgm_writepgm(alpha_file, alpha, cols, rows, (pixval) maxval, 0);
+    if (imageout_file) 
+        pm_close(imageout_file);
+    if (alpha_file)
+        pm_close(alpha_file);
+
+    return 0;
+}
diff --git a/converter/ppm/vidtoppm.c b/converter/ppm/vidtoppm.c
new file mode 100644
index 00000000..f3c20404
--- /dev/null
+++ b/converter/ppm/vidtoppm.c
@@ -0,0 +1,270 @@
+/* Bryan got this from mm.ftp-cs.berkeley.edu from the package
+   mpeg-encode-1.5b-src under the name vidtoppm.c on March 30, 2000.  
+   The file was dated January 19, 1995.  
+
+   This program does not use the netpbm libraries, but generates its
+   output via the program rawtoppm.  If any work is ever done on it
+   (or, more to the point, any interest ever expressed in it), it
+   should be converted just to call ppmwrite(), etc. directly.
+
+   There was no attached documentation, but the program appears to 
+   convert from Parallax XVideo JPEG format to a sequence of PPM files.  It
+   does this conversion by putting each frame in a window and then 
+   reading it out of the window, using libXvid.
+
+   Because it requires special libraries and there is no known
+   requirement for it today, we are not including this program in the
+   standard netpbm build.  But the source code is here in case someone
+   is interested in it later.
+
+*/
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/* gcc -o playone playone.c -lX11 -lXvid -I/n/picasso/project/mm/xvideo/include
+ */
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <XPlxExt.h>
+
+#include "ppm.h"
+
+usage (p)
+char *p;
+
+{
+    fprintf (stderr, "Usage: %s filename width height start end outbase [quality]\n", p);
+    exit (1);
+}
+
+static char buffer[300000];
+
+Visual *
+FindFullColorVisual (dpy, depth)
+    Display *dpy;
+    int *depth;
+{
+  XVisualInfo vinfo;
+  XVisualInfo *vinfo_ret;
+  int numitems, maxdepth;
+  
+  vinfo.class = TrueColor;
+  
+  vinfo_ret = XGetVisualInfo(dpy, VisualClassMask, &vinfo, &numitems);
+  
+  if (numitems == 0) return NULL;
+
+  maxdepth = 0;
+  while(numitems > 0) {
+    if (vinfo_ret[numitems-1].depth > maxdepth) {
+      maxdepth = vinfo_ret[numitems-1 ].depth;
+    }
+    numitems--;
+  }
+  XFree(vinfo_ret);
+
+  if (maxdepth < 24) return NULL;
+
+  if (XMatchVisualInfo(dpy, DefaultScreen(dpy), maxdepth, 
+		       TrueColor, &vinfo)) {
+    *depth = maxdepth;
+    return vinfo.visual;
+  }
+  
+  return NULL;
+}
+
+Window
+CreateFullColorWindow (dpy, x, y, w, h)
+    Display *dpy;
+    int x, y, w, h;
+{
+    int depth;
+    Visual *visual;
+    XSetWindowAttributes xswa;
+    unsigned int mask;
+    unsigned int class;
+    int screen;
+
+    screen = XDefaultScreen(dpy);
+    class = InputOutput;	/* Could be InputOnly */
+    visual = FindFullColorVisual (dpy, &depth);
+    if (visual == NULL) {
+	return 0;
+    }
+    mask = CWBackPixel | CWColormap | CWBorderPixel;
+    xswa.colormap = XCreateColormap(dpy, XRootWindow(dpy, screen),
+		    visual, AllocNone);
+    xswa.background_pixel = BlackPixel(dpy, DefaultScreen(dpy));
+    xswa.border_pixel = WhitePixel(dpy, DefaultScreen(dpy));
+
+    return XCreateWindow(dpy, RootWindow(dpy, screen), x, y, w, h,
+	1, depth, class, visual, mask, &xswa);
+}
+
+main (argc, argv)
+int argc;
+char **argv;
+
+{
+  char *filename;
+  Display *dpy;
+  int screen;
+  Window root;
+  XEvent event;
+  GC gc;
+  Window win;
+  XPlxCImage image;
+  int size;
+  char *qTable;
+  FILE *inFile;
+  FILE *outFile;
+  extern char *malloc();
+  int fd, r1, r2, i, j, r;
+  int quality;
+  int start, end;
+  XImage *ximage;
+  char *tdata;
+  char *obase;
+  char ofname[256];
+  int height, width;
+  char command[256];
+
+  ppm_init(&argc, argv);
+
+  if ((argc != 7) && (argc != 8))usage (argv[0]);
+  filename = argv[1];
+
+  width = atoi(argv[2]);
+  height = atoi(argv[3]);
+
+  start = atoi(argv[4]);
+  end = atoi(argv[5]);
+
+  if ((start < 1) || (end < start)) {
+    perror ("Bad start and end values.");
+    exit();
+  }
+
+  obase = argv[6];
+
+  quality = 100;
+  
+  if (argc > 7)
+    quality = atoi (argv[7]);
+  
+  dpy = XOpenDisplay (NULL);
+  screen = DefaultScreen(dpy);
+  root = DefaultRootWindow(dpy);
+/*  gc = DefaultGC(dpy, screen); */
+/*  win = XCreateSimpleWindow (dpy, root, 0, 0, width, height,
+			     0, NULL, NULL);
+*/
+  win = CreateFullColorWindow(dpy, 0, 0, width+4, height+4);
+  gc = XCreateGC(dpy, win, 0, NULL);
+
+  if (!win ) {
+    perror ("Unable to create window");
+    exit(1);
+  }
+
+  XMapWindow (dpy, win);
+  XSelectInput (dpy, win, ExposureMask |ButtonPressMask);
+
+  size = MakeQTables(quality, &qTable);
+  XPlxPutTable(dpy, win, gc, qTable, size, 0);
+  XPlxPutTable(dpy, win, gc, qTable, size, 1);
+  XPlxVideoTag (dpy, win, gc, PLX_VIDEO);
+  
+  inFile = fopen(filename, "rb");
+  if (inFile == NULL) {
+    perror (filename);
+    exit (1);
+  }
+  fd = fileno(inFile);
+  wait(2);
+
+  for (i=0; i<start; i++) {
+    if (read (fd, &image, sizeof(image)) != sizeof(image)) {
+      perror("End of file.");
+      exit();
+    }
+    image.data = buffer;
+    if (read (fd, buffer, image.size) != image.size) {
+      perror("End of file.");
+      exit();
+    }
+  }
+    
+  for (i=start; i<=end; i++) {
+    fprintf(stdout, "GRABBING FRAME %d\n", i);
+
+    if (read (fd, &image, sizeof(image)) != sizeof(image)) {
+      perror("End of file.");
+      exit();
+    }
+    image.data = buffer;
+    if (read (fd, buffer, image.size) != image.size) {
+      perror("End of file.");
+      exit();
+    }
+    
+    XPlxPutCImage (dpy, win, gc, &image, 0, 0, image.width,
+		   image.height, 0, 0, width+2, height+2, 1);
+
+    XFlush(dpy);
+
+    ximage = XGetImage(dpy, win, 0, 0, width, height, 0x00ffffff,
+		       ZPixmap);
+    
+    if (i == 0) {
+      fprintf(stderr, "Depth %d\n", ximage->depth);
+      fprintf(stderr, "Height: %d Width: %d\n", height, width );
+    }
+    tdata = ximage->data;
+
+
+    sprintf(ofname, "%s%d.ppm", obase, i);
+    outFile = fopen("/tmp/foobar", "wb");
+    if (!outFile) {
+      perror("Couldn't open output file.");
+    }
+
+    for (r=0; r<height; r++) {
+      for (j=0; j<width; j++) {
+	fputc(*(tdata+3), outFile);
+	fputc(*(tdata+2), outFile);
+	fputc(*(tdata+1), outFile);
+	tdata += 4;
+      }
+    }
+
+    fclose(outFile);
+
+    free(tdata);
+
+    sprintf(command, "rawtoppm %d %d < /tmp/foobar > %s",
+	    width, height, ofname);
+    system(command);
+  }
+}
diff --git a/converter/ppm/winico.h b/converter/ppm/winico.h
new file mode 100644
index 00000000..4b8ac38b
--- /dev/null
+++ b/converter/ppm/winico.h
@@ -0,0 +1,88 @@
+#ifndef WINICO_H_INCLUDED
+#define WINICO_H_INCLUDED
+
+/* A specification for the Windows icon format is at   (2000.06.08)
+
+   http://www.daubnet.com/formats/ICO.html
+
+*/
+
+typedef unsigned char      u1;
+typedef unsigned short int u2;
+typedef unsigned int       u4;
+
+typedef struct MS_Ico_ *          MS_Ico;
+typedef struct IC_Entry_ *        IC_Entry;
+typedef struct IC_InfoHeader_ *   IC_InfoHeader;
+typedef struct IC_Color_ *        IC_Color;
+/* Not part of the spec, but useful in constructing the icon. */
+typedef struct IC_Palette_ *      IC_Palette;
+typedef struct ICON_bmp_ *        ICON_bmp;
+
+struct MS_Ico_ {
+   u2 reserved;
+   u2 type;
+   u2 count;
+   IC_Entry * entries;
+};
+
+
+struct IC_Entry_ {
+   u1 width;
+   u1 height;
+   /*
+    * color_count is actually a byte (u1)... but 0 = 256, so I've used a short (u2).
+    */
+   u2 color_count;
+   u1 reserved;
+   u2 planes;
+   u2 bitcount;
+   u4 size_in_bytes;
+   u4 file_offset;
+   IC_InfoHeader ih;
+   IC_Color * colors;
+   /*
+    * Below here, I have useful fields which aren't in the spec, but 
+    * save having to keep stoopid amounts of global data.
+    */
+   u1 * andBitmap;        /* Used in reader. */
+   u1 * xorBitmap;
+   int xBytesXor;         /* Not used in reading, but saved for writing. */
+   int xBytesAnd;         /* Not used in reading, but saved for writing. */
+   u1 ** andBitmapOut;    /* it's just easier to use a 2d array in the code.*/
+   u1 ** xorBitmapOut;    /* Sorry! :) */
+};
+
+struct IC_InfoHeader_ {
+   u4 size;
+   u4 width;
+   u4 height;
+   u2 planes;
+   u2 bitcount;
+   u4 compression;
+   u4 imagesize;
+   u4 x_pixels_per_m;
+   u4 y_pixels_per_m;
+   u4 colors_used;
+   u4 colors_important;
+};
+
+struct IC_Color_ {
+   u1 red;
+   u1 green;
+   u1 blue;
+   u1 reserved;
+};
+
+struct IC_Palette_ {
+   u4 col_amount;
+   IC_Color * colors;
+};
+
+struct ICON_bmp_ {
+   int xBytes;
+   u4 size;    /* just col_amount * height, but save calculating too often. */
+   u1 ** data;
+};
+
+#endif
diff --git a/converter/ppm/winico.html b/converter/ppm/winico.html
new file mode 100644
index 00000000..6e33874b
--- /dev/null
+++ b/converter/ppm/winico.html
@@ -0,0 +1,701 @@
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<BASE HREF="http://www.oreilly.com/centers/gff/formats/miccur/"><table border=1 width=100%><tr><td><table border=1 bgcolor=#ffffff cellpadding=10 cellspacing=0 width=100% color=#ffffff><tr><td><font face=arial,sans-serif color=black size=-1>This is <b><font color=#0039b6>G</font> <font color=#c41200>o</font> <font color=#f3c518>o</font> <font color=#0039b6>g</font> <font color=#30a72f>l</font> <font color=#c41200>e</font></b>'s <a href="http://www.google.com/help/features.html#cached"><font color=blue>cache</font></a> of <A HREF="http://www.oreilly.com/centers/gff/formats/miccur/"><font color=blue>http://www.oreilly.com/centers/gff/formats/miccur/</font></a>.<br>
+<b><font color=#0039b6>G</font> <font color=#c41200>o</font> <font color=#f3c518>o</font> <font color=#0039b6>g</font> <font color=#30a72f>l</font> <font color=#c41200>e</font></b>'s cache is the snapshot that we took of the page as we crawled the web.<br>
+The page may have changed since that time.  Click here for the <A HREF="http://www.oreilly.com/centers/gff/formats/miccur/"><font color=blue>current page</font></a> without highlighting.<br>To link to or bookmark this page, use the following url: <code>http://www.google.com/search?q=cache:dgpSCZ8_GJwC:www.oreilly.com/centers/gff/formats/miccur/+windows++icon+transparent&amp;hl=en&amp;ie=UTF-8</code></font><br><br><center><font size=-2><i>Google is not affiliated with the authors of this page nor responsible for its content.</i></font></center></td></tr>
+<tr><td>
+<table border=0 cellpadding=0 cellspacing=0><tr><td><font face=arial,sans-serif color=black size=-1>These search terms have been highlighted:&nbsp;</font></td><td bgcolor=#ffff66><B><font face=arial,sans-serif color=black size=-1>windows&nbsp;</font></B></td><td bgcolor=#A0FFFF><B><font face=arial,sans-serif color=black size=-1>icon&nbsp;</font></B></td><td bgcolor=#99ff99><B><font face=arial,sans-serif color=black size=-1>transparent&nbsp;</font></B></td></tr></table>
+</td></tr></table></td></tr></table>
+<hr>
+<html>
+<head>
+<title>GFF Format Summary: Microsoft Windows Cursor and Icon</title>
+</head>
+<body>
+<h1><A NAME="SPEC-MICCUR">Microsoft <B style="color:black;background-color:#ffff66">Windows</B> Cursor and <B style="color:black;background-color:#A0FFFF">Icon</B></A></h1>
+<p>
+<b>Also Known As:</b> CUR, ICO
+<p>
+<hr>
+<p>
+
+<p>
+<table border=0>
+<tr valign=top>
+  <td align=left><b>Type</b></td>
+  <td align=left>Bitmap</td>
+</tr>
+<tr valign=top>
+  <td align=left><b>Colors</b></td>
+  <td align=left>1-bit and 4-bit</td>
+</tr>
+<tr valign=top>
+  <td align=left><b>Compression</b></td>
+  <td align=left>None</td>
+</tr>
+<tr valign=top>
+  <td align=left><b>Maximum Image Size</b></td>
+  <td align=left>4Gx4G pixels</td>
+</tr>
+<tr valign=top>
+  <td align=left><b>Multiple Images Per File</b></td>
+  <td align=left>Yes</td>
+</tr>
+<tr valign=top>
+  <td align=left><b>Numerical Format</b></td>
+  <td align=left>Little-endian</td>
+</tr>
+<tr valign=top>
+  <td align=left><b>Originator</b></td>
+  <td align=left>Microsoft</td>
+</tr>
+<tr valign=top>
+  <td align=left><b>Platform</b></td>
+  <td align=left>Microsoft <B style="color:black;background-color:#ffff66">Windows</B></td>
+</tr>
+<tr valign=top>
+  <td align=left><b>Supporting Applications</b></td>
+  <td align=left>Microsoft <B style="color:black;background-color:#ffff66">Windows</B></td>
+</tr>
+<tr valign=top>
+  <td align=left><b>See Also</b></td>
+  <td align=left><A HREF="gffse:/format.micbmp">Microsoft Bitmap</A>, <A HREF="gffse:/format.os2bmp">OS/2 Bitmap</A>, <A HREF="gffse:/format.micriff">Microsoft RIFF</A></td>
+</tr>
+</table>
+<p>
+<b>Usage</b><br>
+The <B style="color:black;background-color:#ffff66">Windows</B> environment uses icons as graphical links to objects
+(data files and executable programs). Cursors are used by the pointing device
+as graphical indications of the current state of the <B style="color:black;background-color:#ffff66">Windows</B> environment.
+<p>
+<b>Comments</b><br>
+<B style="color:black;background-color:#ffff66">Windows</B> icons and cursors are almost identical in format.
+In fact, cursor and <B style="color:black;background-color:#A0FFFF">icon</B> bitmaps can be used interchangeably.
+<p>
+<hr>
+<p>
+<A NAME="MICCUR-DMYID.1"><!-- Overview anchor --></A>
+<p>
+In an object-oriented graphical user interface--as Microsoft
+<B style="color:black;background-color:#ffff66">Windows</B> is sometimes described--icons are small bitmaps containing an
+iconographic picture that makes an object accessible to the user. The
+"objects" made available by icons are: collections of data (files);
+executable programs (more files); and peripheral devices (the toys
+you have hooked up to your computer). Rather than typing a file or
+program name on a command line, applications are started and files
+manipulated by clicking on an <B style="color:black;background-color:#A0FFFF">icon</B>, or "dragging and dropping" an
+<B style="color:black;background-color:#A0FFFF">icon</B> on to a window or another <B style="color:black;background-color:#A0FFFF">icon</B>. 
+<p>
+A cursor, or pointer as they are also called, is a special type of
+<B style="color:black;background-color:#A0FFFF">icon</B> that is used to track the location of the pointing device on the
+user interface. The cursor changes its appearance to indicate the
+state of the system and the user interface. Such states include that
+a search is in progress, an application is starting or stopping, or a
+user-initiated action is being processed. The appearance of the
+cursor also indicates what action may be performed based on the
+current position of the pointer on the display. For example, actions
+such as resizing a window or entering text may only be performed at
+specific locations within a window.
+<p>
+Although a wide variety of physical pointing devices (mouse,
+joystick, trackball, touch pad, light pen, pointing stick, etc.) are
+typically supported by a user interface, the input received by
+<B style="color:black;background-color:#ffff66">Windows</B> is identical from all types of pointing devices.
+<p>
+<P>
+<B>Contents:</B><br>
+<A HREF="#MICCUR-DMYID.2">File Organization</A><br>
+<A HREF="#MICCUR-DMYID.3">File Details</A><br>
+<A HREF="#MICCUR-DMYID.4">For Further Information</A><br>
+<p>
+Icons and cursors themselves are types of resources available in the
+<B style="color:black;background-color:#ffff66">Windows</B> environment. When an application makes a request for <B style="color:black;background-color:#ffff66">Windows</B>
+to display an <B style="color:black;background-color:#A0FFFF">icon</B> or cursor, <B style="color:black;background-color:#ffff66">Windows</B> must locate the appropriate
+file and choose which of the bitmaps stored in the file best fits the
+resolution and color depth of the display. 
+<p>
+The <B style="color:black;background-color:#A0FFFF">icon</B> displayed is chosen based on its size and number of colors.
+For example, a two-color <B style="color:black;background-color:#A0FFFF">icon</B> 16x16 pixels in size might look good on
+a monochrome display at 640x480 resolution, but the same <B style="color:black;background-color:#A0FFFF">icon</B> bitmap
+will probably look terrible on a 256-color, 1024x768 display. For
+such a high-resolution display, the choice of the 16-color, 64x64
+pixel version of the <B style="color:black;background-color:#A0FFFF">icon</B> would look much better.  However, if the
+<B style="color:black;background-color:#A0FFFF">icon</B> file only contains one bitmap, then that is the <B style="color:black;background-color:#A0FFFF">icon</B> bitmap that
+will be displayed.
+<p>
+<B style="color:black;background-color:#A0FFFF">Icon</B> and cursor bitmaps are typically very small. Under the 16-bit
+<B style="color:black;background-color:#ffff66">Windows</B> environment (Win16) icons are traditionally square (16x16,
+32x32, or 64x64 pixels in size) and have 16 or fewer colors. The
+32-bit <B style="color:black;background-color:#ffff66">Windows</B> (Win32) environment allows icons to be larger and have
+a greater pixel depth, such as 72x72 pixels with 256 colors. Win32 is
+also tolerant of rectangular <B style="color:black;background-color:#A0FFFF">icon</B> formats.
+<p>
+Cursors under <B style="color:black;background-color:#ffff66">Windows</B> 3.<i>x</i> are two-color (1-bit) bitmaps. Under
+<B style="color:black;background-color:#ffff66">Windows</B> 4.<i>x</i> cursors with 8, 16, or more colors are possible. Cursors
+are also not limited to a specific range of sizes as are icons,
+although most cursors are 32x32 pixels in size.
+<p>
+Both cursor and <B style="color:black;background-color:#A0FFFF">icon</B> data may be stored in separate file formats
+(<i>*.ico</i> and <i>*.cur</i>) on disk, or be stored as a <B style="color:black;background-color:#ffff66">Windows</B>
+resource file (<i>*.res</i>) and embedded directly into a <B style="color:black;background-color:#ffff66">Windows</B>
+executable file (<i>*.exe</i>), Dynamic Link Library (<i>*.dll</i>),
+Visual Basic control (<i>*.vbx</i>), or OLE
+control (<i>*.ocx</i>). (This article only discusses the ICO and CUR file
+formats and will leave the <B style="color:black;background-color:#ffff66">Windows</B> Resource format to a future
+article.)
+<p>
+Simple animations may be created by the sequential display of several
+<B style="color:black;background-color:#A0FFFF">icon</B> or cursor bitmaps in a continuous loop. These "flipbook"
+animations are not directly supported by the Win16 environment, but
+they do appear in some <B style="color:black;background-color:#ffff66">Windows</B> applications.
+<p>
+A true animated cursor format was defined by Microsoft in 1992 as part
+of the RIFF multimedia specification. Animated cursors are the
+<i>*.ani</i> files you might have noticed on your hard drive if you
+use <B style="color:black;background-color:#ffff66">Windows</B> 95 or <B style="color:black;background-color:#ffff66">Windows</B> NT. They are actually little more than a
+collection of ICO files stored in a single ANI file. For more
+information on the format of <B style="color:black;background-color:#ffff66">Windows</B> animated icons, see the
+<a href="gffse:/format.micriff">Microsoft RIFF</a> article.
+<p>
+If you are interested in the <B style="color:black;background-color:#A0FFFF">icon</B> and pointer file formats used by
+OS/2, then have a look at the <a href="gffse:/format.os2bmp">OS/2
+Bitmap</a> article. OS/2 uses its own flavor of the BMP format to
+store <B style="color:black;background-color:#A0FFFF">icon</B> and pointer data.
+<p>
+It should also be noted that there is no formal specification for
+either the <B style="color:black;background-color:#ffff66">Windows </B><B style="color:black;background-color:#A0FFFF">icon</B> or cursor file formats. In fact, there are no
+definitions for these file formats in the <B style="color:black;background-color:#ffff66">Windows</B> Software Development
+Kit (SDK) header files. What information that can be found must be
+scraped up from <B style="color:black;background-color:#ffff66">Windows</B> SDK manuals, Knowledge Base articles, and
+several sample applications. See the <a href="#MICCUR-DMYID.4">For
+Further Information</a> section in this article for more details on
+additional reference material for <B style="color:black;background-color:#ffff66">Windows</B> icons and cursors.
+<p>
+
+<h2><A NAME="MICCUR-DMYID.2">File Organization</A></h2>
+<p>
+The <B style="color:black;background-color:#ffff66">Windows </B><B style="color:black;background-color:#A0FFFF">icon</B> (ICO) and cursor (CUR) file formats are identical.
+Only the interpretation of the file data differs slightly. In fact,
+<B style="color:black;background-color:#A0FFFF">icon</B> and cursor files may be used interchangeably by applications that
+realize this fact.
+<p>
+Every ICO and CUR file contains a header, a directory of bitmap
+entries, and one or more bitmaps that describes the appearance of an
+<B style="color:black;background-color:#A0FFFF">icon</B> or cursor (shown below).
+<p>
+<table border=1>
+<tr valign=center>
+  <td align=center>Header</td>
+</tr>
+<tr valign=center>
+  <td align=center>Bitmap Directory</td>
+</tr>
+<tr valign=center>
+  <td align=center>Bitmap 1</td>
+</tr>
+<tr valign=center>
+  <td align=center>Bitmap 2</td>
+</tr>
+<tr valign=center>
+  <td align=center>...</td>
+</tr>
+<tr valign=center>
+  <td align=center>Bitmap N</td>
+</tr>
+</table>
+<p>
+The header stores information that is used to determine how many
+bitmaps are in the file. ICO and CUR files will contain one or more
+uncompressed bitmaps. Each entry in the bitmap directory will contain
+information that describes one of the bitmaps stored in the file. The
+directory will contain one entry per bitmap.
+<p>
+
+<h3><A NAME="MICCUR-DMYID.3">File Details</A></h3>
+<p>
+As <B style="color:black;background-color:#ffff66">Windows</B> ICO and CUR files are nearly identical, we will first look
+at the ICO format and then discuss the differences between the ICO
+and CUR formats.
+<p>
+The ICO header contains only a <B style="color:black;background-color:#ffff66">Windows</B> resource identification value
+and the count of the number of icons stored in the file. The header
+is immediately followed by a directory that contains information for
+all the icons stored in the ICO file. The directory is followed by
+the <B style="color:black;background-color:#A0FFFF">icon</B> bitmaps themselves.
+<p>
+The following structure illustrates the entire format of an ICO
+(or CUR) file:
+<pre>
+typedef struct _IconFile
+{
+    WORD      Reserved;      /* Reserved (always 0) */
+    WORD      ResourceType;  /* Resource ID (always 1) */
+    WORD      IconCount;     /* Number of <B style="color:black;background-color:#A0FFFF">icon</B> bitmaps in file */
+    ICONENTRY IconDir[];     /* Directory of <B style="color:black;background-color:#A0FFFF">icon</B> entries */
+    ICONDATA  IconData[];    /* Listing of ICO bitmaps */
+} ICONFILE;
+</pre>
+<p>
+Reserved is a two-byte value that is always zero in all ICO files.
+<p>
+ResourceType is the <B style="color:black;background-color:#ffff66">Windows</B> resource identifier type value. For icons
+this value is always 1.
+<p>
+IconCount is the number of icons stored in the ICO file. It is also
+the number of elements in the Icons array.
+<p>
+IconDir is an array of directory entries that describe the icons stored
+in the ICO file. There will be one entry per <B style="color:black;background-color:#A0FFFF">icon</B> stored. The number
+of entries will also equal the value of the IconCount field. Each
+ICONENTRY element has the following format:
+<pre>
+typedef struct _IconEntry
+{
+    BYTE  Width;        /* Width of <B style="color:black;background-color:#A0FFFF">icon</B> in pixels */
+    BYTE  Height;       /* Height of <B style="color:black;background-color:#A0FFFF">icon</B> in pixels */
+    BYTE  NumColors;    /* Maximum number of colors */
+    BYTE  Reserved;     /* Not used (always 0) */
+    WORD  NumPlanes;    /* Not used (always 0) */
+    WORD  BitsPerPixel; /* Not used (always 0) */
+    DWORD DataSize;     /* Length of <B style="color:black;background-color:#A0FFFF">icon</B> bitmap in bytes */
+    DWORD DataOffset;   /* Offset position of <B style="color:black;background-color:#A0FFFF">icon</B> bitmap in file */
+} ICONENTRY;
+</pre>
+<p>
+Width and Height are the size of the <B style="color:black;background-color:#A0FFFF">icon</B> in pixels. Only the values
+of 16, 32, and 64 for these fields are accepted by Win16.  Other
+values are also accepted by Win32. And as square icons are the most
+common, both of these fields will typically store the same value
+(although 32x16 icons were commonly used for the now antiquated
+300x200 CGA display mode).
+<p>
+NumColors is the maximum number of colors that may appear in the
+<B style="color:black;background-color:#A0FFFF">icon</B>. The values 2, 8, and 16 are the only values accepted by Win16.
+Other values are accepted by Win32. If the bitmap contains 256 or
+more colors the value of NumColors will be 0.
+<p>
+Reserved is not used and is always zero. This field was probably
+included as an element-alignment padding structure.
+<p>
+NumPlanes and BitsPerPixel are not used and are always 0. Earlier
+revisions of the ICO format may have used these fields, but in the
+current revision of ICO this information is now stored in the <B style="color:black;background-color:#A0FFFF">icon</B>
+data itself.
+<p>
+DataSize is the length of the <B style="color:black;background-color:#A0FFFF">icon</B> data in bytes for this entry.
+This value is the total size of both bitmaps used to render
+the <B style="color:black;background-color:#A0FFFF">icon</B> (explained below).
+<p>
+DataOffset is the location of the <B style="color:black;background-color:#A0FFFF">icon</B> data for this entry. The
+offset is measured in bytes from the start of the ICO file.
+<p>
+Following the <B style="color:black;background-color:#A0FFFF">icon</B> directory is the data for the <B style="color:black;background-color:#A0FFFF">icon</B>(s) themselves
+(the IconData array in the ICONFILE structure). Each <B style="color:black;background-color:#A0FFFF">icon</B> stored in
+an ICO file is actually an independent file format in itself, and
+contains a header, a color palette, <B style="color:black;background-color:#A0FFFF">icon</B> bitmap data, and a display
+bit mask. 
+<p>
+The start of each section of <B style="color:black;background-color:#A0FFFF">icon</B> data is specified by the IconOffset
+field in each <B style="color:black;background-color:#A0FFFF">icon</B> directory entry. Each section of <B style="color:black;background-color:#A0FFFF">icon</B> data has the
+following format:
+<pre>
+typedef struct _IconData
+{
+    WIN3XBITMAPHEADER   Header;         /* Bitmap header data */
+    WIN3XPALETTEELEMENT Palette[];      /* Color palette */
+    BYTE                XorMap[];       /* <B style="color:black;background-color:#A0FFFF">Icon</B> bitmap */
+    BYTE                AndMap[];       /* Display bit mask */
+} ICONDATA;
+</pre>
+<p>
+Header is a 40-byte <B style="color:black;background-color:#ffff66">Windows</B> 3.<i>x</i> BMP file header structure. Only the
+Size, Width, Height, Planes, BitsPerPixel, and SizeOfBitmap fields of
+this header are actually used. All other fields in this structure
+(Compression, SizeOfBitmap, HorzResolution, VertResolution,
+ColorsUsed, and ColorsImportant) are set to zero. Refer to the
+<a href="gffse:/format.micbmp">Microsoft <B style="color:black;background-color:#ffff66">Windows</B> Bitmap</a> article
+for more information on this header structure.
+<p>
+Palette is the color palette for the data in the XorMap array. The
+BitsPerPixel field of the Header is used to determine the number of
+elements in the Palette array (BitsPerPixel >= 1). For two-color
+icons there will be two palette entries; for 8- and 16-color icons
+there will be 16 entries. Each palette element is a four-byte RGB
+structure as described in the <a href="gffse:/format.micbmp">Microsoft
+<B style="color:black;background-color:#ffff66">Windows</B> Bitmap</a> article.
+<p>
+XorMap contains the <B style="color:black;background-color:#A0FFFF">icon's</B> foreground bitmap. The size of the pixels
+is indicated by the BitsPerPixel values in the header. Two-color
+(monochrome) bitmaps are stored as one bit per pixel; 8- and 16-color
+bitmap data is stored as 4 bits per pixel. Each pixel value is
+actually an index into the Palette color map.
+<p>
+AndMap is the <B style="color:black;background-color:#A0FFFF">icon's</B> background bit mask. This is a 1-bit-per-pixel
+mask that is the same size (in pixels) as the XorMap. This mask is
+used to map the visible area of the <B style="color:black;background-color:#A0FFFF">icon</B> on the screen before the
+<B style="color:black;background-color:#A0FFFF">icon</B> is actually displayed.
+<p>
+At first it may seem redundant to have Height and Width fields in
+both the <B style="color:black;background-color:#A0FFFF">icon</B> entry (ICONENTRY) and the <B style="color:black;background-color:#A0FFFF">icon</B> header
+(WIN3XBITMAPHEADER) structures. In fact, the Height and Width values
+stored in the <B style="color:black;background-color:#A0FFFF">icon</B> header are the combined size of the XorMap and
+AndMap bitmaps.  The Width values in the two structures will be the
+same, but the Height value in the <B style="color:black;background-color:#A0FFFF">icon</B> header will be double that of
+the Height value in the <B style="color:black;background-color:#A0FFFF">icon</B> directory entry. The <B style="color:black;background-color:#A0FFFF">icon</B> directory
+specifies the actual size of the <B style="color:black;background-color:#A0FFFF">icon</B> as it appears on the display,
+and the <B style="color:black;background-color:#A0FFFF">icon</B> header specifies the size of the data used to create the
+<B style="color:black;background-color:#A0FFFF">icon</B>.
+<p>
+Under the <B style="color:black;background-color:#ffff66">Windows</B> environment an <B style="color:black;background-color:#A0FFFF">icon</B> is displayed by first looking
+through an ICO file and determining which <B style="color:black;background-color:#A0FFFF">icon</B> bitmap best matches
+the number of colors and resolution of the display. The AND bit mask
+for the chosen <B style="color:black;background-color:#A0FFFF">icon</B> is bitwise ANDed with the pixels on the display
+where the <B style="color:black;background-color:#A0FFFF">icon</B> will appear. This removes the pixels from the display
+and leaves a virtual "hole" in the display where the non-<B style="color:black;background-color:#99ff99">transparent</B>
+parts of the <B style="color:black;background-color:#A0FFFF">icon</B> will appear.  Finally, the XOR map is bitwise XORed
+to the same pixels on the display. This operation adds the <B style="color:black;background-color:#A0FFFF">icon's</B>
+color to the display.
+<p>
+Let's look at this process in more detail.  Assume we have an ICO
+file that contains several "happy face" <B style="color:black;background-color:#A0FFFF">icon</B> bitmaps.  The ICO file
+actually stores four different bitmap variations of the same happy
+face:
+<p>
+<table border=0>
+<tr>
+  <th align=left>Width</th>
+  <th align=left>Height</th>
+  <th align=left>Number of Colors</th>
+  <th align=left>BitsPerPixel</th>
+</tr>
+<tr>
+  <td align=left>8</td>
+  <td align=left>8</td>
+  <td align=left>16</td>
+  <td align=left>4</td>
+</tr>
+<tr>
+  <td align=left>16</td>
+  <td align=left>16</td>
+  <td align=left>16</td>
+  <td align=left>4</td>
+</tr>
+<tr>
+  <td align=left>32</td>
+  <td align=left>32</td>
+  <td align=left>16</td>
+  <td align=left>4</td>
+</tr>
+<tr>
+  <td align=left>48</td>
+  <td align=left>48</td>
+  <td align=left>64</td>
+  <td align=left>8</td>
+</tr>
+</table>
+<p>
+When <i>happyfac.ico</i> is loaded, one of the <B style="color:black;background-color:#A0FFFF">icon</B> bitmaps must be chosen 
+that matches resolution of the display. The ICO reader searches each
+ICO directory entry for a "best fit" <B style="color:black;background-color:#A0FFFF">icon</B> bitmap.  For a 320x240x16
+display the 8x8, 16-color "happy face" <B style="color:black;background-color:#A0FFFF">icon</B> would probably look best.
+The reader then seeks to the offset of the selected <B style="color:black;background-color:#A0FFFF">icon</B> data.
+<p>
+Next, the AndMap bitmap must be loaded and ANDed to proper location
+on the display. The AndMap data follows the XorMap data, so the ICO
+reader must skip past the XorMap data to read the AndMap data. It is
+important to note that the scan lines of data in the AndMap and
+XorMap bitmaps are stored from the bottom up (the origin is in the
+lower left-hand corner). That is, the first scan line of the bitmap
+is actually the last scan line of the <B style="color:black;background-color:#A0FFFF">icon</B>.
+<p>
+In the 8x8x16 bitmap chosen, the AndMap contains only eight bytes
+of data (8 rows x 8 columns):
+<pre>
+FF 99 99 E7 66 BD C3 FF
+</pre>
+<p>
+In this form the data looks meaningless.  Looking at the AndMap as an
+array of bits we can see our happy face more clearly:
+<pre>
+1 1 1 1 1 1 1 1
+1 0 0 1 1 0 0 1
+1 0 0 1 1 0 0 1
+1 1 1 0 0 1 1 1
+0 1 1 0 0 1 1 0
+1 0 1 1 1 1 0 1
+1 1 0 0 0 0 1 1
+1 1 1 1 1 1 1 1
+</pre>
+<p>
+If we ANDed this bitmap to a location on the display that was all the
+same color we would see this result:
+<pre>
+C C C C C C C C     1 1 1 1 1 1 1 1     C C C C C C C C
+C C C C C C C C     1 0 0 1 1 0 0 1     C 0 0 C C 0 0 C
+C C C C C C C C     1 0 0 1 1 0 0 1     C 0 0 C C 0 0 C
+C C C C C C C C  +  1 1 1 0 0 1 1 1  =  C C C 0 0 C C C
+C C C C C C C C     0 1 1 0 0 1 1 0     0 C C 0 0 C C 0
+C C C C C C C C     1 0 1 1 1 1 0 1     C 0 C C C C 0 C
+C C C C C C C C     1 1 0 0 0 0 1 1     C C 0 0 0 0 C C
+C C C C C C C C     1 1 1 1 1 1 1 1     C C C C C C C C
+   Display              AndMap           ANDed Display
+</pre>
+<p>
+Each 1 bit in the AndMap preserved a display pixel (the <B style="color:black;background-color:#99ff99">transparent</B>
+portion of the <B style="color:black;background-color:#A0FFFF">icon</B>) and each 0 bit removed a display pixel (the
+opaque portion of the <B style="color:black;background-color:#A0FFFF">icon</B>).
+<p>
+Finally, we need to XOR the XorMap <B style="color:black;background-color:#A0FFFF">icon</B> pixel values into the hole we
+punched into the display. Remember that the actual pixel color values
+are stored in the color palette, and the bitmap data is only an index
+map that indicates where the pixel colors should be written.
+<p>
+In our happy face <B style="color:black;background-color:#A0FFFF">icon</B> each pixel in the bitmap is four bits in size
+and packed two pixels per byte. The XorMap bitmap data appears as
+such:
+<pre>
+00 00 00 00
+04 40 04 40
+04 40 04 40
+00 0F F0 00
+90 0F F0 09
+09 00 00 90
+00 99 99 00
+00 00 00 00
+</pre>
+<p>
+In case you can't see the happy face, the eyes are the value 0x4,
+the nose 0xF, and the smile 0x9. We now need to map the bitmap
+index values to color values in the palette. Let's assume that
+index 0x04 maps to color value 0x1, 0xF to 0xF, and 0x9 to 0x07.
+<p>
+With the color values determined, we XOR the pixel values to the same
+region we applied the AndMap mask. The 0 bits on the display (the
+black regions) indicate the non-<B style="color:black;background-color:#99ff99">transparent</B> pixels of the <B style="color:black;background-color:#A0FFFF">icon</B>. It is
+the pixel represented by the 0 bits that will have their color
+changed to that of the <B style="color:black;background-color:#A0FFFF">icon</B>. The <B style="color:black;background-color:#99ff99">transparent</B> pixels will retain the
+original color of the display:
+<pre>
+C C C C C C C C     0 0 0 0 0 0 0 0     C C C C C C C C
+C 0 0 C C 0 0 C     0 1 1 0 0 1 1 0     C 1 1 C C 1 1 C
+C 0 0 C C 0 0 C     0 1 1 0 0 1 1 0     C 1 1 C C 1 1 C
+C C C 0 0 C C C  +  0 0 0 F F 0 0 0  =  C C C F F C C C
+0 C C 0 0 C C 0     7 0 0 F F 0 0 7     7 C C F F C C 7
+C 0 C C C C 0 C     0 7 0 0 0 0 7 0     C 7 C C C C 7 C
+C C 0 0 0 0 C C     0 0 7 7 7 7 0 0     C C 7 7 7 7 C C
+C C C C C C C C     0 0 0 0 0 0 0 0     C C C C C C C C
+ ANDed Display       XORed values       <B style="color:black;background-color:#A0FFFF">Icon</B> on Display
+</pre>
+<p>
+A monochrome (1-bit) <B style="color:black;background-color:#A0FFFF">icon</B> or cursor will contain only four possible 
+pixels values: black, white, <B style="color:black;background-color:#99ff99">transparent</B>, and inverted. A <B style="color:black;background-color:#99ff99">transparent</B>
+or inverted pixel may be either black or white in color. The color
+palette will contain only two colors, which are black (entry zero)
+and white (entry one). The <B style="color:black;background-color:#99ff99">transparent</B> color is the original color of
+the display pixels. Inverted is the inverse color of the display
+pixels. Inverted pixels are responsible for the shadowy or shimmering
+effect you may have noticed when some cursors are moved across the
+display.
+<p>
+The possible combined bitmaps values are shown in Table Microsoft
+<B style="color:black;background-color:#ffff66">Windows</B> Cursor and <B style="color:black;background-color:#A0FFFF">Icon</B>-1.
+<p>
+<table border=0>
+<caption>Table Microsoft <B style="color:black;background-color:#ffff66">Windows</B> and Cursor <B style="color:black;background-color:#A0FFFF">Icon</B>-1. Monochrome <B style="color:black;background-color:#A0FFFF">icon</B> and cursor mask value combinations</caption>
+<tr valign=center>
+  <th></th>
+  <th align=left>AndMap Value</th>
+  <th align=left>XorMap Value</th>
+  <th align=left>Display Pixel Value</th>
+  <th align=left>Resulting Color</th>
+</tr>
+<tr>
+  <td align=left>Black</td>
+  <td align=left>0</td>
+  <td align=left>0</td>
+  <td align=left>0 or 1</td>
+  <td align=left>0</td>
+</tr>
+<tr>
+  <td align=left>White</td>
+  <td align=left>0</td>
+  <td align=left>1</td>
+  <td align=left>0 or 1</td>
+  <td align=left>1</td>
+</tr>
+<tr>
+  <td align=left><B style="color:black;background-color:#99ff99">Transparent</B></td>
+  <td align=left>1</td>
+  <td align=left>0</td>
+  <td align=left>0</td>
+  <td align=left>0</td>
+</tr>
+<tr>
+  <td></td>
+  <td align=left>1</td>
+  <td align=left>0</td>
+  <td align=left>1</td>
+  <td align=left>1</td>
+</tr>
+<tr>
+  <td align=left>Inverted</td>
+  <td align=left>1</td>
+  <td align=left>1</td>
+  <td align=left>0</td>
+  <td align=left>1</td>
+</tr>
+<tr>
+  <td></td>
+  <td align=left>1</td>
+  <td align=left>1</td>
+  <td align=left>1</td>
+  <td align=left>0</td>
+</tr>
+</table>
+<p>
+
+<h3><A NAME="MICCUR-DMYID.3.1">CUR File Format</A></h3>
+<p>
+Everything we have covered for the ICO format also applies to the
+CUR format with only a few exceptions:
+<p>
+The value of the ResourceType field in the header (ICONFILE) is 2,
+indicating the file contains cursor bitmap data.
+<p>
+The cursor directory entry redefines two unused fields to store hot
+spot information and modifies the possible values of two additional
+fields:
+<pre>
+typedef struct _IconEntry
+{
+    BYTE  Width;        /* Width of cursor in pixels */
+    BYTE  Height;       /* Height of cursor in pixels */
+    BYTE  NumColors;    /* Maximum number of colors */
+    BYTE  Reserved;     /* Not used (always 0) */
+    WORD  XHotSpot;     /* X location of cursor's hot spot */
+    WORD  YHotSpot;     /* Y location of cursor's hot spot */
+    DWORD DataSize;     /* Length of cursor bitmap in bytes */
+    DWORD DataOffset;   /* Offset position of cursor bitmap in file */
+} ICONENTRY;
+</pre>
+<p>
+Width and Height values in the <B style="color:black;background-color:#A0FFFF">icon</B> directory may be any size,
+although 32x32 pixels is the most common size for <B style="color:black;background-color:#ffff66">Windows</B> cursors.
+<p>
+NumColors is always 2 (1-bit, black and white) in a Win16 cursor
+file. Win32 cursors have the same color ranges as icons.
+<p>
+XHotSpot and YHotSpot store the coordinates of the cursor's hot spot.
+The X coordinate is relative to the cursor bitmap's left edge; the Y
+coordinate is relative to the cursor's top edge. Both coordinates are
+measured in pixels.
+<p>
+The hot spot is a single pixel in size. When the user clicks the
+pointing device, the coordinates of the hot spot are sent to <B style="color:black;background-color:#ffff66">Windows</B>.
+<B style="color:black;background-color:#ffff66">Windows</B> then performs an action based on where the hot spot is on the
+display and on the current state of <B style="color:black;background-color:#ffff66">Windows</B> itself. 
+<p>
+And finally, ICO and CUR files do not contain any type of
+identification signature or "magic number". A file reader may assume
+that all <i>.ico</i> files are <B style="color:black;background-color:#ffff66">Windows</B> icons and all <i>.cur</i>
+files are <B style="color:black;background-color:#ffff66">Windows</B>
+cursors, but it would haphazard to do so. If we assume instead that
+all ICO files will never contain more than 256 bitmaps (a very safe
+assumption) then an ICO file will begin with the byte sequence 00 00
+01 00 XX 00, where XX may be any byte value. Making the same
+assumption for cursors, all CUR files will begin with the byte
+sequence 00 00 02 00 XX 00.
+<p>
+
+<h2><A NAME="MICCUR-DMYID.4">For Further Information</A></h2>
+<p>
+The primary sources of ICO and CUR information are the Microsoft
+Win16 and Win32 Software Development Kits (SDK) and the Microsoft
+Developer Network Library (MSDN) CD-ROMs.
+<p>
+The Win16 and Win32 SDKs are distributed with the Microsoft Visual
+C++ compiler and the MSDN CD-ROMs. The following SDK references and
+Knowledge Base articles discuss the ICO, CUR, and DIB bitmap formats:
+<p>
+<blockquote>
+<p>
+<i>Microsoft <B style="color:black;background-color:#ffff66">Windows</B> Software Development Kit, Programmers'
+Reference, Volume 4: Resources</i>
+<p>
+Win32 SDK on-line help for the BITMAPINFO structure
+<p>  
+Q81498 SAMPLE: DIBs and Their Uses
+<p>
+Q94326 SAMPLE: 16 and 32 Bits-Per-Pel Bitmap Formats
+<p>
+Specs: Icons in Win32
+<p>
+Technical Articles: Win32 Binary Resource Formats
+<p>
+</blockquote>
+<p>
+You can find the SDK documents on the MSDN Library CD-ROMs. The MSDN
+Library is only available by subscription. However, Microsoft has made
+the October 1995 MSDN library available at:
+<p>
+<blockquote>
+<p>
+<i>ftp://ftp.microsoft.com/developr/MSDN/OctCD/</i>
+</blockquote>
+<p>
+One other MSDN Library file of interest is:
+<p>
+<blockquote>
+MSDN Frequently Asked Questions (FAQ)<br>
+PSS ID Number: Q116437, 02-16-1996<br>
+<i>ftp://ftp.microsoft.com/developr/MSDN/kb/Q116/4/37.txt</i><br>
+<i>http://www.microsoft.com/msdn/msdnfaq.htm</i>
+</blockquote>
+<p>
+The Win32 SDK also contains two sample applications that are necessary
+for understanding icons and cursors. They are <i>IconPro</i> and
+<i>imagedit</i> (an <B style="color:black;background-color:#A0FFFF">icon</B> and cursor bitmap editor). Both of these
+sample applications are distributed with complete source code, but no
+compiled binaries. The binaries are only distributed with the <B style="color:black;background-color:#ffff66">Windows</B>
+95 and <B style="color:black;background-color:#ffff66">Windows</B> NT Resource Kits.
+<p>
+You can contact the Microsoft MSDN group at:
+<p>
+<blockquote>
+Microsoft Developers Network<br>
+Voice: 206-936-2490<br>
+Email: <i>msdn@microsoft.com</i><br>
+WWW: <i>http://www.microsoft.com/msdn/</i>
+</blockquote>
+<p>
+And here's the addresses of Microsoft. Try getting what you need from
+their FTP and web sites first:
+<p>
+<blockquote>
+Microsoft Corporation<br>
+One Microsoft Way<br>
+Redmond, WA 98052-6399<br>
+FTP: <i>ftp://ftp.microsoft.com/</i><br>
+WWW: <i>http://www.microsoft.com/</i><br>
+CIS: WINSDK and MSWIN32 forums
+</blockquote>
+<p>
+
+<hr>
+<p>
+<A HREF="gffse:/format.micclip"><img src="../../images/txtpreva.gif"></A>
+<A HREF="gffse:/format.micmeta"><img src="../../images/txtnexta.gif"></A>
+<A HREF="../book.htm"><img src="../../images/txtupa.gif"></A>
+<a href="../bookidx.htm"><img src="../../images/txttoidx.gif"></a>
+<br>
+<a href="../booktoc.htm"><img src="../../images/btntoc.gif"></a>
+<a href="../book/glossary.htm"><img src="../../images/btnglos.gif"></a>
+<a href="../main.htm"><img src="../../images/btnmain.gif"></a>
+<a href="gffse:/page.formats"><img src="../../images/btnfmt.gif"></a>
+<a href="../software.htm"><img src="../../images/btnsoft.gif"></a>
+<a href="../internet.htm"><img src="../../images/btninet.gif"></a>
+<a href="../book.htm"><img src="../../images/btnbook.gif"></a>
+<P>
+Copyright &copy; 1996, 1994 O'Reilly &amp; Associates, Inc.  All Rights Reserved.
+<p>
+</body>
+</html>
diff --git a/converter/ppm/winicontoppm.c b/converter/ppm/winicontoppm.c
new file mode 100644
index 00000000..2d9de567
--- /dev/null
+++ b/converter/ppm/winicontoppm.c
@@ -0,0 +1,899 @@
+/* winicontoppm.c - read a MS Windows .ico file and write portable pixmap(s)
+**
+** Copyright (C) 2000,2003 by Lee Benfield - lee@benf.org
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+**
+** Changes:
+** 
+** 03/2003 - Added 24+32 bpp capability.
+*/
+
+#define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include <math.h>
+#include <string.h>
+#include <assert.h>
+
+#include "ppm.h"
+#include "shhopt.h"
+#include "nstring.h"
+#include "mallocvar.h"
+#include "winico.h"
+
+#define MAJVERSION 0
+#define MINVERSION 4
+
+static int file_offset = 0;    /* not actually used, but useful for debug */
+static const char     er_read[] = "%s: read error";
+static const char *   infname;
+static FILE *   ifp;
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFilespec;
+    const char * outputFilespec;
+    unsigned int allicons;
+    unsigned int bestqual;
+    unsigned int writeands;
+    unsigned int multippm;
+    unsigned int verbose;
+};
+
+
+
+
+static void
+parseCommandLine ( int argc, char ** argv,
+                   struct cmdlineInfo *cmdlineP ) {
+/*----------------------------------------------------------------------------
+   parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def = malloc(100*sizeof(optEntry));
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "allicons",     OPT_FLAG,   NULL,                  
+            &cmdlineP->allicons,       0 );
+    OPTENT3(0, "bestqual",     OPT_FLAG,   NULL,                  
+            &cmdlineP->bestqual,       0 );
+    OPTENT3(0, "writeands",    OPT_FLAG,   NULL,                  
+            &cmdlineP->writeands,      0 );
+    OPTENT3(0, "multippm",     OPT_FLAG,   NULL,                  
+            &cmdlineP->multippm,       0 );
+    OPTENT3(0, "verbose",      OPT_FLAG,   NULL,                  
+            &cmdlineP->verbose,        0 );
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+
+    if (argc-1 < 1) 
+        cmdlineP->inputFilespec = "-";
+    else
+        cmdlineP->inputFilespec = argv[1];
+
+    if (argc-1 < 2) {
+        cmdlineP->outputFilespec = "-";
+        
+        if (cmdlineP->writeands || cmdlineP->allicons)
+            pm_error("If you specify the -writeands or -allicons option, "
+                     "you must also specify an output file name argument.");
+    } else
+        cmdlineP->outputFilespec = argv[2];
+
+    if (argc-1 > 2)
+        pm_error("Too many arguments (%d).  Input filespec and "
+                 "output filespec are the only possible arguments.",
+                 argc-1);
+}
+
+
+
+
+static int 
+GetByte(void) {
+    int v;
+   
+    if ((v = getc(ifp)) == EOF)
+    {
+        pm_error(er_read, infname);
+    }
+   
+    return v;
+}
+   
+static short 
+GetShort(void) {
+    short v;
+   
+    if (pm_readlittleshort(ifp, &v) == -1)
+    {
+        pm_error(er_read, infname);
+    }
+   
+    return v;
+}
+   
+static long 
+GetLong(void) {
+    long v;
+   
+    if (pm_readlittlelong(ifp, &v) == -1)
+    {
+        pm_error(er_read, infname);
+    }
+   
+    return v;
+}
+   
+
+
+/*
+ * These have no purpose but to wrapper the Byte, Short & Long 
+ * functions.
+ */
+static u1 
+readU1 (void) {
+    file_offset++;
+    return GetByte();
+}
+
+static u1 * 
+readU1String (int length)
+{
+   
+    u1 * string;
+    
+    MALLOCARRAY(string, length + 1);
+    if (string == NULL)
+        pm_error("out of memory");
+
+    fread(string,sizeof(u1),length,ifp);
+    string[length] = 0;
+    file_offset += length * sizeof(u1);
+    return string;
+}
+
+static u2 
+readU2 (void) {
+    file_offset +=2;
+    return GetShort();
+}
+
+static u4 
+readU4 (void) {
+    file_offset += 4;
+    return GetLong();
+}
+
+static IC_Entry 
+readICEntry (void) 
+{
+    IC_Entry entry;
+
+    MALLOCVAR(entry);
+
+    if (entry == NULL)
+        pm_error("Unable to allcoate memory for IC entry");
+
+    entry->width         = readU1();
+    entry->height        = readU1();
+    entry->color_count   = readU1();
+    entry->reserved      = readU1();
+    entry->planes        = readU2();
+    entry->bitcount      = readU2();
+    entry->size_in_bytes = readU4();
+    entry->file_offset   = readU4();
+    entry->colors        = NULL;
+    entry->ih            = NULL;
+    entry->xorBitmap     = NULL;
+    entry->andBitmap     = NULL;
+    
+    return entry;
+}
+
+
+
+static IC_InfoHeader 
+readInfoHeader (IC_Entry entry) 
+{
+    IC_InfoHeader ih;
+
+    MALLOCVAR(ih);
+    
+    if (ih == NULL)
+        pm_error("Unable to allocate memory for info header");
+
+    ih->size            = readU4();
+    ih->width           = readU4();
+    ih->height          = readU4();
+    ih->planes          = readU2();
+    ih->bitcount        = readU2();
+    ih->compression     = readU4();
+    ih->imagesize       = readU4();
+    ih->x_pixels_per_m  = readU4();
+    ih->y_pixels_per_m  = readU4();
+    ih->colors_used     = readU4();
+    ih->colors_important = readU4();
+    
+    if (!entry->bitcount) entry->bitcount = ih->bitcount;
+    if (entry->color_count == 0 && 
+        entry->bitcount <= 8) entry->color_count = 256;
+    if (ih->compression) {
+        pm_error("Can't handle compressed icons");
+    }
+    return ih;
+}
+
+/*
+ * I don't know why this isn't the same as the spec, it just <b>isn't</b>
+ * The colors honestly seem to be stored BGR.  Bizarre.
+ * 
+ * I've checked this in the BMP code for bmptoppm and the gimp.  Guess the
+ * spec I have is just plain wrong.
+ */
+static IC_Color 
+readICColor (void) 
+{
+    IC_Color col;
+
+    MALLOCVAR(col);
+
+    if (col == NULL)
+        pm_error("Unable to allocate memory for color");
+
+    col->blue     = readU1();
+    col->green    = readU1();
+    col->red      = readU1();
+    col->reserved = readU1();
+    return col;
+}
+   
+
+
+/*
+ * Depending on if the image is stored as 1bpp, 4bpp or 8bpp, the 
+ * encoding mechanism is different.
+ * 
+ * 8bpp => 1 byte/palette index.
+ * 4bpp => High Nibble, Low Nibble
+ * 1bpp => 1 palette value per bit, high bit 1st.
+ */
+static u1 * 
+read1Bitmap (int width, int height) 
+{
+    int tmp;
+    int xBytes;
+    u1 * bitmap;
+    int wt = width;
+
+    MALLOCARRAY(bitmap, width * height);
+    if (bitmap == NULL)
+        pm_error("out of memory");
+
+    wt >>= 3;
+    if (wt & 3) {
+        wt = (wt & ~3) + 4;
+    }
+    xBytes = wt;
+    for (tmp = 0; tmp<height; tmp++ ) {
+        int x;
+        int rowByte = 0;
+        int xOrVal = 128;
+        u1 * row = readU1String(xBytes);
+        for (x = 0; x< width; x++) {
+            *(bitmap+((height-tmp-1)*width) + (x)) = 
+                (row[rowByte] & xOrVal) / xOrVal;
+            if (xOrVal == 1) {
+                xOrVal = 128;
+                rowByte++;
+            } else {
+                xOrVal >>= 1;
+            }
+        }
+        free(row);
+    }
+    return bitmap;
+}
+
+
+   
+static u1 * 
+read4Bitmap (int width, int height) 
+{
+    int tmp;
+    u1 * bitmap;
+
+    int wt = width;
+    int xBytes;
+
+    MALLOCARRAY(bitmap, width * height);
+    if (bitmap == NULL)
+        pm_error("out of memory");
+
+
+    wt >>= 1;
+    if (wt & 3) {
+        wt = (wt & ~3) + 4;
+    }
+    xBytes = wt;
+    for (tmp = 0; tmp<height ; tmp++ ) {
+        int rowByte = 0;
+        int bottom = 1;
+        int x;
+        u1 * row = readU1String(xBytes);
+        for (x = 0; x< width; x++) {
+            /*
+             * 2 nibbles, 2 values.
+             */
+            if (bottom) {
+                *(bitmap+((height-tmp-1)*width) + (x)) = 
+                    (row[rowByte] & 0xF0) >> 4;
+            } else {
+                *(bitmap+((height-tmp-1)*width) + (x)) = (row[rowByte] & 0xF);
+                rowByte++;
+            }
+            bottom = !bottom;
+        }
+    free(row);
+    }
+    return bitmap;
+}
+
+
+   
+static u1 * 
+read8Bitmap (int width, int height) 
+{
+    int tmp;
+    unsigned int xBytes;
+    unsigned int wt = width;
+    u1 * bitmap;
+   
+    MALLOCARRAY(bitmap, width * height);
+    if (bitmap == NULL)
+        pm_error("out of memory");
+
+    if (wt & 3) {
+        wt = (wt & ~3) + 4;
+    }
+    xBytes = wt;
+    for (tmp = 0; tmp<height ; tmp++ ) {
+        int rowByte = 0;
+        int x;
+        u1 * row = readU1String(xBytes);
+        for ( x = 0; x< width; x++) {
+            *(bitmap+((height-tmp-1)*width) + (x)) = row[rowByte];
+            rowByte++;
+        }
+        free(row);
+    }
+    return bitmap;
+}
+
+
+
+/*
+ * Read a true color bitmap. (24/32 bits)
+ * 
+ * The output routine deplanarizes it for us, we keep it flat here.
+ */
+static u1 *
+readXBitmap (int const width, 
+             int const height, 
+             int const bpp) {
+    int          const bytes  = bpp >> 3;
+    unsigned int const xBytes = width * bytes;
+
+    u1 * bitmap;
+        /* remember - bmp (dib) stored upside down, so reverse */
+
+    MALLOCARRAY(bitmap, bytes * width * height);
+    if (bitmap == NULL)
+        pm_error("out of memory allocating bitmap array");
+
+    {
+        unsigned int i;
+        u1 * bitcurptr;
+
+        for (i = 0, bitcurptr = &bitmap[bytes * width * (height-1)];
+             i < height; 
+             ++i, bitcurptr -= xBytes) {
+
+            u1 * const row = readU1String(xBytes);
+            memcpy(bitcurptr, row, xBytes);
+            free(row);
+        }
+    }
+    return bitmap;
+}
+
+
+
+static MS_Ico 
+readIconFile (bool const verbose) {
+    int iter,iter2;
+
+    MS_Ico MSIconData;
+
+    MALLOCVAR(MSIconData);
+   
+    /*
+     * reserved - should equal 0.
+     */
+    MSIconData->reserved = readU2();
+    /*
+     * Type - should equal 1
+     */
+    MSIconData->type     = readU2();
+    /*
+     * count - no of icons in file..
+     */
+    MSIconData->count    = readU2();
+    /*
+     * Allocate "count" array of entries.
+     */
+    if (verbose) 
+        pm_message("Icon file contains %d icons.", MSIconData->count);
+
+    MALLOCARRAY(MSIconData->entries, MSIconData->count);
+    if (MSIconData->entries == NULL)
+        pm_error("out of memory");
+    /*
+     * Read in each of the entries
+     */
+    for (iter = 0;iter < MSIconData->count ; iter++ ) {
+        MSIconData->entries[iter] = readICEntry();
+    }
+    /* After that, we have to read in the infoheader, color map (if
+     * any) and the actual bit/pix maps for the icons.  
+     */
+    if (verbose) 
+        fprintf (stderr,"#\tColors\tBPP\tWidth\tHeight\n");
+    for (iter = 0;iter < MSIconData->count ; iter++ ) {
+        int bpp;
+        MSIconData->entries[iter]->ih =
+            readInfoHeader (MSIconData->entries[iter]);
+       
+        /* What's the bits per pixel? */
+        bpp = MSIconData->entries[iter]->bitcount; 
+        /* Read the palette, if appropriate */
+        switch (bpp) {
+        case 24:
+        case 32:
+            /* 24/32 bpp icon has no palette */
+            break;
+        default:
+            MALLOCARRAY(MSIconData->entries[iter]->colors, 
+                        MSIconData->entries[iter]->color_count);
+            if (MSIconData->entries[iter]->colors == NULL)
+                pm_error("out of memory");
+
+            for (iter2 = 0;
+                 iter2 < MSIconData->entries[iter]->color_count ; 
+                 iter2++ ) {
+                MSIconData->entries[iter]->colors[iter2] = readICColor();
+            }
+            break;
+        }
+        if (verbose) {
+            char cols_text[10];
+            sprintf (cols_text, "%d", MSIconData->entries[iter]->color_count);
+            fprintf (stderr,
+                     "%d\t%s\t%d\t%d\t%d\n", iter,
+                     MSIconData->entries[iter]->color_count ? 
+                     cols_text : "TRUE",
+                     bpp, MSIconData->entries[iter]->width, 
+                     MSIconData->entries[iter]->height);
+        }
+        /* Pixels are stored bottom-up, left-to-right. Pixel lines are
+         * padded with zeros to end on a 32bit (4byte) boundary. Every
+         * line will have the same number of bytes. Color indices are
+         * zero based, meaning a pixel color of 0 represents the first
+         * color table entry, a pixel color of 255 (if there are that
+         * many) represents the 256th entry.  
+         * 
+         * 24+32 bit (16 is an abomination, which I'll avoid, and expect
+         * no-one to mind) are stored 1byte/plane with a spare (alpha?)
+         * byte for 32 bit.
+         */
+        {
+            /*
+             * Read XOR Bitmap
+             */
+            switch (bpp) {
+            case 1:
+                MSIconData->entries[iter]->xorBitmap = 
+                    read1Bitmap(MSIconData->entries[iter]->width,
+                                MSIconData->entries[iter]->height);
+                break;
+            case 4:
+                MSIconData->entries[iter]->xorBitmap = 
+                    read4Bitmap(MSIconData->entries[iter]->width,
+                                MSIconData->entries[iter]->height);
+                break;
+            case 8:
+                MSIconData->entries[iter]->xorBitmap = 
+                    read8Bitmap(MSIconData->entries[iter]->width,
+                                MSIconData->entries[iter]->height);
+                break;
+            case 24:
+            case 32:
+                MSIconData->entries[iter]->xorBitmap = 
+                    readXBitmap(MSIconData->entries[iter]->width,
+                                MSIconData->entries[iter]->height,bpp);
+                break;
+            default:
+                pm_error("Uncatered bit depth %d",bpp);
+            }
+            /*
+             * Read AND Bitmap
+             */
+            MSIconData->entries[iter]->andBitmap = 
+                read1Bitmap(MSIconData->entries[iter]->width,
+                            MSIconData->entries[iter]->height);
+        }
+      
+    }
+    return MSIconData;
+}
+
+
+
+static char * 
+trimOutputName(const char inputName[])
+{
+    /*
+     * Just trim off the final ".ppm", if there is one, else return as is.
+     * oh, for =~ ... :)
+     */
+    char * outFile = strdup(inputName);
+    if (STREQ(outFile + (strlen (outFile) - 4), ".ppm")) {
+        *(outFile + (strlen (outFile) - 4)) = 0;
+    }
+    return outFile;
+
+}
+
+
+
+static int 
+getBestQualityIcon(MS_Ico MSIconData)
+{
+    int x,best,best_size,best_bpp,bpp,size;
+    IC_Entry entry;
+
+    best_size = best_bpp = 0;
+    for (x = 0; x < MSIconData->count; x++) {
+        entry =  MSIconData->entries[x];
+        size = entry->width * entry->height;
+        bpp  = entry->bitcount ? entry->bitcount : entry->ih->bitcount;
+        if (size > best_size) {
+            best = x;
+            best_size = size;
+        } else if (size == best_size && bpp > best_bpp) {
+            best = x;
+            best_bpp = bpp;
+        }
+    }
+    return best;
+}
+
+static void
+writeXors(FILE *   const multiOutF,
+          char           outputFileBase[], 
+          IC_Entry const entry,
+          int      const entryNum,
+          bool     const multiple, 
+          bool     const xor) {
+/*----------------------------------------------------------------------------
+   Write an "xor" image (i.e. the main image) out.
+
+   'multiple' means this is one of multiple images that are being written.
+   'entryNum' is the sequence number within the winicon file of the image
+   we are writing.
+
+   'xor' means to include "xor" in the output file name.
+
+   if 'multiOutF' is non-null, it is the stream descriptor of an open
+   stream to which we are to write the image.  If it is null, 
+   we are to open a file using outputFileBase[] and 'entryNum' and 'xor'
+   to derive its name, and close it afterward.
+-----------------------------------------------------------------------------*/
+    FILE * outF;
+    pixel ** ppm_array;
+    int row;
+    int pel_size;
+    const char *outputFile;
+    int maxval;
+    int forcetext;
+
+    if (multiOutF) {
+        outF = multiOutF;
+        outputFile = strdup("");
+    } else {
+        if (outputFileBase) {
+            if (multiple) {
+                asprintfN(&outputFile, "%s%s_%d.ppm",
+                          outputFileBase,(xor ? "_xor" : ""), entryNum);
+            } else { 
+                asprintfN(&outputFile, "%s%s.ppm",
+                          outputFileBase,(xor ? "_xor" : ""));
+            }
+        } else
+            outputFile = strdup("-");
+        
+        outF = pm_openw(outputFile);
+    }
+    /* 
+     * allocate an array to save the bmp data into.
+     * note that entry->height will be 1/2 entry->ih->height,
+     * as the latter adds "and" and "xor" height.
+     */
+    ppm_array = ppm_allocarray(entry->width, entry->height);
+    for (row=0; row < entry->height; row++) {
+        u1 * xorRow;
+        int col;
+        switch (entry->bitcount) {
+        case 24:
+        case 32:
+            pel_size = entry->bitcount >> 3;
+            xorRow = entry->xorBitmap + row * entry->width * pel_size;
+            for (col=0; col < entry->width*pel_size;col+=pel_size) {
+                PPM_ASSIGN(ppm_array[row][col/pel_size],
+                           xorRow[col+2],xorRow[col+1],xorRow[col]);
+            }
+            break;
+        default:
+            xorRow = entry->xorBitmap + row * entry->width;
+            for (col=0; col < entry->width; col++) {
+                int colorIndex;
+                IC_Color color;
+                colorIndex  = xorRow[col];
+                color = entry->colors[colorIndex];
+                PPM_ASSIGN(ppm_array[row][col],
+                           color->red,color->green,color->blue);
+            }
+            break;
+        }
+    }    
+    
+    maxval = 255;
+    forcetext = 0;
+
+    ppm_writeppm(outF,ppm_array,entry->width, entry->height, 
+                 (pixval) maxval, forcetext);
+    ppm_freearray(ppm_array,entry->height);
+
+    strfree(outputFile);
+    
+    if (!multiOutF) 
+        pm_close(outF);
+}
+            
+
+
+static void
+writeAnds(FILE * const multiOutF, 
+          char outputFileBase[], IC_Entry const entry, int const entryNum, 
+          bool multiple) {
+/*----------------------------------------------------------------------------
+   Write the "and" image (i.e. the alpha mask) of the image 'IC_Entry' out.
+
+   'multiple' means this is one of multiple images that are being written.
+   'entryNum' is the sequence number within the winicon file of the image
+   we are writing.
+
+   if 'multiOutF' is non-null, it is the stream descriptor of an open
+   stream to which we are to write the image.  If it is null, 
+   we are to open a file using outputFileBase[] and 'entryNum' and 'xor'
+   to derive its name, and close it afterward.
+-----------------------------------------------------------------------------*/
+    FILE * outF;
+    bit ** pbm_array;
+    u1 * andRow;
+    int row;
+
+    if (multiOutF)
+        outF = multiOutF;
+    else {
+        const char *outputFile;
+
+        assert(outputFileBase);
+
+        if (multiple) 
+            asprintfN(&outputFile, "%s_and_%d.pbm", outputFileBase, entryNum);
+        else 
+            asprintfN(&outputFile, "%s_and.pbm", outputFileBase);
+        outF = pm_openw(outputFile);
+        strfree(outputFile);
+    }
+    pbm_array = pbm_allocarray(entry->width, entry->height);
+    for (row=0; row < entry->height; row++) {
+        int col;
+        andRow = entry->andBitmap + row * entry->width;
+        for (col=0; col < entry->width; col++) {
+            /* Note: black is transparent in a Netpbm alpha mask */
+            pbm_array[row][col] = andRow[col] ? PBM_BLACK: PBM_WHITE;
+        }
+    }
+
+    pbm_writepbm(outF, pbm_array, entry->width, entry->height, 0);
+
+    pbm_freearray(pbm_array, entry->height);
+    if (!multiOutF)
+        pm_close (outF);     
+}
+
+
+
+static void
+openMultiXor(char          outputFileBase[], 
+             bool    const writeands,
+             FILE ** const multiOutFP) {
+
+    const char *outputFile;
+
+    if (outputFileBase) {
+        asprintfN(&outputFile, "%s%s.ppm",
+                  outputFileBase, (writeands ? "_xor" : ""));
+    } else
+        outputFile = strdup("-");
+
+    /*
+     * Open the output file now, it'll stay open the whole time.
+     */
+    *multiOutFP = pm_openw(outputFile);
+
+    strfree(outputFile);
+}
+
+
+
+static void
+openMultiAnd(char outputFileBase[], FILE ** const multiAndOutFP) {
+
+    const char *outputFile;
+
+    assert(outputFileBase);
+
+    asprintfN(&outputFile, "%s_and.pbm", outputFileBase);
+    
+    *multiAndOutFP = pm_openw(outputFile);
+
+    strfree(outputFile);
+}
+
+static void free_iconentry(IC_Entry entry) {
+    int x;
+    if (entry->colors && entry->color_count) {
+        for (x=0;x<entry->color_count;x++) free(entry->colors[x]);
+        free(entry->colors);
+    }
+    if (entry->andBitmap) free(entry->andBitmap);
+    if (entry->xorBitmap) free(entry->xorBitmap);
+    if (entry->ih) free(entry->ih);
+    free(entry);
+}
+
+static void free_icondata(MS_Ico MSIconData)
+{
+    int x;
+    for (x=0;x<MSIconData->count;x++) {
+    free_iconentry(MSIconData->entries[x]);
+    }
+    free(MSIconData);
+}
+
+
+int 
+main(int argc, char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    int startEntry, endEntry;
+    MS_Ico MSIconData;
+    char * outputFileBase;
+    FILE * multiOutF;
+    FILE * multiAndOutF;
+   
+    ppm_init (&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    if (cmdline.bestqual && cmdline.allicons)
+        pm_message("-bestqual doesn't make sense with -allicons.  "
+                   "Ignoring -bestqual.");
+   
+    if (STREQ(cmdline.outputFilespec, "-"))
+        outputFileBase = NULL;
+    else
+        outputFileBase = trimOutputName(cmdline.outputFilespec);
+
+    ifp = pm_openr(cmdline.inputFilespec);
+
+    infname = cmdline.inputFilespec;
+
+    MSIconData = readIconFile(cmdline.verbose);
+    /*
+     * Now we've read the icon file in (Hopefully! :)
+     * Go through each of the entries, and write out files of the
+     * form
+     * 
+     * fname_0_xor.ppm
+     * fname_0_and.ppm
+     * 
+     * (or to stdout, depending on args parsing above).
+     */
+    /*
+     * If allicons is set, we want everything, if not, just go through once.
+     */
+    startEntry = 0;
+    if (cmdline.allicons) {
+        endEntry = MSIconData->count;
+    } else {
+        endEntry = 1;
+    }
+    /*
+     * If bestqual is set, find the icon with highest size & bpp.
+     */
+    if (cmdline.bestqual) {
+        startEntry = getBestQualityIcon(MSIconData);
+        endEntry = startEntry+1;
+    }
+   
+    if (cmdline.multippm) 
+        openMultiXor(outputFileBase, cmdline.writeands, &multiOutF);
+    else
+        multiOutF = NULL;
+
+    if (cmdline.writeands && cmdline.multippm) 
+        openMultiAnd(outputFileBase, &multiAndOutF);
+    else
+        multiAndOutF = NULL;
+
+    {
+        int entryNum;
+
+        for (entryNum = startEntry ; entryNum < endEntry ; entryNum++ ) {
+            IC_Entry const entry = MSIconData->entries[entryNum];
+
+            writeXors(multiOutF, outputFileBase, entry, entryNum, 
+                      cmdline.allicons, cmdline.writeands);
+            if (cmdline.writeands)
+                writeAnds(multiAndOutF, outputFileBase, 
+                          entry, entryNum, cmdline.allicons);
+        }
+    }
+    if (multiOutF)
+        pm_close (multiOutF);    
+    if (multiAndOutF)
+        pm_close(multiAndOutF);
+    
+    /* free up the image data here. */
+    free_icondata(MSIconData);
+    return 0;
+}
diff --git a/converter/ppm/xim.h b/converter/ppm/xim.h
new file mode 100644
index 00000000..ffe60fb6
--- /dev/null
+++ b/converter/ppm/xim.h
@@ -0,0 +1,125 @@
+#ifndef XIM_H_INCLUDED
+#define XIM_H_INCLUDED
+
+/* xim.h - header file for Xim files
+**
+** Taken from the X.V11R4 version of XimHeader.h:
+**
+** Author: Philip R. Thompson
+** Address:  phils@athena.mit.edu, 9-526 
+** Note:  size of header should be 1024 (1K) bytes.
+** $Header: /mit/phils/X/RCS/XimHeader.h,v 1.7 89/11/09 17:26:54 phils Exp Locker: phils $
+** $Date: 89/11/09 17:26:54 $
+** $Source: /mit/phils/X/RCS/XimHeader.h,v $
+*/
+
+#define IMAGE_VERSION    3
+#ifndef _BYTE
+typedef unsigned char  byte;
+#define _BYTE  1
+#endif
+
+/* External ascii file format. */
+typedef struct ImageHeader {
+    char file_version[8];   /* header version */
+    char header_size[8];    /* Size of file header in bytes  */
+    char image_width[8];    /* Width of the raster image */
+    char image_height[8];   /* Height of the raster imgage */
+    char num_colors[8];     /* Actual number of entries in c_map */
+    char num_channels[3];   /* 0 or 1 = pixmap, 3 = RG&B buffers */
+    char bytes_per_line[5]; /* bytes per scanline */
+    char num_pictures[4];   /* Number of pictures in file */
+    char bits_per_channel[4]; /* usually 1 or 8 */
+    char alpha_channel[4];  /* Alpha channel flag */
+    char runlength[4];      /* Runlength encoded flag */
+    char author[48];        /* Name of who made it */
+    char date[32];          /* Date and time image was made */
+    char program[16];       /* Program that created this file */
+    char comment[96];       /* other viewing info. for this image */
+    unsigned char c_map[256][3]; /* RGB values of the pixmap indices */
+} ImageHeader, XimAsciiHeader;
+
+
+/* Internal binary format. */
+typedef struct Color {
+    byte pixel, red, grn, blu;
+} Color;
+
+typedef struct XimImage {
+    int width;             /* width of the image in pixels */
+    int height;            /* height of the image in pixels */
+    unsigned datasize;     /* size of one channel of data */
+    short nchannels;     /* number data channels in image */
+    short bits_channel;  /* usually 1 or 8 */
+    short bytes_per_line; /* bytes to hold one scanline */
+    byte* data;            /* pixmap or red channel data */
+    byte* grn_data;        /* green channel data */
+    byte* blu_data;        /* blue  channel data */
+    byte* other;           /* other (alpha) data */
+    unsigned alpha_flag :1; /* alpha channel flag */
+    unsigned packed_flag:1; /* data packed in one chunk of memory */
+    unsigned runlen_flag:1; /* runlength encoded data flag */
+    unsigned : 0;           /* future flags, word alignment */
+    short tpics, npics;    /* number of images, total & left in file */
+    short ncolors;         /*   "    "  colors in the color table */
+    Color* colors;         /* colortable, one byte per r/g/b & pixel */
+    char* author;         /* author credit, copyright, etc */
+    char* date;           /* date image was made, grabbed, etc. */
+    char* program;        /* program used to make this */
+    short ncomments;       /* number of comments strings */
+    char** comments;      /* pointers to null terminated strings */
+    char* offset;         /* original offset in machine memory */
+    float chroma_red[2];   /* x, y image chromacity coords */
+    float chroma_grn[2];
+    float chroma_blu[2];
+    float chroma_wht[2];
+    float gamma;           /* image storage gamma */
+} XimImage;
+
+/* Future external ascii variable length header - under review. */
+#if (IMAGE_VERSION == 4)
+typedef struct XimAsciiHeader {
+    char file_version[4];   /* header version */
+    char header_size[8];    /* Size of file header (fixed part only) */
+    char image_height[8];   /* Height of the raster imgage in pixels */
+    char image_width[8];    /* Width of the raster image in pixels */
+    char bytes_line[8];     /* Actual # of bytes separating scanlines */
+    char bits_channel[4];   /* Bits per channel (usually 1 or 8) */
+    char num_channels[4];   /* 1 = pixmap, 3 = RG&B buffers */
+    char alpha_channel[2];  /* Alpha channel flag */
+    char num_colors[4];     /* Number of entries in c_map (if any) */
+    char num_pictures[4];   /* Number of images in file */
+    char runlength_flag[2]; /* Runlength encoded flag */
+    char future_flags[8];
+    char author[48];        /* Name of who made it, from passwd entry */
+    char date[32];          /* Unix format date */
+    char program[32];       /* Program that created this */
+    char gamma[12];         /* image storage gamma */
+    char chroma_red[24];    /* image red primary chromaticity coords. */
+    char chroma_grn[24];    /*   "   green "          "         "     */
+    char chroma_blu[24];    /*   "   blue  "          "         "     */
+    char chroma_wht[24];    /*   "   white point      "         "     */
+    char comment_length[8]  /* Total length of comments */
+    /* char* comment;           Null separated comments  */
+    /* unsigned char c_map[];   RGB Colortable, (ncolors * 3 bytes) */
+}  XimAsciiHeader;
+#endif /*IMAGE_VERSION 4*/
+
+#ifndef rnd
+#define rnd(x)  ((int)((float)(x) + 0.5)) /* round a float to an int */
+#endif
+
+/* Note:
+* - All data is in char's in order to maintain easily portability
+*   across machines, and some human readibility.
+* - Images may be stored as pixmaps (8 bits/pixel) or as seperate
+*   red, green, blue channel data (24+ bits/pixel).
+* - An alpha channel is optional and is found after every num_channels
+*   of data.
+* - Pixmaps or RGB (and alpha) channel data are stored respectively
+*   after the header.
+* - If num_channels = 1, a pixmap is assumed and the colormap in the
+*   header is used.
+* - Data size = image_width * image_height.
+*/
+#endif
diff --git a/converter/ppm/ximtoppm.c b/converter/ppm/ximtoppm.c
new file mode 100644
index 00000000..96798707
--- /dev/null
+++ b/converter/ppm/ximtoppm.c
@@ -0,0 +1,438 @@
+/* ximtoppm.c - read an Xim file and produce a portable pixmap
+**
+** Copyright (C) 1991 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include <string.h>
+#include "ppm.h"
+#include "xim.h"
+#include "shhopt.h"
+#include "nstring.h"
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *input_filename;
+    const char *alpha_filename;
+    bool alpha_stdout;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo *cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that many of the strings that this function returns in the
+   *cmdlineP structure are actually in the supplied argv array.  And
+   sometimes, one of these strings is actually just a suffix of an entry
+   in argv!
+-----------------------------------------------------------------------------*/
+    optEntry option_def[100];
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int alphaoutSpec;
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "alphaout",   OPT_STRING, 
+            &cmdlineP->alpha_filename, &alphaoutSpec, 0);
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and all of *cmdlineP. */
+
+    if (!alphaoutSpec)
+        cmdlineP->alpha_filename = NULL;
+
+    if (argc - 1 == 0)
+        cmdlineP->input_filename = "-";  /* he wants stdin */
+    else if (argc - 1 == 1)
+        cmdlineP->input_filename = strdup(argv[1]);
+    else 
+        pm_error("Too many arguments.  The only argument accepted "
+                 "is the input file specification");
+
+    if (cmdlineP->alpha_filename && 
+        STREQ(cmdlineP->alpha_filename, "-"))
+        cmdlineP->alpha_stdout = TRUE;
+    else 
+        cmdlineP->alpha_stdout = FALSE;
+}
+
+
+
+/* The subroutines are excerpted and slightly modified from the
+   X.V11R4 version of xim_io.c. 
+*/
+
+static int
+ReadXimHeader(FILE *     const in_fp,
+              XimImage * const header) {
+
+    int  i;
+    char *cp;
+    XimAsciiHeader  a_head;
+
+    cp = (char *) header;
+    for (i = 0; i < sizeof(XimImage); ++i )
+    *cp++ = 0;
+    /* Read header and verify image file formats */
+    if (fread((char *)&a_head, sizeof(ImageHeader), 1, in_fp) != 1) {
+        pm_message("ReadXimHeader: unable to read file header" );
+        return(0);
+    }
+    if (atoi(a_head.header_size) != sizeof(ImageHeader)) {
+        pm_message("ReadXimHeader: header size mismatch" );
+        return(0);
+    }
+    if (atoi(a_head.file_version) != IMAGE_VERSION) {
+        pm_message("ReadXimHeader: incorrect Image_file version" );
+        return(0);
+    }
+    header->width = atoi(a_head.image_width);
+    header->height = atoi(a_head.image_height);
+    header->ncolors = atoi(a_head.num_colors);
+    header->nchannels = atoi(a_head.num_channels);
+    header->bytes_per_line = atoi(a_head.bytes_per_line);
+/*    header->npics = atoi(a_head.num_pictures);
+*/
+    header->bits_channel = atoi(a_head.bits_per_channel);
+    header->alpha_flag = atoi(a_head.alpha_channel);
+    if (strlen(a_head.author)) {
+        if (!(header->author = calloc((unsigned int)strlen(a_head.author)+1,
+                1))) {
+            pm_message("ReadXimHeader: can't calloc author string" );
+            return(0);
+        }
+    header->width = atoi(a_head.image_width);
+        strncpy(header->author, a_head.author, strlen(a_head.author));
+    }
+    if (strlen(a_head.date)) {
+        if (!(header->date =calloc((unsigned int)strlen(a_head.date)+1,1))){
+            pm_message("ReadXimHeader: can't calloc date string" );
+            return(0);
+        }
+    header->width = atoi(a_head.image_width);
+        strncpy(header->date, a_head.date, strlen(a_head.date));
+    }
+    if (strlen(a_head.program)) {
+        if (!(header->program = calloc(
+                    (unsigned int)strlen(a_head.program) + 1, 1))) {
+            pm_message("ReadXimHeader: can't calloc program string" );
+            return(0);
+        }
+    header->width = atoi(a_head.image_width);
+        strncpy(header->program, a_head.program,strlen(a_head.program));
+    }
+    /* Do double checking for bakwards compatibility */
+    if (header->npics == 0)
+        header->npics = 1;
+    if (header->bits_channel == 0)
+        header->bits_channel = 8;
+    else if (header->bits_channel == 24) {
+        header->nchannels = 3;
+        header->bits_channel = 8;
+    }
+    if ((int)header->bytes_per_line == 0)
+        header->bytes_per_line = 
+            (header->bits_channel == 1 && header->nchannels == 1) ?
+                (header->width + 7) / 8 :
+                header->width;
+    header->datasize =(unsigned int)header->bytes_per_line * header->height;
+    if (header->nchannels == 3 && header->bits_channel == 8)
+        header->ncolors = 0;
+    else if (header->nchannels == 1 && header->bits_channel == 8) {
+        header->colors = (Color *)calloc((unsigned int)header->ncolors,
+                sizeof(Color));
+        if (header->colors == NULL) {
+            pm_message("ReadXimHeader: can't calloc colors" );
+            return(0);
+        }
+        for (i=0; i < header->ncolors; i++) {
+            header->colors[i].red = a_head.c_map[i][0];
+            header->colors[i].grn = a_head.c_map[i][1];
+            header->colors[i].blu = a_head.c_map[i][2];
+        }
+    }
+    return(1);
+}
+
+
+
+static int
+ReadImageChannel(FILE *         const infp,
+                 byte *         const buf,
+                 unsigned int * const bufsize,
+                 int            const encoded) {
+
+    int  i, runlen, nbytes;
+    unsigned int  j;
+    byte *line;
+    long  marker;
+
+    if (!encoded)
+        j = fread((char *)buf, 1, (int)*bufsize, infp);
+    else {
+        if ((line=(byte *)malloc((unsigned int)BUFSIZ)) == NULL) {
+            pm_message("ReadImageChannel: can't malloc() fread string" );
+            return(0);
+        }
+        /* Unrunlength encode data */
+        marker = ftell(infp);
+        j = 0;
+        while (((nbytes=fread((char *)line, 1, BUFSIZ, infp)) > 0) &&
+            (j < *bufsize)) {
+            for (i=0; (i < nbytes) && (j < *bufsize); i++) {
+                runlen = (int)line[i]+1;
+                i++;
+                while (runlen--)
+                    buf[j++] = line[i];
+            }
+            marker += i;
+        }
+        /* return to the begining of the next image's bufffer */
+        if (fseek(infp, marker, 0) == -1) {
+            pm_message("ReadImageChannel: can't fseek to location in image buffer" );
+            return(0);
+        }
+        free((char *)line);
+    }
+    if (j != *bufsize) {
+        pm_message("unable to complete channel: %u / %u (%d%%)",
+            j, *bufsize, (int)(j*100.0 / *bufsize) );
+        *bufsize = j;
+    }
+    return(1);
+}
+
+
+
+static int
+ReadXimImage(FILE *     const in_fp,
+             XimImage * const xim) {
+
+    if (xim->data) {
+        free((char *)xim->data);
+        xim->data = (byte *)0;
+    }
+    if (xim->grn_data) {
+        free((char *)xim->grn_data);
+        xim->grn_data = (byte *)0;
+    }
+    if (xim->blu_data) {
+        free((char *)xim->blu_data);
+        xim->blu_data = (byte *)0;
+    }
+    if (xim->other) {
+        free((char *)xim->other);
+        xim->other = (byte *)0;
+    }
+    xim->npics = 0;
+    if (!(xim->data = (byte *)calloc(xim->datasize, 1))) {
+        pm_message("ReadXimImage: can't malloc pixmap data" );
+        return(0);
+    }
+    if (!ReadImageChannel(in_fp, xim->data, &xim->datasize, 0)) {
+        pm_message("ReadXimImage: end of the images" );
+        return(0);
+    }
+    if (xim->nchannels == 3) {
+        xim->grn_data = (byte *)malloc(xim->datasize);
+        xim->blu_data = (byte *)malloc(xim->datasize);
+        if (xim->grn_data == NULL || xim->blu_data == NULL) {
+            pm_message("ReadXimImage: can't malloc rgb channel data" );
+            free((char *)xim->data);
+            if (xim->grn_data)  free((char *)xim->grn_data);
+            if (xim->blu_data)  free((char *)xim->blu_data);
+            xim->data = xim->grn_data = xim->blu_data = (byte*)0;
+            return(0);
+        }
+        if (!ReadImageChannel(in_fp, xim->grn_data, &xim->datasize, 0))
+            return(0);
+        if (!ReadImageChannel(in_fp, xim->blu_data, &xim->datasize, 0))
+            return(0);
+    }
+    if (xim->nchannels > 3) {
+        /* In theory, this can be any fourth channel, but the only one we
+           know about is an Alpha channel, so we'll call it that, even
+           though we process it generically.
+           */
+        if ((xim->other = (byte *)malloc(xim->datasize)) == NULL) {
+            pm_message("ReadXimImage: can't malloc alpha data" );
+            return(0);
+        }
+        if (!ReadImageChannel(in_fp, xim->other, &xim->datasize, 0))
+            return(0);
+    }
+    xim->npics = 1;
+    return(1);
+}
+
+
+
+/***********************************************************************
+*  File:   xlib.c
+*  Author: Philip Thompson
+*  $Date: 89/11/01 10:14:23 $
+*  $Revision: 1.14 $
+*  Purpose: General xim libray of utililities
+*  Copyright (c) 1988  Philip R. Thompson
+*                Computer Resource Laboratory (CRL)
+*                Dept. of Architecture and Planning
+*                M.I.T., Rm 9-526
+*                Cambridge, MA  02139
+*   This  software and its documentation may be used, copied, modified,
+*   and distributed for any purpose without fee, provided:
+*       --  The above copyright notice appears in all copies.
+*       --  This disclaimer appears in all source code copies.
+*       --  The names of M.I.T. and the CRL are not used in advertising
+*           or publicity pertaining to distribution of the software
+*           without prior specific written permission from me or CRL.
+*   I provide this software freely as a public service.  It is NOT a
+*   commercial product, and therefore is not subject to an an implied
+*   warranty of merchantability or fitness for a particular purpose.  I
+*   provide it as is, without warranty.
+*   This software is furnished  only on the basis that any party who
+*   receives it indemnifies and holds harmless the parties who furnish
+*   it against any claims, demands, or liabilities connected with using
+*   it, furnishing it to others, or providing it to a third party.
+*
+*   Philip R. Thompson (phils@athena.mit.edu)
+***********************************************************************/
+
+static int
+ReadXim(in_fp, xim)
+    FILE *in_fp;
+    XimImage *xim;
+{
+    if (!ReadXimHeader(in_fp, xim)) {
+        pm_message("can't read xim header" );
+    return(0);
+    }
+    if (!ReadXimImage(in_fp, xim)) {
+        pm_message("can't read xim data" );
+    return(0);
+    }
+    return(1);
+}
+
+
+
+int
+main(int argc,
+     char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE *ifP, *imageout_file, *alpha_file;
+    XimImage xim;
+    pixel *pixelrow, colormap[256];
+    gray *alpharow;
+        /* The alpha channel of the row we're currently converting, in
+           pgm fmt
+        */
+    int rows, cols, row, mapped;
+    pixval maxval;
+    bool success;
+
+    ppm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+    
+    ifP = pm_openr(cmdline.input_filename);
+    
+    if (cmdline.alpha_stdout)
+        alpha_file = stdout;
+    else if (cmdline.alpha_filename == NULL) 
+        alpha_file = NULL;
+    else
+        alpha_file = pm_openw(cmdline.alpha_filename);
+    
+    if (cmdline.alpha_stdout) 
+        imageout_file = NULL;
+    else
+        imageout_file = stdout;
+    
+    success = ReadXim(ifP, &xim);
+    if (!success)
+        pm_error("can't read Xim file");
+
+    rows = xim.height;
+    cols = xim.width;
+
+    if (xim.nchannels == 1 && xim.bits_channel == 8) {
+        int i;
+
+        mapped = true;
+        maxval = 255;
+        for (i = 0; i < xim.ncolors; ++i) {
+            PPM_ASSIGN(
+                colormap[i], xim.colors[i].red,
+                xim.colors[i].grn, xim.colors[i].blu );
+            /* Should be colormap[xim.colors[i].pixel], but Xim is broken. */
+        }
+    } else if (xim.nchannels == 3 || xim.nchannels == 4) {
+        mapped = false;
+        maxval = pm_bitstomaxval(xim.bits_channel);
+    } else
+        pm_error(
+            "unknown Xim file type, nchannels == %d, bits_channel == %d",
+            xim.nchannels, xim.bits_channel);
+
+    if (imageout_file) 
+        ppm_writeppminit(imageout_file, cols, rows, maxval, 0);
+    if (alpha_file)
+        pgm_writepgminit(alpha_file, cols, rows, maxval, 0);
+
+    pixelrow = ppm_allocrow(cols);
+    alpharow = pgm_allocrow(cols);
+
+    for (row = 0; row < rows; ++row) {
+        if (mapped) {
+            byte * const ximrow = xim.data + row * xim.bytes_per_line;
+            unsigned int col;
+            
+            for (col = 0; col < cols; ++col) 
+                pixelrow[col] = colormap[ximrow[col]];
+            alpharow[col] = 0;
+        } else {
+            byte * const redrow = xim.data     + row * xim.bytes_per_line;
+            byte * const grnrow = xim.grn_data + row * xim.bytes_per_line;
+            byte * const blurow = xim.blu_data + row * xim.bytes_per_line;
+            byte * const othrow = xim.other    + row * xim.bytes_per_line;
+
+            unsigned int col;
+
+            for (col = 0; col < cols; ++col) {
+                PPM_ASSIGN(pixelrow[col],
+                           redrow[col], grnrow[col], blurow[col]);
+                if (xim.nchannels > 3)
+                    alpharow[col] = othrow[col];
+                else 
+                    alpharow[col] = 0;
+            }
+        }
+        if (imageout_file) 
+            ppm_writeppmrow(imageout_file, pixelrow, cols, maxval, 0);
+        if (alpha_file)
+            pgm_writepgmrow(alpha_file, alpharow, cols, maxval, 0);
+    }
+    pm_close(ifP);
+    if (imageout_file)
+        pm_close(imageout_file);
+    if (alpha_file)
+        pm_close(alpha_file);
+
+    return 0;
+}
diff --git a/converter/ppm/xpmtoppm.README b/converter/ppm/xpmtoppm.README
new file mode 100644
index 00000000..b5e254fa
--- /dev/null
+++ b/converter/ppm/xpmtoppm.README
@@ -0,0 +1,69 @@
+PPM Stuff 
+Convert portable pixmap to X11 Pixmap format (version 3) and vice versa
+-----------------------------------------------------------------------
+
+The program ppmtoxpm is a modified version of one sent out by Mark Snitily
+(mark@zok.uucp) and upgraded to XPM version 2 by Paul Breslaw
+(paul@mecazh.uu.ch).
+
+It converts Jeff Poskanzer's (jef@well.sf.ca.us) portable pixmap format
+(PBMPlus) into the new X11 pixmap format: XPM version 3 distributed by Arnaud
+Le Hors (lehors@mirsa.inria.fr).
+
+It is built using the PBMPlus libraries in the same way as any of the
+ppm utilities in the PBMPlus package.
+
+Paul Breslaw - Thu Nov 22 09:55:31 MET 1990
+--
+Paul Breslaw, Mecasoft SA,          |  telephone :  41 1 362 2040
+Guggachstrasse 10, CH-8057 Zurich,  |  e-mail    :  paul@mecazh.uu.ch
+Switzerland.                        |               mcsun!chx400!mecazh!paul
+--
+
+The program xpmtoppm is a modified version of the one distributed in the
+PBMPlus package by Jeff Poskanzer's which converts XPM version 1 or 3 files
+into a portable pixmap format.
+
+Upgraded to XPM version 3 by
+  Arnaud LE HORS     BULL Research France -- Koala Project
+  lehors@sa.inria.fr  Phone:(33) 93 65 77 71  Fax:(33) 93 65 77 66
+  Inria Sophia Antipolis B.P.109 06561 Valbonne Cedex France
+
+
+Installation
+-----------
+You should copy The ppmtoxpm.c, ppmtoxpm.1 and xpmtoppm.c, xpmtoppm.1 into
+your .../pbmplus/ppm directory.
+
+
+Patches
+-------
+* Rainer Sinkwitz sinkwitz@ifi.unizh.ch - 21 Nov 91:
+
+xpmtoppm.c:
+  - Bug fix, no advance of read ptr, would not read 
+    colors like "ac c black" because it would find 
+    the "c" of "ac" and then had problems with "c"
+    as color.
+    
+  - Now understands multword X11 color names
+  
+  - Now reads multiple color keys. Takes the color
+    of the hightest available key. Lines no longer need
+    to begin with key 'c'.
+    
+  - expanded line buffer to from 500 to 2048 for bigger files
+    
+ppmtoxpm.c:
+  - Bug fix, should should malloc space for rgbn[j].name+1 in line 441
+    caused segmentation faults
+    
+  - lowercase conversion of RGB names def'ed out,
+    considered harmful.
+    
+Suggestions:
+  ppmtoxpm should read /usr/lib/X11/rgb.txt by default.
+  With the Imakefiles of pbmplus it even gets compiled 
+  with -DRGB_DB=\"/usr/lib/X11/rgb.txt\"
+
+
diff --git a/converter/ppm/xpmtoppm.c b/converter/ppm/xpmtoppm.c
new file mode 100644
index 00000000..9dddfd83
--- /dev/null
+++ b/converter/ppm/xpmtoppm.c
@@ -0,0 +1,832 @@
+/* xpmtoppm.c - read an X11 pixmap file and produce a portable pixmap
+**
+** Copyright (C) 1991 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+**
+** Upgraded to handle XPM version 3 by
+**   Arnaud Le Hors (lehors@mirsa.inria.fr)
+**   Tue Apr 9 1991
+**
+** Rainer Sinkwitz sinkwitz@ifi.unizh.ch - 21 Nov 91:
+**  - Bug fix, no advance of read ptr, would not read 
+**    colors like "ac c black" because it would find 
+**    the "c" of "ac" and then had problems with "c"
+**    as color.
+**    
+**  - Now understands multiword X11 color names
+**  
+**  - Now reads multiple color keys. Takes the color
+**    of the hightest available key. Lines no longer need
+**    to begin with key 'c'.
+**    
+**  - expanded line buffer to from 500 to 2048 for bigger files
+*/
+
+#define _BSD_SOURCE   /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include <string.h>
+
+#include "ppm.h"
+#include "shhopt.h"
+#include "nstring.h"
+#include "mallocvar.h"
+
+#define MAX_LINE (8 * 1024)
+  /* The maximum size XPM input line we can handle. */
+
+/* number of xpmColorKeys */
+#define NKEYS 5
+
+const char *xpmColorKeys[] =
+{
+ "s",					/* key #1: symbol */
+ "m",					/* key #2: mono visual */
+ "g4",					/* key #3: 4 grays visual */
+ "g",					/* key #4: gray visual */
+ "c",					/* key #5: color visual */
+};
+
+struct cmdline_info {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    char *input_filespec;  /* Filespecs of input files */
+    char *alpha_filename;
+    int alpha_stdout;
+    int verbose;
+};
+
+
+static int verbose;
+
+
+static void
+parse_command_line(int argc, char ** argv,
+                   struct cmdline_info *cmdline_p) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optStruct *option_def = malloc(100*sizeof(optStruct));
+        /* Instructions to OptParseOptions2 on how to parse our options.
+         */
+    optStruct2 opt;
+
+    unsigned int option_def_index;
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENTRY(0,   "alphaout",   OPT_STRING, &cmdline_p->alpha_filename, 0);
+    OPTENTRY(0,   "verbose",    OPT_FLAG,   &cmdline_p->verbose,        0);
+
+    cmdline_p->alpha_filename = NULL;
+    cmdline_p->verbose = FALSE;
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = TRUE;  /* We may have parms that are negative numbers */
+
+    optParseOptions2(&argc, argv, opt, 0);
+        /* Uses and sets argc, argv, and some of *cmdline_p and others. */
+
+    if (argc - 1 == 0)
+        cmdline_p->input_filespec = NULL;  /* he wants stdin */
+    else if (argc - 1 == 1)
+        cmdline_p->input_filespec = strdup(argv[1]);
+    else 
+        pm_error("Too many arguments.  The only argument accepted\n"
+                 "is the input file specification");
+
+    if (cmdline_p->alpha_filename && 
+        STREQ(cmdline_p->alpha_filename, "-"))
+        cmdline_p->alpha_stdout = TRUE;
+    else 
+        cmdline_p->alpha_stdout = FALSE;
+
+}
+
+
+static char lastInputLine[MAX_LINE+1];
+    /* contents of line most recently read from input */
+static bool backup;
+    /* TRUE means next read should be a reread of the most recently read
+       line, i.e. lastInputLine, instead of a read from the input file.
+    */
+
+
+static void
+getline(char * const line,
+        size_t const size,
+        FILE * const stream) {
+/*----------------------------------------------------------------------------
+   Read the next line from the input file 'stream', through the one-line
+   buffer lastInputLine[].
+
+   If 'backup' is true, the "next line" is the previously read line, i.e.
+   the one in that one-line buffer.  Otherwise, the "next line" is the next
+   line from the real file.  After reading the backed up line, we reset 
+   'backup' to false.
+
+   Return the line as a null terminated string in *line, which is an
+   array of 'size' bytes.
+
+   Exit program if the line doesn't fit in the buffer.
+-----------------------------------------------------------------------------*/
+    if (size > sizeof(lastInputLine))
+        pm_error("INTERNAL ERROR: getline() received 'size' parameter "
+                 "which is out of bounds");
+
+    if (backup) {
+        strncpy(line, lastInputLine, size); 
+        backup = FALSE;
+    } else {
+        if (fgets(line, size, stream) == NULL)
+            pm_error("EOF or read error on input file");
+        if (strlen(line) == size - 1)
+            pm_error("Input file has line that is too long (longer than "
+                     "%u bytes).", (unsigned)size - 1);
+        STRSCPY(lastInputLine, line);
+    }
+}
+
+
+
+static unsigned int
+getNumber(char * const p, unsigned int const size) {
+
+    unsigned int retval;
+    char * q;
+    
+    retval = 0;
+    for (q = p; q < p+size; ++q)
+        retval = (retval << 8) + *q;
+
+    return retval;
+}
+
+
+
+static void
+getword(char * const output, char ** const cursorP) {
+
+    char *t1;
+    char *t2;
+
+    for (t1=*cursorP; ISSPACE(*t1); t1++); /* skip white space */
+    for (t2 = t1; !ISSPACE(*t2) && *t2 != '"' && *t2 != '\0'; t2++);
+        /* Move to next white space, ", or eol */
+    if (t2 > t1)
+        strncpy(output, t1, t2 - t1);
+    output[t2 - t1] = '\0';
+    *cursorP = t2;
+}    
+
+
+
+static void
+addToColorMap(unsigned int const seqNum,
+              unsigned int const colorNumber, 
+              pixel * const colors, int * const ptab, 
+              char colorspec[], int const isTransparent,
+              int * const transparentP) {
+/*----------------------------------------------------------------------------
+   Add the color named by colorspec[] to the colormap contained in
+   'colors' and 'ptab', as the color associated with XPM color number
+   'colorNumber', which is the seqNum'th color in the XPM color map.
+
+   Iff 'transparent', set *transparentP to the colormap index that 
+   corresponds to this color.
+-----------------------------------------------------------------------------*/
+    if (ptab == NULL) {
+        /* Index into table. */
+        colors[colorNumber] = ppm_parsecolor(colorspec,
+                                             (pixval) PPM_MAXMAXVAL);
+        if (isTransparent) 
+            *transparentP = colorNumber;
+    } else {
+        /* Set up linear search table. */
+        colors[seqNum] = ppm_parsecolor(colorspec,
+                                        (pixval) PPM_MAXMAXVAL);
+        ptab[seqNum] = colorNumber;
+        if (isTransparent)
+            *transparentP = seqNum;
+    }
+}
+
+
+
+static void
+interpretXpm3ColorTableLine(char line[], int const seqNum, 
+                            int const chars_per_pixel,
+                            pixel * const colors, int * const ptab,
+                            int * const transparentP) {
+/*----------------------------------------------------------------------------
+   Interpret one line of the color table in the XPM header.  'line' is
+   the line from the XPM file.  It is the seqNum'th color table entry in
+   the file.  The file uses 'chars_per_pixel' characters per pixel.
+
+   Add the information from this color table entry to the color table
+   'colors' and, if it isn't NULL, the corresponding lookup shadow table
+   'ptab' (see readXpm3ColorTable for a description of these data 
+   structures).
+
+   The line may include values for multiple kinds of color (grayscale,
+   color, etc.).  We take the highest of these (e.g. color over grayscale).
+
+   If a color table entry indicates transparency, set *transparentP
+   to the colormap index that corresponds to the indicated color.
+-----------------------------------------------------------------------------*/
+    /* Note: this code seems to allow for multi-word color specifications,
+       but I'm not aware that such are legal.  Ultimately, ppm_parsecolor()
+       interprets the name, and I believe it only takes single word 
+       color specifications.  -Bryan 2001.05.06.
+    */
+    char str2[MAX_LINE+1];    
+    char *t1;
+    char *t2;
+    int endOfEntry;   /* boolean */
+    
+    unsigned int curkey, key, highkey;	/* current color key */
+    unsigned int lastwaskey;	
+        /* The last token we processes was a key, and we have processed
+           at least one token.
+        */
+    char curbuf[BUFSIZ];		/* current buffer */
+    int isTransparent;
+    
+    int colorNumber;
+        /* A color number that appears in the raster */
+    /* read the chars */
+    t1 = strchr(line, '"');
+    if (t1 == NULL)
+        pm_error("A line that is supposed to be an entry in the color "
+                 "table does not start with a quote.  The line is '%s'.  "
+                 "It is the %dth entry in the color table.", 
+                 line, seqNum);
+    else
+        t1++;  /* Points now to first color number character */
+
+    colorNumber = getNumber(t1, chars_per_pixel);
+    t1 += chars_per_pixel;
+
+    /*
+     * read color keys and values 
+     */
+    curkey = 0; 
+    highkey = 1;
+    lastwaskey = FALSE;
+    t2 = t1;
+    endOfEntry = FALSE;
+    while ( !endOfEntry ) {
+        int isKey;   /* boolean */
+        getword(str2, &t2);
+        if (strlen(str2) == 0)
+            endOfEntry = TRUE;
+        else {
+            /* See if the word we got is a valid key (and get its key
+               number if so)
+            */
+            for (key = 1; 
+                 key <= NKEYS && !STREQ(xpmColorKeys[key - 1], str2); 
+                 key++);
+            isKey = (key <= NKEYS);
+
+            if (lastwaskey || !isKey) {
+                /* This word is a color specification (or "none" for
+                   transparent).
+                */
+                if (!curkey) 
+                    pm_error("Missing color key token in color table line "
+                             "'%s' before '%s'.", line, str2);
+                if (!lastwaskey) 
+                    strcat(curbuf, " ");		/* append space */
+                if ( (strncmp(str2, "None", 4) == 0) 
+                     || (strncmp(str2, "none", 4) == 0) ) {
+                    /* This entry identifies the transparent color number */
+                    strcat(curbuf, "#000000");  /* Make it black */
+                    isTransparent = TRUE;
+                } else 
+                    strcat(curbuf, str2);		/* append buf */
+                lastwaskey = 0;
+            } else { 
+                /* This word is a key.  So we've seen the last of the 
+                   info for the previous key, and we must either put it
+                   in the color map or ignore it if we already have a higher
+                   color form in the colormap for this colormap entry.
+                */
+                if (curkey > highkey) {	/* flush string */
+                    addToColorMap(seqNum, colorNumber, colors, ptab, curbuf,
+                                  isTransparent, transparentP);
+                    highkey = curkey;
+                }
+                curkey = key;			/* set new key  */
+                curbuf[0] = '\0';		/* reset curbuf */
+                isTransparent = FALSE;
+                lastwaskey = 1;
+            }
+            if (*t2 == '"') break;
+        }
+    }
+    /* Put the info for the last key in the line into the colormap (or
+       ignore it if there's already a higher color form for this colormap
+       entry in it)
+    */
+    if (curkey > highkey) {
+        addToColorMap(seqNum, colorNumber, colors, ptab, curbuf,
+                      isTransparent, transparentP);
+        highkey = curkey;
+    }
+    if (highkey == 1) 
+        pm_error("C error scanning color table");
+}
+
+
+
+static void
+readXpm3Header(FILE * const stream, int * const widthP, int * const heightP, 
+               int * const chars_per_pixelP, int * const ncolorsP,
+               pixel ** const colorsP, int ** const ptabP,
+               int * const transparentP) {
+/*----------------------------------------------------------------------------
+  Read the header of the XPM file on stream 'stream'.  Assume the
+  getline() stream is presently positioned to the beginning of the
+  file and it is a Version 3 XPM file.  Leave the stream positioned
+  after the header.
+
+  We have two ways to return the colormap, depending on the number of
+  characters per pixel in the XPM:  
+  
+  If it is 1 or 2 characters per pixel, we return the colormap as a
+  Netpbm 'pixel' array *colorsP (in newly malloc'ed storage), such
+  that if a color in the raster is identified by index N, then
+  (*colorsP)[N] is that color.  So this array is either 256 or 64K
+  pixels.  In this case, we return *ptabP = NULL.
+
+  If it is more than 2 characters per pixel, we return the colormap as
+  both a Netpbm 'pixel' array *colorsP and a lookup table *ptabP (both
+  in newly malloc'ed storage).
+
+  If a color in the raster is identified by index N, then for some I,
+  (*ptabP)[I] is N and (*colorsP)[I] is the color in question.  So 
+  you iterate through *ptabP looking for N and then look at the 
+  corresponding entry in *colorsP to get the color.
+
+  Return as *transColorNumberP the value of the XPM color number that
+  represents a transparent pixel, or -1 if no color number does.
+-----------------------------------------------------------------------------*/
+    char line[MAX_LINE+1];
+    const char * xpm3_signature = "/* XPM */";
+    
+    *widthP = *heightP = *ncolorsP = *chars_per_pixelP = -1;
+
+    /* Read the XPM signature comment */
+    getline(line, sizeof(line), stream);
+    if (strncmp(line, xpm3_signature, strlen(xpm3_signature)) != 0) 
+        pm_error("Apparent XPM 3 file does not start with '/* XPM */'.  "
+                 "First line is '%s'", xpm3_signature);
+
+    /* Read the assignment line */
+    getline(line, sizeof(line), stream);
+    if (strncmp(line, "static char", 11) != 0)
+        pm_error("Cannot find data structure declaration.  Expected a "
+                 "line starting with 'static char', but found the line "
+                 "'%s'.", line);
+
+	/* Read the hints line */
+    getline(line, sizeof(line), stream);
+    /* skip the comment line if any */
+    if (!strncmp(line, "/*", 2)) {
+        while (!strstr(line, "*/"))
+            getline(line, sizeof(line), stream);
+        getline(line, sizeof(line), stream);
+    }
+    if (sscanf(line, "\"%d %d %d %d\",", widthP, heightP,
+               ncolorsP, chars_per_pixelP) != 4)
+        pm_error("error scanning hints line");
+
+    if (verbose == 1) 
+    {
+        pm_message("Width x Height:  %d x %d", *widthP, *heightP);
+        pm_message("no. of colors:  %d", *ncolorsP);
+        pm_message("chars per pixel:  %d", *chars_per_pixelP);
+    }
+
+    /* Allocate space for color table. */
+    if (*chars_per_pixelP <= 2) {
+        /* Set up direct index (see above) */
+        *colorsP = ppm_allocrow(*chars_per_pixelP == 1 ? 256 : 256*256);
+        *ptabP = NULL;
+    } else {
+        /* Set up lookup table (see above) */
+        *colorsP = ppm_allocrow(*ncolorsP);
+        MALLOCARRAY(*ptabP, *ncolorsP);
+        if (*ptabP == NULL)
+            pm_error("Unable to allocate memory for %d colors", *ncolorsP);
+    }
+    
+    { 
+        /* Read the color table */
+        int seqNum;
+            /* Sequence number of entry within color table in XPM header */
+
+        *transparentP = -1;  /* initial value */
+
+        for (seqNum = 0; seqNum < *ncolorsP; seqNum++) {
+            getline(line, sizeof(line), stream);
+            /* skip the comment line if any */
+            if (!strncmp(line, "/*", 2))
+                getline(line, sizeof(line), stream);
+            
+            interpretXpm3ColorTableLine(line, seqNum, *chars_per_pixelP, 
+                                        *colorsP, *ptabP, transparentP);
+        }
+    }
+}
+
+
+static void
+readXpm1Header(FILE * const stream, int * const widthP, int * const heightP, 
+               int * const chars_per_pixelP, int * const ncolorsP, 
+               pixel ** const colorsP, int ** const ptabP) {
+/*----------------------------------------------------------------------------
+  Read the header of the XPM file on stream 'stream'.  Assume the
+  getline() stream is presently positioned to the beginning of the
+  file and it is a Version 1 XPM file.  Leave the stream positioned
+  after the header.
+  
+  Return the information from the header the same as for readXpm3Header.
+-----------------------------------------------------------------------------*/
+    char line[MAX_LINE+1], str1[MAX_LINE+1], str2[MAX_LINE+1];
+    char *t1;
+    char *t2;
+    int format, v;
+    int i, j;
+    bool processedStaticChar;  
+        /* We have read up to and interpreted the "static char..." line */
+
+    *widthP = *heightP = *ncolorsP = *chars_per_pixelP = format = -1;
+
+    /* Read the initial defines. */
+    processedStaticChar = FALSE;
+    while (!processedStaticChar) {
+        getline(line, sizeof(line), stream);
+
+        if (sscanf(line, "#define %s %d", str1, &v) == 2) {
+            char *t1;
+            if ((t1 = strrchr(str1, '_')) == NULL)
+                t1 = str1;
+            else
+                ++t1;
+            if (STREQ(t1, "format"))
+                format = v;
+            else if (STREQ(t1, "width"))
+                *widthP = v;
+            else if (STREQ(t1, "height"))
+                *heightP = v;
+            else if (STREQ(t1, "ncolors"))
+                *ncolorsP = v;
+            else if (STREQ(t1, "pixel"))
+                *chars_per_pixelP = v;
+        } else if (!strncmp(line, "static char", 11)) {
+            if ((t1 = strrchr(line, '_')) == NULL)
+                t1 = line;
+            else
+                ++t1;
+            processedStaticChar = TRUE;
+        }
+    }
+    /* File is positioned to "static char" line, which is in line[] and
+       t1 points to position of last "_" in the line, or the beginning of
+       the line if there is no "_"
+    */
+    if (format == -1)
+        pm_error("missing or invalid format");
+    if (format != 1)
+        pm_error("can't handle XPM version %d", format);
+    if (*widthP == -1)
+        pm_error("missing or invalid width");
+    if (*heightP == -1)
+        pm_error("missing or invalid height");
+    if (*ncolorsP == -1)
+        pm_error("missing or invalid ncolors");
+    if (*chars_per_pixelP == -1)
+        pm_error("missing or invalid *chars_per_pixelP");
+    if (*chars_per_pixelP > 2)
+        pm_message("WARNING: *chars_per_pixelP > 2 uses a lot of memory");
+
+    /* If there's a monochrome color table, skip it. */
+    if (!strncmp(t1, "mono", 4)) {
+        for (;;) {
+            getline(line, sizeof(line), stream);
+            if (!strncmp(line, "static char", 11))
+                break;
+        }
+    }
+    /* Allocate space for color table. */
+    if (*chars_per_pixelP <= 2) {
+        /* Up to two chars per pixel, we can use an indexed table. */
+        v = 1;
+        for (i = 0; i < *chars_per_pixelP; ++i)
+            v *= 256;
+        *colorsP = ppm_allocrow(v);
+        *ptabP = NULL;
+    } else {
+        /* Over two chars per pixel, we fall back on linear search. */
+        *colorsP = ppm_allocrow(*ncolorsP);
+        MALLOCARRAY(*ptabP, *ncolorsP);
+        if (*ptabP == NULL)
+            pm_error("Unable to allocate memory for %d colors", *ncolorsP);
+    }
+
+    /* Read color table. */
+    for (i = 0; i < *ncolorsP; ++i) {
+        getline(line, sizeof(line), stream);
+
+        if ((t1 = strchr(line, '"')) == NULL)
+            pm_error("D error scanning color table");
+        if ((t2 = strchr(t1 + 1, '"')) == NULL)
+            pm_error("E error scanning color table");
+        if (t2 - t1 - 1 != *chars_per_pixelP)
+            pm_error("wrong number of chars per pixel in color table");
+        strncpy(str1, t1 + 1, t2 - t1 - 1);
+        str1[t2 - t1 - 1] = '\0';
+
+        if ((t1 = strchr(t2 + 1, '"')) == NULL)
+            pm_error("F error scanning color table");
+        if ((t2 = strchr(t1 + 1, '"')) == NULL)
+            pm_error("G error scanning color table");
+        strncpy(str2, t1 + 1, t2 - t1 - 1);
+        str2[t2 - t1 - 1] = '\0';
+
+        v = 0;
+        for (j = 0; j < *chars_per_pixelP; ++j)
+            v = (v << 8) + str1[j];
+        if (*chars_per_pixelP <= 2)
+            /* Index into table. */
+            (*colorsP)[v] = ppm_parsecolor(str2,
+                                           (pixval) PPM_MAXMAXVAL);
+        else {
+            /* Set up linear search table. */
+            (*colorsP)[i] = ppm_parsecolor(str2,
+                                           (pixval) PPM_MAXMAXVAL);
+            (*ptabP)[i] = v;
+        }
+    }
+    /* Position to first line of raster (which is the line after
+       "static char ...").
+    */
+    for (;;) {
+        getline(line, sizeof(line), stream);
+        if (strncmp(line, "static char", 11) == 0)
+            break;
+    }
+}
+
+
+
+static void
+interpretXpmLine(char   const line[],
+                 int    const chars_per_pixel,
+                 int    const ncolors,
+                 int *  const ptab, 
+                 int ** const cursorP,
+                 int *  const maxCursor) {
+/*----------------------------------------------------------------------------
+   Interpret one line of XPM input.  The line is in 'line', and its
+   format is 'chars_per_pixel' characters per pixel.  'ptab' is the
+   color table that applies to the line, which table has 'ncolors'
+   colors.
+
+   Put the colormap indexes for the pixels represented in 'line' at
+   *cursorP, lined up in the order they are in 'line', and return
+   *cursorP positioned just after the last one.
+
+   If the line doesn't start with a quote (e.g. it is empty), we issue
+   a warning and just treat the line as one that describes no pixels.
+
+   Stop processing the line either at the end of the line or when
+   the output would go beyond maxCursor, whichever comes first.
+-----------------------------------------------------------------------------*/
+    char * lineCursor;
+
+    lineCursor = strchr(line, '"');  /* position to 1st quote in line */
+    if (lineCursor == NULL) {
+        /* We've seen a purported XPM that had a blank line in it.  Just
+           ignoring it was the right thing to do.  05.05.27.
+        */
+        pm_message("WARNING:  No opening quotation mark in XPM input "
+                   "line which is supposed to be a line of raster data: "
+                   "'%s'.  Ignoring this line.", line);
+    } else {
+        ++lineCursor; /* Skip to first character after quote */
+
+        /* Handle pixels until a close quote, eol, or we've returned all
+           the pixels Caller wants.
+        */
+        while (*lineCursor && *lineCursor != '"' && *cursorP <= maxCursor) {
+            int colorNumber;
+            int i;
+            colorNumber = 0;  /* initial value */
+            for (i = 0; i < chars_per_pixel; ++i)
+                colorNumber = (colorNumber << 8) + *(lineCursor++);
+            if (ptab == NULL)
+                /* colormap is indexed directly by XPM color number */
+                *(*cursorP)++ = colorNumber;
+            else {
+                /* colormap shadows ptab[].  Find this color # in ptab[] */
+                int i;
+                for (i = 0; i < ncolors && ptab[i] != colorNumber; ++i);
+                if (i < ncolors)
+                    *(*cursorP)++ = i;
+                else
+                    pm_error("Color number %d is in raster, but not in "
+                             "colormap.  Line it's in: '%s'",
+                             colorNumber, line);
+            }
+        }
+    }
+}
+
+
+
+static void
+ReadXPMFile(FILE * const stream, int * const widthP, int * const heightP, 
+            pixel ** const colorsP, int ** const dataP, 
+            int * const transparentP) {
+/*----------------------------------------------------------------------------
+   Read the XPM file from stream 'stream'.
+
+   Return the dimensions of the image as *widthP and *heightP.
+   Return the color map as *colorsP, which is an array of *ncolorsP
+   colors.
+
+   Return the raster in newly malloced storage, an array of *widthP by
+   *heightP integers, each of which is an index into the colormap
+   *colorsP (and therefore less than *ncolorsP).  Return the address
+   of the array as *dataP.
+
+   In the colormap, put black for the transparent color, if the XPM 
+   image contains one.
+-----------------------------------------------------------------------------*/
+    char line[MAX_LINE+1], str1[MAX_LINE+1];
+    int totalpixels;
+    int *cursor;  /* cursor into *dataP */
+    int *maxcursor;  /* value of above cursor for last pixel in image */
+    int *ptab;   /* colormap - malloc'ed */
+    int rc;
+    int ncolors;
+    int chars_per_pixel;
+
+    backup = FALSE;
+
+    /* Read the header line */
+    getline(line, sizeof(line), stream);
+    backup = TRUE;  /* back up so next read reads this line again */
+    
+    rc = sscanf(line, "/* %s */", str1);
+    if (rc == 1 && strncmp(str1, "XPM", 3) == 0) {
+        /* It's an XPM version 3 file */
+        readXpm3Header(stream, widthP, heightP, &chars_per_pixel,
+                       &ncolors, colorsP, &ptab, transparentP);
+    } else {				/* try as an XPM version 1 file */
+        /* Assume it's an XPM version 1 file */
+        readXpm1Header(stream, widthP, heightP, &chars_per_pixel, 
+                       &ncolors, colorsP, &ptab);
+        *transparentP = -1;  /* No transparency in version 1 */
+    }
+    totalpixels = *widthP * *heightP;
+    MALLOCARRAY(*dataP, totalpixels);
+    if (*dataP == NULL)
+        pm_error("Could not get %d bytes of memory for image", totalpixels);
+    cursor = *dataP;
+    maxcursor = *dataP + totalpixels - 1;
+	getline(line, sizeof(line), stream); 
+        /* read next line (first line may not always start with comment) */
+    while (cursor <= maxcursor) {
+        if (strncmp(line, "/*", 2) == 0) {
+            /* It's a comment.  Ignore it. */
+        } else {
+            interpretXpmLine(line, chars_per_pixel, 
+                             ncolors, ptab, &cursor, maxcursor);
+        }
+        if (cursor <= maxcursor)
+            getline(line, sizeof(line), stream);
+    }
+    if (ptab) free(ptab);
+}
+ 
+
+
+static void
+writeOutput(FILE * const imageout_file,
+            FILE * const alpha_file,
+            int const cols, int const rows, 
+            pixel * const colors, int * const data,
+            int transparent) {
+/*----------------------------------------------------------------------------
+   Write the image in 'data' to open PPM file stream 'imageout_file',
+   and the alpha mask for it to open PBM file stream 'alpha_file',
+   except if either is NULL, skip it.
+
+   'data' is an array of cols * rows integers, each one being an index
+   into the colormap 'colors'.
+
+   Where the index 'transparent' occurs in 'data', the pixel is supposed
+   to be transparent.  If 'transparent' < 0, no pixels are transparent.
+-----------------------------------------------------------------------------*/
+    int row;
+    pixel *pixrow;
+    bit * alpharow;
+
+    if (imageout_file)
+        ppm_writeppminit(imageout_file, cols, rows, PPM_MAXMAXVAL, 0);
+    if (alpha_file)
+        pbm_writepbminit(alpha_file, cols, rows, 0);
+
+    pixrow = ppm_allocrow(cols);
+    alpharow = pbm_allocrow(cols);
+
+    for (row = 0; row < rows; ++row ) {
+        int col;
+        int * const datarow = data+(row*cols);
+
+        for (col = 0; col < cols; ++col) {
+            pixrow[col] = colors[datarow[col]];
+            if (datarow[col] == transparent)
+                alpharow[col] = PBM_BLACK;
+            else
+                alpharow[col] = PBM_WHITE;
+        }
+        if (imageout_file)
+            ppm_writeppmrow(imageout_file, 
+                            pixrow, cols, (pixval) PPM_MAXMAXVAL, 0);
+        if (alpha_file)
+            pbm_writepbmrow(alpha_file, alpharow, cols, 0);
+    }
+    ppm_freerow(pixrow);
+    pbm_freerow(alpharow);
+
+    if (imageout_file)
+        pm_close(imageout_file);
+    if (alpha_file)
+        pm_close(alpha_file);
+}    
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    FILE *ifp;
+    FILE *alpha_file, *imageout_file;
+    pixel *colormap;
+    int cols, rows;
+    int transparent;  /* value of 'data' that means transparent */
+    int *data;
+        /* The image as an array of width * height integers, each one
+           being an index int colormap[].
+        */
+
+    struct cmdline_info cmdline;
+
+    ppm_init(&argc, argv);
+
+    parse_command_line(argc, argv, &cmdline);
+
+    verbose = cmdline.verbose;
+
+    if ( cmdline.input_filespec != NULL ) 
+        ifp = pm_openr( cmdline.input_filespec);
+    else
+        ifp = stdin;
+
+    if (cmdline.alpha_stdout)
+        alpha_file = stdout;
+    else if (cmdline.alpha_filename == NULL) 
+        alpha_file = NULL;
+    else {
+        alpha_file = pm_openw(cmdline.alpha_filename);
+    }
+
+    if (cmdline.alpha_stdout) 
+        imageout_file = NULL;
+    else
+        imageout_file = stdout;
+
+    ReadXPMFile(ifp, &cols, &rows, &colormap, &data, &transparent);
+    
+    pm_close(ifp);
+
+    writeOutput(imageout_file, alpha_file, cols, rows, colormap, data,
+                transparent);
+
+    free(colormap);
+    
+    return 0;
+}
diff --git a/converter/ppm/xvminitoppm.c b/converter/ppm/xvminitoppm.c
new file mode 100644
index 00000000..dfc76fcf
--- /dev/null
+++ b/converter/ppm/xvminitoppm.c
@@ -0,0 +1,207 @@
+/*=============================================================================
+                                    xvminitoppm
+===============================================================================
+   Convert XV mini thumbnail image to PPM.
+
+   This replaces the program of the same name by Ingo Wilken
+   (Ingo.Wilken@informatik.uni-oldenburg.de), 1993.
+
+   Written by Bryan Henderson in April 2006 and contributed to the public
+   domain.
+=============================================================================*/
+
+#include <assert.h>
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "nstring.h"
+#include "ppm.h"
+
+#define BUFSIZE 256
+
+
+typedef struct xvPalette {
+    unsigned int red[256];
+    unsigned int grn[256];
+    unsigned int blu[256];
+} xvPalette;
+
+
+struct cmdlineInfo {
+    const char * inputFileName;
+};
+
+
+
+static void
+parseCommandLine(int const argc,
+                 char *    argv[],
+                 struct cmdlineInfo * const cmdlineP) {
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else {
+        cmdlineP->inputFileName = argv[1];
+
+        if (argc-1 > 1)
+            pm_error("Too many arguments: %u.  Only argument is optional "
+                     "input file name.", argc-1);
+    }
+}
+
+
+
+static void
+getline(FILE * const ifP,
+        char * const buf,
+        size_t const size) {
+
+    char * rc;
+
+    rc = fgets(buf, size, ifP);
+    if (rc == NULL) {
+        if (ferror(ifP))
+            pm_perror("read error");
+        else
+            pm_error("unexpected EOF");
+    }
+}
+
+
+
+static void
+makeXvPalette(xvPalette * const xvPaletteP) {
+
+    unsigned int paletteIndex;
+    unsigned int r;
+
+    paletteIndex = 0;
+
+    for (r = 0; r < 8; ++r) {
+        unsigned int g;
+        for (g = 0; g < 8; ++g) {
+            unsigned int b;
+            for (b = 0; b < 4; ++b) {
+                xvPaletteP->red[paletteIndex] = (r*255)/7;
+                xvPaletteP->grn[paletteIndex] = (g*255)/7;
+                xvPaletteP->blu[paletteIndex] = (b*255)/3;
+                ++paletteIndex;
+            }
+        }
+    }
+
+}
+
+
+
+static void
+readXvHeader(FILE *         const ifP,
+             unsigned int * const colsP,
+             unsigned int * const rowsP,
+             unsigned int * const maxvalP) {
+           
+    char buf[256];
+    unsigned int cols, rows, maxval;
+    int rc;
+    bool endOfComments;
+    
+    getline(ifP, buf, sizeof(buf));
+
+    if (!STRNEQ(buf, "P7 332", 6))
+        pm_error("Input is not a XV thumbnail picture.  It does not "
+                 "begin with the characters 'P7 332'.");
+
+    endOfComments = FALSE;
+    while (!endOfComments) {
+        getline(ifP, buf, sizeof(buf));
+        if (STRNEQ(buf, "#END_OF_COMMENTS", 16))
+            endOfComments = TRUE;
+        else if (STRNEQ(buf, "#BUILTIN", 8))
+            pm_error("This program does not know how to "
+                     "convert builtin XV thumbnail pictures");
+    }
+    getline(ifP, buf, sizeof(buf));
+    rc = sscanf(buf, "%u %u %u", &cols, &rows, &maxval);
+    if (rc != 3)
+        pm_error("error parsing dimension info '%s'.  "
+                 "It does not consist of 3 decimal numbers.", buf);
+    if (maxval != 255)
+        pm_error("bogus XV thumbnail maxval %u.  Should be 255", maxval);
+}
+
+
+
+static void
+writePpm(FILE *             const ifP,
+         const xvPalette *  const xvPaletteP,
+         unsigned int       const cols,
+         unsigned int       const rows,
+         pixval             const maxval,
+         FILE *             const ofP) {
+/*----------------------------------------------------------------------------
+   Write out the PPM image, from the XV-mini input file ifP, which is
+   positioned to the raster.
+
+   The raster contains indices into the palette *xvPaletteP.
+-----------------------------------------------------------------------------*/
+    pixel * pixrow;
+    unsigned int row;
+
+    pixrow = ppm_allocrow(cols);
+
+    ppm_writeppminit(ofP, cols, rows, maxval, 0);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        for (col = 0; col < cols; ++col) {
+            int byte;
+            byte = fgetc(ifP);
+            if (byte == EOF)
+                pm_error("unexpected EOF");
+            else {
+                unsigned int const paletteIndex = byte;
+                assert(byte > 0);
+                
+                PPM_ASSIGN(pixrow[col],
+                           xvPaletteP->red[paletteIndex],
+                           xvPaletteP->grn[paletteIndex],
+                           xvPaletteP->blu[paletteIndex]);
+            }
+        }
+        ppm_writeppmrow(ofP, pixrow, cols, maxval, 0);
+    }
+
+    ppm_freerow(pixrow);
+}
+
+
+
+int 
+main(int    argc,
+     char * argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    unsigned int cols, rows;
+    pixval maxval;
+    xvPalette xvPalette;
+ 
+    ppm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    makeXvPalette(&xvPalette);
+
+    readXvHeader(ifP, &cols, &rows, &maxval);
+
+    writePpm(ifP, &xvPalette, cols, rows, maxval, stdout);
+
+    pm_close(ifP);
+
+    return 0;
+}
+
+
+
diff --git a/converter/ppm/yuvsplittoppm.c b/converter/ppm/yuvsplittoppm.c
new file mode 100644
index 00000000..0f5e19a3
--- /dev/null
+++ b/converter/ppm/yuvsplittoppm.c
@@ -0,0 +1,251 @@
+/* yuvsplittoppm.c - construct a portable pixmap from 3 raw files:
+** - basename.Y : The Luminance chunk at the size of the Image
+** - basename.U : The Chrominance chunk U at 1/4
+** - basename.V : The Chrominance chunk V at 1/4
+** The subsampled U and V values are made by arithmetic mean.
+**
+** If ccir601 is defined, the produced YUV triples have been scaled again
+** to fit into the smaller range of values for this standard.
+**
+** by Marcel Wijkstra <wijkstra@fwi.uva.nl>
+**
+** Based on ppmtoyuvsplit.c
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <string.h>
+#include "ppm.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+
+/* x must be signed for the following to work correctly */
+#define limit(x) (((x>0xffffff)?0xff0000:((x<=0xffff)?0:x&0xff0000))>>16)
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *filenameBase;
+    unsigned int width;
+    unsigned int height;
+    unsigned int ccir601;
+        /* Whether to create YUV in JFIF(JPEG) or CCIR.601(MPEG) scale */
+};
+
+
+static void
+parseCommandLine(int                 argc, 
+                 char **             argv,
+                 struct cmdlineInfo *cmdlineP ) {
+/*----------------------------------------------------------------------------
+   Parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def = malloc(100*sizeof(optEntry));
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "ccir601",     OPT_FLAG,   NULL,                  
+            &cmdlineP->ccir601,       0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+
+    if (argc-1 !=3)
+        pm_error("You must specify 3 arguments.  "
+                 "You specified %d", argc-1);
+    else {
+        int width, height;
+        cmdlineP->filenameBase = argv[1];
+        width = atoi(argv[2]);
+        if (width < 1)
+            pm_error("Width must be at least 1.  You specified %d", width);
+        height = atoi(argv[3]);
+        if (height < 1)
+            pm_error("Height must be at least 1.  You specified %d", height);
+        cmdlineP->width  = width;
+        cmdlineP->height = height;
+    }
+}
+
+
+
+static void
+computeTwoOutputRows(int             const cols,
+                     bool            const ccir601,
+                     unsigned char * const y1buf,
+                     unsigned char * const y2buf,
+                     unsigned char * const ubuf,
+                     unsigned char * const vbuf,
+                     pixel *         const pixelrow1,
+                     pixel *         const pixelrow2) {
+                     
+    int col;
+
+    for (col = 0; col < cols; col += 2) {
+        long int r0,g0,b0,r1,g1,b1,r2,g2,b2,r3,g3,b3;
+        long int  u,v,y0,y1,y2,y3,u0,u1,u2,u3,v0,v1,v2,v3;
+        
+        y0 = y1buf[col];
+        y1 = y1buf[col+1];
+        y2 = y2buf[col];
+        y3 = y2buf[col+1];
+
+        u = ubuf[col/2] - 128;
+        v = vbuf[col/2] - 128;
+        
+        if (ccir601) {
+            y0 = ((y0-16)*255)/219;
+            y1 = ((y1-16)*255)/219;
+            y2 = ((y2-16)*255)/219;
+            y3 = ((y3-16)*255)/219;
+
+            u  = (u*255)/224 ;
+            v  = (v*255)/224 ;
+        }
+        /* mean the chroma for subsampling */
+        
+        u0=u1=u2=u3=u;
+        v0=v1=v2=v3=v;
+
+
+        /* The inverse of the JFIF RGB to YUV Matrix for $00010000 = 1.0
+
+           [Y]   [65496        0   91880] [R]
+           [U] = [65533   -22580  -46799] [G]
+           [V]   [65537   116128      -8] [B]
+           
+        */
+
+        r0 = 65536 * y0               + 91880 * v0;
+        g0 = 65536 * y0 -  22580 * u0 - 46799 * v0;
+        b0 = 65536 * y0 + 116128 * u0             ;
+        
+        r1 = 65536 * y1               + 91880 * v1;
+        g1 = 65536 * y1 -  22580 * u1 - 46799 * v1;
+        b1 = 65536 * y1 + 116128 * u1             ;
+        
+        r2 = 65536 * y2               + 91880 * v2;
+        g2 = 65536 * y2 -  22580 * u2 - 46799 * v2;
+        b2 = 65536 * y2 + 116128 * u2             ;
+        
+        r3 = 65536 * y3               + 91880 * v3;
+        g3 = 65536 * y3 -  22580 * u3 - 46799 * v3;
+        b3 = 65536 * y3 + 116128 * u3             ;
+        
+        r0 = limit(r0);
+        r1 = limit(r1);
+        r2 = limit(r2);
+        r3 = limit(r3);
+        g0 = limit(g0);
+        g1 = limit(g1);
+        g2 = limit(g2);
+        g3 = limit(g3);
+        b0 = limit(b0);
+        b1 = limit(b1);
+        b2 = limit(b2);
+        b3 = limit(b3);
+
+        PPM_ASSIGN(pixelrow1[col],   (pixval)r0, (pixval)g0, (pixval)b0);
+        PPM_ASSIGN(pixelrow1[col+1], (pixval)r1, (pixval)g1, (pixval)b1);
+        PPM_ASSIGN(pixelrow2[col],   (pixval)r2, (pixval)g2, (pixval)b2);
+        PPM_ASSIGN(pixelrow2[col+1], (pixval)r3, (pixval)g3, (pixval)b3);
+    }
+}
+
+
+
+int
+main(int argc, char **argv) {
+
+    struct cmdlineInfo cmdline;
+    FILE *vf,*uf,*yf;
+    int cols, rows;
+    pixel *pixelrow1,*pixelrow2;
+    int row;
+    unsigned char  *y1buf,*y2buf,*ubuf,*vbuf;
+    const char * ufname;
+    const char * vfname;
+    const char * yfname;
+
+    ppm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+        
+    asprintfN(&ufname, "%s.U", cmdline.filenameBase);
+    asprintfN(&vfname, "%s.V", cmdline.filenameBase);
+    asprintfN(&yfname, "%s.Y", cmdline.filenameBase);
+
+    uf = pm_openr(ufname);
+    vf = pm_openr(vfname);
+    yf = pm_openr(yfname);
+
+    ppm_writeppminit(stdout, cmdline.width, cmdline.height, 255, 0);
+
+    if (cmdline.width % 2 != 0) {
+        pm_message("Warning: odd width; last column ignored");
+        cols = cmdline.width - 1;
+    } else
+        cols = cmdline.width;
+
+    if (cmdline.height % 2 != 0) {
+        pm_message("Warning: odd height; last row ignored");
+        rows = cmdline.height - 1;
+    } else 
+        rows = cmdline.height;
+
+    pixelrow1 = ppm_allocrow(cols);
+    pixelrow2 = ppm_allocrow(cols);
+
+    MALLOCARRAY_NOFAIL(y1buf, cmdline.width);
+    MALLOCARRAY_NOFAIL(y2buf, cmdline.width);
+    MALLOCARRAY_NOFAIL(ubuf,  cmdline.width/2);
+    MALLOCARRAY_NOFAIL(vbuf,  cmdline.width/2);
+
+    for (row = 0; row < rows; row += 2) {
+        fread(y1buf, cmdline.width,   1, yf);
+        fread(y2buf, cmdline.width,   1, yf);
+        fread(ubuf,  cmdline.width/2, 1, uf);
+        fread(vbuf,  cmdline.width/2, 1, vf);
+
+        computeTwoOutputRows(cols, cmdline.ccir601,
+                             y1buf, y2buf, ubuf, vbuf,
+                             pixelrow1, pixelrow2);
+
+        ppm_writeppmrow(stdout, pixelrow1, cols, (pixval) 255, 0);
+        ppm_writeppmrow(stdout, pixelrow2, cols, (pixval) 255, 0);
+    }
+    pm_close(stdout);
+
+    strfree(yfname);
+    strfree(vfname);
+    strfree(ufname);
+
+    pm_close(yf);
+    pm_close(uf);
+    pm_close(vf);
+
+    exit(0);
+}
diff --git a/converter/ppm/yuvtoppm.c b/converter/ppm/yuvtoppm.c
new file mode 100644
index 00000000..2b44c65f
--- /dev/null
+++ b/converter/ppm/yuvtoppm.c
@@ -0,0 +1,115 @@
+/* yuvtoppm.c - convert Abekas YUV bytes into a portable pixmap
+**
+** by Marc Boucher
+** Internet: marc@PostImage.COM
+** 
+** Based on Example Conversion Program, A60/A64 Digital Video Interface
+** Manual, page 69
+**
+** Uses integer arithmetic rather than floating point for better performance
+**
+** Copyright (C) 1991 by DHD PostImage Inc.
+** Copyright (C) 1987 by Abekas Video Systems Inc.
+** Copyright (C) 1991 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "ppm.h"
+#include "mallocvar.h"
+
+/* x must be signed for the following to work correctly */
+#define limit(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
+
+int
+main(argc, argv)
+	char          **argv;
+{
+	FILE           *ifp;
+	pixel          *pixrow;
+	int             argn, rows, cols, row;
+	const char     * const usage = "<width> <height> [yuvfile]";
+    struct yuv {
+        /* This is an element of a YUV file.  It describes two 
+           side-by-side pixels.
+        */
+        unsigned char u;
+        unsigned char y1;
+        unsigned char v;
+        unsigned char y2;
+    } *yuvbuf;
+
+	ppm_init(&argc, argv);
+
+	argn = 1;
+
+	if (argn + 2 > argc)
+		pm_usage(usage);
+
+	cols = atoi(argv[argn++]);
+	rows = atoi(argv[argn++]);
+	if (cols <= 0 || rows <= 0)
+		pm_usage(usage);
+
+	if (argn < argc) {
+		ifp = pm_openr(argv[argn]);
+		++argn;
+	} else
+		ifp = stdin;
+
+	if (argn != argc)
+		pm_usage(usage);
+
+    if (cols % 2 != 0) {
+        pm_error("Number of columns (%d) is odd.  A YUV image must have an "
+                 "even number of columns.", cols);
+    }
+
+	ppm_writeppminit(stdout, cols, rows, (pixval) 255, 0);
+	pixrow = ppm_allocrow(cols);
+    MALLOCARRAY(yuvbuf, (cols+1)/2);
+    if (yuvbuf == NULL)
+        pm_error("Unable to allocate YUV buffer for %d columns.", cols);
+
+	for (row = 0; row < rows; ++row) {
+		int    col;
+
+		fread(yuvbuf, cols * 2, 1, ifp);
+
+		for (col = 0; col < cols; col += 2) {
+            /* Produce two pixels in pixrow[] */
+            int y1, u, v, y2, r, g, b;
+
+			u = yuvbuf[col/2].u-128;
+
+            y1 = yuvbuf[col/2].y1 - 16;
+			if (y1 < 0) y1 = 0;
+
+            v = yuvbuf[col/2].v - 128;
+
+            y2 = yuvbuf[col/2].y2 - 16;
+			if (y2 < 0) y2 = 0;
+
+			r = 104635 * v;
+			g = -25690 * u + -53294 * v;
+			b = 132278 * u;
+
+			y1*=76310; y2*=76310;
+
+			PPM_ASSIGN(pixrow[col], limit(r+y1), limit(g+y1), limit(b+y1));
+			PPM_ASSIGN(pixrow[col+1], limit(r+y2), limit(g+y2), limit(b+y2));
+		}
+		ppm_writeppmrow(stdout, pixrow, cols, (pixval) 255, 0);
+	}
+    free(yuvbuf);
+    ppm_freerow(pixrow);
+	pm_close(ifp);
+	pm_close(stdout);
+
+	exit(0);
+}