about summary refs log tree commit diff
path: root/converter
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
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')
-rw-r--r--converter/Makefile17
-rw-r--r--converter/bmp.h256
-rw-r--r--converter/other/Makefile238
-rw-r--r--converter/other/README.JPEG397
-rwxr-xr-xconverter/other/anytopnm544
-rw-r--r--converter/other/bmepsoe.c539
-rw-r--r--converter/other/bmepsoe.h79
-rw-r--r--converter/other/bmptopnm.c1477
-rw-r--r--converter/other/cameratopam/COPYRIGHT35
-rw-r--r--converter/other/cameratopam/Makefile37
-rw-r--r--converter/other/cameratopam/bayer.h43
-rw-r--r--converter/other/cameratopam/camera.c1781
-rw-r--r--converter/other/cameratopam/camera.h131
-rw-r--r--converter/other/cameratopam/cameratopam.c906
-rw-r--r--converter/other/cameratopam/canon.c172
-rw-r--r--converter/other/cameratopam/canon.h13
-rw-r--r--converter/other/cameratopam/decode.c172
-rw-r--r--converter/other/cameratopam/decode.h22
-rw-r--r--converter/other/cameratopam/dng.c73
-rw-r--r--converter/other/cameratopam/dng.h2
-rw-r--r--converter/other/cameratopam/foveon.c790
-rw-r--r--converter/other/cameratopam/foveon.h14
-rw-r--r--converter/other/cameratopam/global_variables.h55
-rw-r--r--converter/other/cameratopam/identify.c1183
-rw-r--r--converter/other/cameratopam/identify.h11
-rw-r--r--converter/other/cameratopam/ljpeg.c141
-rw-r--r--converter/other/cameratopam/ljpeg.h17
-rw-r--r--converter/other/cameratopam/util.c83
-rw-r--r--converter/other/cameratopam/util.h16
-rw-r--r--converter/other/dithers.h91
-rw-r--r--converter/other/exif.c1030
-rw-r--r--converter/other/exif.h56
-rw-r--r--converter/other/fiasco/Makefile59
-rw-r--r--converter/other/fiasco/README.netpbm41
-rw-r--r--converter/other/fiasco/binerror.c143
-rw-r--r--converter/other/fiasco/binerror.h50
-rw-r--r--converter/other/fiasco/buttons.c510
-rw-r--r--converter/other/fiasco/buttons.h50
-rw-r--r--converter/other/fiasco/codec/Makefile27
-rw-r--r--converter/other/fiasco/codec/approx.c702
-rw-r--r--converter/other/fiasco/codec/approx.h30
-rw-r--r--converter/other/fiasco/codec/bintree.c94
-rw-r--r--converter/other/fiasco/codec/bintree.h43
-rw-r--r--converter/other/fiasco/codec/coder.c965
-rw-r--r--converter/other/fiasco/codec/coder.h24
-rw-r--r--converter/other/fiasco/codec/coeff.c369
-rw-r--r--converter/other/fiasco/codec/coeff.h61
-rw-r--r--converter/other/fiasco/codec/control.c276
-rw-r--r--converter/other/fiasco/codec/control.h33
-rw-r--r--converter/other/fiasco/codec/cwfa.h107
-rw-r--r--converter/other/fiasco/codec/decoder.c1532
-rw-r--r--converter/other/fiasco/codec/decoder.h70
-rw-r--r--converter/other/fiasco/codec/dfiasco.c398
-rw-r--r--converter/other/fiasco/codec/dfiasco.h38
-rw-r--r--converter/other/fiasco/codec/domain-pool.c986
-rw-r--r--converter/other/fiasco/codec/domain-pool.h75
-rw-r--r--converter/other/fiasco/codec/ip.c324
-rw-r--r--converter/other/fiasco/codec/ip.h37
-rw-r--r--converter/other/fiasco/codec/motion.c338
-rw-r--r--converter/other/fiasco/codec/motion.h35
-rw-r--r--converter/other/fiasco/codec/mwfa.c864
-rw-r--r--converter/other/fiasco/codec/mwfa.h44
-rw-r--r--converter/other/fiasco/codec/options.c894
-rw-r--r--converter/other/fiasco/codec/options.h80
-rw-r--r--converter/other/fiasco/codec/prediction.c629
-rw-r--r--converter/other/fiasco/codec/prediction.h36
-rw-r--r--converter/other/fiasco/codec/subdivide.c650
-rw-r--r--converter/other/fiasco/codec/subdivide.h33
-rw-r--r--converter/other/fiasco/codec/tiling.c239
-rw-r--r--converter/other/fiasco/codec/tiling.h40
-rw-r--r--converter/other/fiasco/codec/wfa.h141
-rw-r--r--converter/other/fiasco/codec/wfalib.c774
-rw-r--r--converter/other/fiasco/codec/wfalib.h66
-rw-r--r--converter/other/fiasco/config.h96
-rw-r--r--converter/other/fiasco/display.c422
-rw-r--r--converter/other/fiasco/display.h49
-rw-r--r--converter/other/fiasco/doc/README.LIB51
-rw-r--r--converter/other/fiasco/doc/fiasco_c_options.31
-rw-r--r--converter/other/fiasco/doc/fiasco_c_options_delete.31
-rw-r--r--converter/other/fiasco/doc/fiasco_c_options_new.3432
-rw-r--r--converter/other/fiasco/doc/fiasco_c_options_set_basisfile.31
-rw-r--r--converter/other/fiasco/doc/fiasco_c_options_set_chroma_quality.31
-rw-r--r--converter/other/fiasco/doc/fiasco_c_options_set_comment.31
-rw-r--r--converter/other/fiasco/doc/fiasco_c_options_set_frame_pattern.31
-rw-r--r--converter/other/fiasco/doc/fiasco_c_options_set_optimizations.31
-rw-r--r--converter/other/fiasco/doc/fiasco_c_options_set_prediction.31
-rw-r--r--converter/other/fiasco/doc/fiasco_c_options_set_progress_meter.31
-rw-r--r--converter/other/fiasco/doc/fiasco_c_options_set_quantization.31
-rw-r--r--converter/other/fiasco/doc/fiasco_c_options_set_smoothing.31
-rw-r--r--converter/other/fiasco/doc/fiasco_c_options_set_tiling.31
-rw-r--r--converter/other/fiasco/doc/fiasco_c_options_set_title.31
-rw-r--r--converter/other/fiasco/doc/fiasco_c_options_set_video_param.31
-rw-r--r--converter/other/fiasco/doc/fiasco_coder.3106
-rw-r--r--converter/other/fiasco/doc/fiasco_d_options.31
-rw-r--r--converter/other/fiasco/doc/fiasco_d_options_delete.31
-rw-r--r--converter/other/fiasco/doc/fiasco_d_options_new.3122
-rw-r--r--converter/other/fiasco/doc/fiasco_d_options_set_4_2_0_format.31
-rw-r--r--converter/other/fiasco/doc/fiasco_d_options_set_magnification.31
-rw-r--r--converter/other/fiasco/doc/fiasco_d_options_set_smoothing.31
-rw-r--r--converter/other/fiasco/doc/fiasco_decoder.31
-rw-r--r--converter/other/fiasco/doc/fiasco_decoder_delete.31
-rw-r--r--converter/other/fiasco/doc/fiasco_decoder_get_comment.31
-rw-r--r--converter/other/fiasco/doc/fiasco_decoder_get_frame.31
-rw-r--r--converter/other/fiasco/doc/fiasco_decoder_get_framerate.31
-rw-r--r--converter/other/fiasco/doc/fiasco_decoder_get_height.31
-rw-r--r--converter/other/fiasco/doc/fiasco_decoder_get_length.31
-rw-r--r--converter/other/fiasco/doc/fiasco_decoder_get_title.31
-rw-r--r--converter/other/fiasco/doc/fiasco_decoder_get_width.31
-rw-r--r--converter/other/fiasco/doc/fiasco_decoder_is_color.31
-rw-r--r--converter/other/fiasco/doc/fiasco_decoder_new.3194
-rw-r--r--converter/other/fiasco/doc/fiasco_decoder_write_frame.31
-rw-r--r--converter/other/fiasco/doc/fiasco_get_error_message.341
-rw-r--r--converter/other/fiasco/doc/fiasco_get_verbosity.31
-rw-r--r--converter/other/fiasco/doc/fiasco_image.31
-rw-r--r--converter/other/fiasco/doc/fiasco_image_delete.31
-rw-r--r--converter/other/fiasco/doc/fiasco_image_get_height.31
-rw-r--r--converter/other/fiasco/doc/fiasco_image_get_width.31
-rw-r--r--converter/other/fiasco/doc/fiasco_image_is_color.31
-rw-r--r--converter/other/fiasco/doc/fiasco_image_new.395
-rw-r--r--converter/other/fiasco/doc/fiasco_options.31
-rw-r--r--converter/other/fiasco/doc/fiasco_options_delete.31
-rw-r--r--converter/other/fiasco/doc/fiasco_options_new.3441
-rw-r--r--converter/other/fiasco/doc/fiasco_options_set_4_2_0_format.31
-rw-r--r--converter/other/fiasco/doc/fiasco_options_set_basisfile.31
-rw-r--r--converter/other/fiasco/doc/fiasco_options_set_chroma_quality.31
-rw-r--r--converter/other/fiasco/doc/fiasco_options_set_frame_pattern.31
-rw-r--r--converter/other/fiasco/doc/fiasco_options_set_magnification.31
-rw-r--r--converter/other/fiasco/doc/fiasco_options_set_optimizations.31
-rw-r--r--converter/other/fiasco/doc/fiasco_options_set_prediction.31
-rw-r--r--converter/other/fiasco/doc/fiasco_options_set_progress_meter.31
-rw-r--r--converter/other/fiasco/doc/fiasco_options_set_quantization.31
-rw-r--r--converter/other/fiasco/doc/fiasco_options_set_smoothing.31
-rw-r--r--converter/other/fiasco/doc/fiasco_options_set_tiling.31
-rw-r--r--converter/other/fiasco/doc/fiasco_options_set_video_param.31
-rw-r--r--converter/other/fiasco/doc/fiasco_renderer.31
-rw-r--r--converter/other/fiasco/doc/fiasco_renderer_delete.31
-rw-r--r--converter/other/fiasco/doc/fiasco_renderer_new.3125
-rw-r--r--converter/other/fiasco/doc/fiasco_renderer_render.31
-rw-r--r--converter/other/fiasco/doc/fiasco_set_verbosity.346
-rw-r--r--converter/other/fiasco/fiasco.h425
-rw-r--r--converter/other/fiasco/fiascotopnm.c477
-rw-r--r--converter/other/fiasco/getopt.c1002
-rw-r--r--converter/other/fiasco/getopt.h129
-rw-r--r--converter/other/fiasco/getopt1.c189
-rw-r--r--converter/other/fiasco/input/Makefile26
-rw-r--r--converter/other/fiasco/input/basis.c141
-rw-r--r--converter/other/fiasco/input/basis.h26
-rw-r--r--converter/other/fiasco/input/matrices.c644
-rw-r--r--converter/other/fiasco/input/matrices.h27
-rw-r--r--converter/other/fiasco/input/mc.c334
-rw-r--r--converter/other/fiasco/input/mc.h28
-rw-r--r--converter/other/fiasco/input/nd.c237
-rw-r--r--converter/other/fiasco/input/nd.h28
-rw-r--r--converter/other/fiasco/input/read.c499
-rw-r--r--converter/other/fiasco/input/read.h31
-rw-r--r--converter/other/fiasco/input/tree.c303
-rw-r--r--converter/other/fiasco/input/tree.h28
-rw-r--r--converter/other/fiasco/input/weights.c200
-rw-r--r--converter/other/fiasco/input/weights.h27
-rw-r--r--converter/other/fiasco/lib/Makefile33
-rw-r--r--converter/other/fiasco/lib/arith.c708
-rw-r--r--converter/other/fiasco/lib/arith.h122
-rw-r--r--converter/other/fiasco/lib/bit-io.c327
-rw-r--r--converter/other/fiasco/lib/bit-io.h58
-rw-r--r--converter/other/fiasco/lib/dither.c1892
-rw-r--r--converter/other/fiasco/lib/dither.h27
-rw-r--r--converter/other/fiasco/lib/error.c326
-rw-r--r--converter/other/fiasco/lib/error.h39
-rw-r--r--converter/other/fiasco/lib/image.c512
-rw-r--r--converter/other/fiasco/lib/image.h59
-rw-r--r--converter/other/fiasco/lib/list.c258
-rw-r--r--converter/other/fiasco/lib/list.h72
-rw-r--r--converter/other/fiasco/lib/macros.h70
-rw-r--r--converter/other/fiasco/lib/misc.c563
-rw-r--r--converter/other/fiasco/lib/misc.h98
-rw-r--r--converter/other/fiasco/lib/mvcode.c14
-rw-r--r--converter/other/fiasco/lib/mvcode.h6
-rw-r--r--converter/other/fiasco/lib/rpf.c223
-rw-r--r--converter/other/fiasco/lib/rpf.h47
-rw-r--r--converter/other/fiasco/lib/types.h38
-rw-r--r--converter/other/fiasco/output/Makefile26
-rw-r--r--converter/other/fiasco/output/matrices.c547
-rw-r--r--converter/other/fiasco/output/matrices.h28
-rw-r--r--converter/other/fiasco/output/mc.c250
-rw-r--r--converter/other/fiasco/output/mc.h28
-rw-r--r--converter/other/fiasco/output/nd.c244
-rw-r--r--converter/other/fiasco/output/nd.h27
-rw-r--r--converter/other/fiasco/output/tree.c176
-rw-r--r--converter/other/fiasco/output/tree.h27
-rw-r--r--converter/other/fiasco/output/weights.c200
-rw-r--r--converter/other/fiasco/output/weights.h27
-rw-r--r--converter/other/fiasco/output/write.c250
-rw-r--r--converter/other/fiasco/output/write.h28
-rw-r--r--converter/other/fiasco/params.c727
-rw-r--r--converter/other/fiasco/params.h61
-rw-r--r--converter/other/fiasco/pnmtofiasco.c411
-rw-r--r--converter/other/fiasco/system.fiascorc120
-rw-r--r--converter/other/fitstopnm.c494
-rw-r--r--converter/other/gemtopnm.c305
-rw-r--r--converter/other/giftopnm.c1460
-rw-r--r--converter/other/hdifftopam.c156
-rw-r--r--converter/other/infotopam.c543
-rw-r--r--converter/other/jbig/ANNOUNCE243
-rw-r--r--converter/other/jbig/Makefile47
-rw-r--r--converter/other/jbig/README.Netpbm12
-rw-r--r--converter/other/jbig/jbig.c2905
-rw-r--r--converter/other/jbig/jbig.doc721
-rw-r--r--converter/other/jbig/jbig.h267
-rw-r--r--converter/other/jbig/jbig_tab.c428
-rw-r--r--converter/other/jbig/jbigtopnm.c286
-rw-r--r--converter/other/jbig/pnmtojbig.c463
-rw-r--r--converter/other/jpeg2000/Makefile77
-rw-r--r--converter/other/jpeg2000/jpeg2ktopam.c498
-rw-r--r--converter/other/jpeg2000/libjasper/Makefile28
-rw-r--r--converter/other/jpeg2000/libjasper/Makefile.common41
-rw-r--r--converter/other/jpeg2000/libjasper/README17
-rw-r--r--converter/other/jpeg2000/libjasper/base/Makefile21
-rw-r--r--converter/other/jpeg2000/libjasper/base/jas_debug.c186
-rw-r--r--converter/other/jpeg2000/libjasper/base/jas_getopt.c217
-rw-r--r--converter/other/jpeg2000/libjasper/base/jas_image.c968
-rw-r--r--converter/other/jpeg2000/libjasper/base/jas_init.c200
-rw-r--r--converter/other/jpeg2000/libjasper/base/jas_malloc.c167
-rw-r--r--converter/other/jpeg2000/libjasper/base/jas_seq.c475
-rw-r--r--converter/other/jpeg2000/libjasper/base/jas_stream.c1204
-rw-r--r--converter/other/jpeg2000/libjasper/base/jas_string.c145
-rw-r--r--converter/other/jpeg2000/libjasper/base/jas_tvp.c286
-rw-r--r--converter/other/jpeg2000/libjasper/base/jas_version.c116
-rw-r--r--converter/other/jpeg2000/libjasper/base/partlist1
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_debug.h161
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_fix.h406
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_getopt.h178
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_image.h593
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_init.h128
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_malloc.h171
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_math.h164
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_seq.h348
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_stream.h498
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_string.h143
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_tvp.h198
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_types.h14
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig228
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_version.h167
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jasper.h137
-rw-r--r--converter/other/jpeg2000/libjasper/jp2/Makefile19
-rw-r--r--converter/other/jpeg2000/libjasper/jp2/jp2_cod.c928
-rw-r--r--converter/other/jpeg2000/libjasper/jp2/jp2_cod.h347
-rw-r--r--converter/other/jpeg2000/libjasper/jp2/jp2_dec.c649
-rw-r--r--converter/other/jpeg2000/libjasper/jp2/jp2_dec.h134
-rw-r--r--converter/other/jpeg2000/libjasper/jp2/jp2_enc.c414
-rw-r--r--converter/other/jpeg2000/libjasper/jp2/partlist1
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/Makefile22
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_bs.c478
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_bs.h280
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_cod.h127
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_cs.c1614
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_cs.h812
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_dec.c2334
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_dec.h745
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_enc.c2624
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_enc.h695
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_fix.h193
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_flt.h129
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_math.c170
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_math.h155
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_mct.c337
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_mct.h160
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.c228
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.h174
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_mqdec.c339
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_mqdec.h320
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_mqenc.c441
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_mqenc.h285
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c1125
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.h235
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_t1cod.c537
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_t1cod.h344
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_t1dec.c954
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_t1dec.h137
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c1008
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.h142
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_t2cod.c733
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_t2cod.h348
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_t2dec.c626
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_t2dec.h144
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_t2enc.c704
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_t2enc.h155
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_tagtree.c426
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_tagtree.h216
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_tsfb.c662
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_tsfb.h222
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_util.c243
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_util.h126
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/partlist1
-rw-r--r--converter/other/jpeg2000/libjasper/partlist4
-rw-r--r--converter/other/jpeg2000/libjasper_compat.h24
-rw-r--r--converter/other/jpeg2000/pamtojpeg2k.c522
-rw-r--r--converter/other/jpegdatasource.c183
-rw-r--r--converter/other/jpegdatasource.h18
-rw-r--r--converter/other/jpegtopnm.c970
-rw-r--r--converter/other/pamrgbatopng.c150
-rw-r--r--converter/other/pamtodjvurle.c293
-rw-r--r--converter/other/pamtofits.c304
-rw-r--r--converter/other/pamtohdiff.c142
-rw-r--r--converter/other/pamtohtmltbl.c284
-rw-r--r--converter/other/pamtopfm.c304
-rw-r--r--converter/other/pamtopnm.c155
-rw-r--r--converter/other/pamtosvg/Makefile55
-rw-r--r--converter/other/pamtosvg/README109
-rw-r--r--converter/other/pamtosvg/autotrace.c220
-rw-r--r--converter/other/pamtosvg/autotrace.h258
-rw-r--r--converter/other/pamtosvg/bitmap.c116
-rw-r--r--converter/other/pamtosvg/bitmap.h53
-rw-r--r--converter/other/pamtosvg/curve.c314
-rw-r--r--converter/other/pamtosvg/curve.h160
-rw-r--r--converter/other/pamtosvg/epsilon-equal.c19
-rw-r--r--converter/other/pamtosvg/epsilon-equal.h20
-rw-r--r--converter/other/pamtosvg/exception.c55
-rw-r--r--converter/other/pamtosvg/exception.h43
-rw-r--r--converter/other/pamtosvg/fit.c1923
-rw-r--r--converter/other/pamtosvg/fit.h34
-rw-r--r--converter/other/pamtosvg/image-header.h19
-rw-r--r--converter/other/pamtosvg/image-proc.c516
-rw-r--r--converter/other/pamtosvg/image-proc.h42
-rw-r--r--converter/other/pamtosvg/logreport.c17
-rw-r--r--converter/other/pamtosvg/logreport.h28
-rw-r--r--converter/other/pamtosvg/message.h47
-rw-r--r--converter/other/pamtosvg/output-svg.c130
-rw-r--r--converter/other/pamtosvg/output-svg.h37
-rw-r--r--converter/other/pamtosvg/pamtosvg.c395
-rw-r--r--converter/other/pamtosvg/pamtosvg.test6
-rw-r--r--converter/other/pamtosvg/point.h8
-rw-r--r--converter/other/pamtosvg/pxl-outline.c1370
-rw-r--r--converter/other/pamtosvg/pxl-outline.h79
-rw-r--r--converter/other/pamtosvg/spline.c193
-rw-r--r--converter/other/pamtosvg/spline.h90
-rw-r--r--converter/other/pamtosvg/testgrid.svg5
-rw-r--r--converter/other/pamtosvg/testline.svg5
-rw-r--r--converter/other/pamtosvg/thin-image.c373
-rw-r--r--converter/other/pamtosvg/thin-image.h37
-rw-r--r--converter/other/pamtosvg/vector.c254
-rw-r--r--converter/other/pamtosvg/vector.h71
-rw-r--r--converter/other/pamtotga.c572
-rw-r--r--converter/other/pamtotiff.c1110
-rw-r--r--converter/other/pamtouil.c438
-rw-r--r--converter/other/pamtoxvmini.c250
-rw-r--r--converter/other/pbmtopgm.c80
-rw-r--r--converter/other/pclxl.h390
-rw-r--r--converter/other/pfmtopam.c395
-rw-r--r--converter/other/pgmtopbm.c723
-rw-r--r--converter/other/pgmtoppm.c153
-rw-r--r--converter/other/pm_tiff.h41
-rw-r--r--converter/other/pngtopnm.c1096
-rw-r--r--converter/other/pngtxt.c315
-rw-r--r--converter/other/pngtxt.h13
-rw-r--r--converter/other/pnmtoddif.c611
-rw-r--r--converter/other/pnmtojpeg.c1098
-rw-r--r--converter/other/pnmtopalm/LICENSE16
-rw-r--r--converter/other/pnmtopalm/Makefile35
-rw-r--r--converter/other/pnmtopalm/README57
-rw-r--r--converter/other/pnmtopalm/gen_palm_colormap.c24
-rw-r--r--converter/other/pnmtopalm/palm.h66
-rw-r--r--converter/other/pnmtopalm/palmcolor8.map235
-rw-r--r--converter/other/pnmtopalm/palmcolormap.c277
-rw-r--r--converter/other/pnmtopalm/palmgray1.map4
-rw-r--r--converter/other/pnmtopalm/palmgray2.map6
-rw-r--r--converter/other/pnmtopalm/palmgray4.map8
-rw-r--r--converter/other/pnmtopalm/palmtopnm.c1131
-rw-r--r--converter/other/pnmtopalm/pnmtopalm.c1235
-rw-r--r--converter/other/pnmtopclxl.c1204
-rwxr-xr-xconverter/other/pnmtoplainpnm3
-rw-r--r--converter/other/pnmtopng.README101
-rw-r--r--converter/other/pnmtopng.c2745
-rw-r--r--converter/other/pnmtops.c1323
-rw-r--r--converter/other/pnmtorast.c310
-rw-r--r--converter/other/pnmtorle.c267
-rw-r--r--converter/other/pnmtosgi.c354
-rw-r--r--converter/other/pnmtosir.c146
-rw-r--r--converter/other/pnmtotiffcmyk.c1000
-rw-r--r--converter/other/pnmtoxwd.c505
-rw-r--r--converter/other/ppmtopgm.c95
-rw-r--r--converter/other/pstopnm.c899
-rwxr-xr-xconverter/other/pstopnm.csh301
-rw-r--r--converter/other/rast.c465
-rw-r--r--converter/other/rast.h111
-rw-r--r--converter/other/rasttopnm.c263
-rw-r--r--converter/other/rla.h45
-rw-r--r--converter/other/rlatopam.c433
-rw-r--r--converter/other/rletopnm.c497
-rw-r--r--converter/other/sgi.h33
-rw-r--r--converter/other/sgitopnm.c463
-rw-r--r--converter/other/sirtopnm.c95
-rw-r--r--converter/other/svgtopam.c940
-rw-r--r--converter/other/tiff.c468
-rw-r--r--converter/other/tifftopnm.c1062
-rw-r--r--converter/other/x10wd.h32
-rw-r--r--converter/other/x11wd.h82
-rw-r--r--converter/other/xwdtopnm.c1280
-rw-r--r--converter/other/zeisstopnm.c187
-rw-r--r--converter/pbm/Makefile78
-rw-r--r--converter/pbm/atktopbm.c362
-rw-r--r--converter/pbm/brushtopbm.c107
-rw-r--r--converter/pbm/cmuwm.h17
-rw-r--r--converter/pbm/cmuwmtopbm.c114
-rw-r--r--converter/pbm/ddbugtopbm.c173
-rw-r--r--converter/pbm/escp2topbm.c143
-rw-r--r--converter/pbm/g3.h146
-rw-r--r--converter/pbm/g3topbm.c739
-rw-r--r--converter/pbm/icontopbm.c159
-rw-r--r--converter/pbm/macp.h12
-rw-r--r--converter/pbm/macptopbm.c140
-rw-r--r--converter/pbm/mdaspec.txt239
-rw-r--r--converter/pbm/mdatopbm.c271
-rw-r--r--converter/pbm/mgr.h25
-rw-r--r--converter/pbm/mgrtopbm.c145
-rw-r--r--converter/pbm/mrftopbm.c210
-rw-r--r--converter/pbm/pbmto10x.c169
-rw-r--r--converter/pbm/pbmto4425.c179
-rw-r--r--converter/pbm/pbmtoascii.c165
-rw-r--r--converter/pbm/pbmtoatk.c188
-rw-r--r--converter/pbm/pbmtobbnbg.c152
-rw-r--r--converter/pbm/pbmtocmuwm.c117
-rw-r--r--converter/pbm/pbmtodjvurle.c140
-rw-r--r--converter/pbm/pbmtoepsi.c252
-rw-r--r--converter/pbm/pbmtoepson.c338
-rw-r--r--converter/pbm/pbmtoescp2.c186
-rw-r--r--converter/pbm/pbmtog3.c485
-rw-r--r--converter/pbm/pbmtog3.test23
-rw-r--r--converter/pbm/pbmtogem.c252
-rw-r--r--converter/pbm/pbmtogo.c309
-rw-r--r--converter/pbm/pbmtoibm23xx.c213
-rw-r--r--converter/pbm/pbmtoicon.c134
-rw-r--r--converter/pbm/pbmtolj.c564
-rw-r--r--converter/pbm/pbmtoln03.c260
-rw-r--r--converter/pbm/pbmtolps.c181
-rw-r--r--converter/pbm/pbmtomacp.c301
-rw-r--r--converter/pbm/pbmtomatrixorbital.c87
-rw-r--r--converter/pbm/pbmtomda.c201
-rw-r--r--converter/pbm/pbmtomgr.c120
-rw-r--r--converter/pbm/pbmtomrf.c338
-rw-r--r--converter/pbm/pbmtonokia.c225
-rw-r--r--converter/pbm/pbmtopi3.c123
-rw-r--r--converter/pbm/pbmtopk.c976
-rw-r--r--converter/pbm/pbmtoplot.c76
-rw-r--r--converter/pbm/pbmtoppa/CREDITS12
-rw-r--r--converter/pbm/pbmtoppa/INSTALL-MORE187
-rw-r--r--converter/pbm/pbmtoppa/LICENSE342
-rw-r--r--converter/pbm/pbmtoppa/Makefile25
-rw-r--r--converter/pbm/pbmtoppa/README.Netpbm30
-rw-r--r--converter/pbm/pbmtoppa/README.REDHAT29
-rw-r--r--converter/pbm/pbmtoppa/cutswath.c360
-rw-r--r--converter/pbm/pbmtoppa/cutswath.h4
-rw-r--r--converter/pbm/pbmtoppa/defaults.h65
-rw-r--r--converter/pbm/pbmtoppa/pbm.c135
-rw-r--r--converter/pbm/pbmtoppa/pbmtoppa.c449
-rw-r--r--converter/pbm/pbmtoppa/pbmtoppa.conf.hp100018
-rw-r--r--converter/pbm/pbmtoppa/pbmtoppa.conf.hp72018
-rw-r--r--converter/pbm/pbmtoppa/pbmtoppa.conf.hp82018
-rw-r--r--converter/pbm/pbmtoppa/ppa.c526
-rw-r--r--converter/pbm/pbmtoppa/ppa.h74
-rw-r--r--converter/pbm/pbmtoppa/ppapbm.h29
-rw-r--r--converter/pbm/pbmtopsg3.c374
-rw-r--r--converter/pbm/pbmtoptx.c101
-rw-r--r--converter/pbm/pbmtowbmp.c70
-rw-r--r--converter/pbm/pbmtox10bm.c120
-rw-r--r--converter/pbm/pbmtoxbm.c165
-rw-r--r--converter/pbm/pbmtoybm.c114
-rw-r--r--converter/pbm/pbmtozinc.c128
-rw-r--r--converter/pbm/pi3topbm.c112
-rw-r--r--converter/pbm/pktopbm.c442
-rw-r--r--converter/pbm/thinkjettopbm.c1792
-rw-r--r--converter/pbm/thinkjettopbm.l251
-rw-r--r--converter/pbm/wbmptopbm.c112
-rw-r--r--converter/pbm/xbmtopbm.c255
-rw-r--r--converter/pbm/ybmtopbm.c113
-rw-r--r--converter/pgm/Makefile23
-rw-r--r--converter/pgm/asciitopgm.c163
-rw-r--r--converter/pgm/bioradtopgm.c152
-rw-r--r--converter/pgm/fstopgm.c138
-rw-r--r--converter/pgm/hipstopgm.c181
-rw-r--r--converter/pgm/lispmtopgm.c172
-rw-r--r--converter/pgm/pgmtofs.c153
-rw-r--r--converter/pgm/pgmtolispm.c148
-rw-r--r--converter/pgm/pgmtopgm.c44
-rw-r--r--converter/pgm/psidtopgm.c128
-rw-r--r--converter/pgm/rawtopgm.c284
-rw-r--r--converter/pgm/sbigtopgm.c208
-rw-r--r--converter/pgm/spottopgm.c231
-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
-rw-r--r--converter/tga.h46
813 files changed, 200917 insertions, 0 deletions
diff --git a/converter/Makefile b/converter/Makefile
new file mode 100644
index 00000000..02cab3be
--- /dev/null
+++ b/converter/Makefile
@@ -0,0 +1,17 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+SUBDIRS = pbm pgm ppm other
+
+all: $(SUBDIRS:%=%/all)
+
+SCRIPTS = 
+BINARIES =
+
+include $(SRCDIR)/Makefile.common
diff --git a/converter/bmp.h b/converter/bmp.h
new file mode 100644
index 00000000..b22be82f
--- /dev/null
+++ b/converter/bmp.h
@@ -0,0 +1,256 @@
+/*\
+ * $Id: bmp.h,v 1.3 1992/11/24 19:39:56 dws Exp dws $
+ * 
+ * bmp.h - routines to calculate sizes of parts of BMP files
+ *
+ * Some fields in BMP files contain offsets to other parts
+ * of the file.  These routines allow us to calculate these
+ * offsets, so that we can read and write BMP files without
+ * the need to fseek().
+ * 
+ * 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.
+ * 
+ * $Log: bmp.h,v $
+ * Revision 1.3  1992/11/24  19:39:56  dws
+ * Added copyright.
+ *
+ * Revision 1.2  1992/11/17  02:13:37  dws
+ * Adjusted a string's name.
+ *
+ * Revision 1.1  1992/11/16  19:54:44  dws
+ * Initial revision
+ *
+\*/
+
+/* 
+   There is a specification of BMP (2003.07.24) at:
+
+     http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_4v1h.asp
+
+   There is a better written specification of the Windows BMP format
+   in (2000.06.08) <http://www.daubnet.com/formats/BMP.html>.
+   However, the "Windows BMP" format used in practice is much closer
+   to the Microsoft definition.  The Microsoft spec was not known for
+   Netpbm development until 2003.07.24.
+
+   The ColorsImportant field is defined in the daubnet spec as "Number of
+   important colors.  0 = all"  That is the entire definition.  The
+   spec also says the number of entries in the color map is a function
+   of the BitCount field alone.
+
+   But Marc Moorcroft says (2000.07.23) that he found two BMP files
+   some time ago that had a color map whose number of entries was not
+   as specified and was in fact the value of ColorsImportant.
+
+   And Bill Janssen actually produced some BMPs in January 2001 that
+   appear to have the size of the colormap determined by ColorsUsed.
+   They have 8 bits per pixel in the raster, but ColorsUsed is 4 and
+   there are in fact 4 entries in the color map.  He got these from
+   the Palm emulator for Windows, using the "Save Screen" menu 
+   option.
+
+   Bmptoppm had, for a few releases in 2000, code by Marc to use
+   ColorsImportant as the color map size unless it was zero, in which
+   case it determined color map size as specified.  The current
+   thinking is that there are probably more BMPs that need to be
+   interpreted per the spec than that need to be interpreted Marc's
+   way.  
+
+   But in light of Janssen's discovery, we have made the assumption
+   since February 2001 that when ColorsUsed is zero, the colormap size
+   is as specified, and when it is nonzero, the colormap size is given
+   by ColorsUsed.
+
+   But we were also assuming that if ColorsUsed is nonzero, the image
+   is colormapped.  We heard from "Ron & Bes Vantreese"
+   <eaglesun@aggienetwork.com> in February 2003 that his understanding
+   of the format was that ColorsUsed == 2**24 is appropriate for a
+   non-colormapped (24 bit) BMP, and images he created that way caused
+   trouble for Bmptopnm.  So since then, we look at ColorsUsed only if
+   we know because bits per pixel <= 8 that it is a colormapped image.
+
+*/
+
+#ifndef BMP_H_INCLUDED
+#define BMP_H_INCLUDED
+
+#include "pm.h"  /* For pm_error() */
+
+enum bmpClass {C_WIN=1, C_OS2=2};
+
+static char const er_internal[] = "%s: internal error!";
+
+/* Values of the "compression" field of the BMP info header */
+#define COMP_RGB       0
+#define COMP_RLE8      1
+#define COMP_RLE4      2
+#define COMP_BITFIELDS 3
+#define COMP_JPEG      4
+#define COMP_PNG       5
+
+static __inline__ unsigned int
+BMPlenfileheader(enum bmpClass const class) {
+
+    unsigned int retval;
+
+    switch (class) {
+    case C_WIN: retval = 14; break;
+    case C_OS2: retval = 14; break;
+    }
+    return retval;
+}
+
+
+
+static __inline__ unsigned int
+BMPleninfoheader(enum bmpClass const class) {
+
+    unsigned int retval;
+
+    switch (class) {
+    case C_WIN: retval = 40; break;
+    case C_OS2: retval = 12; break;
+    }
+    return retval;
+}
+
+
+
+static __inline__ unsigned int
+BMPlencolormap(enum bmpClass const class,
+               unsigned int  const bitcount, 
+               int           const cmapsize) {
+
+    unsigned int lenrgb;
+    unsigned int lencolormap;
+
+    if (bitcount < 1)
+        pm_error(er_internal, "BMPlencolormap");
+    else if (bitcount > 8) 
+        lencolormap = 0;
+    else {
+        switch (class) {
+        case C_WIN: lenrgb = 4; break;
+        case C_OS2: lenrgb = 3; break;
+        }
+
+        if (cmapsize < 0) 
+            lencolormap = (1 << bitcount) * lenrgb;
+        else 
+            lencolormap = cmapsize * lenrgb;
+    }
+    return lencolormap;
+}
+
+
+
+static __inline__ unsigned int
+BMPlenline(enum bmpClass const class,
+           unsigned int  const bitcount, 
+           unsigned int  const x) {
+/*----------------------------------------------------------------------------
+  length, in bytes, of a line of the image
+
+  Each row is padded on the right as needed to make it a multiple of 4
+  bytes int.  This appears to be true of both OS/2 and Windows BMP
+  files.
+-----------------------------------------------------------------------------*/
+    unsigned int bitsperline;
+    unsigned int retval;
+
+    bitsperline = x * bitcount;  /* tentative */
+
+    /*
+     * if bitsperline is not a multiple of 32, then round
+     * bitsperline up to the next multiple of 32.
+     */
+    if ((bitsperline % 32) != 0)
+        bitsperline += (32 - (bitsperline % 32));
+
+    if ((bitsperline % 32) != 0) {
+        pm_error(er_internal, "BMPlenline");
+        retval = 0;
+    } else 
+        /* number of bytes per line == bitsperline/8 */
+        retval = bitsperline >> 3;
+
+    return retval;
+}
+
+
+
+static __inline__ unsigned int
+BMPlenbits(enum bmpClass const class,
+           unsigned int  const bitcount, 
+           unsigned int  const x,
+           unsigned int  const y) {
+/*----------------------------------------------------------------------------
+  Return the number of bytes used to store the image bits
+  for an uncompressed image.
+-----------------------------------------------------------------------------*/
+    return y * BMPlenline(class, bitcount, x);
+}
+
+
+
+static __inline__ unsigned int
+BMPoffbits(enum bmpClass const class,
+           unsigned int  const bitcount, 
+           unsigned int  const cmapsize) {
+/*----------------------------------------------------------------------------
+  return the offset to the BMP image bits.
+-----------------------------------------------------------------------------*/
+    return BMPlenfileheader(class)
+        + BMPleninfoheader(class)
+        + BMPlencolormap(class, bitcount, cmapsize);
+}
+
+
+static __inline__ unsigned int
+BMPlenfileGen(enum bmpClass     const class,
+              unsigned int      const bitcount, 
+              int               const cmapsize,
+              unsigned int      const x,
+              unsigned int      const y,
+              unsigned int      const imageSize,
+              unsigned long int const compression) {
+/*----------------------------------------------------------------------------
+  Return the size of the BMP file in bytes.
+-----------------------------------------------------------------------------*/
+    unsigned int retval;
+
+    switch (compression) {
+    case COMP_RGB:
+    case COMP_BITFIELDS:
+        retval =
+            BMPoffbits(class, bitcount, cmapsize) +
+            BMPlenbits(class, bitcount, x, y);
+        break;
+    default:
+        retval = BMPoffbits(class, bitcount, cmapsize) + imageSize;
+    }
+    return retval;
+}
+
+
+
+static __inline__ unsigned int
+BMPlenfile(enum bmpClass const class,
+           unsigned int  const bitcount, 
+           int           const cmapsize,
+           unsigned int  const x,
+           unsigned int  const y) {
+/*----------------------------------------------------------------------------
+  return the size of the BMP file in bytes; no compression
+-----------------------------------------------------------------------------*/
+    return BMPlenfileGen(class, bitcount, cmapsize, x, y, 0, COMP_RGB);
+}
+
+#endif
diff --git a/converter/other/Makefile b/converter/other/Makefile
new file mode 100644
index 00000000..4585c45d
--- /dev/null
+++ b/converter/other/Makefile
@@ -0,0 +1,238 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/other
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+ifeq ($(shell xml2-config --version),)
+  XML2_LIBS=NONE
+  XML2_CFLAGS=NONE
+else
+  XML2_LIBS=$(shell xml2-config --libs)
+  XML2_CFLAGS=$(shell xml2-config --cflags)
+endif
+
+SUBDIRS = jbig pnmtopalm jpeg2000 cameratopam pamtosvg
+ifneq ($(BUILD_FIASCO), N)
+  SUBDIRS += fiasco
+endif
+
+INCLUDES = -I$(SRCDIR)/util 
+ifneq ($(TIFFLIB),NONE)
+  ifneq ($(TIFFHDR_DIR)x,x)
+    INCLUDES += -I$(TIFFHDR_DIR)
+  endif
+endif
+
+ifeq ($(shell libpng-config --version),)
+  ifneq ($(PNGLIB),NONE)
+    HAVE_PNGLIB = Y
+    ifneq ($(PNGHDR_DIR)x,x)
+      INCLUDES += -I$(PNGHDR_DIR)
+    endif
+    ifneq ($(ZHDR_DIR)x,x)
+      INCLUDES += -I$(ZHDR_DIR)
+    endif
+  endif
+else
+  HAVE_PNGLIB = Y
+  INCLUDES += $(shell libpng-config --cflags)
+endif
+
+ifneq ($(JPEGLIB),NONE)
+  ifneq ($(JPEGHDR_DIR)x,x)
+    INCLUDES += -I$(JPEGHDR_DIR)
+  endif
+endif
+ifneq ($(URTLIB),NONE)
+  ifneq ($(URTHDR_DIR)x,x)
+    INCLUDES += -I$(URTHDR_DIR)
+  endif
+endif
+ifneq ($(XML2_LIBS),NONE)
+  ifneq ($(XML2_CFLAGS),NONE)
+    INCLUDES += $(XML2_CFLAGS)
+  endif
+endif
+
+ifeq ($(TIFFLIB),NONE)
+  TIFF_PREREQ_MISSING = Y
+endif
+
+TIFFLIB_EXTRALIBS =
+ifeq ($(TIFFLIB_NEEDS_JPEG),Y)
+  ifeq ($(JPEGLIB),NONE)
+    TIFF_PREREQ_MISSING = Y
+  else
+    TIFFLIB_EXTRALIBS += $(JPEGLIB)
+  endif
+endif
+ifeq ($(TIFFLIB_NEEDS_Z),Y)
+  ifeq ($(ZLIB),NONE)
+    TIFF_PREREQ_MISSING = Y
+  else
+    TIFFLIB_EXTRALIBS += $(ZLIB)
+  endif
+endif
+
+PORTBINARIES =  bmptopnm fitstopnm \
+		gemtopnm giftopnm hdifftopam infotopam \
+		pamtodjvurle pamtofits \
+		pamtohdiff pamtohtmltbl pamtopfm pamtopnm pamtouil \
+		pamtoxvmini \
+		pbmtopgm pfmtopam \
+	        pgmtopbm pgmtoppm ppmtopgm pnmtoddif \
+		pnmtopclxl \
+		pnmtosgi pnmtosir pamtotga pnmtoxwd pstopnm \
+		rlatopam sgitopnm sirtopnm xwdtopnm zeisstopnm
+
+BINARIES = $(PORTBINARIES) pnmtorast rasttopnm
+
+ifeq ($(HAVE_PNGLIB),Y)
+  BINARIES += pnmtopng pngtopnm pamrgbatopng
+endif
+ifneq ($(JPEGLIB),NONE)
+  BINARIES += jpegtopnm pnmtojpeg
+endif
+ifneq ($(TIFF_PREREQ_MISSING),Y)
+  BINARIES += tifftopnm pamtotiff pnmtotiffcmyk
+endif
+ifneq ($(URTLIB),NONE)
+  BINARIES += rletopnm pnmtorle
+endif
+ifneq ($(ZLIB),NONE)
+  BINARIES += pnmtops
+endif
+
+ifneq ($(XML2_LIBS),NONE)
+  BINARIES += svgtopam
+endif 
+
+MERGEBINARIES = $(BINARIES)
+
+EXTRA_OBJECTS = exif.o rast.o pngtxt.o bmepsoe.o
+ifneq ($(JPEGLIB),NONE)
+  EXTRA_OBJECTS += jpegdatasource.o
+endif
+ifneq (($TIFF_PREREQ_MISSING),Y)
+  EXTRA_OBJECTS += tiff.o
+endif
+
+OBJECTS = $(BINARIES:%=%.o) $(EXTRA_OBJECTS)
+MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2) $(EXTRA_OBJECTS)
+
+
+SCRIPTS = anytopnm pnmtoplainpnm
+
+.PHONY: all
+all:	$(BINARIES) $(SUBDIRS:%=%/all)
+
+include $(SRCDIR)/Makefile.common
+
+ifeq ($(NEED_RUNTIME_PATH),Y)
+  LIBOPTR = -runtime
+else
+  LIBOPTR =
+endif
+
+LIBOPTS_TIFF = $(shell $(LIBOPT) $(NETPBMLIB) \
+  $(LIBOPTR) $(TIFFLIB) $(TIFFLIB_EXTRALIBS))
+
+tifftopnm pamtotiff pnmtotiffcmyk: %: %.o tiff.o $(NETPBMLIB) $(LIBOPT)
+	$(LD) $(LDFLAGS) -o $@ $@.o tiff.o \
+	  $(LIBOPTS_TIFF) $(MATHLIB) $(LDLIBS) $(RPATH) $(LADD)
+
+ifeq ($(shell libpng-config --version),)
+  PNGLIB_LIBOPTS = $(shell $(LIBOPT) $(LIBOPTR) $(PNGLIB) $(ZLIB))
+else
+  PNGLIB_LIBOPTS = $(shell libpng-config --ldflags)
+endif
+
+pngtopnm: %: %.o $(NETPBMLIB) $(LIBOPT)
+	$(LD) $(LDFLAGS) -o $@ $@.o \
+	  $(shell $(LIBOPT) $(NETPBMLIB)) \
+	  $(PNGLIB_LIBOPTS) $(MATHLIB) $(LDLIBS) $(RPATH) $(LADD)
+
+pnmtopng: %: %.o pngtxt.o $(NETPBMLIB) $(LIBOPT)
+	$(LD) $(LDFLAGS) -o $@ $@.o pngtxt.o \
+	  $(shell $(LIBOPT) $(NETPBMLIB)) \
+	  $(PNGLIB_LIBOPTS) $(MATHLIB) $(LDLIBS) $(RPATH) $(LADD)
+
+pamrgbatopng: %: %.o $(NETPBMLIB) $(LIBOPT)
+	$(LD) $(LDFLAGS) -o $@ $@.o \
+	  $(shell $(LIBOPT) $(NETPBMLIB)) $(PNGLIB_LIBOPTS) \
+	  $(MATHLIB) $(LDLIBS) $(RPATH) $(LADD)
+
+jpegtopnm: %: %.o jpegdatasource.o exif.o $(NETPBMLIB) $(LIBOPT)
+	$(LD) $(LDFLAGS) -o $@ $< jpegdatasource.o exif.o \
+	  $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR) $(JPEGLIB)) \
+	  $(MATHLIB) $(LDLIBS) $(RPATH) $(LADD) 
+
+pnmtojpeg: %: %.o $(NETPBMLIB) $(LIBOPT)
+	$(LD) $(LDFLAGS) -o $@ $@.o \
+	  $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR) $(JPEGLIB)) \
+	  $(MATHLIB) $(LDLIBS) $(RPATH) $(LADD)
+
+svgtopam: %: %.o $(NETPBMLIB) $(LIBOPT)
+	$(LD) $(LDFLAGS) -o $@ $@.o \
+	  $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR)) \
+	  $(XML2_LIBS) $(MATHLIB) $(LDLIBS) $(RPATH) $(LADD)
+
+# If URTLIB is BUNDLED_URTLIB, then we're responsible for building it, which
+# means it needs to be a dependency:
+ifeq ($(URTLIB), $(BUNDLED_URTLIB))
+  URTLIBDEP = $(URTLIB)
+endif
+
+rletopnm pnmtorle: %: %.o $(NETPBMLIB) $(URTLIBDEP) $(LIBOPT)
+	$(LD) $(LDFLAGS) -o $@ $@.o \
+	  $(shell $(LIBOPT) $(URTLIB) $(NETPBMLIB)) \
+	  $(MATHLIB) $(LDLIBS) $(RPATH) $(LADD)
+
+pnmtops: %: %.o bmepsoe.o $(NETPBMLIB) $(LIBOPT)
+	$(LD) $(LDFLAGS) -o $@ $@.o bmepsoe.o \
+	  $(shell $(LIBOPT) $(NETPBMLIB) $(ZLIB)) \
+	  $(MATHLIB) $(LDLIBS) $(RPATH) $(LADD)
+
+pnmtorast rasttopnm: %: %.o rast.o $(NETPBMLIB) $(LIBOPT)
+	$(LD) $(LDFLAGS) -o $@ $@.o rast.o \
+	  $(shell $(LIBOPT) $(NETPBMLIB)) \
+	  $(MATHLIB) $(LDLIBS) $(RPATH) $(LADD)
+
+bmptopnm.o bmptopnm.o2: bmp.h
+
+pamtotga.o pamtotga.o2: tga.h
+
+install.bin: install.bin.local
+.PHONY: install.bin.local
+install.bin.local: $(PKGDIR)/bin
+# Remember that $(SYMLINK) might just be a copy command.
+# backward compatibility: program used to be named pnmnoraw
+	cd $(PKGDIR)/bin ; \
+	$(SYMLINK) pnmtoplainpnm$(EXE) pnmnoraw
+# backward compatibility: program used to be gemtopbm
+	cd $(PKGDIR)/bin ; \
+	$(SYMLINK) gemtopnm$(EXE) gemtopbm
+# In October 2001, pnmtojpeg replaced ppmtojpeg
+	cd $(PKGDIR)/bin ; \
+	$(SYMLINK) pnmtojpeg$(EXE) ppmtojpeg
+# In March 2002, bmptopnm replaced bmptoppm
+	cd $(PKGDIR)/bin ; \
+	$(SYMLINK) bmptopnm$(EXE) bmptoppm
+# In May 2002, pamtouil replaced ppmtouil
+	cd $(PKGDIR)/bin ; \
+	$(SYMLINK) pamtouil$(EXE) ppmtouil
+# In March 2005, we realized that pamtopnm obviates pnmtopnm
+	cd $(PKGDIR)/bin ; \
+	$(SYMLINK) pamtopnm$(EXE) pnmtopnm
+# In October 2005, pamtofits replaced pnmtofits
+	cd $(PKGDIR)/bin ; \
+	$(SYMLINK) pamtofits$(EXE) pnmtofits
+ifneq ($(TIFF_PREREQ_MISSING),Y)
+# In October 2005, pamtotiff replaced pnmtotiff
+	cd $(PKGDIR)/bin ; \
+	$(SYMLINK) pamtotiff$(EXE) pnmtotiff
+endif
\ No newline at end of file
diff --git a/converter/other/README.JPEG b/converter/other/README.JPEG
new file mode 100644
index 00000000..5d2d941c
--- /dev/null
+++ b/converter/other/README.JPEG
@@ -0,0 +1,397 @@
+THIS IS THE README FILE FROM THE JPEG LIBRARY, 2000.03.06.  IT IS INCLUDED
+VERBATIM, INCLUDING ITS COPYRIGHT AND LICENSE NOTICES, IN THE NETPBM 
+PACKAGE AS ONE OF THE TERMS UNDER WHICH THE INDEPENDENT JPEG GROUP GRANTED
+PERMISSION TO BRYAN HENDERSON TODAY TO DISTRIBUTE THE PPMTOJPEG AND 
+JPEGTOPPM COMPONENTS OF NETPBM, WHICH ARE DERIVED FROM THE CJPEG AND DJPEG
+PROGRAMS IN THE JPEG LIBRARY PACKAGE.
+
+AS PROVIDED BY THE LICENSE GIVEN TO BRYAN, BRYAN PROPAGATES THE SAME
+LICENSE TO YOU, WITH RESPECT TO PPMTOJPEG AND JPEGTOPPM.
+
+
+
+The Independent JPEG Group's JPEG software
+==========================================
+
+README for release 6b of 27-Mar-1998
+====================================
+
+This distribution contains the sixth public release of the Independent JPEG
+Group's free JPEG software.  You are welcome to redistribute this software and
+to use it for any purpose, subject to the conditions under LEGAL ISSUES, below.
+
+Serious users of this software (particularly those incorporating it into
+larger programs) should contact IJG at jpeg-info@uunet.uu.net to be added to
+our electronic mailing list.  Mailing list members are notified of updates
+and have a chance to participate in technical discussions, etc.
+
+This software is the work of Tom Lane, Philip Gladstone, Jim Boucher,
+Lee Crocker, Julian Minguillon, Luis Ortiz, George Phillips, Davide Rossi,
+Guido Vollbeding, Ge' Weijers, and other members of the Independent JPEG
+Group.
+
+IJG is not affiliated with the official ISO JPEG standards committee.
+
+
+DOCUMENTATION ROADMAP
+=====================
+
+This file contains the following sections:
+
+OVERVIEW            General description of JPEG and the IJG software.
+LEGAL ISSUES        Copyright, lack of warranty, terms of distribution.
+REFERENCES          Where to learn more about JPEG.
+ARCHIVE LOCATIONS   Where to find newer versions of this software.
+RELATED SOFTWARE    Other stuff you should get.
+FILE FORMAT WARS    Software *not* to get.
+TO DO               Plans for future IJG releases.
+
+Other documentation files in the distribution are:
+
+User documentation:
+  install.doc       How to configure and install the IJG software.
+  usage.doc         Usage instructions for cjpeg, djpeg, jpegtran,
+                    rdjpgcom, and wrjpgcom.
+  *.1               Unix-style man pages for programs (same info as usage.doc).
+  wizard.doc        Advanced usage instructions for JPEG wizards only.
+  change.log        Version-to-version change highlights.
+Programmer and internal documentation:
+  libjpeg.doc       How to use the JPEG library in your own programs.
+  example.c         Sample code for calling the JPEG library.
+  structure.doc     Overview of the JPEG library's internal structure.
+  filelist.doc      Road map of IJG files.
+  coderules.doc     Coding style rules --- please read if you contribute code.
+
+Please read at least the files install.doc and usage.doc.  Useful information
+can also be found in the JPEG FAQ (Frequently Asked Questions) article.  See
+ARCHIVE LOCATIONS below to find out where to obtain the FAQ article.
+
+If you want to understand how the JPEG code works, we suggest reading one or
+more of the REFERENCES, then looking at the documentation files (in roughly
+the order listed) before diving into the code.
+
+
+OVERVIEW
+========
+
+This package contains C software to implement JPEG image compression and
+decompression.  JPEG (pronounced "jay-peg") is a standardized compression
+method for full-color and gray-scale images.  JPEG is intended for compressing
+"real-world" scenes; line drawings, cartoons and other non-realistic images
+are not its strong suit.  JPEG is lossy, meaning that the output image is not
+exactly identical to the input image.  Hence you must not use JPEG if you
+have to have identical output bits.  However, on typical photographic images,
+very good compression levels can be obtained with no visible change, and
+remarkably high compression levels are possible if you can tolerate a
+low-quality image.  For more details, see the references, or just experiment
+with various compression settings.
+
+This software implements JPEG baseline, extended-sequential, and progressive
+compression processes.  Provision is made for supporting all variants of these
+processes, although some uncommon parameter settings aren't implemented yet.
+For legal reasons, we are not distributing code for the arithmetic-coding
+variants of JPEG; see LEGAL ISSUES.  We have made no provision for supporting
+the hierarchical or lossless processes defined in the standard.
+
+We provide a set of library routines for reading and writing JPEG image files,
+plus two sample applications "cjpeg" and "djpeg", which use the library to
+perform conversion between JPEG and some other popular image file formats.
+The library is intended to be reused in other applications.
+
+In order to support file conversion and viewing software, we have included
+considerable functionality beyond the bare JPEG coding/decoding capability;
+for example, the color quantization modules are not strictly part of JPEG
+decoding, but they are essential for output to colormapped file formats or
+colormapped displays.  These extra functions can be compiled out of the
+library if not required for a particular application.  We have also included
+"jpegtran", a utility for lossless transcoding between different JPEG
+processes, and "rdjpgcom" and "wrjpgcom", two simple applications for
+inserting and extracting textual comments in JFIF files.
+
+The emphasis in designing this software has been on achieving portability and
+flexibility, while also making it fast enough to be useful.  In particular,
+the software is not intended to be read as a tutorial on JPEG.  (See the
+REFERENCES section for introductory material.)  Rather, it is intended to
+be reliable, portable, industrial-strength code.  We do not claim to have
+achieved that goal in every aspect of the software, but we strive for it.
+
+We welcome the use of this software as a component of commercial products.
+No royalty is required, but we do ask for an acknowledgement in product
+documentation, as described under LEGAL ISSUES.
+
+
+LEGAL ISSUES
+============
+
+In plain English:
+
+1. We don't promise that this software works.  (But if you find any bugs,
+   please let us know!)
+2. You can use this software for whatever you want.  You don't have to pay us.
+3. You may not pretend that you wrote this software.  If you use it in a
+   program, you must acknowledge somewhere in your documentation that
+   you've used the IJG code.
+
+In legalese:
+
+The authors make NO WARRANTY or representation, either express or implied,
+with respect to this software, its quality, accuracy, merchantability, or
+fitness for a particular purpose.  This software is provided "AS IS", and you,
+its user, assume the entire risk as to its quality and accuracy.
+
+This software is copyright (C) 1991-1998, Thomas G. Lane.
+All Rights Reserved except as specified below.
+
+Permission is hereby granted to use, copy, modify, and distribute this
+software (or portions thereof) for any purpose, without fee, subject to these
+conditions:
+(1) If any part of the source code for this software is distributed, then this
+README file must be included, with this copyright and no-warranty notice
+unaltered; and any additions, deletions, or changes to the original files
+must be clearly indicated in accompanying documentation.
+(2) If only executable code is distributed, then the accompanying
+documentation must state that "this software is based in part on the work of
+the Independent JPEG Group".
+(3) Permission for use of this software is granted only if the user accepts
+full responsibility for any undesirable consequences; the authors accept
+NO LIABILITY for damages of any kind.
+
+These conditions apply to any software derived from or based on the IJG code,
+not just to the unmodified library.  If you use our work, you ought to
+acknowledge us.
+
+Permission is NOT granted for the use of any IJG author's name or company name
+in advertising or publicity relating to this software or products derived from
+it.  This software may be referred to only as "the Independent JPEG Group's
+software".
+
+We specifically permit and encourage the use of this software as the basis of
+commercial products, provided that all warranty or liability claims are
+assumed by the product vendor.
+
+
+ansi2knr.c is included in this distribution by permission of L. Peter Deutsch,
+sole proprietor of its copyright holder, Aladdin Enterprises of Menlo Park, CA.
+ansi2knr.c is NOT covered by the above copyright and conditions, but instead
+by the usual distribution terms of the Free Software Foundation; principally,
+that you must include source code if you redistribute it.  (See the file
+ansi2knr.c for full details.)  However, since ansi2knr.c is not needed as part
+of any program generated from the IJG code, this does not limit you more than
+the foregoing paragraphs do.
+
+The Unix configuration script "configure" was produced with GNU Autoconf.
+It is copyright by the Free Software Foundation but is freely distributable.
+The same holds for its supporting scripts (config.guess, config.sub,
+ltconfig, ltmain.sh).  Another support script, install-sh, is copyright
+by M.I.T. but is also freely distributable.
+
+It appears that the arithmetic coding option of the JPEG spec is covered by
+patents owned by IBM, AT&T, and Mitsubishi.  Hence arithmetic coding cannot
+legally be used without obtaining one or more licenses.  For this reason,
+support for arithmetic coding has been removed from the free JPEG software.
+(Since arithmetic coding provides only a marginal gain over the unpatented
+Huffman mode, it is unlikely that very many implementations will support it.)
+So far as we are aware, there are no patent restrictions on the remaining
+code.
+
+The IJG distribution formerly included code to read and write GIF files.
+To avoid entanglement with the Unisys LZW patent, GIF reading support has
+been removed altogether, and the GIF writer has been simplified to produce
+"uncompressed GIFs".  This technique does not use the LZW algorithm; the
+resulting GIF files are larger than usual, but are readable by all standard
+GIF decoders.
+
+We are required to state that
+    "The Graphics Interchange Format(c) is the Copyright property of
+    CompuServe Incorporated.  GIF(sm) is a Service Mark property of
+    CompuServe Incorporated."
+
+
+REFERENCES
+==========
+
+We highly recommend reading one or more of these references before trying to
+understand the innards of the JPEG software.
+
+The best short technical introduction to the JPEG compression algorithm is
+	Wallace, Gregory K.  "The JPEG Still Picture Compression Standard",
+	Communications of the ACM, April 1991 (vol. 34 no. 4), pp. 30-44.
+(Adjacent articles in that issue discuss MPEG motion picture compression,
+applications of JPEG, and related topics.)  If you don't have the CACM issue
+handy, a PostScript file containing a revised version of Wallace's article is
+available at ftp://ftp.uu.net/graphics/jpeg/wallace.ps.gz.  The file (actually
+a preprint for an article that appeared in IEEE Trans. Consumer Electronics)
+omits the sample images that appeared in CACM, but it includes corrections
+and some added material.  Note: the Wallace article is copyright ACM and IEEE,
+and it may not be used for commercial purposes.
+
+A somewhat less technical, more leisurely introduction to JPEG can be found in
+"The Data Compression Book" by Mark Nelson and Jean-loup Gailly, published by
+M&T Books (New York), 2nd ed. 1996, ISBN 1-55851-434-1.  This book provides
+good explanations and example C code for a multitude of compression methods
+including JPEG.  It is an excellent source if you are comfortable reading C
+code but don't know much about data compression in general.  The book's JPEG
+sample code is far from industrial-strength, but when you are ready to look
+at a full implementation, you've got one here...
+
+The best full description of JPEG is the textbook "JPEG Still Image Data
+Compression Standard" by William B. Pennebaker and Joan L. Mitchell, published
+by Van Nostrand Reinhold, 1993, ISBN 0-442-01272-1.  Price US$59.95, 638 pp.
+The book includes the complete text of the ISO JPEG standards (DIS 10918-1
+and draft DIS 10918-2).  This is by far the most complete exposition of JPEG
+in existence, and we highly recommend it.
+
+The JPEG standard itself is not available electronically; you must order a
+paper copy through ISO or ITU.  (Unless you feel a need to own a certified
+official copy, we recommend buying the Pennebaker and Mitchell book instead;
+it's much cheaper and includes a great deal of useful explanatory material.)
+In the USA, copies of the standard may be ordered from ANSI Sales at (212)
+642-4900, or from Global Engineering Documents at (800) 854-7179.  (ANSI
+doesn't take credit card orders, but Global does.)  It's not cheap: as of
+1992, ANSI was charging $95 for Part 1 and $47 for Part 2, plus 7%
+shipping/handling.  The standard is divided into two parts, Part 1 being the
+actual specification, while Part 2 covers compliance testing methods.  Part 1
+is titled "Digital Compression and Coding of Continuous-tone Still Images,
+Part 1: Requirements and guidelines" and has document numbers ISO/IEC IS
+10918-1, ITU-T T.81.  Part 2 is titled "Digital Compression and Coding of
+Continuous-tone Still Images, Part 2: Compliance testing" and has document
+numbers ISO/IEC IS 10918-2, ITU-T T.83.
+
+Some extensions to the original JPEG standard are defined in JPEG Part 3,
+a newer ISO standard numbered ISO/IEC IS 10918-3 and ITU-T T.84.  IJG
+currently does not support any Part 3 extensions.
+
+The JPEG standard does not specify all details of an interchangeable file
+format.  For the omitted details we follow the "JFIF" conventions, revision
+1.02.  A copy of the JFIF spec is available from:
+	Literature Department
+	C-Cube Microsystems, Inc.
+	1778 McCarthy Blvd.
+	Milpitas, CA 95035
+	phone (408) 944-6300,  fax (408) 944-6314
+A PostScript version of this document is available by FTP at
+ftp://ftp.uu.net/graphics/jpeg/jfif.ps.gz.  There is also a plain text
+version at ftp://ftp.uu.net/graphics/jpeg/jfif.txt.gz, but it is missing
+the figures.
+
+The TIFF 6.0 file format specification can be obtained by FTP from
+ftp://ftp.sgi.com/graphics/tiff/TIFF6.ps.gz.  The JPEG incorporation scheme
+found in the TIFF 6.0 spec of 3-June-92 has a number of serious problems.
+IJG does not recommend use of the TIFF 6.0 design (TIFF Compression tag 6).
+Instead, we recommend the JPEG design proposed by TIFF Technical Note #2
+(Compression tag 7).  Copies of this Note can be obtained from ftp.sgi.com or
+from ftp://ftp.uu.net/graphics/jpeg/.  It is expected that the next revision
+of the TIFF spec will replace the 6.0 JPEG design with the Note's design.
+Although IJG's own code does not support TIFF/JPEG, the free libtiff library
+uses our library to implement TIFF/JPEG per the Note.  libtiff is available
+from ftp://ftp.sgi.com/graphics/tiff/.
+
+
+ARCHIVE LOCATIONS
+=================
+
+The "official" archive site for this software is ftp.uu.net (Internet
+address 192.48.96.9).  The most recent released version can always be found
+there in directory graphics/jpeg.  This particular version will be archived
+as ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz.  If you don't have
+direct Internet access, UUNET's archives are also available via UUCP; contact
+help@uunet.uu.net for information on retrieving files that way.
+
+Numerous Internet sites maintain copies of the UUNET files.  However, only
+ftp.uu.net is guaranteed to have the latest official version.
+
+You can also obtain this software in DOS-compatible "zip" archive format from
+the SimTel archives (ftp://ftp.simtel.net/pub/simtelnet/msdos/graphics/), or
+on CompuServe in the Graphics Support forum (GO CIS:GRAPHSUP), library 12
+"JPEG Tools".  Again, these versions may sometimes lag behind the ftp.uu.net
+release.
+
+The JPEG FAQ (Frequently Asked Questions) article is a useful source of
+general information about JPEG.  It is updated constantly and therefore is
+not included in this distribution.  The FAQ is posted every two weeks to
+Usenet newsgroups comp.graphics.misc, news.answers, and other groups.
+It is available on the World Wide Web at http://www.faqs.org/faqs/jpeg-faq/
+and other news.answers archive sites, including the official news.answers
+archive at rtfm.mit.edu: ftp://rtfm.mit.edu/pub/usenet/news.answers/jpeg-faq/.
+If you don't have Web or FTP access, send e-mail to mail-server@rtfm.mit.edu
+with body
+	send usenet/news.answers/jpeg-faq/part1
+	send usenet/news.answers/jpeg-faq/part2
+
+
+RELATED SOFTWARE
+================
+
+Numerous viewing and image manipulation programs now support JPEG.  (Quite a
+few of them use this library to do so.)  The JPEG FAQ described above lists
+some of the more popular free and shareware viewers, and tells where to
+obtain them on Internet.
+
+If you are on a Unix machine, we highly recommend Jef Poskanzer's free
+PBMPLUS software, which provides many useful operations on PPM-format image
+files.  In particular, it can convert PPM images to and from a wide range of
+other formats, thus making cjpeg/djpeg considerably more useful.  The latest
+version is distributed by the NetPBM group, and is available from numerous
+sites, notably ftp://wuarchive.wustl.edu/graphics/graphics/packages/NetPBM/.
+Unfortunately PBMPLUS/NETPBM is not nearly as portable as the IJG software is;
+you are likely to have difficulty making it work on any non-Unix machine.
+
+A different free JPEG implementation, written by the PVRG group at Stanford,
+is available from ftp://havefun.stanford.edu/pub/jpeg/.  This program
+is designed for research and experimentation rather than production use;
+it is slower, harder to use, and less portable than the IJG code, but it
+is easier to read and modify.  Also, the PVRG code supports lossless JPEG,
+which we do not.  (On the other hand, it doesn't do progressive JPEG.)
+
+
+FILE FORMAT WARS
+================
+
+Some JPEG programs produce files that are not compatible with our library.
+The root of the problem is that the ISO JPEG committee failed to specify a
+concrete file format.  Some vendors "filled in the blanks" on their own,
+creating proprietary formats that no one else could read.  (For example, none
+of the early commercial JPEG implementations for the Macintosh were able to
+exchange compressed files.)
+
+The file format we have adopted is called JFIF (see REFERENCES).  This format
+has been agreed to by a number of major commercial JPEG vendors, and it has
+become the de facto standard.  JFIF is a minimal or "low end" representation.
+We recommend the use of TIFF/JPEG (TIFF revision 6.0 as modified by TIFF
+Technical Note #2) for "high end" applications that need to record a lot of
+additional data about an image.  TIFF/JPEG is fairly new and not yet widely
+supported, unfortunately.
+
+The upcoming JPEG Part 3 standard defines a file format called SPIFF.
+SPIFF is interoperable with JFIF, in the sense that most JFIF decoders should
+be able to read the most common variant of SPIFF.  SPIFF has some technical
+advantages over JFIF, but its major claim to fame is simply that it is an
+official standard rather than an informal one.  At this point it is unclear
+whether SPIFF will supersede JFIF or whether JFIF will remain the de-facto
+standard.  IJG intends to support SPIFF once the standard is frozen, but we
+have not decided whether it should become our default output format or not.
+(In any case, our decoder will remain capable of reading JFIF indefinitely.)
+
+Various proprietary file formats incorporating JPEG compression also exist.
+We have little or no sympathy for the existence of these formats.  Indeed,
+one of the original reasons for developing this free software was to help
+force convergence on common, open format standards for JPEG files.  Don't
+use a proprietary file format!
+
+
+TO DO
+=====
+
+The major thrust for v7 will probably be improvement of visual quality.
+The current method for scaling the quantization tables is known not to be
+very good at low Q values.  We also intend to investigate block boundary
+smoothing, "poor man's variable quantization", and other means of improving
+quality-vs-file-size performance without sacrificing compatibility.
+
+In future versions, we are considering supporting some of the upcoming JPEG
+Part 3 extensions --- principally, variable quantization and the SPIFF file
+format.
+
+As always, speeding things up is of great interest.
+
+Please send bug reports, offers of help, etc. to jpeg-info@uunet.uu.net.
diff --git a/converter/other/anytopnm b/converter/other/anytopnm
new file mode 100755
index 00000000..6c56b5ef
--- /dev/null
+++ b/converter/other/anytopnm
@@ -0,0 +1,544 @@
+#!/bin/sh
+#
+# anytopnm - attempt to convert an unknown type of image file to a P?M 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.
+
+
+
+putInputIntoTempfile() {
+
+    # $1 is the input file specification
+    # $2 is the temporary file name
+    
+    rm -f "$2"
+    if [ "$1" = "-" ] ; then
+            cat > "$2"
+    else
+        # We'd like to do a -e here to test the existence of the filesystem
+        # object per se, and thus give a more specific error message.  But
+        # some systems don't have -e.
+        if [ ! -f "$1" ] ; then
+            echo "$progname: '$1' does not exist or is not a file" 1>&2
+            exit 1
+            fi
+        
+        if [ ! -r "$1" ] ; then
+            echo "$progname: '$1' is not readable" 1>&2
+            exit 1
+            fi
+        
+        if [ -z "$1" ] ; then
+            echo "$progname: '$1' is empty" 1>&2
+            exit 1
+            fi
+        
+        cat < "$1" > "$2"
+        fi
+}
+
+
+
+setMimeType() {
+    # $1 is the file name
+
+    # Christos Zoulas's current 'file' (see Freshmeat) has the --mime option.
+
+    file --mime /dev/null >/dev/null 2>/dev/null
+    if [ $? -eq 0 ]; then
+        # Now that we know the --mime option exists, use it.
+        mimeType=`file --mime "$1" | cut -d: -f2- | cut -c2-`
+    else
+        # file --mime failed; we don't know why, but we assume it's because it
+        # is a traditional 'file' program that doesn't have a --mime option.
+    mimeType="unknown"
+    fi
+}
+
+
+
+computeTypeFromMimeType () {
+
+# $1 is the mime type string
+    
+    case "$1" in
+
+        image/jpeg )
+            filetype="jfif"
+            ;;
+        image/gif )
+            filetype="gif"
+            ;;
+        image/tiff )
+            filetype="tiff"
+            ;;
+        image/bmp )
+            filetype="bmp"
+            ;;
+        image/x-ms-bmp )
+            filetype="bmp"
+            ;;
+        image/png )
+            filetype="png"
+            ;;
+        image/x-portable-bitmap | image/x-portable-pixmap | \
+                image/x-portable-greymap)
+            filetype="pnm"
+            ;;
+        image/x-xpm )
+            filetype="xpm"
+            ;;
+        * )
+            filetype=unknown
+            ;;
+        esac
+}
+
+
+
+computeTypeFromTypeDescription () {
+
+# $1 is the full description from 'file' of the file type
+
+    case "$1" in
+    
+        *PBM* | *PGM* | *PPM* )
+            filetype=pnm
+            ;;
+    
+        *JPEG* | *JFIF* )
+            filetype=jfif
+            ;;
+    
+        *PNG* )
+            filetype=png
+            ;;
+    
+        *GIF* )
+            filetype=gif
+            ;;
+    
+        *TIFF* )
+            filetype=tiff
+            ;;
+    
+        *PC*bitmap*data* )
+            filetype=bmp
+            ;;
+        
+        *uuencoded* )
+            filetype=uuencoded
+            ;;
+    
+        *bzip2*compressed*data* )
+            filetype=bzip2
+            ;;
+    
+        *bzip*compressed*data* )
+            filetype=bzip
+            ;;
+        
+        *gzip*compressed*data* )
+            filetype=gzip
+            ;;
+    
+        *compress* )
+            filetype=compress
+            ;;
+    
+        *btoa* )
+            filetype=btoa
+            ;;
+    
+        *Sun* | *rasterfile* )
+            filetype=rast
+            ;;
+    
+        *IFF*ILBM* )
+            filetype=ilbm
+            ;;
+    
+        *Lisp* )
+            filetype=lispm
+            ;;
+    
+        *PC*Paintbrush* )
+            filetype=pcx
+            ;;
+    
+        *Bennet* )
+            filetype=ybm
+            ;;
+    
+        *pixmap*image*text* )
+            filetype=xpm
+            ;;
+    
+        # This has to come after all other 'text' files, or you may be
+        # disappointed.
+        *text* )
+            filetype=text
+            ;;
+    
+        *MicroDesign* )
+            filetype=mda
+            ;;
+    
+        * )
+            filetype=unknown
+            ;;
+        esac
+}
+
+
+
+computeTypeFromFilenameExtension () {
+# $1 is the filename extension (".gif", etc.)
+
+    case "$1" in
+
+        *.pbm | *.pbm.* | *.pgm | *.pgm.* | *.ppm | *.ppm.* )
+            filetype=pnm
+            ;;
+        *.JPEG | *.jpeg | *.jpg | *.JPG )
+            filetype=jfif
+            ;;
+        *.gif | *.gif.* )
+            filetype=gif
+            ;;
+        *.png | *.PNG )
+            filetype=png
+            ;;
+        *.tif | *.tif.* | *.tiff | *.tiff.* )
+            filetype=tiff
+            ;;
+        *.bmp )
+            filetype=bmp
+            ;;
+        *.x | *.x.* | *.xbm | *.xbm.* | *.x10bm | *.x10bm.* | \
+            *.x11bm | *.x11bm.* | *.bitmap | *.bitmap.* )
+            filetype=xbm
+            ;;
+        *.r | *.r.* | *.rast | *.rast.* )
+            filetype=rast
+            ;;
+        *.mac | *.mac.* | *.macp | *.macp.* )
+            filetype=macp
+            ;;
+        *.g3 | *.g3.* | *.fax | *.fax.* )
+            filetype=g3
+            ;;
+        *.xwd | *.xwd.* | *.x10wd | *.x10wd.* \
+                | *.x11wd | *.x11wd.* )
+            filetype=xwd
+            ;;
+        *.brush | *.brush.* )
+            filetype=brush
+            ;;
+        *.img | *.img.* )
+            filetype=gem
+            ;;
+        *.pcx | *.pcx.* )
+            filetype=pcx
+            ;;
+        *.pic | *.pic.* | *.pict | *.pict.* | *.pict2 | *.pict2.* )
+            filetype=pict
+            ;;
+        *.fs | *.fs.* | *.face | *.face.* )
+            filetype=fs
+            ;;
+        *.hips | *.hips.* )
+            filetype=hips
+            ;;
+        *.fits | *.fits.* )
+            filetype=fits
+            ;;
+        *.iff | *.iff.* | *.ilbm | *.ilbm.* )
+            filetype=ilbm
+            ;;
+        *.lispm | *.lispm.* )
+            filetype=lispm
+            ;;
+        *.mtv | *.mtv.* )
+            filetype=mtv
+            ;;
+        *.qrt | *.qrt.* )
+            filetype=qrt
+            ;;
+        *.tga | *.tga.* | *.targa | *.targa.* )
+            filetype=tga
+            ;;
+        *.xim | *.xim.* )
+            filetype=xim
+            ;;
+        *.xpm | *.xpm.* | *.xpm2 | *.xpm2.* )
+            filetype=xpm
+            ;;
+        *.pi1 | *.pi1.* )
+            filetype=pi1
+            ;;
+        *.pi3 | *.pi3.* )
+            filetype=pi3
+            ;;
+        *.spu | *.spu.* )
+            filetype=spu
+            ;;
+        *.spc | *.spc.* )
+            filetype=spc
+            ;;
+        *.ybm | *.ybm.* | *.face | *.face.* )
+            filetype=ybm
+            ;;
+        *.mda | *.mdp )
+            filetype=mda
+            ;;
+        * )
+            filetype=unknown
+            ;;
+        esac
+}
+
+
+
+determineType () {
+
+# $1 is the name of the file that contains the subject file's contents
+# $2 is the mime type or "unknown"
+# $3 is the type description from 'file'
+
+    if [ "$2" = "unknown" ]; then
+        filetype="unknown"
+    else
+        computeTypeFromMimeType "$2"
+        fi
+
+    if [ "$filetype" = "unknown" ]; then
+        computeTypeFromTypeDescription "$3"
+        fi
+
+    if [ "$filetype" = "unknown" ]; then
+        computeTypeFromFilenameExtension "$4"
+        fi
+}
+
+
+
+convertIt () {
+# Based on the file type computed, do the conversion
+
+# $1 is the input file name
+# $2 is our file type code
+
+case "$2" in
+
+    pnm )
+        cat "$file"
+        ;;
+
+    uuencoded )
+        newfile="$tempdir/atn.decode.$$"
+        rm -f "$newfile"
+        (echo begin 600 $newfile; sed 1d < "$file") | uudecode
+        anytopnm "$newfile"
+        ;;
+
+    bzip2 )
+        bzip2 -dk < "$file" | anytopnm -
+        ;;
+
+    bzip )
+        bzip -dk < "$file" | anytopnm -
+        ;;
+
+    gzip )
+        gzip --decompress --to-stdout < "$file" | anytopnm -
+        ;;
+
+    compress )
+        uncompress -c < "$file" | anytopnm -
+        ;;
+
+    btoa )
+        atob < "$file" | anytopnm -
+        ;;
+
+    rast )
+        rasttopnm "$file"
+        ;;
+
+    gif )
+        giftopnm "$file"
+        ;;
+
+    tiff )
+        tifftopnm "$file"
+        ;;
+
+    ilbm )
+        ilbmtoppm "$file"
+        ;;
+
+    lispm )
+        lispmtopgm "$file"
+        ;;
+
+    pcx )
+        pcxtoppm "$file"
+        ;;
+
+    ybm )
+        ybmtopbm "$file"
+        ;;
+
+    xpm )
+        xpmtoppm < "$file"
+        ;;
+
+    # This has to come after all other 'text' files, or you may be
+    # disappointed.
+    text )
+        pbmtext -builtin fixed < "$file"
+        ;;
+
+    jfif )
+        jpegtopnm "$file"
+        ;;
+
+    png )
+        pngtopnm "$file"
+        ;;
+
+    mda )
+        mdatopbm -d -- "$file"
+        ;;
+
+    bmp )
+        bmptoppm "$file"
+        ;;
+    
+    xbm )
+        xbmtopbm "$file"
+        ;;
+    macp )
+        macptopbm "$file"
+        ;;
+    g3 )
+        g3topbm "$file"
+        ;;
+    xwd )
+        xwdtopnm "$file"
+        ;;
+    brush )
+        brushtopbm "$file"
+        ;;
+    img )
+        gemtopbm "$file"
+        ;;
+    pcx ) 
+        pcxtoppm "$file"
+        ;;
+    pict )
+        picttoppm "$file"
+        ;;
+    fs )
+        fstopgm "$file"
+        ;;
+    hips )
+        hipstopgm "$file"
+        ;;
+    fits )
+        fitstopnm "$file"
+        ;;
+    mtv )
+        mtvtoppm "$file"
+        ;;
+    qrt )
+        qrttoppm "$file"
+        ;;
+    tga )
+        tgatoppm "$file"
+        ;;
+    xim )
+        ximtoppm "$file"
+        ;;
+    pi1 )
+        pi1toppm "$file"
+        ;;
+    pi3 )
+        pi3topbm "$file"
+        ;;
+    spu )
+        sputoppm "$file"
+        ;;
+    spc )
+        spctoppm "$file"
+        ;;
+    * )
+        echo "$progname: INTERNAL ERROR.  Illegal value of filetype variable"
+        exit 1
+        ;;
+
+    esac
+}
+
+
+
+###############################################################################
+#                                MAINLINE
+###############################################################################
+
+progname=$0
+
+if [ $# -gt 1 ] ; then
+    echo "Too many arguments: $#.  The only valid argument is the" \
+         "input file name." 1>&2
+    exit 1
+elif [ $# -eq 1 ] ; then
+    inputFile="$1"
+else
+    inputFile="-"
+fi
+
+tempdir="${TMPDIR-/tmp}/anytopnm.$$"
+mkdir $tempdir || { echo "Could not create temporary file. Exiting."; exit 1;}
+chmod 700 $tempdir
+
+trap 'rm -rf $tempdir' 0
+
+# Take out all spaces
+# Find the filename extension for last-ditch efforts later
+
+# We used to do this, but it doesn't work with all Awks.  The sed below
+# is universal.  2005.11.02.
+#fileExtension=`echo "$inputFile" | \
+#               $AWK '{gsub(" ","");gsub(".*\\\\.",".");print}'`
+
+fileExtension=`echo "$inputFile" | sed 's/ //;s/.*\././g'`
+
+file="$tempdir/atn.stdin.$$"
+
+putInputIntoTempfile "$inputFile" "$file"
+
+setMimeType "$file"
+
+typeDescription=`file "$file" | cut -d: -f2- | cut -c2`
+
+determineType "$file" "$mimeType" "$typeDescription" "$fileExtension"
+
+if [ "$filetype" = "unknown" ]; then
+    echo "$progname: unknown file type.  " \
+        "'file' says mime type is '$mimeType', " 1>&2
+    echo "type description is '$typeDescription'" 1>&2
+    exit 1
+    fi
+
+convertIt $file $filetype
+
+exit 0
diff --git a/converter/other/bmepsoe.c b/converter/other/bmepsoe.c
new file mode 100644
index 00000000..cdb52779
--- /dev/null
+++ b/converter/other/bmepsoe.c
@@ -0,0 +1,539 @@
+/* 
+ * This was adapted for Netpbm from bmpesoe.c in Dirk Krause's Bmeps package
+ * by Bryan Henderson on 2005.01.05.
+ *
+ * Differences:
+ *   - doesn't require Bmeps configuration stuff (bmepsco.h)
+ *   - doesn't include pngeps.h
+ *   - doesn't have test scaffold code
+ *   - a few compiler warnings fixed
+ *
+ * Copyright (C) 2000 - Dirk Krause
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * In this package the copy of the GNU Library General Public License
+ * is placed in file COPYING.
+ */
+
+#include "bmepsoe.h"
+
+#define RL_RUNLENGTH(i) (257 - (i))
+#define RL_STRINGLENGTH(i) ((i) - 1)
+#define RL_MAXLENGTH (127)
+
+#define FINALOUTPUT(c) fputc((c),o->out)
+
+void oe_init(Output_Encoder *o, FILE *out, int mode, int rate, int *buf,
+  Bytef *flib, size_t flis, Bytef *flob, size_t flos
+)
+{
+  
+  o->out = out;
+  o->mode = mode;
+  o->textpos = 0;
+  o->a85_value = 0UL; o->a85_consumed = 0;
+  o->a85_0 = 1UL;
+  o->a85_1 = 85UL;
+  o->a85_2 = 85UL * 85UL;
+  o->a85_3 = 85UL * o->a85_2;
+  o->a85_4 = 85UL * o->a85_3;
+  o->rl_lastbyte = 0;
+  o->rl_buffer = buf;
+  o->rl_bufused = 0;
+  o->rl_state = 0;
+  o->bit_value = 0;
+  o->bit_consumed = 0;
+  o->flate_rate = rate;
+  if(o->flate_rate < 0) {
+    o->mode &= (~OE_FLATE);
+  }
+  if(o->flate_rate > 9) {
+    o->flate_rate = 9;
+  }
+  if((o->mode & OE_FLATE) && flib && flis && flob && flos) {
+    (o->flate_stream).zfree = (free_func)0;
+    (o->flate_stream).zalloc = (alloc_func)0;
+    (o->flate_stream).opaque = (voidpf)0;
+    if(deflateInit((&(o->flate_stream)),o->flate_rate) != Z_OK) {
+      o->mode &= (~OE_FLATE);
+    }
+  }
+  o->fl_i_buffer = flib;
+  o->fl_o_buffer = flob;
+  o->fl_i_size   = flis;
+  o->fl_o_size   = flos;
+  o->fl_i_used   = 0;
+  
+}
+
+static char hexdigits[] = {
+  "0123456789ABCDEF"
+};
+
+static void asciihex_add(Output_Encoder *o, int b)
+{
+  
+  FINALOUTPUT(hexdigits[(b/16)]) ;
+  FINALOUTPUT(hexdigits[(b%16)]) ;
+  o->textpos = o->textpos + 2;
+  if(o->textpos >= 64) {
+    FINALOUTPUT('\n') ;
+    o->textpos = 0;
+  }
+  
+}
+
+static void asciihex_flush(Output_Encoder *o)
+{
+  
+  if(o->textpos > 0) {
+    FINALOUTPUT('\n') ;
+    o->textpos = 0;
+  }
+  
+}
+
+static char   ascii85_char(unsigned long x)
+{
+  unsigned u;
+  int      i;
+  char back;
+  back = (char)0;
+  u = (unsigned)x;
+  i = (int)u;
+  i += 33;
+  back = (char)i;
+  return back;
+}
+
+
+
+static void
+ascii85_output(Output_Encoder * const o) {
+    unsigned long value;
+
+    value = o->a85_value;  /* initial value */
+
+    if (value == 0 && o->a85_consumed == 4) {
+        FINALOUTPUT('z');
+        ++o->textpos;
+    } else {
+        unsigned int i;
+        unsigned int j;
+        char buffer[6];
+
+        buffer[0] = ascii85_char(value / (o->a85_4));
+        value = value % (o->a85_4);
+        buffer[1] = ascii85_char(value / (o->a85_3));
+        value = value % (o->a85_3);
+        buffer[2] = ascii85_char(value / (o->a85_2));
+        value = value % (o->a85_2);
+        buffer[3] = ascii85_char(value / (o->a85_1));
+        value = value % (o->a85_1);
+        buffer[4] = ascii85_char(value);
+        buffer[5] = '\0';
+
+        i = o->a85_consumed + 1;
+        o->textpos += i;
+
+        for (j = 0; j < i; ++j)
+            FINALOUTPUT(buffer[j]);
+    }
+    if (o->textpos >= 64) {
+        FINALOUTPUT('\n');
+        o->textpos = 0;
+    }
+}
+
+
+
+static void ascii85_add(Output_Encoder *o, int b)
+{
+  unsigned u;
+  unsigned long ul;
+  
+  u = (unsigned)b;
+  ul = (unsigned long)u;
+  o->a85_value = 256UL * (o->a85_value) + ul;
+  o->a85_consumed = o->a85_consumed + 1;
+  if(o->a85_consumed >= 4) {
+    ascii85_output(o);
+    o->a85_value = 0UL;
+    o->a85_consumed = 0;
+  }
+  
+}
+
+static void ascii85_flush(Output_Encoder *o)
+{
+  int i;
+  
+  if(o->a85_consumed > 0) {
+    i = o->a85_consumed;
+    while(i < 4) {
+      o->a85_value = 256UL * o->a85_value;
+      i++;
+    }
+    ascii85_output(o);
+    o->a85_value = 0UL;
+    o->a85_consumed = 0;
+  }
+  if(o->textpos > 0) {
+    FINALOUTPUT('\n') ;
+    o->textpos = 0;
+  }
+  
+}
+
+static void after_flate_add(Output_Encoder *o, int b)
+{
+  
+  if(o->mode & OE_ASC85) {
+    ascii85_add(o,b);
+  } else {
+    asciihex_add(o,b);
+  }
+  
+}
+
+static void do_flate_flush(Output_Encoder *o, int final)
+{
+  Bytef *iptr, *optr, *xptr;
+  uLong  is, os, xs;
+  int err, must_continue;
+  
+  iptr = o->fl_i_buffer; optr = o->fl_o_buffer;
+  is = o->fl_i_size; os = o->fl_o_size;
+  
+  if(iptr && optr && is && os) {
+    is = o->fl_i_used;
+    if(is) {
+      (o->flate_stream).next_in = iptr;
+      (o->flate_stream).avail_in = is;
+      if(final) { 
+    must_continue = 1;
+    while(must_continue) {
+      (o->flate_stream).next_out = optr;
+      (o->flate_stream).avail_out = os;
+      must_continue = 0;
+      err = deflate(&(o->flate_stream), Z_FINISH);
+      switch(err) {
+        case Z_STREAM_END: { 
+              xptr = optr;
+          
+          xs = os - ((o->flate_stream).avail_out);
+          while(xs--) {
+        after_flate_add(o, (*(xptr++)));
+          }
+        } break;
+        case Z_OK : {
+          must_continue = 1;
+              xptr = optr;
+          
+          xs = os - ((o->flate_stream).avail_out);
+          while(xs--) {
+        after_flate_add(o, (*(xptr++)));
+          }
+        } break;
+        default : { 
+        } break;
+      }
+    }
+      } else { 
+    must_continue = 1;
+    while(must_continue) {
+      must_continue = 0;
+      (o->flate_stream).avail_out = os; (o->flate_stream).next_out = optr;
+      err = deflate(&(o->flate_stream), 0);
+      switch(err) {
+        case Z_OK: {
+          if((o->flate_stream).avail_in) {
+        must_continue = 1;
+          }
+          
+          xptr = optr; xs = os - ((o->flate_stream).avail_out);
+          while(xs--) {
+        after_flate_add(o, (*(xptr++)));
+          }
+        } break;
+        default : { 
+        } break;
+      }
+    }
+      }
+    }
+  }
+  
+}
+
+static void flate_add(Output_Encoder *o, int b)
+{
+  Byte bt;
+  Bytef *btptr;
+  
+  btptr = o->fl_i_buffer;
+  if(btptr) { 
+    bt = (Byte)b; 
+    btptr[(o->fl_i_used)] = bt;
+    o->fl_i_used += 1UL;
+    if(o->fl_i_used >= o->fl_i_size) {
+      do_flate_flush(o, 0);
+      o->fl_i_used = 0UL;
+    }
+  }
+  
+}
+
+static void after_flate_flush(Output_Encoder *o)
+{
+  
+  if(o->mode & OE_ASC85) {
+    ascii85_flush(o);
+  } else {
+    asciihex_flush(o);
+  }
+  
+}
+
+static void flate_flush(Output_Encoder *o)
+{
+  do_flate_flush(o,1);
+  deflateEnd(&(o->flate_stream));
+  after_flate_flush(o);
+}
+
+
+static void after_rl_add(Output_Encoder *o, int b)
+{
+  
+  if(o->mode & OE_FLATE) {
+    flate_add(o,b);
+  } else {
+    after_flate_add(o,b);
+  }
+  
+}
+
+static void rl_add(Output_Encoder *o, int b)
+{
+  int lgt, i;
+  int *buffer;
+  /* ##### */
+  
+  buffer = o->rl_buffer;
+  lgt = o->rl_bufused;
+  if(buffer) {
+    
+    if(lgt > 0) {
+      if(o->rl_lastbyte == b) {
+    switch(o->rl_state) {
+      case 2: {
+        buffer[lgt++] = b;
+        o->rl_bufused = lgt;
+        o->rl_state = 2;
+        o->rl_lastbyte = b;
+        if(lgt >= RL_MAXLENGTH) {
+          after_rl_add(o, RL_RUNLENGTH(lgt));
+          after_rl_add(o, b);
+          o->rl_bufused = 0;
+          o->rl_state = 0;
+          o->rl_lastbyte = b;
+        }
+      } break;
+      case 1: {
+        buffer[lgt++] = b;
+        o->rl_bufused = lgt;
+        o->rl_state = 2;
+        o->rl_lastbyte = b;
+        lgt = lgt - 3;
+        if(lgt > 0) {
+          after_rl_add(o, RL_STRINGLENGTH(lgt));
+          for(i = 0; i < lgt; i++) {
+        after_rl_add(o, buffer[i]);
+          }
+          buffer[0] = buffer[1] = buffer[2] = b;
+          o->rl_bufused = 3;
+          o->rl_state = 2;
+          o->rl_lastbyte = b;
+        }
+      } break;
+      default: {
+        buffer[lgt++] = b;
+        o->rl_bufused = lgt;
+        o->rl_state = 1;
+        o->rl_lastbyte = b;
+        if(lgt >= RL_MAXLENGTH) {
+          lgt = lgt - 2;
+          after_rl_add(o, RL_STRINGLENGTH(lgt));
+          for(i = 0; i < lgt; i++) {
+        after_rl_add(o, buffer[i]);
+          }
+          buffer[0] = buffer[1] = b;
+          o->rl_bufused = 2;
+          o->rl_state = 1;
+          o->rl_lastbyte = b;
+        }
+      } break;
+    }
+      } else {
+    if(o->rl_state == 2) {
+      after_rl_add(o, RL_RUNLENGTH(lgt));
+      after_rl_add(o, (o->rl_lastbyte));
+      buffer[0] = b; o->rl_bufused = 1; o->rl_lastbyte = b;
+      o->rl_state = 0;
+    } else {
+      buffer[lgt++] = b;
+      o->rl_bufused = lgt;
+      o->rl_lastbyte = b;
+      if(lgt >= RL_MAXLENGTH) {
+        after_rl_add(o, RL_STRINGLENGTH(lgt));
+        for(i = 0; i < lgt; i++) {
+          after_rl_add(o, buffer[i]);
+        }
+        o->rl_bufused = 0;
+      }
+      o->rl_state = 0;
+    }
+      }
+    } else {
+      buffer[0] = b;
+      o->rl_bufused = 1;
+      o->rl_lastbyte = b;
+    }
+    o->rl_lastbyte = b;
+    
+  } else { 
+    after_rl_add(o,0);
+    after_rl_add(o,b);
+  }
+  
+}
+
+static void after_rl_flush(Output_Encoder *o)
+{
+  
+  if(o->mode & OE_FLATE) {
+    flate_flush(o);
+  } else {
+    after_flate_flush(o);
+  }
+  
+}
+
+static void rl_flush(Output_Encoder *o)
+{
+  int lgt;
+  int *buffer;
+  int i;
+  
+  buffer = o->rl_buffer;
+  lgt = o->rl_bufused;
+  if(lgt > 0) {
+    if(o->rl_state == 2) {
+      i = o->rl_lastbyte;
+      after_rl_add(o,RL_RUNLENGTH(lgt));
+      after_rl_add(o,i);
+    } else {
+      after_rl_add(o,RL_STRINGLENGTH(lgt));
+      for(i = 0; i < lgt; i++) {
+    after_rl_add(o,buffer[i]);
+      }
+    }
+  }
+  after_rl_flush(o);
+  
+}
+
+static void internal_byte_add(Output_Encoder *o, int b)
+{
+  
+  if((o->mode) & OE_RL) {
+    rl_add(o,b);
+  } else {
+    after_rl_add(o,b);
+  }
+  
+}
+
+static void internal_byte_flush(Output_Encoder *o)
+{
+  
+  if((o->mode) & OE_RL) {
+    rl_flush(o);
+  } else {
+    after_rl_flush(o);
+  }
+  
+}
+
+void oe_bit_add(Output_Encoder *o, int b)
+{
+  
+  o->bit_value = 2 * o->bit_value + (b ? 1 : 0);
+  o->bit_consumed = o->bit_consumed + 1;
+  if(o->bit_consumed >= 8) {
+    o->bit_consumed = 0;
+    internal_byte_add(o, (o->bit_value));
+    o->bit_value = 0;
+  }
+  
+}
+
+void oe_bit_flush(Output_Encoder *o)
+{
+  
+  if(o->bit_consumed) {
+    int v, i;
+    v = o->bit_value;
+    i = o->bit_consumed;
+    while(i < 8) {
+      i++;
+      v = v * 2;
+    }
+    internal_byte_add(o,v);
+    o->bit_value = 0;
+    o->bit_consumed = 0;
+  }
+  internal_byte_flush(o);
+  
+}
+
+void oe_byte_add(Output_Encoder *o, int b)
+{
+  
+  if(o->bit_consumed) {
+    int testval,i;
+    testval = 128;
+    for(i = 0; i < 8; i++) {
+      if(b & testval) {
+    oe_bit_add(o,1);
+      } else {
+    oe_bit_add(o,0);
+      }
+      testval = testval / 2;
+    }
+  } else {
+    internal_byte_add(o,b);
+  }
+  
+}
+
+void oe_byte_flush(Output_Encoder *o)
+{
+  
+  oe_bit_flush(o);
+  
+}
diff --git a/converter/other/bmepsoe.h b/converter/other/bmepsoe.h
new file mode 100644
index 00000000..99c59f77
--- /dev/null
+++ b/converter/other/bmepsoe.h
@@ -0,0 +1,79 @@
+/* 
+ * libbmeps - Bitmap to EPS conversion library
+ * Copyright (C) 2000 - Dirk Krause
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * In this package the copy of the GNU Library General Public License
+ * is placed in file COPYING.
+ */
+
+#ifndef BMPESOE_H_INCLUDED
+#define BMPESOE_H_INCLUDED
+
+#include <stdio.h>
+
+#include <zlib.h>
+
+typedef struct {
+  int mode;
+  FILE *out;
+  int textpos;
+  unsigned long a85_value;
+  int      a85_consumed;
+  unsigned long a85_4;
+  unsigned long a85_3;
+  unsigned long a85_2;
+  unsigned long a85_1;
+  unsigned long a85_0;
+  int rl_lastbyte;
+  int *rl_buffer;
+  int rl_bufused;
+  int rl_state;
+  int bit_value;
+  int bit_consumed;
+  z_stream flate_stream;
+  Bytef *fl_i_buffer;
+  Bytef *fl_o_buffer;
+  uLong  fl_i_size;
+  uLong  fl_o_size;
+  uLong  fl_i_used;
+  int    flate_rate;
+} Output_Encoder;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void oe_init(Output_Encoder *o, FILE *out, int mode, int rate, int *buf,
+  Bytef *flib, size_t flis, Bytef *flob, size_t flos
+);
+void oe_byte_add(Output_Encoder *o, int b);
+void oe_byte_flush(Output_Encoder *o);
+void oe_bit_add(Output_Encoder *o, int b);
+void oe_bit_flush(Output_Encoder *o);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+#define OE_ASC85 1
+#define OE_FLATE 2
+#define OE_RL    4
+
+#endif
+
diff --git a/converter/other/bmptopnm.c b/converter/other/bmptopnm.c
new file mode 100644
index 00000000..3ba1d77c
--- /dev/null
+++ b/converter/other/bmptopnm.c
@@ -0,0 +1,1477 @@
+/*****************************************************************************
+                                    bmptopnm.c
+******************************************************************************
+ 
+ Bmptopnm - Converts from a Microsoft Windows or OS/2 .BMP file to a
+ PBM, PGM, or PPM file.
+
+ This program was formerly called Bmptoppm (and generated only PPM output).
+ The name was changed in March 2002.
+
+ 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.
+
+*****************************************************************************/
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+
+#include "pnm.h"
+#include "shhopt.h"
+#include "nstring.h"
+#include "bmp.h"
+
+/* MAXCOLORS is the maximum size of a color map in a BMP image */
+#define MAXCOLORS       256
+
+static xelval const bmpMaxval = 255;
+    /* The maxval for intensity values in a BMP image -- either in a
+       truecolor raster or in a colormap
+    */
+
+enum rowOrder {BOTTOMUP, TOPDOWN};
+
+struct bitPosition {
+    /* mask and shift count to describe a set of bits in a binary value.
+
+       Example: if 16 bits are laid out as XRRRRRGGGGGBBBBB then the shift
+       count for the R component is 10 and the mask is 0000000000011111.
+    */
+    unsigned int shift;
+        /* How many bits right you have to shift the value to get the subject
+           bits in the least significant bit positions.
+        */
+    unsigned int mask;
+        /* Has one bits in positions where the subject bits are after
+           shifting.
+        */
+};
+
+struct pixelformat {
+    /* The format of a pixel representation from the raster.  i.e. which 
+       bits apply to red, green, blue, and transparency 
+    */
+    struct bitPosition red;
+    struct bitPosition blu;
+    struct bitPosition grn;
+    struct bitPosition trn;
+    
+    bool conventionalBgr;
+        /* This means that the above bit positions are just the conventional
+           BGR format -- one byte Blue, one byte Green, one byte Red,
+           no alpha.  Though it's totally redundant with the members above,
+           this member speeds up computation:  We've never actually seen
+           a BMP file that doesn't use conventional BGR, and it doesn't
+           require any masking or shifting at all to interpret.
+        */
+};
+
+struct bmpInfoHeader {
+    enum rowOrder rowOrder;
+    int cols;
+    int rows;
+    unsigned int cBitCount;
+        /* Number of bits in the BMP file that each pixel occupies. */
+    enum bmpClass class;
+    bool bitFields;
+        /* The raster values are arranged in arbitrary bit fields as
+           described by the "mask" values in the header, rather than
+           fixed formats.
+        */
+    int cmapsize;
+        /* Size in bytes of the colormap (palette) in the BMP file */
+    unsigned int imageSize;
+        /* Size in bytes of the image data.  We only reference this 
+           when the image is compressed. */    
+    unsigned short cPlanes;
+    unsigned long int compression;
+    struct pixelformat pixelformat;
+};
+
+
+
+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 files */
+    int verbose;    /* -verbose option */
+};
+
+static const char *ifname;
+
+
+
+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,   "verbose",     OPT_FLAG,   &cmdline_p->verbose,         0);
+ 
+    /* Set the defaults */
+    cmdline_p->verbose = FALSE;
+
+    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 (argc-1 == 0) 
+        cmdline_p->input_filespec = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
+    else
+        cmdline_p->input_filespec = argv[1];
+
+}
+
+
+
+static const char er_read[] = "%s: read error";
+
+static int
+GetByte(FILE * const fp) {
+
+    int             v;
+
+    if ((v = getc(fp)) == EOF)
+        pm_error(er_read, ifname);
+
+    return v;
+}
+
+
+
+static short
+GetShort(FILE * const fp) {
+
+    short           v;
+
+    if (pm_readlittleshort(fp, &v) == -1)
+        pm_error(er_read, ifname);
+
+    return v;
+}
+
+static short
+GetBigShort(FILE * const fp) {
+
+    short           v;
+
+    if (pm_readbigshort(fp, &v) == -1)
+        pm_error(er_read, ifname);
+
+    return v;
+}
+
+
+
+static long
+GetLong(FILE * const fp) {
+
+    long v;
+
+    if (pm_readlittlelong(fp, &v) == -1)
+        pm_error(er_read, ifname);
+
+    return v;
+}
+
+
+
+typedef struct {
+    long dummy[12];
+} cieXyzTriple;
+
+static cieXyzTriple
+GetCieXyzTriple(FILE * const fp) {
+
+    cieXyzTriple v;
+    unsigned int i;
+
+    for (i = 0; i < 12; ++i) 
+        if (pm_readlittlelong(fp, &v.dummy[i]) == -1)
+            pm_error(er_read, ifname);
+
+    return v;
+}
+
+
+
+static void
+readOffBytes(FILE * const fp, unsigned int const nbytes) {
+/*----------------------------------------------------------------------------
+   Read 'nbytes' from file 'fp'.  Abort program if read error.
+-----------------------------------------------------------------------------*/
+    int i;
+    
+    for(i = 0; i < nbytes; ++i) {
+        int rc;
+        rc = getc(fp);
+        if (rc == EOF)
+            pm_error(er_read, ifname);
+    }
+}
+
+
+
+static void
+BMPreadfileheader(FILE *         const ifP, 
+                  unsigned int * const bytesReadP, 
+                  unsigned int * const offBitsP) {
+
+    unsigned short  xHotSpot;
+    unsigned short  yHotSpot;
+    unsigned long   offBits;
+    unsigned long int fileSize;
+
+
+    if (GetByte(ifP) != 'B')
+        pm_error("'%s' is not a BMP file.  (It doesn't start with 'BM')",
+                 ifname);
+    if (GetByte(ifP) != 'M')
+        pm_error("'%s' is not a BMP file.  (It doesn't start with 'BM')",
+                 ifname);
+
+
+    fileSize = GetLong(ifP);  /* This is not always reliable. */
+    xHotSpot  = GetShort(ifP);
+    yHotSpot  = GetShort(ifP);
+    offBits   = GetLong(ifP);
+
+    *offBitsP = offBits;
+
+    *bytesReadP = 14;
+}
+
+
+
+static void
+readOs2InfoHeader(FILE *                 const ifP,
+                  struct bmpInfoHeader * const headerP) {
+
+    headerP->class = C_OS2;
+
+    headerP->cols = GetShort(ifP);
+    headerP->rows = GetShort(ifP);
+    headerP->rowOrder = BOTTOMUP;
+    headerP->cPlanes = GetShort(ifP);
+    headerP->cBitCount = GetShort(ifP);
+    /* I actually don't know if the OS/2 BMP format allows
+       cBitCount > 8 or if it does, what it means, but ppmtobmp
+       creates such BMPs, more or less as a byproduct of creating
+       the same for Windows BMP, so we interpret cBitCount > 8 the
+       same as for Windows.
+    */
+    if (headerP->cBitCount <= 8)
+        headerP->cmapsize = 1 << headerP->cBitCount;
+    else if (headerP->cBitCount == 24)
+        headerP->cmapsize = 0;
+    /* There is a 16 bit truecolor format, but we don't know how the
+       bits are divided among red, green, and blue, so we can't handle it.
+    */
+    else
+        pm_error("Unrecognized bits per pixel in OS/2 BMP file header: %d",
+                 headerP->cBitCount);
+                 
+    headerP->compression = COMP_RGB;
+    
+    pm_message("OS/2 BMP, %dx%dx%d",
+               headerP->cols, headerP->rows, headerP->cBitCount);
+}
+
+
+
+static void
+readWindowsBasic40ByteInfoHeader(FILE *                 const ifP,
+                                 struct bmpInfoHeader * const headerP) {
+/*----------------------------------------------------------------------------
+   Read from the file stream 'ifP' the basic BMP Info header.  This does
+   not include any Info header extension.  The Info header is the data
+   that comes after the BMP file header.
+
+   Return the information from the info header as *headerP.
+-----------------------------------------------------------------------------*/
+    int colorsimportant;   /* ColorsImportant value from header */
+    int colorsused;        /* ColorsUsed value from header */
+
+    headerP->class = C_WIN;
+
+    headerP->cols = GetLong(ifP);
+    {
+        long const cy = GetLong(ifP);
+        if (cy < 0) {
+            headerP->rowOrder = TOPDOWN;
+            headerP->rows = - cy;
+        } else {
+            headerP->rowOrder = BOTTOMUP;
+            headerP->rows = cy;
+        }
+    }
+    headerP->cPlanes = GetShort(ifP);
+    headerP->cBitCount = GetShort(ifP);
+ 
+    {
+        unsigned long int const compression = GetLong(ifP);
+
+        headerP->bitFields = (compression == COMP_BITFIELDS);
+    
+        if (compression != COMP_RGB && compression != COMP_BITFIELDS &&
+            compression != COMP_RLE4 && compression != COMP_RLE8 ) 
+            pm_error("Input is compressed.  Unsupported encoding method.\n"
+                     "Compression type code = %ld", compression);
+                     
+        if ( (compression == COMP_RLE4 || compression == COMP_RLE8) &&
+              headerP->rowOrder == TOPDOWN )                        
+            pm_error("Invalid BMP header.  Top-down images cannot be compressed.");
+
+        if ( (compression == COMP_RLE4 && headerP->cBitCount !=4) ||
+             (compression == COMP_RLE8 && headerP->cBitCount !=8) )                        
+            pm_error("Invalid BMP header.\n" 
+                     "Compression type (%s) disagrees with number of bits per pixel (%u).",
+                      compression == COMP_RLE4 ? "RLE4" : "RLE8",
+                      headerP->cBitCount);
+
+        headerP->compression = compression;             
+    }
+    /* And read the rest of the junk in the 40 byte header */
+    headerP->imageSize = GetLong(ifP);   /* ImageSize */
+    GetLong(ifP);   /* XpixelsPerMeter */
+    GetLong(ifP);   /* YpixelsPerMeter */
+    colorsused = GetLong(ifP);   /* ColorsUsed */
+    /* See comments in bmp.h for info about the definition of the following
+       word and its relationship to the color map size (*pcmapsize).
+    */
+    colorsimportant = GetLong(ifP);  /* ColorsImportant */
+
+    if (headerP->cBitCount <= 8) {
+        if (colorsused != 0) {
+            if (colorsused > 1 << headerP->cBitCount)
+                pm_error("Invalid BMP header.  Says %u bits per pixel, "
+                         "but %d colors used", 
+                         headerP->cBitCount, colorsused);
+            else
+                headerP->cmapsize = colorsused;
+        } else 
+            headerP->cmapsize = 1 << headerP->cBitCount;
+    } else if (headerP->cBitCount == 24 || 
+               headerP->cBitCount == 16 || 
+               headerP->cBitCount == 32)
+        headerP->cmapsize = 0;
+    else
+        pm_error("Unrecognized bits per pixel in Windows BMP file header: %d",
+                 headerP->cBitCount);
+}
+
+
+
+static unsigned int
+lsbZeroCount(unsigned int const mask)
+/*----------------------------------------------------------------------------
+   Return the number of consecutive zeroes in the mask 'mask', starting with
+   the least significant bit and going up.  E.g. for 0x20, it would be 5.
+   
+   Use GCC built-in when available.
+-----------------------------------------------------------------------------*/
+
+#if ( defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 304) )
+{
+      return ( mask==0 ? sizeof(mask)*8 : __builtin_ctz(mask) );
+}
+#else
+{
+      unsigned int i=0;
+    
+      while (((mask >> i) & 0x1) == 0 && i < sizeof(mask)*8)
+        ++i;
+
+      return i;
+
+}
+#endif
+
+
+static struct bitPosition
+bitPositionFromMask(long const bmpMask) {
+    struct bitPosition retval;
+
+    retval.shift = lsbZeroCount(bmpMask);
+    retval.mask  = bmpMask >> retval.shift;
+
+    return retval;
+}
+
+
+
+static void
+computeConventionalBgr(struct pixelformat * const fP,
+                       unsigned int         const bitCount) {
+
+    switch (bitCount) {
+    case 24:
+        fP->conventionalBgr =
+            fP->red.shift ==  0 && fP->red.mask == 0xFF &&
+            fP->grn.shift ==  8 && fP->grn.mask == 0xFF &&
+            fP->blu.shift == 16 && fP->blu.mask == 0xFF &&
+            fP->trn.mask == 0
+            ;
+        break;
+    case 32:
+        fP->conventionalBgr =
+            fP->red.shift ==  8  && fP->red.mask == 0xFF &&
+            fP->grn.shift == 16  && fP->grn.mask == 0xFF &&
+            fP->blu.shift == 24  && fP->blu.mask == 0xFF &&
+            fP->trn.mask == 0
+            ;
+        break;
+    default:
+        fP->conventionalBgr = FALSE;
+    }
+}
+
+
+
+static struct pixelformat
+defaultPixelformat(unsigned int const bitCount) {
+
+    struct pixelformat retval;
+
+    switch (bitCount) {
+    case 16:
+        retval.conventionalBgr = FALSE;
+        retval.red.shift = 10;
+        retval.grn.shift = 5;
+        retval.blu.shift = 0;
+        retval.trn.shift = 0;
+        retval.red.mask = 0x1f;  /* 5 bits */
+        retval.grn.mask = 0x1f;  /* 5 bits */
+        retval.blu.mask = 0x1f;  /* 5 bits */
+        retval.trn.mask = 0;
+        break;
+    case 24:
+    case 32:
+        retval.conventionalBgr = TRUE;
+        retval.red.shift = 16;
+        retval.grn.shift = 8;
+        retval.blu.shift = 0;
+        retval.trn.shift = 0;
+        retval.red.mask = 0xff;  /* 8 bits */
+        retval.grn.mask = 0xff;  /* 8 bits */
+        retval.blu.mask = 0xff;  /* 8 bits */
+        retval.trn.mask = 0;
+        break;
+    default:
+        /* colormapped - masks are undefined */
+        break;
+    }
+
+    return retval;
+}
+
+
+
+static void
+readV4InfoHeaderExtension(FILE *                 const ifP, 
+                          struct bmpInfoHeader * const headerP) {
+
+    if (headerP->bitFields) {
+        headerP->pixelformat.red = bitPositionFromMask(GetLong(ifP));
+        headerP->pixelformat.grn = bitPositionFromMask(GetLong(ifP));
+        headerP->pixelformat.blu = bitPositionFromMask(GetLong(ifP));
+        headerP->pixelformat.trn = bitPositionFromMask(GetLong(ifP));
+
+        computeConventionalBgr(&headerP->pixelformat, headerP->cBitCount);
+    } else
+        headerP->pixelformat = defaultPixelformat(headerP->cBitCount);
+
+    GetLong(ifP);  /* Color space */
+    GetCieXyzTriple(ifP);  /* Endpoints */
+    GetLong(ifP);  /* GammaRed */
+    GetLong(ifP);  /* GammaGreen */
+    GetLong(ifP);  /* GammaBlue */
+} 
+
+
+
+static void
+defaultV4InfoHeaderExtension(struct bmpInfoHeader * const headerP) {
+
+    headerP->pixelformat = defaultPixelformat(headerP->cBitCount);
+
+}
+
+
+
+static void
+readWindowsInfoHeader(FILE *                 const ifP, 
+                      unsigned int           const cInfoHeaderSize,
+                      struct bmpInfoHeader * const headerP) {
+
+    /* There are 3 major formats of Windows
+       BMP, identified by the 3 info header lengths.  The original
+       one is 40 bytes.  The "V4 header" is 108 bytes and was
+       new with Windows 95 and NT 4.0.  The "V5 header" is 124 bytes
+       and was new with Windows 98 and Windows 2000.
+    */
+    readWindowsBasic40ByteInfoHeader(ifP, headerP);
+
+    if (cInfoHeaderSize >= 108) 
+        readV4InfoHeaderExtension(ifP, headerP);
+    else 
+        defaultV4InfoHeaderExtension(headerP);
+
+    if (cInfoHeaderSize >= 124) {
+        /* Read off the V5 info header extension. */
+        GetLong(ifP);  /* Intent */
+        GetLong(ifP);  /* ProfileData */
+        GetLong(ifP);  /* ProfileSize */
+        GetLong(ifP);  /* Reserved */
+    }
+
+    pm_message("Windows BMP, %dx%dx%d",
+               headerP->cols, headerP->rows, headerP->cBitCount);
+}
+
+
+
+static void
+BMPreadinfoheader(FILE *                 const ifP, 
+                  unsigned int *         const bytesReadP,
+                  struct bmpInfoHeader * const headerP) {
+
+    unsigned int const cInfoHeaderSize = GetLong(ifP);
+
+    switch (cInfoHeaderSize) {
+    case 12:
+        readOs2InfoHeader(ifP, headerP);
+        break;
+    case 40: 
+    case 108:
+    case 124:
+        readWindowsInfoHeader(ifP, cInfoHeaderSize, headerP);
+        break;
+    default:
+        pm_error("%s: unknown Info Header size: %u bytes", 
+                 ifname, cInfoHeaderSize);
+        break;
+    }
+    *bytesReadP = cInfoHeaderSize;
+}
+
+
+
+static void
+BMPreadcolormap(FILE *         const ifP, 
+                int            const class, 
+                xel **         const colormapP, 
+                int            const cmapsize,
+                unsigned int * const bytesReadP) {
+/*----------------------------------------------------------------------------
+   Read the color map from the present position in the input BMP file
+   *ifP.
+
+   The map has 'cmapsize' entries in it.  cmapsize == 0 means there is
+   no color map.
+
+   We return a color map as *colormapP.  If there is no color map in the
+   BMP, this is just an arbitrary color map.
+ 
+   'class' is the class of BMP image - Windows or OS/2.
+-----------------------------------------------------------------------------*/
+
+    int i;
+
+    xel * colormap;
+    unsigned int bytesRead;
+
+    colormap = pnm_allocrow(MAX(1,cmapsize));
+    
+    bytesRead = 0;  /* initial value */
+
+    for (i = 0; i < cmapsize; ++i) {
+        /* There is a document that says the bytes are ordered R,G,B,Z,
+           but in practice it appears to be the following instead:
+        */
+        unsigned int r, g, b;
+        
+        b = GetByte(ifP);
+        g = GetByte(ifP);
+        r = GetByte(ifP);
+
+        PNM_ASSIGN(colormap[i], r, g, b);
+
+        bytesRead += 3;
+
+        if (class == C_WIN) {
+            GetByte(ifP);
+            bytesRead += 1;
+        }
+    }
+    *colormapP = colormap;
+    *bytesReadP = bytesRead;
+}
+
+
+
+static void
+extractBitFields(unsigned int       const rasterval,
+                 struct pixelformat const pixelformat,
+                 pixval             const maxval,
+                 pixval *           const rP,
+                 pixval *           const gP,
+                 pixval *           const bP,
+                 pixval *           const aP) {
+
+    unsigned int const rbits = 
+        (rasterval >> pixelformat.red.shift) & pixelformat.red.mask;
+    unsigned int const gbits = 
+        (rasterval >> pixelformat.grn.shift) & pixelformat.grn.mask;
+    unsigned int const bbits = 
+        (rasterval >> pixelformat.blu.shift) & pixelformat.blu.mask;
+    unsigned int const abits = 
+        (rasterval >> pixelformat.trn.shift) & pixelformat.trn.mask;
+    
+    *rP = (unsigned int) rbits * maxval / pixelformat.red.mask;
+    *gP = (unsigned int) gbits * maxval / pixelformat.blu.mask;
+    *bP = (unsigned int) bbits * maxval / pixelformat.grn.mask;
+    *aP = (unsigned int) abits * maxval / pixelformat.trn.mask;
+}        
+
+
+
+static void
+convertRow16(unsigned char      const bmprow[],
+             xel                      xelrow[],
+             int                const cols,
+             struct pixelformat const pixelformat) {
+    /* It's truecolor.  */
+
+    unsigned int col;
+    unsigned int cursor;
+    cursor = 0;
+    for (col=0; col < cols; ++col) {
+        unsigned short const rasterval = (unsigned short)
+            bmprow[cursor+1] << 8 | bmprow[cursor+0];
+
+        pixval r, g, b, a;
+
+        extractBitFields(rasterval, pixelformat, 255, &r, &g, &b, &a);
+
+        PNM_ASSIGN(xelrow[col], r, g, b);
+        
+        cursor += 2;
+    }
+}
+
+
+
+static void
+convertRow24(unsigned char      const bmprow[],
+             xel                      xelrow[],
+             int                const cols,
+             struct pixelformat const pixelformat) {
+    
+    /* It's truecolor */
+    /* There is a document that gives a much different format for
+       24 bit BMPs.  But this seems to be the de facto standard, and is,
+       with a little ambiguity and contradiction resolved, defined in the
+       Microsoft BMP spec.
+    */
+
+    unsigned int col;
+    unsigned int cursor;
+    
+    cursor = 0;
+    for (col = 0; col < cols; ++col) {
+        pixval r, g, b, a;
+
+        if (pixelformat.conventionalBgr) {
+            r = bmprow[cursor+2];
+            g = bmprow[cursor+1];
+            b = bmprow[cursor+0];
+            a = 0;
+        } else {
+            unsigned int const rasterval = 
+                (bmprow[cursor+0] << 16) +
+                (bmprow[cursor+1] << 8) +
+                (bmprow[cursor+2] << 0);
+            
+            extractBitFields(rasterval, pixelformat, 255, &r, &g, &b, &a);
+        }
+        PNM_ASSIGN(xelrow[col], r, g, b);
+        cursor += 3;
+    }
+} 
+
+
+
+static void
+convertRow32(unsigned char      const bmprow[],
+             xel                      xelrow[],
+             int                const cols,
+             struct pixelformat const pixelformat) {
+    
+    /* It's truecolor */
+
+    unsigned int col;
+    unsigned int cursor;
+    cursor = 0;
+    for (col = 0; col < cols; ++col) {
+        pixval r, g, b, a;
+
+        if (pixelformat.conventionalBgr) {
+            /* bmprow[cursor+3] is just padding */
+            r = bmprow[cursor+2];
+            g = bmprow[cursor+1];
+            b = bmprow[cursor+0];
+            a = 0;
+        } else {
+            unsigned int const rasterval = 
+                (bmprow[cursor+0] << 24) +
+                (bmprow[cursor+1] << 16) +
+                (bmprow[cursor+2] << 8) +
+                (bmprow[cursor+3] << 0);
+            
+            extractBitFields(rasterval, pixelformat, 255, &r, &g, &b, &a);
+        }
+
+        PNM_ASSIGN(xelrow[col], 
+                   bmprow[cursor+2], bmprow[cursor+1], bmprow[cursor+0]);
+        cursor += 4;
+    }
+} 
+
+
+
+static void
+convertRow(unsigned char      const bmprow[], 
+           xel                      xelrow[],
+           int                const cols, 
+           unsigned int       const cBitCount, 
+           struct pixelformat const pixelformat,
+           xel                const colormap[]
+           ) {
+/*----------------------------------------------------------------------------
+   Convert a row in raw BMP raster format bmprow[] to a row of xels xelrow[].
+
+   Use maxval 255 for the output xels.
+
+   The BMP image has 'cBitCount' bits per pixel.
+
+   If the image is colormapped, colormap[] is the colormap
+   (colormap[i] is the color with color index i).
+-----------------------------------------------------------------------------*/
+    if (cBitCount == 24) 
+        convertRow24(bmprow, xelrow, cols, pixelformat);
+    else if (cBitCount == 16) 
+        convertRow16(bmprow, xelrow, cols, pixelformat);
+    else if (cBitCount == 32) 
+        convertRow32(bmprow, xelrow, cols, pixelformat);
+    else if (cBitCount == 8) {            
+        /* It's a whole byte colormap index */
+        unsigned int col;
+        for (col = 0; col < cols; ++col)
+            xelrow[col] = colormap[bmprow[col]];
+    } else if (cBitCount < 8) {
+        /* It's a bit field color index */
+        unsigned char const mask = ( 1 << cBitCount ) - 1;
+
+        unsigned int col;
+
+        for (col = 0; col < cols; ++col) {
+            unsigned int const cursor = (col*cBitCount)/8;
+            unsigned int const shift = 8 - ((col*cBitCount) % 8) - cBitCount;
+            unsigned int const index = 
+                (bmprow[cursor] & (mask << shift)) >> shift;
+            xelrow[col] = colormap[index];
+        }
+    } else
+        pm_error("Internal error: invalid cBitCount in convertRow()");
+}
+
+
+
+static unsigned char **
+allocBMPraster(unsigned int const rows, unsigned int const bytesPerRow) {
+
+    unsigned int const storageSize = 
+        rows * sizeof(unsigned char *) + rows * bytesPerRow;        
+    unsigned char ** BMPraster;
+    unsigned int row;
+    unsigned char * startOfRows;
+
+    /* The raster array consists of an array of pointers to the rows
+       followed by the rows of bytes, in a single allocated chunk of storage.
+    */
+
+    if (UINT_MAX / (bytesPerRow + sizeof(unsigned char *)) < rows)
+        pm_error("raster is ridiculously large.");
+
+    BMPraster = (unsigned char **) malloc(storageSize);
+
+    if (BMPraster == NULL)
+        pm_error("Unable to allocate %u bytes for the BMP raster\n",
+                 storageSize);
+
+    startOfRows = (unsigned char *)(BMPraster + rows);
+
+    for (row = 0; row < rows; ++row) 
+        BMPraster[row] = startOfRows + row * bytesPerRow;
+
+    return BMPraster;
+}
+
+
+
+static void
+readrow(FILE *           const ifP,
+        unsigned int     const row,
+        unsigned int     const bytesPerRow,
+        unsigned char ** const BMPraster,
+        unsigned int *   const bytesReadP) {
+
+    size_t bytesRead;
+
+    assert(bytesPerRow > 0);
+    
+    bytesRead = fread(BMPraster[row], 1, bytesPerRow, ifP);
+
+    if (bytesRead < bytesPerRow) {
+        if (feof(ifP))
+            pm_error("End of file reading row %u of BMP raster.", row);
+        else 
+            pm_error("Error reading BMP raster.  Errno=%d (%s)",
+                     errno, strerror(errno));
+    }
+    *bytesReadP += bytesRead;
+}
+ 
+
+
+static void
+nibbleAlign(unsigned char * const ptr,
+            unsigned int    const nibbles){
+/*----------------------------------------------------------------------------
+  Shift data pointed by ptr one half byte toward the MSB (to the left).
+ 
+  Example:
+ 
+  (Numbers in hex, 8 nibbles)
+            5F 13 7E 89 A1
+   becomes  51 37 E8 9A 10
+-----------------------------------------------------------------------------*/
+    unsigned int const fullByteCount = (nibbles-1) / 2;
+    unsigned int i;
+              
+    ptr[0] = ptr[0] & ptr[1] >> 4;
+                                    
+    for (i = 0; i < fullByteCount; ++i)
+        ptr[i+1] = ptr[i+1] << 4 & ptr[i+2] >> 4;
+    
+    if (nibbles % 2 == 1)   /* if there is a final odd nibble */
+        ptr[fullByteCount+1] <<= 4; /* shift it a half byte */
+}
+
+
+           
+enum rleStatus { ABS_MODE, ENC_MODE, END_OF_ROW, END_OF_BMP, DELTA };
+
+static enum rleStatus
+readRLEcode(FILE *          const ifP,
+            unsigned int *  const cntP,
+            unsigned char * const codeP) {
+
+    unsigned short s;
+    enum rleStatus retval;
+    
+    s = GetBigShort(ifP);
+    
+    if      (s == 0) retval = END_OF_ROW;
+    else if (s == 1) retval = END_OF_BMP;
+    else if (s == 2) retval = DELTA;
+    else if (s < 256) {
+        if (cntP)
+            *cntP = s & 0xff;
+        retval = ABS_MODE;
+    } else {
+        if (cntP && codeP) {
+            *cntP  = (s >> 8) & 0xff;
+            *codeP = s & 0xff;
+        }
+        retval = ENC_MODE;
+    }
+    return retval;
+}
+
+
+
+static void
+readrowRLE(FILE *           const ifP,
+           unsigned int     const row,
+           unsigned int     const cols,
+           bool             const lastrow,
+           unsigned long    const compression,
+           unsigned char ** const BMPraster,
+           unsigned int  *  const bytesReadP) {
+
+    bool const RLE4 = (compression == COMP_RLE4);
+    int  const pixelsPerRowMargin = RLE4 ? cols % 2 : 0;
+
+    char const err_decode[] = 
+        "Error while decoding compressed BMP image.  "
+        "%s.  Row: %u  Pixel: %u" ; 
+     
+    unsigned int totalBytesRead;
+    unsigned int pixelsRead;
+
+    /* There are RLE4 images with rows coded up the byte boundary, resulting
+       in each row one pixel larger than the column length stated in the
+       BMP info header (header.cols) when the column length is odd.
+       pixelsPerRowMargin is a "wart" to provide for this case.
+    */
+
+    totalBytesRead = 0;  /* Initial value */
+    pixelsRead = 0;      /* Initial value */
+
+    while (TRUE) {
+        unsigned int n;
+            /* decompressed bytes already read; current write point */ 
+        unsigned int cnt;
+        unsigned char code;
+
+        n = RLE4 ? (pixelsRead + 1) / 2 : pixelsRead;
+
+        switch (readRLEcode(ifP, &cnt, &code)) {
+        case ENC_MODE: {
+            unsigned int const byteCnt = RLE4 ? (cnt + 1) /2 : cnt;
+            unsigned int i; 
+
+            if (pixelsRead + cnt > cols + pixelsPerRowMargin)
+                pm_error(err_decode,  "Too many pixels in encoded mode",
+                         row, pixelsRead ); 
+                 
+            for (i = 0; i < byteCnt; ++i)
+                BMPraster[row][n+i] = code;
+                 
+            if (RLE4 && pixelsRead % 2 == 1)
+                /* previous read ended odd */
+                nibbleAlign(&BMPraster[row][n-1], cnt); 
+            
+            pixelsRead += cnt;
+            totalBytesRead += 2;
+        } break;
+        
+        case ABS_MODE: {
+            unsigned int cmpBytesRead; /* compressed bytes read */
+            /* align read-end to 16 bit boundary */
+            unsigned int const bytesToRead =
+                RLE4 ? (cnt + 3) / 4 * 2 : (cnt + 1) / 2 * 2;
+
+            if (pixelsRead + cnt > cols + pixelsPerRowMargin)
+                pm_error(err_decode,  "Too many pixels in absolute mode",
+                         row, pixelsRead); 
+
+            cmpBytesRead = fread(&BMPraster[row][n], 
+                                 sizeof(char), bytesToRead, ifP);
+
+            if (cmpBytesRead < bytesToRead) {
+                if (feof(ifP))
+                    pm_error("End of file reading row %u "
+                             "of compressed BMP raster.", row);
+                else 
+                    pm_error("Error reading BMP raster.  Errno=%d (%s)",
+                             errno, strerror(errno));
+            }
+            if (RLE4 && pixelsRead % 2 == 1) /* previous read ended odd */
+                nibbleAlign(&BMPraster[row][n-1], cnt); 
+    
+            pixelsRead += cnt;
+            totalBytesRead += cmpBytesRead + 2;
+        } break;
+            
+        case END_OF_ROW: {
+            if (cols == pixelsRead ||
+                cols + pixelsPerRowMargin == pixelsRead) {
+                if (!lastrow) {
+                    *bytesReadP += totalBytesRead + 2;
+                    return;
+                } else if (readRLEcode(ifP, NULL, NULL) == END_OF_BMP) { 
+                    *bytesReadP += totalBytesRead +4;
+                    return;
+                } else
+                    /* lastrow and END_OF_BITMAP not detected */
+                    pm_error(err_decode,  "End of bitmap not marked",
+                             row, pixelsRead ); 
+            } else
+                pm_error(err_decode,  "Premature end of row",
+                         row, pixelsRead);
+        } break;  
+
+        case END_OF_BMP: {
+            if (lastrow && (cols == pixelsRead ||
+                            cols + pixelsPerRowMargin == pixelsRead)){
+                *bytesReadP += totalBytesRead + 2;
+                return;
+            } else
+                pm_error(err_decode,  "Premature end of bitmap",
+                         row, pixelsRead );
+        } break;
+
+        case DELTA: {
+            pm_error(err_decode,
+                     "Delta code in compressed BMP image.  "
+                     "This program does not process deltas.",
+                     row, pixelsRead);
+        } break;
+         
+        default:
+            pm_error("Internal error processing RLE code in row %u", row);
+        }
+   }
+}
+
+
+
+static void
+BMPreadraster(FILE *            const ifP, 
+              unsigned int      const cols, 
+              unsigned int      const rows, 
+              enum rowOrder     const rowOrder,
+              unsigned int      const cBitCount, 
+              unsigned long int const compression,
+              unsigned char *** const BMPrasterP, 
+              unsigned int *    const bytesReadP) {
+
+    unsigned int const bytesPerRow =
+        (compression == COMP_RLE4) ? cols / 2 + 2 :
+        (compression == COMP_RLE8) ? cols + 1 :
+        ((cols * cBitCount + 31) / 32) * 4;
+        /* A BMP raster row is a multiple of 4 bytes, padded on the right
+           with don't cares.
+        */
+    unsigned char ** BMPraster;
+
+    BMPraster = allocBMPraster(rows, bytesPerRow);
+
+    *bytesReadP = 0;
+
+    /* row order BOTTOMUP is by far the most common case - the bottom 
+       line is first in the file, the top line last.
+       
+       We have never actually seen TOPDOWN, except in a Microsoft spec
+    */
+    
+    switch(compression){
+    case COMP_RGB:
+    case COMP_BITFIELDS: {
+        unsigned int i;
+        for (i = 0; i < rows; ++i)
+            readrow(ifP, rowOrder == TOPDOWN ? i : rows - i - 1, 
+                    bytesPerRow, BMPraster, bytesReadP);
+    } break;
+    case COMP_RLE4: 
+    case COMP_RLE8: {
+        unsigned int i;
+        /* Read all rows except last */
+        for (i = 0; i < rows - 1; ++i){
+            readrowRLE(ifP, rowOrder == TOPDOWN ? i : rows - i - 1, 
+                       cols, FALSE, compression, BMPraster, bytesReadP);
+        }
+        /* Read last row */
+        readrowRLE(ifP, rowOrder == TOPDOWN ? i : rows - i - 1, 
+                   cols, TRUE,  compression, BMPraster, bytesReadP);
+    } break;             
+    default:       
+        pm_error("The BMP specifies a compression scheme we don't "
+                 "recognize.  Code= %lu", compression);
+    }
+
+    *BMPrasterP = BMPraster;
+}
+
+
+
+static void
+reportHeader(struct bmpInfoHeader const header,
+             unsigned int         const offBits) {
+             
+    pm_message("BMP image header says:");
+    pm_message("  Class of BMP: %s", 
+               header.class == C_WIN ? "Windows" : 
+               header.class == C_OS2 ? "OS/2" :
+               "???");
+    pm_message("  Width: %d pixels", header.cols);
+    pm_message("  Height: %d pixels", header.rows);
+    pm_message("  Depth: %d planes", header.cPlanes);
+    pm_message("  Row order: %s", 
+               header.rowOrder == BOTTOMUP ? "bottom up" : "top down");
+    pm_message("  Byte offset of raster within file: %u", offBits);
+    pm_message("  Bits per pixel in raster: %u", header.cBitCount);
+    pm_message("  Compression: %s", 
+               header.compression == COMP_RGB ? "none" :
+               header.compression == COMP_RLE4 ? "4 bit run-length coding" :
+               header.compression == COMP_RLE8 ? "8 bit run-length coding" :
+               header.compression == COMP_BITFIELDS ? "none" :
+               header.compression == COMP_JPEG ? "JPEG (not supported)" :
+               header.compression == COMP_PNG ? "PNG (not supported)" :
+               "???");                
+    pm_message("  Colors in color map: %d", header.cmapsize);
+}        
+
+
+
+static void
+analyzeColors(xel          const colormap[],
+              unsigned int const cmapsize,
+              xelval       const maxval,
+              bool *       const grayPresentP,
+              bool *       const colorPresentP) {
+    
+    if (cmapsize == 0) {
+        /* No colormap, and we're not about to search the entire raster,
+           so we just assume it's full color 
+        */
+        *colorPresentP = TRUE;
+        *grayPresentP = TRUE;
+    } else {
+        unsigned int i;
+
+        *colorPresentP = FALSE;  /* initial assumption */
+        *grayPresentP = FALSE;   /* initial assumption */
+        for (i = 0; i < cmapsize; ++i) {
+            if (PPM_ISGRAY(colormap[i])) {
+                if (PPM_GETR(colormap[i]) != 0 &&
+                    PPM_GETR(colormap[i]) != maxval)
+                    *grayPresentP = TRUE;
+            } else
+                *colorPresentP = TRUE;
+        }
+    }
+}
+
+
+
+static void
+warnIfOffBitsWrong(struct bmpInfoHeader const BMPheader,
+                   unsigned int         const offBits) {
+
+    if (offBits != BMPoffbits(BMPheader.class, BMPheader.cBitCount, 
+                              BMPheader.cmapsize)) {
+
+        pm_message("warning: the BMP header says the raster starts "
+                   "at offset %u bytes into the file (offbits), "
+                   "but that there are %u bytes of information before "
+                   "the raster.  This inconsistency probably means the "
+                   "input file is not a legal BMP file and is unusable.",
+                   offBits,
+                   BMPoffbits(BMPheader.class, BMPheader.cBitCount, 
+                              BMPheader.cmapsize));
+    }
+}
+
+
+
+static void
+readColorMap(FILE *               const ifP,
+             struct bmpInfoHeader const BMPheader,
+             xel **               const colorMapP,
+             unsigned int *       const posP) {
+
+    unsigned int bytesRead;
+
+    BMPreadcolormap(ifP, BMPheader.class, 
+                    colorMapP, BMPheader.cmapsize, &bytesRead);
+
+    *posP += bytesRead;
+
+    if (bytesRead != BMPlencolormap(BMPheader.class, BMPheader.cBitCount, 
+                                    BMPheader.cmapsize)) {
+
+        pm_message("warning: %u-byte RGB table, expected %u bytes",
+                   bytesRead,
+                   BMPlencolormap(BMPheader.class, BMPheader.cBitCount, 
+                                  BMPheader.cmapsize));
+}
+}
+
+
+
+static void
+readRaster(FILE *               const ifP,
+           struct bmpInfoHeader const BMPheader,
+           unsigned char ***    const BMPrasterP, 
+           unsigned int *       const posP) {
+
+    unsigned int bytesRead;
+
+    BMPreadraster(ifP, BMPheader.cols, BMPheader.rows, BMPheader.rowOrder,
+                  BMPheader.cBitCount, BMPheader.compression,
+                  BMPrasterP, &bytesRead);
+
+    *posP += bytesRead;
+}
+
+
+
+static void
+warnIfBadFileSize(struct bmpInfoHeader const BMPheader,
+                  unsigned int         const pos) {
+
+    unsigned int const expectedSize =
+        BMPlenfileGen(BMPheader.class, BMPheader.cBitCount, 
+                      BMPheader.cmapsize, BMPheader.cols,
+                      BMPheader.rows, BMPheader.imageSize,
+                      BMPheader.compression);
+
+    if (pos != expectedSize)
+        pm_message("warning: read %u bytes, expected to read %u bytes",
+                   pos, expectedSize);
+}
+
+
+
+static void
+readBmp(FILE *               const ifP, 
+        unsigned char ***    const BMPrasterP, 
+        int *                const colsP, 
+        int *                const rowsP,
+        bool *               const grayPresentP, 
+        bool *               const colorPresentP,
+        unsigned int *       const cBitCountP, 
+        struct pixelformat * const pixelformatP,
+        xel **               const colormapP,
+        bool                 const verbose) {
+
+    xel * colormap;  /* malloc'ed */
+    unsigned int pos;
+        /* Current byte position in the BMP file */
+
+    /* The following are all information from the BMP headers */
+    
+    unsigned int offBits;
+        /* Byte offset into file of raster */
+    struct bmpInfoHeader BMPheader;
+
+    pos = 0;  /* Starting at the beginning ... */
+    { 
+        unsigned int bytesRead;
+        BMPreadfileheader(ifP, &bytesRead, &offBits);
+        pos += bytesRead;
+    }
+    {
+        unsigned int bytesRead;
+        BMPreadinfoheader(ifP, &bytesRead, &BMPheader);
+        if (verbose)
+            pm_message("Read %u bytes of header", bytesRead);
+        pos += bytesRead;
+    }
+
+    if (verbose) 
+        reportHeader(BMPheader, offBits);
+
+    warnIfOffBitsWrong(BMPheader, offBits);
+
+    readColorMap(ifP, BMPheader, &colormap, &pos);
+
+    analyzeColors(colormap, BMPheader.cmapsize, bmpMaxval, 
+                  grayPresentP, colorPresentP);
+
+    readOffBytes(ifP, offBits - pos);
+
+    pos = offBits;
+
+    readRaster(ifP, BMPheader, BMPrasterP, &pos);
+
+    warnIfBadFileSize(BMPheader, pos);
+    
+    if (fgetc(ifP) != EOF)
+        pm_message("warning: some image data remains unread.");
+    
+    *colsP        = BMPheader.cols;
+    *rowsP        = BMPheader.rows;
+    *cBitCountP   = BMPheader.cBitCount;
+    *pixelformatP = BMPheader.pixelformat;
+    *colormapP    = colormap;
+}
+
+
+
+static void
+writeRasterGen(unsigned char **   const BMPraster,
+               int                const cols, 
+               int                const rows, 
+               int                const format,
+               unsigned int       const cBitCount, 
+               struct pixelformat const pixelformat,
+               xel                const colormap[]) {
+/*----------------------------------------------------------------------------
+  Write the PNM raster to Standard Output, corresponding to the raw BMP
+  raster BMPraster.  Write the raster assuming the PNM image has 
+  dimensions 'cols' by 'rows' and format 'format', with maxval 255.
+
+  The BMP image has 'cBitCount' bits per pixel, arranged in format
+  'pixelformat'.
+
+  If the image is colormapped, colormap[] is the colormap
+  (colormap[i] is the color with color index i).
+  
+  writeRasterPbm() is faster for a PBM image.
+-----------------------------------------------------------------------------*/
+    xel * xelrow;
+    unsigned int row;
+
+    xelrow = pnm_allocrow(cols);
+
+    for (row = 0; row < rows; ++row) {
+        convertRow(BMPraster[row], xelrow, cols, cBitCount, pixelformat,
+                   colormap);
+        pnm_writepnmrow(stdout, xelrow, cols, bmpMaxval, format, FALSE);
+    }
+    pnm_freerow(xelrow);
+}
+
+
+
+static void
+writeRasterPbm(unsigned char ** const BMPraster,
+               int              const cols, 
+               int              const rows, 
+               xel              const colormap[]) {
+/*----------------------------------------------------------------------------
+  Write the PBM raster to Standard Output corresponding to the raw BMP
+  raster BMPraster.  Write the raster assuming the PBM image has 
+  dimensions 'cols' by 'rows'.
+
+  The BMP image has 'cBitCount' bits per pixel, arranged in format
+  'pixelformat'.
+
+  The image must be colormapped; colormap[] is the colormap
+  (colormap[i] is the color with color index i).  We cannot handle the
+  abnormal case in which colormap[0] and colormap[1] have the same
+  value (i.e. both white or both black.)
+  
+  We destroy *BMPraster as a side effect.
+-----------------------------------------------------------------------------*/
+    unsigned int const charBits = (sizeof(unsigned char) * 8);
+        /* Number of bits in a character */
+    unsigned int const colChars = pbm_packed_bytes(cols);
+    
+    int row;
+    enum colorFormat {BlackWhite, WhiteBlack};
+    enum colorFormat colorformat;
+                  
+    if (PPM_GETR(colormap[0]) > 0)
+        colorformat = WhiteBlack;
+    else                  
+        colorformat = BlackWhite;
+        
+    for (row=0; row < rows; ++row){
+        unsigned char * const bitrow = BMPraster[row]; 
+
+        if (colorformat == BlackWhite) {
+            unsigned int i;
+            for (i = 0; i < colChars; ++i) 
+                bitrow[i] = ~bitrow[i]; /* flip all pixels */ 
+        }   
+            
+        if (cols % 8 > 0) {
+            /* adjust final partial byte */
+            bitrow[colChars-1] >>= charBits - cols % charBits;
+            bitrow[colChars-1] <<= charBits - cols % charBits;
+        }
+        
+        pbm_writepbmrow_packed(stdout, bitrow, cols, FALSE);
+    }
+}
+
+
+
+int
+main(int argc, char ** argv) {
+
+    struct cmdline_info cmdline;
+    FILE * ifP;
+    int outputType;
+
+    bool grayPresent, colorPresent;
+        /* These tell whether the image contains shades of gray other than
+           black and white and whether it has colors other than black, white,
+           and gray.
+        */
+    int cols, rows;
+    unsigned char **BMPraster;
+        /* The raster part of the BMP image, as a row x column array, with
+           each element being a raw byte from the BMP raster.  Note that
+           BMPraster[0] is really Row 0 -- the top row of the image, even
+           though the bottom row comes first in the BMP format.
+        */
+    unsigned int cBitCount;
+        /* Number of bits in BMP raster for each pixel */
+    struct pixelformat pixelformat;
+        /* Format of the raster bits for a single pixel */
+    xel * colormap;
+        /* Malloc'ed colormap (palette) from the BMP.  Contents of map
+           undefined if not a colormapped BMP.
+         */
+
+    pnm_init(&argc, argv);
+
+    parse_command_line(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.input_filespec);
+    if (streq(cmdline.input_filespec, "-"))
+        ifname = "Standard Input";
+    else 
+        ifname = cmdline.input_filespec;
+
+    readBmp(ifP, &BMPraster, &cols, &rows, &grayPresent, &colorPresent, 
+            &cBitCount, &pixelformat, &colormap,
+            cmdline.verbose);
+    pm_close(ifP);
+
+    if (colorPresent) {
+        outputType = PPM_TYPE;
+        pm_message("WRITING PPM IMAGE");
+    } else if (grayPresent) {
+        outputType = PGM_TYPE;
+        pm_message("WRITING PGM IMAGE");
+    } else {
+        outputType = PBM_TYPE;
+        pm_message("WRITING PBM IMAGE");
+    }
+    
+    if (outputType == PBM_TYPE  && cBitCount == 1){
+        pbm_writepbminit(stdout, cols, rows, FALSE);
+        writeRasterPbm(BMPraster, cols, rows, colormap);
+    } else {
+        pnm_writepnminit(stdout, cols, rows, bmpMaxval, outputType, FALSE);
+        writeRasterGen(BMPraster, cols, rows, outputType, cBitCount,
+                       pixelformat, colormap); 
+    }
+    free(colormap);
+    free(BMPraster);
+
+    return 0;
+}
+
+
diff --git a/converter/other/cameratopam/COPYRIGHT b/converter/other/cameratopam/COPYRIGHT
new file mode 100644
index 00000000..efc77975
--- /dev/null
+++ b/converter/other/cameratopam/COPYRIGHT
@@ -0,0 +1,35 @@
+Cameratopam is derived from Dave Coffin's Dcraw program.
+
+Bryan Henderson derived it in April 2005.  Bryan mainly just split it up
+into manageable pieces and replaced the parts that generate Netpbm formats
+and the command line parser.
+
+Dcraw and therefore Cameratopam is Copyright 1997-2005 by Dave Coffin,
+dcoffin a cybercom o net, and Dave licenses it to the public freely,
+except that the Foveon code (in foveon.c in Netpbm) is copyrighted by
+someone else (I don't know who).
+
+Dave licenses his copyright to the public freely, with the words, "Any
+code not declared GPL is free for all uses."  Only the code in
+foveon.c in Netpbm was declared GPL in Dave's work.
+
+The foveon code is licensed to the public by its unknown copyright owner
+under GPL.
+
+The Netpbm maintainer distributes the entire Cameratopam package under
+GPL.
+
+Dave's distribution to Bryan may be a copyright violation, but
+strangely enough, Bryan's distribution to others is not.  I.e.  you DO
+have license from every copyright owner to do GPL-compatible things
+with this code.
+
+The problem with Dave's distribution is that he combined the Foveon
+code and his code into a single work and distributed it with GPL terms
+applying only to the Foveon code.  The combined work may be a
+derivative work of the Foveon code and therefore cannot be distributed
+with the permission of the Foveon copyright owner, who gives that
+permission only on the condition that the distributor license the
+entire derivative work under GPL.  Dave did not do that.  He did not,
+for example, prohibit Bryan from distributing Dave's part of the code
+in object code only format.
diff --git a/converter/other/cameratopam/Makefile b/converter/other/cameratopam/Makefile
new file mode 100644
index 00000000..c50c9176
--- /dev/null
+++ b/converter/other/cameratopam/Makefile
@@ -0,0 +1,37 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/other/cameratopam
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+ifneq ($(JPEGLIB),NONE)
+  ifneq ($(JPEGHDR_DIR)x,x)
+    INCLUDES += -I$(JPEGHDR_DIR)
+    CFLAGS += -DHAVE_JPEG
+  endif
+endif
+
+include $(BUILDDIR)/Makefile.config
+
+
+.PHONY: all
+all: cameratopam
+
+OBJECTS = util.o identify.o cameratopam.o camera.o foveon.o decode.o \
+	canon.o ljpeg.o dng.o
+
+MERGE_OBJECTS =
+
+BINARIES = cameratopam
+MERGEBINARIES = 
+SCRIPTS = 
+
+include $(SRCDIR)/Makefile.common
+
+cameratopam: $(OBJECTS) $(NETPBMLIB) $(LIBOPT)
+	$(LD) -o $@ $(LDFLAGS) \
+          $(OBJECTS) $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR)) \
+	  $(MATHLIB) $(LDLIBS) \
+	  $(RPATH) $(LADD)
+
diff --git a/converter/other/cameratopam/bayer.h b/converter/other/cameratopam/bayer.h
new file mode 100644
index 00000000..7ac06ec3
--- /dev/null
+++ b/converter/other/cameratopam/bayer.h
@@ -0,0 +1,43 @@
+/*
+   In order to inline this calculation, I make the risky
+   assumption that all filter patterns can be described
+   by a repeating pattern of eight rows and two columns
+
+   Return values are either 0/1/2/3 = G/M/C/Y or 0/1/2/3 = R/G1/B/G2
+ */
+#define FC(row,col) \
+    (filters >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3)
+
+#define BAYER(row,col) \
+    image[((row) >> shrink)*iwidth + ((col) >> shrink)][FC(row,col)]
+
+/*
+    PowerShot 600   PowerShot A50   PowerShot Pro70 Pro90 & G1
+    0xe1e4e1e4: 0x1b4e4b1e: 0x1e4b4e1b: 0xb4b4b4b4:
+
+      0 1 2 3 4 5     0 1 2 3 4 5     0 1 2 3 4 5     0 1 2 3 4 5
+    0 G M G M G M   0 C Y C Y C Y   0 Y C Y C Y C   0 G M G M G M
+    1 C Y C Y C Y   1 M G M G M G   1 M G M G M G   1 Y C Y C Y C
+    2 M G M G M G   2 Y C Y C Y C   2 C Y C Y C Y
+    3 C Y C Y C Y   3 G M G M G M   3 G M G M G M
+            4 C Y C Y C Y   4 Y C Y C Y C
+    PowerShot A5    5 G M G M G M   5 G M G M G M
+    0x1e4e1e4e: 6 Y C Y C Y C   6 C Y C Y C Y
+            7 M G M G M G   7 M G M G M G
+      0 1 2 3 4 5
+    0 C Y C Y C Y
+    1 G M G M G M
+    2 C Y C Y C Y
+    3 M G M G M G
+
+   All RGB cameras use one of these Bayer grids:
+
+    0x16161616: 0x61616161: 0x49494949: 0x94949494:
+
+      0 1 2 3 4 5     0 1 2 3 4 5     0 1 2 3 4 5     0 1 2 3 4 5
+    0 B G B G B G   0 G R G R G R   0 G B G B G B   0 R G R G R G
+    1 G R G R G R   1 B G B G B G   1 R G R G R G   1 G B G B G B
+    2 B G B G B G   2 G R G R G R   2 G B G B G B   2 R G R G R G
+    3 G R G R G R   3 B G B G B G   3 R G R G R G   3 G B G B G B
+ */
+
diff --git a/converter/other/cameratopam/camera.c b/converter/other/cameratopam/camera.c
new file mode 100644
index 00000000..98d6d37a
--- /dev/null
+++ b/converter/other/cameratopam/camera.c
@@ -0,0 +1,1781 @@
+#define _BSD_SOURCE
+    /* Make sure strcasecmp is in string.h */
+#define _XOPEN_SOURCE
+    /* Make sure putenv is in stdlib.h */
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+
+#ifdef HAVE_JPEG
+#include <jpeglib.h>
+#endif
+
+#include "pm.h"
+#include "mallocvar.h"
+
+#include "global_variables.h"
+#include "util.h"
+#include "decode.h"
+#include "bayer.h"
+#include "ljpeg.h"
+#include "dng.h"
+
+#include "camera.h"
+
+#if HAVE_INT64
+   typedef int64_t INT64;
+   static bool const have64BitArithmetic = true;
+#else
+   /* We define INT64 to something that lets the code compile, but we
+      should not execute any INT64 code, because it will get the wrong
+      result.
+   */
+   typedef int INT64;
+   static bool const have64BitArithmetic = false;
+#endif
+
+#ifndef LONG_BIT
+#define LONG_BIT (8 * sizeof (long))
+#endif
+
+#define FORC3 for (c=0; c < 3; c++)
+#define FORC4 for (c=0; c < colors; c++)
+
+static void 
+merror (const void *ptr, const char *where) 
+{
+    if (ptr == NULL)
+        pm_error ("Out of memory in %s", where);
+}
+
+
+
+
+static void  
+adobe_copy_pixel (int row, int col, unsigned short **rp, bool use_secondary)
+{
+  unsigned r=row, c=col;
+
+  if (fuji_secondary && use_secondary) (*rp)++;
+  if (filters) {
+    if (fuji_width) {
+      r = row + fuji_width - 1 - (col >> 1);
+      c = row + ((col+1) >> 1);
+    }
+    if (r < height && c < width)
+      BAYER(r,c) = **rp < 0x1000 ? curve[**rp] : **rp;
+    *rp += 1 + fuji_secondary;
+  } else
+    for (c=0; c < tiff_samples; c++) {
+      image[row*width+col][c] = **rp < 0x1000 ? curve[**rp] : **rp;
+      (*rp)++;
+    }
+  if (fuji_secondary && use_secondary) (*rp)--;
+}
+
+void
+adobe_dng_load_raw_lj()
+{
+  int save, twide, trow=0, tcol=0, jrow, jcol;
+  struct jhead jh;
+  unsigned short *rp;
+
+  while (1) {
+    save = ftell(ifp);
+    fseek (ifp, get4(ifp), SEEK_SET);
+    if (!ljpeg_start (ifp, &jh)) break;
+    if (trow >= raw_height) break;
+    if (jh.high > raw_height-trow)
+    jh.high = raw_height-trow;
+    twide = jh.wide;
+    if (filters) twide *= jh.clrs;
+    else         colors = jh.clrs;
+    if (fuji_secondary) twide /= 2;
+    if (twide > raw_width-tcol)
+    twide = raw_width-tcol;
+
+    for (jrow=0; jrow < jh.high; jrow++) {
+      ljpeg_row (&jh);
+      for (rp=jh.row, jcol=0; jcol < twide; jcol++)
+    adobe_copy_pixel (trow+jrow, tcol+jcol, &rp, use_secondary);
+    }
+    fseek (ifp, save+4, SEEK_SET);
+    if ((tcol += twide) >= raw_width) {
+      tcol = 0;
+      trow += jh.high;
+    }
+    free (jh.row);
+  }
+}
+
+
+
+void
+adobe_dng_load_raw_nc()
+{
+    unsigned short *pixel, *rp;
+    int row, col;
+
+    pixel = calloc (raw_width * tiff_samples, sizeof *pixel);
+    merror (pixel, "adobe_dng_load_raw_nc()");
+    for (row=0; row < raw_height; row++) {
+        read_shorts (ifp, pixel, raw_width * tiff_samples);
+        for (rp=pixel, col=0; col < raw_width; col++)
+            adobe_copy_pixel (row, col, &rp, use_secondary);
+    }
+    free (pixel);
+}
+
+
+
+static int nikon_curve_offset;
+
+void
+nikon_compressed_load_raw(void)
+{
+    static const unsigned char nikon_tree[] = {
+        0,1,5,1,1,1,1,1,1,2,0,0,0,0,0,0,
+        5,4,3,6,2,7,1,0,8,9,11,10,12
+    };
+    int csize, row, col, i, diff;
+    unsigned short vpred[4], hpred[2], *curve;
+
+    init_decoder();
+    make_decoder (nikon_tree, 0);
+
+    fseek (ifp, nikon_curve_offset, SEEK_SET);
+    read_shorts (ifp, vpred, 4);
+    csize = get2(ifp);
+    curve = calloc (csize, sizeof *curve);
+    merror (curve, "nikon_compressed_load_raw()");
+    read_shorts (ifp, curve, csize);
+
+    fseek (ifp, data_offset, SEEK_SET);
+    getbits(ifp, -1);
+
+    for (row=0; row < height; row++)
+        for (col=0; col < raw_width; col++)
+        {
+            diff = ljpeg_diff (first_decode);
+            if (col < 2) {
+                i = 2*(row & 1) + (col & 1);
+                vpred[i] += diff;
+                hpred[col] = vpred[i];
+            } else
+                hpred[col & 1] += diff;
+            if ((unsigned) (col-left_margin) >= width) continue;
+            diff = hpred[col & 1];
+            if (diff >= csize) diff = csize-1;
+            BAYER(row,col-left_margin) = curve[diff];
+        }
+    maximum = curve[csize-1];
+    free (curve);
+}
+
+void
+nikon_load_raw()
+{
+  int irow, row, col, i;
+
+  getbits(ifp, -1);
+  for (irow=0; irow < height; irow++) {
+    row = irow;
+    if (model[0] == 'E') {
+      row = irow * 2 % height + irow / (height/2);
+      if (row == 1 && atoi(model+1) < 5000) {
+    fseek (ifp, 0, SEEK_END);
+    fseek (ifp, ftell(ifp)/2, SEEK_SET);
+    getbits(ifp, -1);
+      }
+    }
+    for (col=0; col < raw_width; col++) {
+      i = getbits(ifp, 12);
+      if ((unsigned) (col-left_margin) < width)
+    BAYER(row,col-left_margin) = i;
+      if (tiff_data_compression == 34713 && (col % 10) == 9)
+    getbits(ifp, 8);
+    }
+  }
+}
+
+/*
+   Figure out if a NEF file is compressed.  These fancy heuristics
+   are only needed for the D100, thanks to a bug in some cameras
+   that tags all images as "compressed".
+ */
+int
+nikon_is_compressed()
+{
+  unsigned char test[256];
+  int i;
+
+  if (tiff_data_compression != 34713)
+    return 0;
+  if (strcmp(model,"D100"))
+    return 1;
+  fseek (ifp, data_offset, SEEK_SET);
+  fread (test, 1, 256, ifp);
+  for (i=15; i < 256; i+=16)
+    if (test[i]) return 1;
+  return 0;
+}
+
+/*
+   Returns 1 for a Coolpix 990, 0 for a Coolpix 995.
+ */
+int
+nikon_e990()
+{
+  int i, histo[256];
+  const unsigned char often[] = { 0x00, 0x55, 0xaa, 0xff };
+
+  memset (histo, 0, sizeof histo);
+  fseek (ifp, 2064*1540*3/4, SEEK_SET);
+  for (i=0; i < 2000; i++)
+    histo[fgetc(ifp)]++;
+  for (i=0; i < 4; i++)
+    if (histo[often[i]] > 400)
+      return 1;
+  return 0;
+}
+
+/*
+   Returns 1 for a Coolpix 2100, 0 for anything else.
+ */
+int
+nikon_e2100()
+{
+  unsigned char t[12];
+  int i;
+
+  fseek (ifp, 0, SEEK_SET);
+  for (i=0; i < 1024; i++) {
+    fread (t, 1, 12, ifp);
+    if (((t[2] & t[4] & t[7] & t[9]) >> 4
+    & t[1] & t[6] & t[8] & t[11] & 3) != 3)
+      return 0;
+  }
+  return 1;
+}
+
+/*
+   Separates a Pentax Optio 33WR from a Nikon E3700.
+ */
+int
+pentax_optio33()
+{
+  int i, sum[] = { 0, 0 };
+  unsigned char tail[952];
+
+  fseek (ifp, -sizeof tail, SEEK_END);
+  fread (tail, 1, sizeof tail, ifp);
+  for (i=0; i < sizeof tail; i++)
+    sum[(i>>2) & 1] += tail[i];
+  return sum[0] < sum[1]*4;
+}
+
+/*
+   Separates a Minolta DiMAGE Z2 from a Nikon E4300.
+ */
+int
+minolta_z2()
+{
+  int i;
+  char tail[424];
+
+  fseek (ifp, -sizeof tail, SEEK_END);
+  fread (tail, 1, sizeof tail, ifp);
+  for (i=0; i < sizeof tail; i++)
+    if (tail[i]) return 1;
+  return 0;
+}
+
+void
+nikon_e2100_load_raw()
+{
+  unsigned char   data[3432], *dp;
+  unsigned short pixel[2288], *pix;
+  int row, col;
+
+  for (row=0; row <= height; row+=2) {
+    if (row == height) {
+      fseek (ifp, ((width==1616) << 13) - (-ftell(ifp) & -2048), SEEK_SET);
+      row = 1;
+    }
+    fread (data, 1, width*3/2, ifp);
+    for (dp=data, pix=pixel; pix < pixel+width; dp+=12, pix+=8) {
+      pix[0] = (dp[2] >> 4) + (dp[ 3] << 4);
+      pix[1] = (dp[2] << 8) +  dp[ 1];
+      pix[2] = (dp[7] >> 4) + (dp[ 0] << 4);
+      pix[3] = (dp[7] << 8) +  dp[ 6];
+      pix[4] = (dp[4] >> 4) + (dp[ 5] << 4);
+      pix[5] = (dp[4] << 8) +  dp[11];
+      pix[6] = (dp[9] >> 4) + (dp[10] << 4);
+      pix[7] = (dp[9] << 8) +  dp[ 8];
+    }
+    for (col=0; col < width; col++)
+      BAYER(row,col) = (pixel[col] & 0xfff);
+  }
+}
+
+void
+nikon_e950_load_raw()
+{
+  int irow, row, col;
+
+  getbits(ifp, -1);
+  for (irow=0; irow < height; irow++) {
+    row = irow * 2 % height;
+    for (col=0; col < width; col++)
+      BAYER(row,col) = getbits(ifp, 10);
+    for (col=28; col--; )
+      getbits(ifp, 8);
+  }
+  maximum = 0x3dd;
+}
+
+/*
+   The Fuji Super CCD is just a Bayer grid rotated 45 degrees.
+ */
+void
+fuji_s2_load_raw()
+{
+  unsigned short pixel[2944];
+  int row, col, r, c;
+
+  fseek (ifp, (2944*24+32)*2, SEEK_CUR);
+  for (row=0; row < 2144; row++) {
+    read_shorts(ifp, pixel, 2944);
+    for (col=0; col < 2880; col++) {
+      r = row + ((col+1) >> 1);
+      c = 2143 - row + (col >> 1);
+      BAYER(r,c) = pixel[col];
+    }
+  }
+}
+
+void
+fuji_s3_load_raw()
+{
+  unsigned short pixel[4352];
+  int row, col, r, c;
+
+  fseek (ifp, (4352*2+32)*2, SEEK_CUR);
+  for (row=0; row < 1440; row++) {
+    read_shorts(ifp, pixel, 4352);
+    for (col=0; col < 4288; col++) {
+      r = 2143 + row - (col >> 1);
+      c = row + ((col+1) >> 1);
+      BAYER(r,c) = pixel[col];
+    }
+  }
+}
+
+static void  fuji_common_load_raw (int ncol, int icol, int nrow)
+{
+  unsigned short pixel[2048];
+  int row, col, r, c;
+
+  for (row=0; row < nrow; row++) {
+    read_shorts(ifp, pixel, ncol);
+    for (col=0; col <= icol; col++) {
+      r = icol - col + (row >> 1);
+      c = col + ((row+1) >> 1);
+      BAYER(r,c) = pixel[col];
+    }
+  }
+}
+
+void
+fuji_s5000_load_raw()
+{
+  fseek (ifp, (1472*4+24)*2, SEEK_CUR);
+  fuji_common_load_raw (1472, 1423, 2152);
+}
+
+void
+fuji_s7000_load_raw()
+{
+  fuji_common_load_raw (2048, 2047, 3080);
+}
+
+/*
+   The Fuji Super CCD SR has two photodiodes for each pixel.
+   The secondary has about 1/16 the sensitivity of the primary,
+   but this ratio may vary.
+ */
+void
+fuji_f700_load_raw()
+{
+  unsigned short pixel[2944];
+  int row, col, r, c, val;
+
+  for (row=0; row < 2168; row++) {
+    read_shorts(ifp, pixel, 2944);
+    for (col=0; col < 1440; col++) {
+      r = 1439 - col + (row >> 1);
+      c = col + ((row+1) >> 1);
+      val = pixel[col+16 + use_secondary*1472];
+      BAYER(r,c) = val;
+    }
+  }
+}
+
+void
+rollei_load_raw()
+{
+  unsigned char pixel[10];
+  unsigned iten=0, isix, i, buffer=0, row, col, todo[16];
+
+  isix = raw_width * raw_height * 5 / 8;
+  while (fread (pixel, 1, 10, ifp) == 10) {
+    for (i=0; i < 10; i+=2) {
+      todo[i]   = iten++;
+      todo[i+1] = pixel[i] << 8 | pixel[i+1];
+      buffer    = pixel[i] >> 2 | buffer << 6;
+    }
+    for (   ; i < 16; i+=2) {
+      todo[i]   = isix++;
+      todo[i+1] = buffer >> (14-i)*5;
+    }
+    for (i=0; i < 16; i+=2) {
+      row = todo[i] / raw_width - top_margin;
+      col = todo[i] % raw_width - left_margin;
+      if (row < height && col < width)
+    BAYER(row,col) = (todo[i+1] & 0x3ff);
+    }
+  }
+  maximum = 0x3ff;
+}
+
+void
+phase_one_load_raw()
+{
+  int row, col, a, b;
+  unsigned short *pixel, akey, bkey;
+
+  fseek (ifp, 8, SEEK_CUR);
+  fseek (ifp, get4(ifp) + 296, SEEK_CUR);
+  akey = get2(ifp);
+  bkey = get2(ifp);
+  fseek (ifp, data_offset + 12 + top_margin*raw_width*2, SEEK_SET);
+  pixel = calloc (raw_width, sizeof *pixel);
+  merror (pixel, "phase_one_load_raw()");
+  for (row=0; row < height; row++) {
+    read_shorts(ifp, pixel, raw_width);
+    for (col=0; col < raw_width; col+=2) {
+      a = pixel[col+0] ^ akey;
+      b = pixel[col+1] ^ bkey;
+      pixel[col+0] = (b & 0xaaaa) | (a & 0x5555);
+      pixel[col+1] = (a & 0xaaaa) | (b & 0x5555);
+    }
+    for (col=0; col < width; col++)
+      BAYER(row,col) = pixel[col+left_margin];
+  }
+  free (pixel);
+}
+
+void
+ixpress_load_raw()
+{
+  unsigned short pixel[4090];
+  int row, col;
+
+  order = 0x4949;
+  fseek (ifp, 304 + 6*2*4090, SEEK_SET);
+  for (row=height; --row >= 0; ) {
+    read_shorts(ifp, pixel, 4090);
+    for (col=0; col < width; col++)
+      BAYER(row,col) = pixel[width-1-col];
+  }
+}
+
+void
+leaf_load_raw()
+{
+  unsigned short *pixel;
+  int r, c, row, col;
+
+  pixel = calloc (raw_width, sizeof *pixel);
+  merror (pixel, "leaf_load_raw()");
+  for (r=0; r < height-32; r+=32)
+    FORC3 for (row=r; row < r+32; row++) {
+      read_shorts(ifp, pixel, raw_width);
+      for (col=0; col < width; col++)
+    image[row*width+col][c] = pixel[col];
+    }
+  free (pixel);
+}
+
+/*
+   For this function only, raw_width is in bytes, not pixels!
+ */
+void
+packed_12_load_raw()
+{
+  int row, col;
+
+  getbits(ifp, -1);
+  for (row=0; row < height; row++) {
+    for (col=0; col < width; col++)
+      BAYER(row,col) = getbits(ifp, 12);
+    for (col = width*3/2; col < raw_width; col++)
+      getbits(ifp, 8);
+  }
+}
+
+void
+unpacked_load_raw()
+{
+  unsigned short *pixel;
+  int row, col;
+
+  pixel = calloc (raw_width, sizeof *pixel);
+  merror (pixel, "unpacked_load_raw()");
+  for (row=0; row < height; row++) {
+    read_shorts(ifp, pixel, raw_width);
+    for (col=0; col < width; col++)
+      BAYER(row,col) = pixel[col];
+  }
+  free (pixel);
+}
+
+void
+olympus_e300_load_raw()
+{
+  unsigned char  *data,  *dp;
+  unsigned short *pixel, *pix;
+  int dwide, row, col;
+
+  dwide = raw_width * 16 / 10;
+  data = malloc (dwide + raw_width*2);
+  merror (data, "olympus_e300_load_raw()");
+  pixel = (unsigned short *) (data + dwide);
+  for (row=0; row < height; row++) {
+    fread (data, 1, dwide, ifp);
+    for (dp=data, pix=pixel; pix < pixel+raw_width; dp+=3, pix+=2) {
+      if (((dp-data) & 15) == 15) dp++;
+      pix[0] = dp[1] << 8 | dp[0];
+      pix[1] = dp[2] << 4 | dp[1] >> 4;
+    }
+    for (col=0; col < width; col++)
+      BAYER(row,col) = (pixel[col] & 0xfff);
+  }
+  free (data);
+}
+
+void
+olympus_cseries_load_raw()
+{
+  int irow, row, col;
+
+  for (irow=0; irow < height; irow++) {
+    row = irow * 2 % height + irow / (height/2);
+    if (row < 2) {
+      fseek (ifp, data_offset - row*(-width*height*3/4 & -2048), SEEK_SET);
+      getbits(ifp, -1);
+    }
+    for (col=0; col < width; col++)
+      BAYER(row,col) = getbits(ifp, 12);
+  }
+}
+
+void
+eight_bit_load_raw()
+{
+  unsigned char *pixel;
+  int row, col;
+
+  pixel = calloc (raw_width, sizeof *pixel);
+  merror (pixel, "eight_bit_load_raw()");
+  for (row=0; row < height; row++) {
+    fread (pixel, 1, raw_width, ifp);
+    for (col=0; col < width; col++)
+      BAYER(row,col) = pixel[col];
+  }
+  free (pixel);
+  maximum = 0xff;
+}
+
+void
+casio_qv5700_load_raw()
+{
+  unsigned char  data[3232],  *dp;
+  unsigned short pixel[2576], *pix;
+  int row, col;
+
+  for (row=0; row < height; row++) {
+    fread (data, 1, 3232, ifp);
+    for (dp=data, pix=pixel; dp < data+3220; dp+=5, pix+=4) {
+      pix[0] = (dp[0] << 2) + (dp[1] >> 6);
+      pix[1] = (dp[1] << 4) + (dp[2] >> 4);
+      pix[2] = (dp[2] << 6) + (dp[3] >> 2);
+      pix[3] = (dp[3] << 8) + (dp[4]     );
+    }
+    for (col=0; col < width; col++)
+      BAYER(row,col) = (pixel[col] & 0x3ff);
+  }
+  maximum = 0x3fc;
+}
+
+void
+nucore_load_raw()
+{
+  unsigned short *pixel;
+  int irow, row, col;
+
+  pixel = calloc (width, 2);
+  merror (pixel, "nucore_load_raw()");
+  for (irow=0; irow < height; irow++) {
+    read_shorts(ifp, pixel, width);
+    row = irow/2 + height/2 * (irow & 1);
+    for (col=0; col < width; col++)
+      BAYER(row,col) = pixel[col];
+  }
+  free (pixel);
+}
+
+static int  radc_token (int tree)
+{
+  int t;
+  static struct decode *dstart[18], *dindex;
+  static const int *s, source[] = {
+    1,1, 2,3, 3,4, 4,2, 5,7, 6,5, 7,6, 7,8,
+    1,0, 2,1, 3,3, 4,4, 5,2, 6,7, 7,6, 8,5, 8,8,
+    2,1, 2,3, 3,0, 3,2, 3,4, 4,6, 5,5, 6,7, 6,8,
+    2,0, 2,1, 2,3, 3,2, 4,4, 5,6, 6,7, 7,5, 7,8,
+    2,1, 2,4, 3,0, 3,2, 3,3, 4,7, 5,5, 6,6, 6,8,
+    2,3, 3,1, 3,2, 3,4, 3,5, 3,6, 4,7, 5,0, 5,8,
+    2,3, 2,6, 3,0, 3,1, 4,4, 4,5, 4,7, 5,2, 5,8,
+    2,4, 2,7, 3,3, 3,6, 4,1, 4,2, 4,5, 5,0, 5,8,
+    2,6, 3,1, 3,3, 3,5, 3,7, 3,8, 4,0, 5,2, 5,4,
+    2,0, 2,1, 3,2, 3,3, 4,4, 4,5, 5,6, 5,7, 4,8,
+    1,0, 2,2, 2,-2,
+    1,-3, 1,3,
+    2,-17, 2,-5, 2,5, 2,17,
+    2,-7, 2,2, 2,9, 2,18,
+    2,-18, 2,-9, 2,-2, 2,7,
+    2,-28, 2,28, 3,-49, 3,-9, 3,9, 4,49, 5,-79, 5,79,
+    2,-1, 2,13, 2,26, 3,39, 4,-16, 5,55, 6,-37, 6,76,
+    2,-26, 2,-13, 2,1, 3,-39, 4,16, 5,-55, 6,-76, 6,37
+  };
+
+  if (free_decode == first_decode)
+    for (s=source, t=0; t < 18; t++) {
+      dstart[t] = free_decode;
+      s = make_decoder_int (s, 0);
+    }
+  if (tree == 18) {
+    if (model[2] == '4')
+      return (getbits(ifp, 5) << 3) + 4; /* DC40 */
+    else
+      return (getbits(ifp, 6) << 2) + 2; /* DC50 */
+  }
+  for (dindex = dstart[tree]; dindex->branch[0]; )
+    dindex = dindex->branch[getbits(ifp, 1)];
+  return dindex->leaf;
+}
+
+#define FORYX for (y=1; y < 3; y++) for (x=col+1; x >= col; x--)
+
+#define PREDICTOR (c ? (buf[c][y-1][x] + buf[c][y][x+1]) / 2 \
+: (buf[c][y-1][x+1] + 2*buf[c][y-1][x] + buf[c][y][x+1]) / 4)
+
+void
+kodak_radc_load_raw()
+{
+  int row, col, tree, nreps, rep, step, i, c, s, r, x, y, val;
+  short last[3] = { 16,16,16 }, mul[3], buf[3][3][386];
+
+  init_decoder();
+  getbits(ifp, -1);
+  for (i=0; i < sizeof(buf)/sizeof(short); i++)
+    buf[0][0][i] = 2048;
+  for (row=0; row < height; row+=4) {
+    for (i=0; i < 3; i++)
+      mul[i] = getbits(ifp, 6);
+    FORC3 {
+      val = ((0x1000000/last[c] + 0x7ff) >> 12) * mul[c];
+      s = val > 65564 ? 10:12;
+      x = ~(-1 << (s-1));
+      val <<= 12-s;
+      for (i=0; i < sizeof(buf[0])/sizeof(short); i++)
+    buf[c][0][i] = (buf[c][0][i] * val + x) >> s;
+      last[c] = mul[c];
+      for (r=0; r <= !c; r++) {
+    buf[c][1][width/2] = buf[c][2][width/2] = mul[c] << 7;
+    for (tree=1, col=width/2; col > 0; ) {
+      if ((tree = radc_token(tree))) {
+        col -= 2;
+        if (tree == 8)
+          FORYX buf[c][y][x] = radc_token(tree+10) * mul[c];
+        else
+          FORYX buf[c][y][x] = radc_token(tree+10) * 16 + PREDICTOR;
+      } else
+        do {
+          nreps = (col > 2) ? radc_token(9) + 1 : 1;
+          for (rep=0; rep < 8 && rep < nreps && col > 0; rep++) {
+        col -= 2;
+        FORYX buf[c][y][x] = PREDICTOR;
+        if (rep & 1) {
+          step = radc_token(10) << 4;
+          FORYX buf[c][y][x] += step;
+        }
+          }
+        } while (nreps == 9);
+    }
+    for (y=0; y < 2; y++)
+      for (x=0; x < width/2; x++) {
+        val = (buf[c][y+1][x] << 4) / mul[c];
+        if (val < 0) val = 0;
+        if (c)
+          BAYER(row+y*2+c-1,x*2+2-c) = val;
+        else
+          BAYER(row+r*2+y,x*2+y) = val;
+      }
+    memcpy (buf[c][0]+!c, buf[c][2], sizeof buf[c][0]-2*!c);
+      }
+    }
+    for (y=row; y < row+4; y++)
+      for (x=0; x < width; x++)
+    if ((x+y) & 1) {
+      val = (BAYER(y,x)-2048)*2 + (BAYER(y,x-1)+BAYER(y,x+1))/2;
+      if (val < 0) val = 0;
+      BAYER(y,x) = val;
+    }
+  }
+  maximum = 0x1fff;     /* wild guess */
+}
+
+#undef FORYX
+#undef PREDICTOR
+
+#ifndef HAVE_JPEG
+void kodak_jpeg_load_raw() {}
+#else
+
+static boolean
+fill_input_buffer (j_decompress_ptr cinfo)
+{
+  static char jpeg_buffer[4096];
+  size_t nbytes;
+
+  nbytes = fread (jpeg_buffer, 1, 4096, ifp);
+  swab (jpeg_buffer, jpeg_buffer, nbytes);
+  cinfo->src->next_input_byte = jpeg_buffer;
+  cinfo->src->bytes_in_buffer = nbytes;
+  return TRUE;
+}
+
+void
+kodak_jpeg_load_raw()
+{
+  struct jpeg_decompress_struct cinfo;
+  struct jpeg_error_mgr jerr;
+  JSAMPARRAY buf;
+  JSAMPLE (*pixel)[3];
+  int row, col;
+
+  cinfo.err = jpeg_std_error (&jerr);
+  jpeg_create_decompress (&cinfo);
+  jpeg_stdio_src (&cinfo, ifp);
+  cinfo.src->fill_input_buffer = fill_input_buffer;
+  jpeg_read_header (&cinfo, TRUE);
+  jpeg_start_decompress (&cinfo);
+  if ((cinfo.output_width      != width  ) ||
+      (cinfo.output_height*2   != height ) ||
+      (cinfo.output_components != 3      )) {
+    pm_error ("incorrect JPEG dimensions");
+  }
+  buf = (*cinfo.mem->alloc_sarray)
+        ((j_common_ptr) &cinfo, JPOOL_IMAGE, width*3, 1);
+
+  while (cinfo.output_scanline < cinfo.output_height) {
+    row = cinfo.output_scanline * 2;
+    jpeg_read_scanlines (&cinfo, buf, 1);
+    pixel = (void *) buf[0];
+    for (col=0; col < width; col+=2) {
+      BAYER(row+0,col+0) = pixel[col+0][1] << 1;
+      BAYER(row+1,col+1) = pixel[col+1][1] << 1;
+      BAYER(row+0,col+1) = pixel[col][0] + pixel[col+1][0];
+      BAYER(row+1,col+0) = pixel[col][2] + pixel[col+1][2];
+    }
+  }
+  jpeg_finish_decompress (&cinfo);
+  jpeg_destroy_decompress (&cinfo);
+  maximum = 0xff << 1;
+}
+
+#endif
+
+void
+kodak_dc120_load_raw()
+{
+  static const int mul[4] = { 162, 192, 187,  92 };
+  static const int add[4] = {   0, 636, 424, 212 };
+  unsigned char pixel[848];
+  int row, shift, col;
+
+  for (row=0; row < height; row++) {
+    fread (pixel, 848, 1, ifp);
+    shift = row * mul[row & 3] + add[row & 3];
+    for (col=0; col < width; col++)
+      BAYER(row,col) = (unsigned short) pixel[(col + shift) % 848];
+  }
+  maximum = 0xff;
+}
+
+void
+kodak_dc20_coeff (float const juice)
+{
+  static const float my_coeff[3][4] =
+  { {  2.25,  0.75, -1.75, -0.25 },
+    { -0.25,  0.75,  0.75, -0.25 },
+    { -0.25, -1.75,  0.75,  2.25 } };
+  static const float flat[3][4] =
+  { {  1, 0,   0,   0 },
+    {  0, 0.5, 0.5, 0 },
+    {  0, 0,   0,   1 } };
+  int r, g;
+
+  for (r=0; r < 3; r++)
+    for (g=0; g < 4; g++)
+      coeff[r][g] = my_coeff[r][g] * juice + flat[r][g] * (1-juice);
+  use_coeff = 1;
+}
+
+void
+kodak_easy_load_raw()
+{
+  unsigned char *pixel;
+  unsigned row, col, icol;
+
+  if (raw_width > width)
+    black = 0;
+  pixel = calloc (raw_width, sizeof *pixel);
+  merror (pixel, "kodak_easy_load_raw()");
+  for (row=0; row < height; row++) {
+    fread (pixel, 1, raw_width, ifp);
+    for (col=0; col < raw_width; col++) {
+      icol = col - left_margin;
+      if (icol < width)
+    BAYER(row,icol) = (unsigned short) curve[pixel[col]];
+      else
+    black += curve[pixel[col]];
+    }
+  }
+  free (pixel);
+  if (raw_width > width)
+    black /= (raw_width - width) * height;
+  if (!strncmp(model,"DC2",3))
+    black = 0;
+  maximum = curve[0xff];
+}
+
+void
+kodak_compressed_load_raw()
+{
+  unsigned char c, blen[256];
+  unsigned short raw[6];
+  unsigned row, col, len, save, i, israw=0, bits=0, pred[2];
+  INT64 bitbuf=0;
+  int diff;
+
+  assert(have64BitArithmetic);
+
+  for (row=0; row < height; row++)
+    for (col=0; col < width; col++)
+    {
+      if ((col & 255) == 0) {       /* Get the bit-lengths of the */
+    len = width - col;      /* next 256 pixel values      */
+    if (len > 256) len = 256;
+    save = ftell(ifp);
+    for (israw=i=0; i < len; i+=2) {
+      c = fgetc(ifp);
+      if ((blen[i+0] = c & 15) > 12 ||
+          (blen[i+1] = c >> 4) > 12 )
+        israw = 1;
+    }
+    bitbuf = bits = pred[0] = pred[1] = 0;
+    if (len % 8 == 4) {
+      bitbuf  = fgetc(ifp) << 8;
+      bitbuf += fgetc(ifp);
+      bits = 16;
+    }
+    if (israw)
+      fseek (ifp, save, SEEK_SET);
+      }
+      if (israw) {          /* If the data is not compressed */
+    switch (col & 7) {
+      case 0:
+        read_shorts(ifp, raw, 6);
+        diff = raw[0] >> 12 << 8 | raw[2] >> 12 << 4 | raw[4] >> 12;
+        break;
+      case 1:
+        diff = raw[1] >> 12 << 8 | raw[3] >> 12 << 4 | raw[5] >> 12;
+        break;
+      default:
+        diff = raw[(col & 7) - 2] & 0xfff;
+    }
+      } else {              /* If the data is compressed */
+    len = blen[col & 255];      /* Number of bits for this pixel */
+    if (bits < len) {       /* Got enough bits in the buffer? */
+      for (i=0; i < 32; i+=8)
+        bitbuf += (INT64) fgetc(ifp) << (bits+(i^8));
+      bits += 32;
+    }
+    diff = bitbuf & (0xffff >> (16-len));  /* Pull bits from buffer */
+    bitbuf >>= len;
+    bits -= len;
+    if ((diff & (1 << (len-1))) == 0)
+      diff -= (1 << len) - 1;
+    pred[col & 1] += diff;
+    diff = pred[col & 1];
+      }
+      BAYER(row,col) = curve[diff];
+    }
+}
+
+void
+kodak_yuv_load_raw()
+{
+  unsigned char c, blen[384];
+  unsigned row, col, len, bits=0;
+  INT64 bitbuf=0;
+  int i, li=0, si, diff, six[6], y[4], cb=0, cr=0, rgb[3];
+  unsigned short *ip;
+
+  assert(have64BitArithmetic);
+
+  for (row=0; row < height; row+=2)
+    for (col=0; col < width; col+=2) {
+      if ((col & 127) == 0) {
+    len = (width - col + 1) * 3 & -4;
+    if (len > 384) len = 384;
+    for (i=0; i < len; ) {
+      c = fgetc(ifp);
+      blen[i++] = c & 15;
+      blen[i++] = c >> 4;
+    }
+    li = bitbuf = bits = y[1] = y[3] = cb = cr = 0;
+    if (len % 8 == 4) {
+      bitbuf  = fgetc(ifp) << 8;
+      bitbuf += fgetc(ifp);
+      bits = 16;
+    }
+      }
+      for (si=0; si < 6; si++) {
+    len = blen[li++];
+    if (bits < len) {
+      for (i=0; i < 32; i+=8)
+        bitbuf += (INT64) fgetc(ifp) << (bits+(i^8));
+      bits += 32;
+    }
+    diff = bitbuf & (0xffff >> (16-len));
+    bitbuf >>= len;
+    bits -= len;
+    if ((diff & (1 << (len-1))) == 0)
+      diff -= (1 << len) - 1;
+    six[si] = diff;
+      }
+      y[0] = six[0] + y[1];
+      y[1] = six[1] + y[0];
+      y[2] = six[2] + y[3];
+      y[3] = six[3] + y[2];
+      cb  += six[4];
+      cr  += six[5];
+      for (i=0; i < 4; i++) {
+    ip = image[(row+(i >> 1))*width + col+(i & 1)];
+    rgb[0] = y[i] + cr;
+    rgb[1] = y[i];
+    rgb[2] = y[i] + cb;
+    FORC3 if (rgb[c] > 0) ip[c] = curve[rgb[c]];
+      }
+    }
+  maximum = 0xe74;
+}
+
+static void  sony_decrypt (unsigned *data, int len, int start, int key)
+{
+  static uint32_t pad[128];
+  unsigned int p;
+
+  if (start) {
+    for (p=0; p < 4; p++)
+      pad[p] = key = key * 48828125 + 1;
+    pad[3] = pad[3] << 1 | (pad[0]^pad[2]) >> 31;
+    for (p=4; p < 127; p++)
+      pad[p] = (pad[p-4]^pad[p-2]) << 1 | (pad[p-3]^pad[p-1]) >> 31;
+
+    /* Convert to big-endian */
+
+    for (p=0; p < 127; p++) {
+        union {
+            unsigned char bytes[4];
+            uint32_t word;
+        } u;
+
+        u.bytes[0] = pad[p] >> 24;
+        u.bytes[1] = pad[p] >> 16;
+        u.bytes[2] = pad[p] >>  8;
+        u.bytes[3] = pad[p] >>  0;
+        
+        pad[p] = u.word;
+    }
+  }
+  while (len--)
+    *data++ ^= pad[p++ & 0x7f] = pad[(p+1) & 0x7f] ^ pad[(p+65) & 0x7f];
+}
+
+void
+sony_load_raw()
+{
+  unsigned char head[40];
+  struct pixel {
+      unsigned char bytes[2];
+  };
+  struct pixel * pixelrow;
+  unsigned i, key, row, col;
+
+  fseek (ifp, 200896, SEEK_SET);
+  fseek (ifp, (unsigned) fgetc(ifp)*4 - 1, SEEK_CUR);
+  order = 0x4d4d;
+  key = get4(ifp);
+  fseek (ifp, 164600, SEEK_SET);
+  fread (head, 1, 40, ifp);
+  sony_decrypt ((void *) head, 10, 1, key);
+  for (i=26; i-- > 22; )
+    key = key << 8 | head[i];
+  fseek (ifp, data_offset, SEEK_SET);
+  MALLOCARRAY(pixelrow, raw_width);
+  merror (pixelrow, "sony_load_raw()");
+  for (row=0; row < height; row++) {
+    fread (pixelrow, 2, raw_width, ifp);
+    sony_decrypt ((void *) pixelrow, raw_width/2, !row, key);
+    for (col=9; col < left_margin; col++)
+      black += pixelrow[col].bytes[0] * 256 + pixelrow[col].bytes[1];
+    for (col=0; col < width; col++)
+      BAYER(row,col) =
+          pixelrow[col+left_margin].bytes[0] * 256 +
+          pixelrow[col+left_margin].bytes[1];
+  }
+  free (pixelrow);
+  if (left_margin > 9)
+    black /= (left_margin-9) * height;
+  maximum = 0x3ff0;
+}
+
+void
+parse_minolta(FILE * const ifp)
+{
+  int save, tag, len, offset, high=0, wide=0;
+
+  fseek (ifp, 4, SEEK_SET);
+  offset = get4(ifp) + 8;
+  while ((save=ftell(ifp)) < offset) {
+    tag = get4(ifp);
+    len = get4(ifp);
+    switch (tag) {
+      case 0x505244:                /* PRD */
+    fseek (ifp, 8, SEEK_CUR);
+    high = get2(ifp);
+    wide = get2(ifp);
+    break;
+      case 0x574247:                /* WBG */
+    get4(ifp);
+    camera_red  = get2(ifp);
+    camera_red /= get2(ifp);
+    camera_blue = get2(ifp);
+    camera_blue = get2(ifp) / camera_blue;
+    break;
+      case 0x545457:                /* TTW */
+    parse_tiff(ifp, ftell(ifp));
+    }
+    fseek (ifp, save+len+8, SEEK_SET);
+  }
+  raw_height = high;
+  raw_width  = wide;
+  data_offset = offset;
+}
+
+/*
+   CIFF block 0x1030 contains an 8x8 white sample.
+   Load this into white[][] for use in scale_colors().
+ */
+static void  ciff_block_1030()
+{
+  static const unsigned short key[] = { 0x410, 0x45f3 };
+  int i, bpp, row, col, vbits=0;
+  unsigned long bitbuf=0;
+
+  get2(ifp);
+  if (get4(ifp) != 0x80008) return;
+  if (get4(ifp) == 0) return;
+  bpp = get2(ifp);
+  if (bpp != 10 && bpp != 12) return;
+  for (i=row=0; row < 8; row++)
+    for (col=0; col < 8; col++) {
+      if (vbits < bpp) {
+    bitbuf = bitbuf << 16 | (get2(ifp) ^ key[i++ & 1]);
+    vbits += 16;
+      }
+      white[row][col] =
+    bitbuf << (LONG_BIT - vbits) >> (LONG_BIT - bpp);
+      vbits -= bpp;
+    }
+}
+
+/*
+   Parse a CIFF file, better known as Canon CRW format.
+ */
+void 
+parse_ciff(FILE * const ifp, 
+           int    const offset, 
+           int    const length)
+{
+  int tboff, nrecs, i, type, len, roff, aoff, save, wbi=-1;
+  static const int remap[] = { 1,2,3,4,5,1 };
+  static const int remap_10d[] = { 0,1,3,4,5,6,0,0,2,8 };
+  static const int remap_s70[] = { 0,1,2,9,4,3,6,7,8,9,10,0,0,0,7,0,0,8 };
+  unsigned short key[] = { 0x410, 0x45f3 };
+
+  if (strcmp(model,"Canon PowerShot G6") &&
+      strcmp(model,"Canon PowerShot S70") &&
+      strcmp(model,"Canon PowerShot Pro1"))
+    key[0] = key[1] = 0;
+  fseek (ifp, offset+length-4, SEEK_SET);
+  tboff = get4(ifp) + offset;
+  fseek (ifp, tboff, SEEK_SET);
+  nrecs = get2(ifp);
+  for (i = 0; i < nrecs; i++) {
+    type = get2(ifp);
+    len  = get4(ifp);
+    roff = get4(ifp);
+    aoff = offset + roff;
+    save = ftell(ifp);
+    if (type == 0x080a) {       /* Get the camera make and model */
+      fseek (ifp, aoff, SEEK_SET);
+      fread (make, 64, 1, ifp);
+      fseek (ifp, aoff+strlen(make)+1, SEEK_SET);
+      fread (model, 64, 1, ifp);
+    }
+    if (type == 0x102a) {       /* Find the White Balance index */
+      fseek (ifp, aoff+14, SEEK_SET);   /* 0=auto, 1=daylight, 2=cloudy ... */
+      wbi = get2(ifp);
+      if (((!strcmp(model,"Canon EOS DIGITAL REBEL") ||
+        !strcmp(model,"Canon EOS 300D DIGITAL"))) && wbi == 6)
+    wbi++;
+    }
+    if (type == 0x102c) {       /* Get white balance (G2) */
+      if (!strcmp(model,"Canon PowerShot G1") ||
+      !strcmp(model,"Canon PowerShot Pro90 IS")) {
+    fseek (ifp, aoff+120, SEEK_SET);
+    white[0][1] = get2(ifp);
+    white[0][0] = get2(ifp);
+    white[1][0] = get2(ifp);
+    white[1][1] = get2(ifp);
+      } else {
+    fseek (ifp, aoff+100, SEEK_SET);
+    goto common;
+      }
+    }
+    if (type == 0x0032) {       /* Get white balance (D30 & G3) */
+      if (!strcmp(model,"Canon EOS D30")) {
+    fseek (ifp, aoff+72, SEEK_SET);
+common:
+    camera_red   = get2(ifp) ^ key[0];
+    camera_red   =(get2(ifp) ^ key[1]) / camera_red;
+    camera_blue  = get2(ifp) ^ key[0];
+    camera_blue /= get2(ifp) ^ key[1];
+      } else if (!strcmp(model,"Canon PowerShot G6") ||
+         !strcmp(model,"Canon PowerShot S70")) {
+    fseek (ifp, aoff+96 + remap_s70[wbi]*8, SEEK_SET);
+    goto common;
+      } else if (!strcmp(model,"Canon PowerShot Pro1")) {
+    fseek (ifp, aoff+96 + wbi*8, SEEK_SET);
+    goto common;
+      } else {
+    fseek (ifp, aoff+80 + (wbi < 6 ? remap[wbi]*8 : 0), SEEK_SET);
+    if (!camera_red)
+      goto common;
+      }
+    }
+    if (type == 0x10a9) {       /* Get white balance (D60) */
+      if (!strcmp(model,"Canon EOS 10D"))
+    wbi = remap_10d[wbi];
+      fseek (ifp, aoff+2 + wbi*8, SEEK_SET);
+      camera_red  = get2(ifp);
+      camera_red /= get2(ifp);
+      camera_blue = get2(ifp);
+      camera_blue = get2(ifp) / camera_blue;
+    }
+    if (type == 0x1030 && (wbi == 6 || wbi == 15)) {
+      fseek (ifp, aoff, SEEK_SET);  /* Get white sample */
+      ciff_block_1030();
+    }
+    if (type == 0x1031) {       /* Get the raw width and height */
+      fseek (ifp, aoff+2, SEEK_SET);
+      raw_width  = get2(ifp);
+      raw_height = get2(ifp);
+    }
+    if (type == 0x180e) {       /* Get the timestamp */
+      fseek (ifp, aoff, SEEK_SET);
+      timestamp = get4(ifp);
+    }
+    if (type == 0x580e)
+      timestamp = len;
+    if (type == 0x1810) {       /* Get the rotation */
+      fseek (ifp, aoff+12, SEEK_SET);
+      flip = get4(ifp);
+    }
+    if (type == 0x1835) {       /* Get the decoder table */
+      fseek (ifp, aoff, SEEK_SET);
+      crw_init_tables (get4(ifp));
+    }
+    if (type >> 8 == 0x28 || type >> 8 == 0x30) /* Get sub-tables */
+      parse_ciff(ifp, aoff, len);
+    fseek (ifp, save, SEEK_SET);
+  }
+  if (wbi == 0 && !strcmp(model,"Canon EOS D30"))
+    camera_red = -1;            /* Use my auto WB for this photo */
+}
+
+void
+parse_rollei(FILE * const ifp)
+{
+  char line[128], *val;
+  int tx=0, ty=0;
+  struct tm t;
+  time_t ts;
+
+  fseek (ifp, 0, SEEK_SET);
+  do {
+    fgets (line, 128, ifp);
+    if ((val = strchr(line,'=')))
+      *val++ = 0;
+    else
+      val = line + strlen(line);
+    if (!strcmp(line,"DAT"))
+      sscanf (val, "%d.%d.%d", &t.tm_mday, &t.tm_mon, &t.tm_year);
+    if (!strcmp(line,"TIM"))
+      sscanf (val, "%d:%d:%d", &t.tm_hour, &t.tm_min, &t.tm_sec);
+    if (!strcmp(line,"HDR"))
+      data_offset = atoi(val);
+    if (!strcmp(line,"X  "))
+      raw_width = atoi(val);
+    if (!strcmp(line,"Y  "))
+      raw_height = atoi(val);
+    if (!strcmp(line,"TX "))
+      tx = atoi(val);
+    if (!strcmp(line,"TY "))
+      ty = atoi(val);
+  } while (strncmp(line,"EOHD",4));
+  t.tm_year -= 1900;
+  t.tm_mon -= 1;
+  putenv((char*)"TZ=");
+  if ((ts = mktime(&t)) > 0)
+    timestamp = ts;
+  data_offset += tx * ty * 2;
+  strcpy (make, "Rollei");
+  strcpy (model,"d530flex");
+}
+
+
+
+void
+parse_mos(FILE * const ifp, 
+          int    const offset)
+{
+    char data[40];
+    int skip, from, i, neut[4];
+
+    fseek (ifp, offset, SEEK_SET);
+    while (1) {
+        fread (data, 1, 8, ifp);
+        if (strcmp(data,"PKTS")) break;
+        fread (data, 1, 40, ifp);
+        skip = get4(ifp);
+        from = ftell(ifp);
+#ifdef USE_LCMS
+        if (!strcmp(data,"icc_camera_profile")) {
+            profile_length = skip;
+            profile_offset = from;
+        }
+#endif
+        if (!strcmp(data,"NeutObj_neutrals")) {
+            for (i=0; i < 4; i++)
+                fscanf (ifp, "%d", neut+i);
+            camera_red  = (float) neut[2] / neut[1];
+            camera_blue = (float) neut[2] / neut[3];
+        }
+        parse_mos(ifp, from);
+        fseek (ifp, skip+from, SEEK_SET);
+    }
+}
+
+void
+nikon_e950_coeff()
+{
+  int r, g;
+  static const float my_coeff[3][4] =
+  { { -1.936280,  1.800443, -1.448486,  2.584324 },
+    {  1.405365, -0.524955, -0.289090,  0.408680 },
+    { -1.204965,  1.082304,  2.941367, -1.818705 } };
+
+  for (r=0; r < 3; r++)
+    for (g=0; g < 4; g++)
+      coeff[r][g] = my_coeff[r][g];
+  use_coeff = 1;
+}
+
+
+
+static double getrat()
+{
+  double num = get4(ifp);
+  return num / get4(ifp);
+}
+
+
+
+static void 
+parse_makernote(FILE * const ifp)
+{
+  unsigned base=0, offset=0, entries, tag, type, len, save;
+  static const int size[] = { 1,1,1,2,4,8,1,1,2,4,8,4,8 };
+  short sorder;
+  char buf[10];
+/*
+   The MakerNote might have its own TIFF header (possibly with
+   its own byte-order!), or it might just be a table.
+ */
+  sorder = order;
+  fread (buf, 1, 10, ifp);
+  if (!strncmp (buf,"KC" ,2) ||     /* these aren't TIFF format */
+      !strncmp (buf,"MLY",3)) return;
+  if (!strcmp (buf,"Nikon")) {
+    base = ftell(ifp);
+    order = get2(ifp);
+    if (get2(ifp) != 42) goto quit;
+    offset = get4(ifp);
+    fseek (ifp, offset-8, SEEK_CUR);
+  } else if (!strncmp (buf,"FUJIFILM",8) ||
+         !strcmp  (buf,"Panasonic")) {
+    order = 0x4949;
+    fseek (ifp,  2, SEEK_CUR);
+  } else if (!strcmp (buf,"OLYMP") ||
+         !strcmp (buf,"LEICA") ||
+         !strcmp (buf,"EPSON"))
+    fseek (ifp, -2, SEEK_CUR);
+  else if (!strcmp (buf,"AOC") ||
+       !strcmp (buf,"QVC"))
+    fseek (ifp, -4, SEEK_CUR);
+  else fseek (ifp, -10, SEEK_CUR);
+
+  entries = get2(ifp);
+  while (entries--) {
+    tag  = get2(ifp);
+    type = get2(ifp);
+    len  = get4(ifp);
+    save = ftell(ifp);
+    if (len * size[type < 13 ? type:0] > 4)
+      fseek (ifp, get4(ifp)+base, SEEK_SET);
+
+    if (tag == 0xc && len == 4) {
+      camera_red  = getrat();
+      camera_blue = getrat();
+    }
+    if (tag == 0x14 && len == 2560 && type == 7) {
+      fseek (ifp, 1248, SEEK_CUR);
+      goto get2_256;
+    }
+    if (strstr(make,"PENTAX")) {
+      if (tag == 0x1b) tag = 0x1018;
+      if (tag == 0x1c) tag = 0x1017;
+    }
+    if (tag == 0x8c)
+      nikon_curve_offset = ftell(ifp) + 2112;
+    if (tag == 0x96)
+      nikon_curve_offset = ftell(ifp) + 2;
+    if (tag == 0x97) {
+      if (!strcmp(model,"NIKON D100 ")) {
+    fseek (ifp, 72, SEEK_CUR);
+    camera_red  = get2(ifp) / 256.0;
+    camera_blue = get2(ifp) / 256.0;
+      } else if (!strcmp(model,"NIKON D2H")) {
+    fseek (ifp, 10, SEEK_CUR);
+    goto get2_rggb;
+      } else if (!strcmp(model,"NIKON D70")) {
+    fseek (ifp, 20, SEEK_CUR);
+    camera_red  = get2(ifp);
+    camera_red /= get2(ifp);
+    camera_blue = get2(ifp);
+    camera_blue/= get2(ifp);
+      }
+    }
+    if (tag == 0xe0 && len == 17) {
+      get2(ifp);
+      raw_width  = get2(ifp);
+      raw_height = get2(ifp);
+    }
+    if (tag == 0x200 && len == 4)
+      black = (get2(ifp)+get2(ifp)+get2(ifp)+get2(ifp))/4;
+    if (tag == 0x201 && len == 4) {
+      camera_red  = get2(ifp);
+      camera_red /= get2(ifp);
+      camera_blue = get2(ifp);
+      camera_blue = get2(ifp) / camera_blue;
+    }
+    if (tag == 0x401 && len == 4) {
+      black = (get4(ifp)+get4(ifp)+get4(ifp)+get4(ifp))/4;
+    }
+    if (tag == 0xe80 && len == 256 && type == 7) {
+      fseek (ifp, 48, SEEK_CUR);
+      camera_red  = get2(ifp) * 508 * 1.078 / 0x10000;
+      camera_blue = get2(ifp) * 382 * 1.173 / 0x10000;
+    }
+    if (tag == 0xf00 && len == 614 && type == 7) {
+      fseek (ifp, 188, SEEK_CUR);
+      goto get2_256;
+    }
+    if (tag == 0x1017)
+      camera_red  = get2(ifp) / 256.0;
+    if (tag == 0x1018)
+      camera_blue = get2(ifp) / 256.0;
+    if (tag == 0x2011 && len == 2) {
+get2_256:
+      order = 0x4d4d;
+      camera_red  = get2(ifp) / 256.0;
+      camera_blue = get2(ifp) / 256.0;
+    }
+    if (tag == 0x4001) {
+      fseek (ifp, strstr(model,"EOS-1D") ? 68:50, SEEK_CUR);
+get2_rggb:
+      camera_red  = get2(ifp);
+      camera_red /= get2(ifp);
+      camera_blue = get2(ifp);
+      camera_blue = get2(ifp) / camera_blue;
+    }
+    fseek (ifp, save+4, SEEK_SET);
+  }
+quit:
+  order = sorder;
+}
+
+
+
+static void
+get_timestamp(FILE * const ifp)
+{
+/*
+   Since the TIFF DateTime string has no timezone information,
+   assume that the camera's clock was set to Universal Time.
+ */
+  struct tm t;
+  time_t ts;
+
+  if (fscanf (ifp, "%d:%d:%d %d:%d:%d", &t.tm_year, &t.tm_mon,
+    &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec) != 6)
+    return;
+  t.tm_year -= 1900;
+  t.tm_mon -= 1;
+  putenv((char*)"TZ=UTC");   /* Remove this to assume local time */
+  if ((ts = mktime(&t)) > 0)
+    timestamp = ts;
+}
+
+static void 
+parse_exif(FILE * const ifp, int base)
+{
+  int entries, tag, type, len, val, save;
+
+  entries = get2(ifp);
+  while (entries--) {
+    tag  = get2(ifp);
+    type = get2(ifp);
+    len  = get4(ifp);
+    val  = get4(ifp);
+    save = ftell(ifp);
+    fseek (ifp, base+val, SEEK_SET);
+    if (tag == 0x9003 || tag == 0x9004)
+      get_timestamp(ifp);
+    if (tag == 0x927c) {
+      if (!strncmp(make,"SONY",4))
+    data_offset = base+val+len;
+      else
+    parse_makernote(ifp);
+    }
+    fseek (ifp, save, SEEK_SET);
+  }
+}
+
+static int 
+parse_tiff_ifd(FILE * const ifp, int base, int level)
+{
+  unsigned entries, tag, type, len, plen=16, save;
+  int done=0, use_cm=0, cfa, i, j, c;
+  static const int size[] = { 1,1,1,2,4,8,1,1,2,4,8,4,8 };
+  char software[64];
+  static const int flip_map[] = { 0,1,3,2,4,6,7,5 };
+  unsigned char cfa_pat[16], cfa_pc[] = { 0,1,2,3 }, tab[256];
+  unsigned short scale[4];
+  double dblack, cc[4][4], cm[4][3];
+  double ab[]={ 1,1,1,1 }, asn[] = { 0,0,0,0 }, xyz[] = { 1,1,1 };
+
+  for (j=0; j < 4; j++)
+    for (i=0; i < 4; i++)
+      cc[j][i] = i == j;
+  entries = get2(ifp);
+  if (entries > 512) return 1;
+  while (entries--) {
+    tag  = get2(ifp);
+    type = get2(ifp);
+    len  = get4(ifp);
+    save = ftell(ifp);
+    if (tag > 50700 && tag < 50800) done = 1;
+    if (len * size[type < 13 ? type:0] > 4)
+      fseek (ifp, get4(ifp)+base, SEEK_SET);
+    switch (tag) {
+      case 0x11:
+    camera_red  = get4(ifp) / 256.0;
+    break;
+      case 0x12:
+    camera_blue = get4(ifp) / 256.0;
+    break;
+      case 0x100:           /* ImageWidth */
+    if (strcmp(make,"Canon") || level)
+      raw_width = type==3 ? get2(ifp) : get4(ifp);
+    break;
+      case 0x101:           /* ImageHeight */
+    if (strcmp(make,"Canon") || level)
+      raw_height = type==3 ? get2(ifp) : get4(ifp);
+    break;
+      case 0x102:           /* Bits per sample */
+    fuji_secondary = len == 2;
+    if (level) maximum = (1 << get2(ifp)) - 1;
+    break;
+      case 0x103:           /* Compression */
+    tiff_data_compression = get2(ifp);
+    break;
+      case 0x106:           /* Kodak color format */
+    kodak_data_compression = get2(ifp);
+    break;
+      case 0x10f:           /* Make */
+    fgets (make, 64, ifp);
+    break;
+      case 0x110:           /* Model */
+    fgets (model, 64, ifp);
+    break;
+      case 0x111:           /* StripOffset */
+    data_offset = get4(ifp);
+    break;
+      case 0x112:           /* Orientation */
+    flip = flip_map[(get2(ifp)-1) & 7];
+    break;
+      case 0x115:           /* SamplesPerPixel */
+    tiff_samples = get2(ifp);
+    break;
+      case 0x131:           /* Software tag */
+    fgets (software, 64, ifp);
+    if (!strncmp(software,"Adobe",5))
+      make[0] = 0;
+    break;
+      case 0x132:           /* DateTime tag */
+    get_timestamp(ifp);
+    break;
+      case 0x144:           /* TileOffsets */
+    if (level) {
+      data_offset = ftell(ifp);
+    } else {
+      strcpy (make, "Leaf");
+      data_offset = get4(ifp);
+    }
+    break;
+      case 0x14a:           /* SubIFD tag */
+    if (len > 2 && !is_dng && !strcmp(make,"Kodak"))
+        len = 2;
+    while (len--) {
+      i = ftell(ifp);
+      fseek (ifp, get4(ifp)+base, SEEK_SET);
+      if (parse_tiff_ifd(ifp, base, level+1)) break;
+      fseek (ifp, i+4, SEEK_SET);
+    }
+    break;
+      case 33405:           /* Model2 */
+    fgets (model2, 64, ifp);
+    break;
+      case 33422:           /* CFAPattern */
+    if ((plen=len) > 16) plen = 16;
+    fread (cfa_pat, 1, plen, ifp);
+    for (colors=cfa=i=0; i < plen; i++) {
+      colors += !(cfa & (1 << cfa_pat[i]));
+      cfa |= 1 << cfa_pat[i];
+    }
+    if (cfa == 070) memcpy (cfa_pc,"\003\004\005",3);   /* CMY */
+    if (cfa == 072) memcpy (cfa_pc,"\005\003\004\001",4);   /* GMCY */
+    goto guess_cfa_pc;
+      case 34665:           /* EXIF tag */
+    fseek (ifp, get4(ifp)+base, SEEK_SET);
+    parse_exif(ifp, base);
+    break;
+      case 50706:           /* DNGVersion */
+    is_dng = 1;
+    if (flip == 7) flip = 4;    /* Adobe didn't read the TIFF spec. */
+    break;
+      case 50710:           /* CFAPlaneColor */
+    if (len > 4) len = 4;
+    colors = len;
+    fread (cfa_pc, 1, colors, ifp);
+guess_cfa_pc:
+    FORC4 tab[cfa_pc[c]] = c;
+    for (i=16; i--; )
+      filters = filters << 2 | tab[cfa_pat[i % plen]];
+    break;
+      case 50711:           /* CFALayout */
+    if (get2(ifp) == 2) {
+      fuji_width = (raw_width+1)/2;
+      filters = 0x49494949;
+    }
+    break;
+      case 0x123:
+      case 0x90d:
+      case 50712:           /* LinearizationTable */
+    if (len > 0x1000)
+        len = 0x1000;
+    read_shorts(ifp, curve, len);
+    for (i=len; i < 0x1000; i++)
+      maximum = curve[i] = curve[i-1];
+    break;
+      case 50714:           /* BlackLevel */
+      case 50715:           /* BlackLevelDeltaH */
+      case 50716:           /* BlackLevelDeltaV */
+    for (dblack=i=0; i < len; i++)
+      dblack += getrat();
+    black += dblack/len + 0.5;
+    break;
+      case 50717:           /* WhiteLevel */
+    maximum = get2(ifp);
+    break;
+      case 50718:           /* DefaultScale */
+    for (i=0; i < 4; i++)
+      scale[i] = get4(ifp);
+    if (scale[1]*scale[2] == 2*scale[0]*scale[3]) ymag = 2;
+    break;
+      case 50721:           /* ColorMatrix1 */
+      case 50722:           /* ColorMatrix2 */
+    FORC4 for (j=0; j < 3; j++)
+      cm[c][j] = getrat();
+    use_cm = 1;
+    break;
+      case 50723:           /* CameraCalibration1 */
+      case 50724:           /* CameraCalibration2 */
+    for (i=0; i < colors; i++)
+      FORC4 cc[i][c] = getrat();    
+      case 50727:           /* AnalogBalance */
+    FORC4 ab[c] = getrat();
+    break;
+      case 50728:           /* AsShotNeutral */
+    FORC4 asn[c] = getrat();
+    break;
+      case 50729:           /* AsShotWhiteXY */
+    xyz[0] = getrat();
+    xyz[1] = getrat();
+    xyz[2] = 1 - xyz[0] - xyz[1];
+    }
+    fseek (ifp, save+4, SEEK_SET);
+  }
+  for (i=0; i < colors; i++)
+    FORC4 cc[i][c] *= ab[i];
+  if (use_cm)
+    dng_coeff (cc, cm, xyz);
+  if (asn[0])
+    FORC4 pre_mul[c] = 1 / asn[c];
+  if (!use_cm)
+    FORC4 pre_mul[c] /= cc[c][c];
+
+  if (is_dng || level) return done;
+
+  if ((raw_height & 1) && !strncmp (make,"OLYMPUS",7))
+       raw_height++;
+
+  if (make[0] == 0 && raw_width == 680 && raw_height == 680) {
+    strcpy (make, "Imacon");
+    strcpy (model,"Ixpress");
+  }
+  return done;
+}
+
+void
+parse_tiff(FILE * const ifp, int base)
+{
+  int doff;
+
+  fseek (ifp, base, SEEK_SET);
+  order = get2(ifp);
+  if (order != 0x4949 && order != 0x4d4d) return;
+  get2(ifp);
+  while ((doff = get4(ifp))) {
+    fseek (ifp, doff+base, SEEK_SET);
+    if (parse_tiff_ifd(ifp, base, 0)) break;
+  }
+  if (!is_dng && !strncmp(make,"Kodak",5)) {
+    fseek (ifp, 12+base, SEEK_SET);
+    parse_tiff_ifd(ifp, base, 2);
+  }
+}
+
+
+
+/*
+   Many cameras have a "debug mode" that writes JPEG and raw
+   at the same time.  The raw file has no header, so try to
+   to open the matching JPEG file and read its metadata.
+ */
+void
+parse_external_jpeg(const char * const ifname)
+{
+    const char *file, *ext;
+    char * jfile;
+    char * jext;
+    char * jname;
+
+    ext  = strrchr (ifname, '.');
+    file = strrchr (ifname, '/');
+    if (!file) file = strrchr (ifname, '\\');
+    if (!file) file = ifname-1;
+    file++;
+    if (strlen(ext) != 4 || ext-file != 8) return;
+    jname = malloc (strlen(ifname) + 1);
+    merror (jname, "parse_external()");
+    strcpy (jname, ifname);
+    jfile = jname + (file - ifname);
+    jext  = jname + (ext  - ifname);
+    if (strcasecmp (ext, ".jpg")) {
+        strcpy (jext, isupper(ext[1]) ? ".JPG":".jpg");
+        memcpy (jfile, file+4, 4);
+        memcpy (jfile+4, file, 4);
+    } else
+        while (isdigit(*--jext)) {
+            if (*jext != '9') {
+                (*jext)++;
+                break;
+            }
+            *jext = '0';
+        }
+    if (strcmp (jname, ifname)) {
+        FILE * ifP;
+        ifP = fopen (jname, "rb");
+        if (ifP) {
+            if (verbose)
+                pm_message ("Reading metadata from %s...", jname);
+            parse_tiff(ifP, 12);
+            fclose (ifP);
+        }
+    }
+    if (!timestamp)
+        pm_message ( "Failed to read metadata from %s", jname);
+    free (jname);
+}
+
diff --git a/converter/other/cameratopam/camera.h b/converter/other/cameratopam/camera.h
new file mode 100644
index 00000000..a1e884cf
--- /dev/null
+++ b/converter/other/cameratopam/camera.h
@@ -0,0 +1,131 @@
+#include <stdio.h>
+
+void 
+parse_ciff(FILE * const ifp,
+           int    const offset,
+           int    const length);
+
+void
+parse_external_jpeg(const char * const ifname);
+
+void
+parse_tiff(FILE * const ifp, int base);
+
+void
+parse_minolta(FILE * const ifp);
+
+void
+parse_rollei(FILE * const ifp);
+
+void
+parse_mos(FILE * const ifp,
+          int    const offset);
+
+void
+adobe_dng_load_raw_lj(void);
+
+void
+adobe_dng_load_raw_nc(void);
+
+int
+nikon_is_compressed(void);
+
+void
+nikon_compressed_load_raw(void);
+
+void
+nikon_e950_load_raw(void);
+
+void
+nikon_e950_coeff(void);
+
+int
+nikon_e990(void);
+
+int
+nikon_e2100(void);
+
+void
+nikon_e2100_load_raw(void);
+
+int
+minolta_z2(void);
+
+void
+fuji_s2_load_raw(void);
+
+void
+fuji_s3_load_raw(void);
+
+void
+fuji_s5000_load_raw(void);
+
+void
+unpacked_load_raw(void);
+
+void
+fuji_s7000_load_raw(void);
+
+void
+fuji_f700_load_raw(void);
+
+void
+packed_12_load_raw(void);
+
+void
+eight_bit_load_raw(void);
+
+void
+phase_one_load_raw(void);
+
+void
+ixpress_load_raw(void);
+
+void
+leaf_load_raw(void);
+
+void
+olympus_e300_load_raw(void);
+
+void
+olympus_cseries_load_raw(void);
+
+void
+sony_load_raw(void);
+
+void
+kodak_easy_load_raw(void);
+
+void
+kodak_compressed_load_raw(void);
+
+void
+kodak_yuv_load_raw(void);
+
+void
+kodak_dc20_coeff (float const juice);
+
+void
+kodak_radc_load_raw(void);
+
+void
+kodak_jpeg_load_raw(void);
+
+void
+kodak_dc120_load_raw(void);
+
+void
+rollei_load_raw(void);
+
+void
+casio_qv5700_load_raw(void);
+
+void
+nucore_load_raw(void);
+
+void
+nikon_load_raw(void);
+
+int
+pentax_optio33(void);
+
diff --git a/converter/other/cameratopam/cameratopam.c b/converter/other/cameratopam/cameratopam.c
new file mode 100644
index 00000000..92773c91
--- /dev/null
+++ b/converter/other/cameratopam/cameratopam.c
@@ -0,0 +1,906 @@
+/*
+   This is derived from Dave Coffin's raw photo decoder, dcraw.c,
+   Copyright 1997-2005 by Dave Coffin, dcoffin a cybercom o net.
+
+   See the COPYRIGHT file in the same directory as this file for
+   information on copyright and licensing.
+ */
+
+
+#define _BSD_SOURCE 1   /* Make sure string.h contains strcasecmp() */
+#define _XOPEN_SOURCE  /* Make sure unistd.h contains swab() */
+
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __CYGWIN__
+#include <io.h>
+#endif
+#ifdef WIN32
+  #include <winsock2.h>
+  #pragma comment(lib, "ws2_32.lib")
+  #define strcasecmp stricmp
+  typedef __int64 INT64;
+  static bool const have64BitArithmetic = true;
+#else
+  #include <unistd.h>
+#endif
+
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pam.h"
+
+#include "global_variables.h"
+#include "util.h"
+#include "decode.h"
+#include "identify.h"
+#include "bayer.h"
+#include "foveon.h"
+#include "dng.h"
+
+#if HAVE_INT64
+   typedef int64_t INT64;
+   static bool const have64BitArithmetic = true;
+#else
+   /* We define INT64 to something that lets the code compile, but we
+      should not execute any INT64 code, because it will get the wrong
+      result.  */
+   typedef int INT64;
+   static bool const have64BitArithmetic = false;
+#endif
+
+/*
+   All global variables are defined here, and all functions that
+   access them are prefixed with "CLASS".  Note that a thread-safe
+   C++ class cannot have non-const static local variables.
+ */
+FILE * ifp;
+short order;
+char make[64], model[70], model2[64], *meta_data;
+time_t timestamp;
+int data_offset, meta_offset, meta_length;
+int tiff_data_compression, kodak_data_compression;
+int raw_height, raw_width, top_margin, left_margin;
+int height, width, fuji_width, colors, tiff_samples;
+int black, maximum, clip_max;
+int iheight, iwidth, shrink;
+int is_dng, is_canon, is_foveon, use_coeff, use_gamma;
+int trim, flip, xmag, ymag;
+int zero_after_ff;
+unsigned filters;
+unsigned short (*image)[4], white[8][8], curve[0x1000];
+int fuji_secondary;
+float cam_mul[4], pre_mul[4], coeff[3][4];
+int histogram[3][0x2000];
+jmp_buf failure;
+bool use_secondary;
+bool verbose;
+
+#ifdef USE_LCMS
+#include <lcms.h>
+int profile_offset, profile_length;
+#endif
+
+#define CLASS
+
+#define FORC3 for (c=0; c < 3; c++)
+#define FORC4 for (c=0; c < colors; c++)
+
+static void CLASS merror (const void *ptr, const char *where)
+{
+    if (ptr == NULL)
+        pm_error ("Out of memory in %s", where);
+}
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;  /* "-" means Standard Input */
+    float bright;
+    float red_scale;
+    float blue_scale;
+    const char * profile;
+    unsigned int identify_only;
+    unsigned int verbose;
+    unsigned int half_size;
+    unsigned int four_color_rgb;
+    unsigned int document_mode;
+    unsigned int quick_interpolate;
+    unsigned int use_auto_wb;
+    unsigned int use_camera_wb;
+    unsigned int use_camera_rgb;
+    unsigned int use_secondary;
+    unsigned int no_clip_color;
+    unsigned int linear;
+};
+
+
+static struct cmdlineInfo cmdline;
+
+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!
+-----------------------------------------------------------------------------*/
+    optStruct3 opt;
+    optEntry *option_def;
+    unsigned int option_def_index;
+    unsigned int brightSpec, red_scaleSpec, blue_scaleSpec,
+        profileSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;
+    opt.allowNegNum = FALSE;
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "bright", 
+            OPT_FLOAT,   &cmdlineP->bright,     &brightSpec, 0);
+    OPTENT3(0, "red_scale", 
+            OPT_FLOAT,   &cmdlineP->red_scale,  &red_scaleSpec, 0);
+    OPTENT3(0, "blue_scale", 
+            OPT_FLOAT,   &cmdlineP->blue_scale, &blue_scaleSpec, 0);
+    OPTENT3(0, "profile", 
+            OPT_STRING,  &cmdlineP->profile,    &profileSpec, 0);
+    OPTENT3(0,   "identify_only",   
+            OPT_FLAG,    NULL, &cmdlineP->identify_only, 0);
+    OPTENT3(0,   "verbose",   
+            OPT_FLAG,    NULL, &cmdlineP->verbose, 0);
+    OPTENT3(0,   "half_size",   
+            OPT_FLAG,    NULL, &cmdlineP->half_size, 0);
+    OPTENT3(0,   "four_color_rgb",   
+            OPT_FLAG,    NULL, &cmdlineP->four_color_rgb, 0);
+    OPTENT3(0,   "document_mode",   
+            OPT_FLAG,    NULL, &cmdlineP->document_mode, 0);
+    OPTENT3(0,   "quick_interpolate",   
+            OPT_FLAG,    NULL, &cmdlineP->quick_interpolate, 0);
+    OPTENT3(0,   "balance_auto",   
+            OPT_FLAG,    NULL, &cmdlineP->use_auto_wb, 0);
+    OPTENT3(0,   "balance_camera",   
+            OPT_FLAG,    NULL, &cmdlineP->use_camera_wb, 0);
+    OPTENT3(0,   "use_secondary",   
+            OPT_FLAG,    NULL, &cmdlineP->use_secondary, 0);
+    OPTENT3(0,   "no_clip_color",   
+            OPT_FLAG,    NULL, &cmdlineP->no_clip_color, 0);
+    OPTENT3(0,   "rgb",   
+            OPT_FLAG,    NULL, &cmdlineP->use_camera_rgb, 0);
+    OPTENT3(0,   "linear",   
+            OPT_FLAG,    NULL, &cmdlineP->linear, 0);
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+
+    if (!brightSpec)
+        cmdlineP->bright = 1.0;
+    if (!red_scaleSpec)
+        cmdlineP->red_scale = 1.0;
+    if (!blue_scaleSpec)
+        cmdlineP->blue_scale = 1.0;
+    if (!profileSpec)
+        cmdlineP->profile = NULL;
+
+
+    if (argc - 1 == 0)
+        cmdlineP->inputFileName = strdup("-");  /* he wants stdin */
+    else if (argc - 1 == 1)
+        cmdlineP->inputFileName = strdup(argv[1]);
+    else 
+        pm_error("Too many arguments.  The only argument accepted "
+                 "is the input file name");
+}
+
+  
+/*
+   Seach from the current directory up to the root looking for
+   a ".badpixels" file, and fix those pixels now.
+ */
+static void CLASS bad_pixels()
+{
+  FILE *fp=NULL;
+  char *fname, *cp, line[128];
+  int len, time, row, col, r, c, rad, tot, n, fixed=0;
+
+  if (!filters) return;
+  for (len=16 ; ; len *= 2) {
+    fname = malloc (len);
+    if (!fname) return;
+    if (getcwd (fname, len-12)) break;
+    free (fname);
+    if (errno != ERANGE) return;
+  }
+#ifdef WIN32
+  if (fname[1] == ':')
+    memmove (fname, fname+2, len-2);
+  for (cp=fname; *cp; cp++)
+    if (*cp == '\\') *cp = '/';
+#endif
+  cp = fname + strlen(fname);
+  if (cp[-1] == '/') cp--;
+  while (*fname == '/') {
+    strcpy (cp, "/.badpixels");
+    if ((fp = fopen (fname, "r"))) break;
+    if (cp == fname) break;
+    while (*--cp != '/');
+  }
+  free (fname);
+  if (!fp) return;
+  while (fgets (line, 128, fp)) {
+    cp = strchr (line, '#');
+    if (cp) *cp = 0;
+    if (sscanf (line, "%d %d %d", &col, &row, &time) != 3) continue;
+    if ((unsigned) col >= width || (unsigned) row >= height) continue;
+    if (time > timestamp) continue;
+    for (tot=n=0, rad=1; rad < 3 && n==0; rad++)
+      for (r = row-rad; r <= row+rad; r++)
+    for (c = col-rad; c <= col+rad; c++)
+      if ((unsigned) r < height && (unsigned) c < width &&
+        (r != row || c != col) && FC(r,c) == FC(row,col)) {
+        tot += BAYER(r,c);
+        n++;
+      }
+    BAYER(row,col) = tot/n;
+    if (cmdline.verbose) {
+      if (!fixed++)
+          pm_message ("Fixed bad pixels at: %d,%d", col, row);
+    }
+  }
+  fclose (fp);
+}
+
+static void CLASS scale_colors()
+{
+  int row, col, c, val, shift=0;
+  int min[4], max[4], count[4];
+  double sum[4], dmin;
+
+  maximum -= black;
+  if (cmdline.use_auto_wb || (cmdline.use_camera_wb && camera_red == -1)) {
+    FORC4 min[c] = INT_MAX;
+    FORC4 max[c] = count[c] = sum[c] = 0;
+    for (row=0; row < height; row++)
+      for (col=0; col < width; col++)
+    FORC4 {
+      val = image[row*width+col][c];
+      if (!val) continue;
+      if (min[c] > val) min[c] = val;
+      if (max[c] < val) max[c] = val;
+      val -= black;
+      if (val > maximum-25) continue;
+      if (val < 0) val = 0;
+      sum[c] += val;
+      count[c]++;
+    }
+    FORC4 pre_mul[c] = count[c] / sum[c];
+  }
+  if (cmdline.use_camera_wb && camera_red != -1) {
+    FORC4 count[c] = sum[c] = 0;
+    for (row=0; row < 8; row++)
+      for (col=0; col < 8; col++) {
+    c = FC(row,col);
+    if ((val = white[row][col] - black) > 0)
+      sum[c] += val;
+    count[c]++;
+      }
+    val = 1;
+    FORC4 if (sum[c] == 0) val = 0;
+    if (val)
+      FORC4 pre_mul[c] = count[c] / sum[c];
+    else if (camera_red && camera_blue)
+      memcpy (pre_mul, cam_mul, sizeof pre_mul);
+    else
+      pm_message ("Cannot use camera white balance.");
+  }
+  if (!use_coeff) {
+    pre_mul[0] *= cmdline.red_scale;
+    pre_mul[2] *= cmdline.blue_scale;
+  }
+  dmin = DBL_MAX;
+  FORC4 if (dmin > pre_mul[c])
+        dmin = pre_mul[c];
+  FORC4 pre_mul[c] /= dmin;
+
+  while (maximum << shift < 0x8000) shift++;
+  FORC4 pre_mul[c] *= 1 << shift;
+  maximum <<= shift;
+
+  if (cmdline.linear || cmdline.bright < 1) {
+      maximum *= cmdline.bright;
+      if (maximum > 0xffff)
+          maximum = 0xffff;
+      FORC4 pre_mul[c] *= cmdline.bright;
+  }
+  if (cmdline.verbose) {
+    fprintf (stderr, "Scaling with black=%d, pre_mul[] =", black);
+    FORC4 fprintf (stderr, " %f", pre_mul[c]);
+    fputc ('\n', stderr);
+  }
+  clip_max = cmdline.no_clip_color ? 0xffff : maximum;
+  for (row=0; row < height; row++)
+    for (col=0; col < width; col++)
+      FORC4 {
+    val = image[row*width+col][c];
+    if (!val) continue;
+    val -= black;
+    val *= pre_mul[c];
+    if (val < 0) val = 0;
+    if (val > clip_max) val = clip_max;
+    image[row*width+col][c] = val;
+      }
+}
+
+/*
+   This algorithm is officially called:
+
+   "Interpolation using a Threshold-based variable number of gradients"
+
+   described in http://www-ise.stanford.edu/~tingchen/algodep/vargra.html
+
+   I've extended the basic idea to work with non-Bayer filter arrays.
+   Gradients are numbered clockwise from NW=0 to W=7.
+ */
+static void CLASS vng_interpolate()
+{
+  static const signed char *cp, terms[] = {
+    -2,-2,+0,-1,0,(char)0x01, -2,-2,+0,+0,1,(char)0x01, -2,-1,-1,+0,0,(char)0x01,
+    -2,-1,+0,-1,0,(char)0x02, -2,-1,+0,+0,0,(char)0x03, -2,-1,+0,+1,1,(char)0x01,
+    -2,+0,+0,-1,0,(char)0x06, -2,+0,+0,+0,1,(char)0x02, -2,+0,+0,+1,0,(char)0x03,
+    -2,+1,-1,+0,0,(char)0x04, -2,+1,+0,-1,1,(char)0x04, -2,+1,+0,+0,0,(char)0x06,
+    -2,+1,+0,+1,0,(char)0x02, -2,+2,+0,+0,1,(char)0x04, -2,+2,+0,+1,0,(char)0x04,
+    -1,-2,-1,+0,0,(char)0x80, -1,-2,+0,-1,0,(char)0x01, -1,-2,+1,-1,0,(char)0x01,
+    -1,-2,+1,+0,1,(char)0x01, -1,-1,-1,+1,0,(char)0x88, -1,-1,+1,-2,0,(char)0x40,
+    -1,-1,+1,-1,0,(char)0x22, -1,-1,+1,+0,0,(char)0x33, -1,-1,+1,+1,1,(char)0x11,
+    -1,+0,-1,+2,0,(char)0x08, -1,+0,+0,-1,0,(char)0x44, -1,+0,+0,+1,0,(char)0x11,
+    -1,+0,+1,-2,1,(char)0x40, -1,+0,+1,-1,0,(char)0x66, -1,+0,+1,+0,1,(char)0x22,
+    -1,+0,+1,+1,0,(char)0x33, -1,+0,+1,+2,1,(char)0x10, -1,+1,+1,-1,1,(char)0x44,
+    -1,+1,+1,+0,0,(char)0x66, -1,+1,+1,+1,0,(char)0x22, -1,+1,+1,+2,0,(char)0x10,
+    -1,+2,+0,+1,0,(char)0x04, -1,+2,+1,+0,1,(char)0x04, -1,+2,+1,+1,0,(char)0x04,
+    +0,-2,+0,+0,1,(char)0x80, +0,-1,+0,+1,1,(char)0x88, +0,-1,+1,-2,0,(char)0x40,
+    +0,-1,+1,+0,0,(char)0x11, +0,-1,+2,-2,0,(char)0x40, +0,-1,+2,-1,0,(char)0x20,
+    +0,-1,+2,+0,0,(char)0x30, +0,-1,+2,+1,1,(char)0x10, +0,+0,+0,+2,1,(char)0x08,
+    +0,+0,+2,-2,1,(char)0x40, +0,+0,+2,-1,0,(char)0x60, +0,+0,+2,+0,1,(char)0x20,
+    +0,+0,+2,+1,0,(char)0x30, +0,+0,+2,+2,1,(char)0x10, +0,+1,+1,+0,0,(char)0x44,
+    +0,+1,+1,+2,0,(char)0x10, +0,+1,+2,-1,1,(char)0x40, +0,+1,+2,+0,0,(char)0x60,
+    +0,+1,+2,+1,0,(char)0x20, +0,+1,+2,+2,0,(char)0x10, +1,-2,+1,+0,0,(char)0x80,
+    +1,-1,+1,+1,0,(char)0x88, +1,+0,+1,+2,0,(char)0x08, +1,+0,+2,-1,0,(char)0x40,
+    +1,+0,+2,+1,0,(char)0x10
+  }, chood[] = { -1,-1, -1,0, -1,+1, 0,+1, +1,+1, +1,0, +1,-1, 0,-1 };
+  unsigned short (*brow[5])[4], *pix;
+  int code[8][2][320], *ip, gval[8], gmin, gmax, sum[4];
+  int row, col, shift, x, y, x1, x2, y1, y2, t, weight, grads, color, diag;
+  int g, diff, thold, num, c;
+
+  for (row=0; row < 8; row++) {     /* Precalculate for bilinear */
+    for (col=1; col < 3; col++) {
+      ip = code[row][col & 1];
+      memset (sum, 0, sizeof sum);
+      for (y=-1; y <= 1; y++)
+    for (x=-1; x <= 1; x++) {
+      shift = (y==0) + (x==0);
+      if (shift == 2) continue;
+      color = FC(row+y,col+x);
+      *ip++ = (width*y + x)*4 + color;
+      *ip++ = shift;
+      *ip++ = color;
+      sum[color] += 1 << shift;
+    }
+      FORC4
+    if (c != FC(row,col)) {
+      *ip++ = c;
+      *ip++ = sum[c];
+    }
+    }
+  }
+  for (row=1; row < height-1; row++) {  /* Do bilinear interpolation */
+    for (col=1; col < width-1; col++) {
+      pix = image[row*width+col];
+      ip = code[row & 7][col & 1];
+      memset (sum, 0, sizeof sum);
+      for (g=8; g--; ) {
+    diff = pix[*ip++];
+    diff <<= *ip++;
+    sum[*ip++] += diff;
+      }
+      for (g=colors; --g; ) {
+    c = *ip++;
+    pix[c] = sum[c] / *ip++;
+      }
+    }
+  }
+  if (cmdline.quick_interpolate)
+    return;
+
+  for (row=0; row < 8; row++) {     /* Precalculate for VNG */
+    for (col=0; col < 2; col++) {
+      ip = code[row][col];
+      for (cp=terms, t=0; t < 64; t++) {
+    y1 = *cp++;  x1 = *cp++;
+    y2 = *cp++;  x2 = *cp++;
+    weight = *cp++;
+    grads = *cp++;
+    color = FC(row+y1,col+x1);
+    if (FC(row+y2,col+x2) != color) continue;
+    diag = (FC(row,col+1) == color && FC(row+1,col) == color) ? 2:1;
+    if (abs(y1-y2) == diag && abs(x1-x2) == diag) continue;
+    *ip++ = (y1*width + x1)*4 + color;
+    *ip++ = (y2*width + x2)*4 + color;
+    *ip++ = weight;
+    for (g=0; g < 8; g++)
+      if (grads & 1<<g) *ip++ = g;
+    *ip++ = -1;
+      }
+      *ip++ = INT_MAX;
+      for (cp=chood, g=0; g < 8; g++) {
+    y = *cp++;  x = *cp++;
+    *ip++ = (y*width + x) * 4;
+    color = FC(row,col);
+    if (FC(row+y,col+x) != color && FC(row+y*2,col+x*2) == color)
+      *ip++ = (y*width + x) * 8 + color;
+    else
+      *ip++ = 0;
+      }
+    }
+  }
+  brow[4] = calloc (width*3, sizeof **brow);
+  merror (brow[4], "vng_interpolate()");
+  for (row=0; row < 3; row++)
+    brow[row] = brow[4] + row*width;
+  for (row=2; row < height-2; row++) {      /* Do VNG interpolation */
+    for (col=2; col < width-2; col++) {
+      pix = image[row*width+col];
+      ip = code[row & 7][col & 1];
+      memset (gval, 0, sizeof gval);
+      while ((g = ip[0]) != INT_MAX) {      /* Calculate gradients */
+    num = (diff = pix[g] - pix[ip[1]]) >> 31;
+    gval[ip[3]] += (diff = ((diff ^ num) - num) << ip[2]);
+    ip += 5;
+    if ((g = ip[-1]) == -1) continue;
+    gval[g] += diff;
+    while ((g = *ip++) != -1)
+      gval[g] += diff;
+      }
+      ip++;
+      gmin = gmax = gval[0];            /* Choose a threshold */
+      for (g=1; g < 8; g++) {
+    if (gmin > gval[g]) gmin = gval[g];
+    if (gmax < gval[g]) gmax = gval[g];
+      }
+      if (gmax == 0) {
+    memcpy (brow[2][col], pix, sizeof *image);
+    continue;
+      }
+      thold = gmin + (gmax >> 1);
+      memset (sum, 0, sizeof sum);
+      color = FC(row,col);
+      for (num=g=0; g < 8; g++,ip+=2) {     /* Average the neighbors */
+    if (gval[g] <= thold) {
+      FORC4
+        if (c == color && ip[1])
+          sum[c] += (pix[c] + pix[ip[1]]) >> 1;
+        else
+          sum[c] += pix[ip[0] + c];
+      num++;
+    }
+      }
+      FORC4 {                   /* Save to buffer */
+    t = pix[color];
+    if (c != color) {
+      t += (sum[c] - sum[color])/num;
+      if (t < 0) t = 0;
+      if (t > clip_max) t = clip_max;
+    }
+    brow[2][col][c] = t;
+      }
+    }
+    if (row > 3)                /* Write buffer to image */
+      memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image);
+    for (g=0; g < 4; g++)
+      brow[(g-1) & 3] = brow[g];
+  }
+  memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image);
+  memcpy (image[(row-1)*width+2], brow[1]+2, (width-4)*sizeof *image);
+  free (brow[4]);
+}
+
+#ifdef USE_LCMS
+static void 
+apply_profile(FILE *       const ifP,
+              const char * const pfname)
+{
+  char *prof;
+  cmsHPROFILE hInProfile=NULL, hOutProfile;
+  cmsHTRANSFORM hTransform;
+
+  if (pfname)
+    hInProfile = cmsOpenProfileFromFile (pfname, "r");
+  else if (profile_length) {
+    prof = malloc (profile_length);
+    merror (prof, "apply_profile()");
+    fseek (ifP, profile_offset, SEEK_SET);
+    fread (prof, 1, profile_length, ifP);
+    hInProfile = cmsOpenProfileFromMem (prof, profile_length);
+    free (prof);
+  }
+  if (!hInProfile) return;
+  if (cmdline.verbose)
+      pm_message( "Applying color profile...");
+  maximum = 0xffff;
+  use_gamma = use_coeff = 0;
+
+  hOutProfile = cmsCreate_sRGBProfile();
+  hTransform = cmsCreateTransform (hInProfile, TYPE_RGBA_16,
+    hOutProfile, TYPE_RGBA_16, INTENT_PERCEPTUAL, 0);
+  cmsDoTransform (hTransform, image, image, width*height);
+
+  cmsDeleteTransform (hTransform);
+  cmsCloseProfile (hInProfile);
+  cmsCloseProfile (hOutProfile);
+}
+#else
+static void 
+apply_profile(FILE *       const ifP,
+              const char * const pfname)
+{
+}
+#endif
+
+/*
+   Convert the entire image to RGB colorspace and build a histogram.
+ */
+static void CLASS convert_to_rgb()
+{
+  int row, col, r, g, c=0;
+  unsigned short *img;
+  float rgb[3];
+
+  if (cmdline.document_mode)
+    colors = 1;
+  memset (histogram, 0, sizeof histogram);
+  for (row = trim; row < height-trim; row++)
+    for (col = trim; col < width-trim; col++) {
+      img = image[row*width+col];
+      if (cmdline.document_mode)
+    c = FC(row,col);
+      if (colors == 4 && !use_coeff)    /* Recombine the greens */
+    img[1] = (img[1] + img[3]) >> 1;
+      if (colors == 1)          /* RGB from grayscale */
+    for (r=0; r < 3; r++)
+      rgb[r] = img[c];
+      else if (use_coeff) {     /* RGB via coeff[][] */
+    for (r=0; r < 3; r++)
+      for (rgb[r]=g=0; g < colors; g++)
+        rgb[r] += img[g] * coeff[r][g];
+      } else                /* RGB from RGB (easy) */
+    goto norgb;
+      for (r=0; r < 3; r++) {
+    if (rgb[r] < 0)        rgb[r] = 0;
+    if (rgb[r] > clip_max) rgb[r] = clip_max;
+    img[r] = rgb[r];
+      }
+norgb:
+      for (r=0; r < 3; r++)
+    histogram[r][img[r] >> 3]++;
+    }
+}
+
+static void CLASS fuji_rotate()
+{
+  int i, wide, high, row, col;
+  double step;
+  float r, c, fr, fc;
+  unsigned ur, uc;
+  unsigned short (*img)[4], (*pix)[4];
+
+  if (!fuji_width) return;
+  if (cmdline.verbose)
+    pm_message ("Rotating image 45 degrees...");
+  fuji_width = (fuji_width + shrink) >> shrink;
+  step = sqrt(0.5);
+  wide = fuji_width / step;
+  high = (height - fuji_width) / step;
+  img = calloc (wide*high, sizeof *img);
+  merror (img, "fuji_rotate()");
+
+  for (row=0; row < high; row++)
+    for (col=0; col < wide; col++) {
+      ur = r = fuji_width + (row-col)*step;
+      uc = c = (row+col)*step;
+      if (ur > height-2 || uc > width-2) continue;
+      fr = r - ur;
+      fc = c - uc;
+      pix = image + ur*width + uc;
+      for (i=0; i < colors; i++)
+    img[row*wide+col][i] =
+      (pix[    0][i]*(1-fc) + pix[      1][i]*fc) * (1-fr) +
+      (pix[width][i]*(1-fc) + pix[width+1][i]*fc) * fr;
+    }
+  free (image);
+  width  = wide;
+  height = high;
+  image  = img;
+  fuji_width = 0;
+}
+
+static void CLASS flip_image()
+{
+    unsigned *flag;
+    int size, base, dest, next, row, col, temp;
+
+    struct imageCell {
+        unsigned char contents[8];
+    };
+    struct imageCell * img;
+    struct imageCell hold;
+
+    switch ((flip+3600) % 360) {
+    case 270:  flip = 5;  break;
+    case 180:  flip = 3;  break;
+    case  90:  flip = 6;
+    }
+    img = (struct imageCell *) image;
+    size = height * width;
+    flag = calloc ((size+31) >> 5, sizeof *flag);
+    merror (flag, "flip_image()");
+    for (base = 0; base < size; base++) {
+        if (flag[base >> 5] & (1 << (base & 31)))
+            continue;
+        dest = base;
+        hold = img[base];
+        while (1) {
+            if (flip & 4) {
+                row = dest % height;
+                col = dest / height;
+            } else {
+                row = dest / width;
+                col = dest % width;
+            }
+            if (flip & 2)
+                row = height - 1 - row;
+            if (flip & 1)
+                col = width - 1 - col;
+            next = row * width + col;
+            if (next == base) break;
+            flag[next >> 5] |= 1 << (next & 31);
+            img[dest] = img[next];
+            dest = next;
+        }
+        img[dest] = hold;
+    }
+    free (flag);
+    if (flip & 4) {
+        temp = height;
+        height = width;
+        width = temp;
+        temp = ymag;
+        ymag = xmag;
+        xmag = temp;
+    }
+}
+
+/*
+   Write the image as an RGB PAM image
+ */
+static void CLASS write_pam_nonlinear (FILE *ofp)
+{
+  unsigned char lut[0x10000];
+  int perc, c, val, total, i, row, col;
+  float white=0, r;
+  struct pam pam;
+  tuple * tuplerow;
+
+  pam.size   = sizeof(pam);
+  pam.len    = PAM_STRUCT_SIZE(tuple_type);
+  pam.file   = ofp;
+  pam.width  = xmag*(width-trim*2);
+  pam.height = ymag*(height-trim*2);
+  pam.depth  = 3;
+  pam.format = PAM_FORMAT;
+  pam.maxval = 255;
+  strcpy(pam.tuple_type, "RGB");
+
+  pnm_writepaminit(&pam);
+
+  tuplerow = pnm_allocpamrow(&pam);
+
+  perc = width * height * 0.01;     /* 99th percentile white point */
+  if (fuji_width) perc /= 2;
+  FORC3 {
+    for (val=0x2000, total=0; --val > 32; )
+      if ((total += histogram[c][val]) > perc) break;
+    if (white < val) white = val;
+  }
+  white *= 8 / cmdline.bright;
+  for (i=0; i < 0x10000; i++) {
+    r = i / white;
+    val = 256 * ( !use_gamma ? r :
+    r <= 0.018 ? r*4.5 : pow(r,0.45)*1.099-0.099 );
+    if (val > 255) val = 255;
+    lut[i] = val;
+  }
+  for (row=trim; row < height-trim; row++) {
+      for (col=trim; col < width-trim; col++) {
+          unsigned int plane;
+          for (plane=0; plane < pam.depth; ++plane) {
+              unsigned int copy;
+              for (copy=0; copy < xmag; ++copy) {
+                  unsigned int const pamcol = xmag*(col-trim)+copy;
+                  tuplerow[pamcol][plane] = lut[image[row*width+col][plane]];
+              }
+          }
+      }
+      {
+          unsigned int copy;
+          for (copy=0; copy < ymag; ++copy)
+              pnm_writepamrow(&pam, tuplerow);
+      }
+  }
+  pnm_freepamrow(tuplerow);
+}
+
+/*
+   Write the image to a 16-bit PAM file with linear color space
+ */
+static void CLASS write_pam_linear (FILE *ofp)
+{
+  int row;
+
+  struct pam pam;
+  tuple * tuplerow;
+
+  if (maximum < 256) maximum = 256;
+
+  pam.size   = sizeof(pam);
+  pam.len    = PAM_STRUCT_SIZE(tuple_type);
+  pam.file   = ofp;
+  pam.width  = width-trim*2;
+  pam.height = height-trim*2;
+  pam.depth  = 3;
+  pam.format = PAM_FORMAT;
+  pam.maxval = MAX(maximum, 256);
+  strcpy(pam.tuple_type, "RGB");
+
+  pnm_writepaminit(&pam);
+
+  tuplerow = pnm_allocpamrow(&pam);
+
+  for (row = trim; row < height-trim; row++) {
+      unsigned int col;
+      for (col = trim; col < width-trim; col++) {
+          unsigned int const pamCol = col - trim;
+          unsigned int plane;
+          for (plane = 0; plane < 3; ++plane)
+              tuplerow[pamCol][plane] = image[row*width+col][plane];
+      }
+      pnm_writepamrow(&pam, tuplerow);
+  }
+  pnm_freepamrow(tuplerow);
+}
+
+
+
+static void CLASS
+writePam(FILE * const ofP,
+         bool   const linear) {
+
+    if (linear)
+        write_pam_linear(ofP);
+    else
+        write_pam_nonlinear(ofP);
+}
+
+
+
+
+static void CLASS
+convertIt(FILE *    const ifP,
+          FILE *    const ofP,
+          loadRawFn const load_raw) {
+
+    shrink = cmdline.half_size && filters;
+    iheight = (height + shrink) >> shrink;
+    iwidth  = (width  + shrink) >> shrink;
+    image = calloc (iheight*iwidth*sizeof *image + meta_length, 1);
+    merror (image, "main()");
+    meta_data = (char *) (image + iheight*iwidth);
+    if (cmdline.verbose)
+        pm_message ("Loading %s %s image ...", make, model);
+
+    use_secondary = cmdline.use_secondary;  /* for load_raw() */
+
+    ifp = ifP;  /* Set global variable for (*load_raw)() */
+
+    load_raw();
+    bad_pixels();
+    height = iheight;
+    width  = iwidth;
+    if (is_foveon) {
+        if (cmdline.verbose)
+            pm_message ("Foveon interpolation...");
+        foveon_interpolate(coeff);
+    } else {
+        scale_colors();
+    }
+    if (shrink) filters = 0;
+    trim = 0;
+    if (filters && !cmdline.document_mode) {
+        trim = 1;
+        if (cmdline.verbose)
+            pm_message ("%s interpolation...",
+                        cmdline.quick_interpolate ? "Bilinear":"VNG");
+        vng_interpolate();
+    }
+    fuji_rotate();
+    apply_profile(ifP, cmdline.profile);
+    if (cmdline.verbose)
+        pm_message ("Converting to RGB colorspace...");
+    convert_to_rgb();
+
+    if (flip) {
+        if (cmdline.verbose)
+            pm_message ("Flipping image %c:%c:%c...",
+                        flip & 1 ? 'H':'0', flip & 2 ? 'V':'0', 
+                        flip & 4 ? 'T':'0');
+        flip_image();
+    }
+    writePam(ofP, cmdline.linear);
+}
+
+
+int 
+main (int argc, char **argv) {
+
+    FILE * const ofP = stdout;
+
+    FILE * ifP;
+    int rc;
+    loadRawFn load_raw;
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    verbose = cmdline.verbose;
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    image = NULL;
+    
+    rc = identify(ifP,
+                  cmdline.use_secondary, cmdline.use_camera_rgb,
+                  cmdline.red_scale, cmdline.blue_scale,
+                  cmdline.four_color_rgb, cmdline.inputFileName,
+                  &load_raw);
+    if (rc != 0)
+        pm_error("Unable to identify the format of the input image");
+    else {
+        if (cmdline.identify_only) {
+            pm_message ("Input is a %s %s image.", make, model);
+        } else {
+            if (cmdline.verbose)
+                pm_message ("Input is a %s %s image.", make, model);
+            convertIt(ifP, ofP, load_raw);
+        }
+    }
+    pm_close(ifP);
+    pm_close(ofP);
+    free(image);
+
+    return 0;
+}
+
+
+
diff --git a/converter/other/cameratopam/canon.c b/converter/other/cameratopam/canon.c
new file mode 100644
index 00000000..a34771d0
--- /dev/null
+++ b/converter/other/cameratopam/canon.c
@@ -0,0 +1,172 @@
+#include <string.h>
+#include "mallocvar.h"
+#include "pm.h"
+#include "global_variables.h"
+#include "util.h"
+#include "decode.h"
+#include "bayer.h"
+#include "canon.h"
+
+
+void 
+canon_600_load_raw(void) {
+    unsigned char  data[1120], *dp;
+    unsigned short pixel[896], *pix;
+    int irow, orow, col;
+
+    for (irow=orow=0; irow < height; irow++)
+    {
+        fread (data, 1120, 1, ifp);
+        for (dp=data, pix=pixel; dp < data+1120; dp+=10, pix+=8)
+        {
+            pix[0] = (dp[0] << 2) + (dp[1] >> 6    );
+            pix[1] = (dp[2] << 2) + (dp[1] >> 4 & 3);
+            pix[2] = (dp[3] << 2) + (dp[1] >> 2 & 3);
+            pix[3] = (dp[4] << 2) + (dp[1]      & 3);
+            pix[4] = (dp[5] << 2) + (dp[9]      & 3);
+            pix[5] = (dp[6] << 2) + (dp[9] >> 2 & 3);
+            pix[6] = (dp[7] << 2) + (dp[9] >> 4 & 3);
+            pix[7] = (dp[8] << 2) + (dp[9] >> 6    );
+        }
+        for (col=0; col < width; col++)
+            BAYER(orow,col) = pixel[col];
+        for (col=width; col < 896; col++)
+            black += pixel[col];
+        if ((orow+=2) > height)
+            orow = 1;
+    }
+    black /= (896 - width) * height;
+    maximum = 0x3ff;
+}
+
+
+
+void
+canon_a5_load_raw(void) {
+    unsigned char  data[1940], *dp;
+    unsigned short pixel[1552], *pix;
+    int row, col;
+
+    for (row=0; row < height; row++) {
+        fread (data, raw_width * 10 / 8, 1, ifp);
+        for (dp=data, pix=pixel; pix < pixel+raw_width; dp+=10, pix+=8)
+        {
+            pix[0] = (dp[1] << 2) + (dp[0] >> 6);
+            pix[1] = (dp[0] << 4) + (dp[3] >> 4);
+            pix[2] = (dp[3] << 6) + (dp[2] >> 2);
+            pix[3] = (dp[2] << 8) + (dp[5]     );
+            pix[4] = (dp[4] << 2) + (dp[7] >> 6);
+            pix[5] = (dp[7] << 4) + (dp[6] >> 4);
+            pix[6] = (dp[6] << 6) + (dp[9] >> 2);
+            pix[7] = (dp[9] << 8) + (dp[8]     );
+        }
+        for (col=0; col < width; col++)
+            BAYER(row,col) = (pixel[col] & 0x3ff);
+        for (col=width; col < raw_width; col++)
+            black += pixel[col] & 0x3ff;
+    }
+    if (raw_width > width)
+        black /= (raw_width - width) * height;
+    maximum = 0x3ff;
+}
+
+
+
+/*
+   Return 0 if the image starts with compressed data,
+   1 if it starts with uncompressed low-order bits.
+
+   In Canon compressed data, 0xff is always followed by 0x00.
+ */
+static int
+canon_has_lowbits()
+{
+    unsigned char test[0x4000];
+    int ret=1, i;
+
+    fseek (ifp, 0, SEEK_SET);
+    fread (test, 1, sizeof test, ifp);
+    for (i=540; i < sizeof test - 1; i++)
+        if (test[i] == 0xff) {
+            if (test[i+1]) return 1;
+            ret=0;
+        }
+    return ret;
+}
+
+
+
+void 
+canon_compressed_load_raw(void) {
+    unsigned short *pixel, *prow;
+    int lowbits, i, row, r, col, save, val;
+    unsigned irow, icol;
+    struct decode *decode, *dindex;
+    int block, diffbuf[64], leaf, len, diff, carry=0, pnum=0, base[2];
+    unsigned char c;
+
+    MALLOCARRAY(pixel, raw_width*8);
+    if (pixel == NULL)
+        pm_error("Unable to allocate space for %u pixels", raw_width*8);
+    lowbits = canon_has_lowbits();
+    if (!lowbits) maximum = 0x3ff;
+    fseek (ifp, 540 + lowbits*raw_height*raw_width/4, SEEK_SET);
+    zero_after_ff = 1;
+    getbits(ifp, -1);
+    for (row = 0; row < raw_height; row += 8) {
+        for (block=0; block < raw_width >> 3; block++) {
+            memset (diffbuf, 0, sizeof diffbuf);
+            decode = first_decode;
+            for (i=0; i < 64; i++ ) {
+                for (dindex=decode; dindex->branch[0]; )
+                    dindex = dindex->branch[getbits(ifp, 1)];
+                leaf = dindex->leaf;
+                decode = second_decode;
+                if (leaf == 0 && i) break;
+                if (leaf == 0xff) continue;
+                i  += leaf >> 4;
+                len = leaf & 15;
+                if (len == 0) continue;
+                diff = getbits(ifp, len);
+                if ((diff & (1 << (len-1))) == 0)
+                    diff -= (1 << len) - 1;
+                if (i < 64) diffbuf[i] = diff;
+            }
+            diffbuf[0] += carry;
+            carry = diffbuf[0];
+            for (i=0; i < 64; i++ ) {
+                if (pnum++ % raw_width == 0)
+                    base[0] = base[1] = 512;
+                pixel[(block << 6) + i] = ( base[i & 1] += diffbuf[i] );
+            }
+        }
+        if (lowbits) {
+            save = ftell(ifp);
+            fseek (ifp, 26 + row*raw_width/4, SEEK_SET);
+            for (prow=pixel, i=0; i < raw_width*2; i++) {
+                c = fgetc(ifp);
+                for (r=0; r < 8; r+=2, prow++) {
+                    val = (*prow << 2) + ((c >> r) & 3);
+                    if (raw_width == 2672 && val < 512) val += 2;
+                    *prow = val;
+                }
+            }
+            fseek (ifp, save, SEEK_SET);
+        }
+        for (r=0; r < 8; r++) {
+            irow = row - top_margin + r;
+            if (irow >= height) continue;
+            for (col = 0; col < raw_width; col++) {
+                icol = col - left_margin;
+                if (icol < width)
+                    BAYER(irow,icol) = pixel[r*raw_width+col];
+                else
+                    black += pixel[r*raw_width+col];
+            }
+        }
+    }
+    free (pixel);
+    if (raw_width > width)
+        black /= (raw_width - width) * height;
+}
+
diff --git a/converter/other/cameratopam/canon.h b/converter/other/cameratopam/canon.h
new file mode 100644
index 00000000..041cdc4d
--- /dev/null
+++ b/converter/other/cameratopam/canon.h
@@ -0,0 +1,13 @@
+#ifndef CANON_H_INCLUDED
+#define CANON_H_INCLUDED
+
+void 
+canon_600_load_raw(void);
+
+void
+canon_a5_load_raw(void);
+
+void 
+canon_compressed_load_raw(void);
+
+#endif
diff --git a/converter/other/cameratopam/decode.c b/converter/other/cameratopam/decode.c
new file mode 100644
index 00000000..e3a62fab
--- /dev/null
+++ b/converter/other/cameratopam/decode.c
@@ -0,0 +1,172 @@
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "pm.h"
+
+#include "decode.h"
+
+struct decode first_decode[2048], *second_decode, *free_decode;
+
+
+
+void init_decoder() {
+  memset (first_decode, 0, sizeof first_decode);
+  free_decode = first_decode;
+}
+
+
+
+/*
+   Construct a decode tree according the specification in *source.
+   The first 16 bytes specify how many codes should be 1-bit, 2-bit
+   3-bit, etc.  Bytes after that are the leaf values.
+
+   For example, if the source is
+
+    { 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0,
+      0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b,0xff  },
+
+   then the code is
+
+    00      0x04
+    010     0x03
+    011     0x05
+    100     0x06
+    101     0x02
+    1100        0x07
+    1101        0x01
+    11100       0x08
+    11101       0x09
+    11110       0x00
+    111110      0x0a
+    1111110     0x0b
+    1111111     0xff
+ */
+
+
+unsigned char * 
+make_decoder(const unsigned char * const source, 
+             int                   const level) {
+
+    struct decode *cur;
+    static int leaf;
+    int i, next;
+
+    if (level==0) 
+        leaf=0;
+    cur = free_decode++;
+    if (free_decode > first_decode+2048) {
+        pm_error ("decoder table overflow");
+    }
+    for (i=next=0; i <= leaf && next < 16; )
+        i += source[next++];
+    if (i > leaf) {
+        if (level < next) {
+            cur->branch[0] = free_decode;
+            make_decoder (source, level+1);
+            cur->branch[1] = free_decode;
+            make_decoder (source, level+1);
+        } else
+            cur->leaf = source[16 + leaf++];
+    }
+    return (unsigned char *) source + 16 + leaf;
+}
+
+
+
+const int * 
+make_decoder_int(const int * const source,
+                 int         const level) {
+
+    const int * p;
+    struct decode *cur;
+
+    p = source;  /* initial value */
+
+    cur = free_decode++;
+    if (level < p[0]) {
+        cur->branch[0] = free_decode;
+        p = make_decoder_int (p, level+1);
+        cur->branch[1] = free_decode;
+        p = make_decoder_int (p, level+1);
+    } else {
+        cur->leaf = p[1];
+        p += 2;
+    }
+    return p;
+}
+
+
+
+void 
+crw_init_tables(unsigned int const table) {
+
+  unsigned int const clippedTableNum = MIN(2, table);
+
+  static const unsigned char first_tree[3][29] = {
+    { 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0,
+      0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b,0xff  },
+
+    { 0,2,2,3,1,1,1,1,2,0,0,0,0,0,0,0,
+      0x03,0x02,0x04,0x01,0x05,0x00,0x06,0x07,0x09,0x08,0x0a,0x0b,0xff  },
+
+    { 0,0,6,3,1,1,2,0,0,0,0,0,0,0,0,0,
+      0x06,0x05,0x07,0x04,0x08,0x03,0x09,0x02,0x00,0x0a,0x01,0x0b,0xff  },
+  };
+
+  static const unsigned char second_tree[3][180] = {
+    { 0,2,2,2,1,4,2,1,2,5,1,1,0,0,0,139,
+      0x03,0x04,0x02,0x05,0x01,0x06,0x07,0x08,
+      0x12,0x13,0x11,0x14,0x09,0x15,0x22,0x00,0x21,0x16,0x0a,0xf0,
+      0x23,0x17,0x24,0x31,0x32,0x18,0x19,0x33,0x25,0x41,0x34,0x42,
+      0x35,0x51,0x36,0x37,0x38,0x29,0x79,0x26,0x1a,0x39,0x56,0x57,
+      0x28,0x27,0x52,0x55,0x58,0x43,0x76,0x59,0x77,0x54,0x61,0xf9,
+      0x71,0x78,0x75,0x96,0x97,0x49,0xb7,0x53,0xd7,0x74,0xb6,0x98,
+      0x47,0x48,0x95,0x69,0x99,0x91,0xfa,0xb8,0x68,0xb5,0xb9,0xd6,
+      0xf7,0xd8,0x67,0x46,0x45,0x94,0x89,0xf8,0x81,0xd5,0xf6,0xb4,
+      0x88,0xb1,0x2a,0x44,0x72,0xd9,0x87,0x66,0xd4,0xf5,0x3a,0xa7,
+      0x73,0xa9,0xa8,0x86,0x62,0xc7,0x65,0xc8,0xc9,0xa1,0xf4,0xd1,
+      0xe9,0x5a,0x92,0x85,0xa6,0xe7,0x93,0xe8,0xc1,0xc6,0x7a,0x64,
+      0xe1,0x4a,0x6a,0xe6,0xb3,0xf1,0xd3,0xa5,0x8a,0xb2,0x9a,0xba,
+      0x84,0xa4,0x63,0xe5,0xc5,0xf3,0xd2,0xc4,0x82,0xaa,0xda,0xe4,
+      0xf2,0xca,0x83,0xa3,0xa2,0xc3,0xea,0xc2,0xe2,0xe3,0xff,0xff  },
+
+    { 0,2,2,1,4,1,4,1,3,3,1,0,0,0,0,140,
+      0x02,0x03,0x01,0x04,0x05,0x12,0x11,0x06,
+      0x13,0x07,0x08,0x14,0x22,0x09,0x21,0x00,0x23,0x15,0x31,0x32,
+      0x0a,0x16,0xf0,0x24,0x33,0x41,0x42,0x19,0x17,0x25,0x18,0x51,
+      0x34,0x43,0x52,0x29,0x35,0x61,0x39,0x71,0x62,0x36,0x53,0x26,
+      0x38,0x1a,0x37,0x81,0x27,0x91,0x79,0x55,0x45,0x28,0x72,0x59,
+      0xa1,0xb1,0x44,0x69,0x54,0x58,0xd1,0xfa,0x57,0xe1,0xf1,0xb9,
+      0x49,0x47,0x63,0x6a,0xf9,0x56,0x46,0xa8,0x2a,0x4a,0x78,0x99,
+      0x3a,0x75,0x74,0x86,0x65,0xc1,0x76,0xb6,0x96,0xd6,0x89,0x85,
+      0xc9,0xf5,0x95,0xb4,0xc7,0xf7,0x8a,0x97,0xb8,0x73,0xb7,0xd8,
+      0xd9,0x87,0xa7,0x7a,0x48,0x82,0x84,0xea,0xf4,0xa6,0xc5,0x5a,
+      0x94,0xa4,0xc6,0x92,0xc3,0x68,0xb5,0xc8,0xe4,0xe5,0xe6,0xe9,
+      0xa2,0xa3,0xe3,0xc2,0x66,0x67,0x93,0xaa,0xd4,0xd5,0xe7,0xf8,
+      0x88,0x9a,0xd7,0x77,0xc4,0x64,0xe2,0x98,0xa5,0xca,0xda,0xe8,
+      0xf3,0xf6,0xa9,0xb2,0xb3,0xf2,0xd2,0x83,0xba,0xd3,0xff,0xff  },
+
+    { 0,0,6,2,1,3,3,2,5,1,2,2,8,10,0,117,
+      0x04,0x05,0x03,0x06,0x02,0x07,0x01,0x08,
+      0x09,0x12,0x13,0x14,0x11,0x15,0x0a,0x16,0x17,0xf0,0x00,0x22,
+      0x21,0x18,0x23,0x19,0x24,0x32,0x31,0x25,0x33,0x38,0x37,0x34,
+      0x35,0x36,0x39,0x79,0x57,0x58,0x59,0x28,0x56,0x78,0x27,0x41,
+      0x29,0x77,0x26,0x42,0x76,0x99,0x1a,0x55,0x98,0x97,0xf9,0x48,
+      0x54,0x96,0x89,0x47,0xb7,0x49,0xfa,0x75,0x68,0xb6,0x67,0x69,
+      0xb9,0xb8,0xd8,0x52,0xd7,0x88,0xb5,0x74,0x51,0x46,0xd9,0xf8,
+      0x3a,0xd6,0x87,0x45,0x7a,0x95,0xd5,0xf6,0x86,0xb4,0xa9,0x94,
+      0x53,0x2a,0xa8,0x43,0xf5,0xf7,0xd4,0x66,0xa7,0x5a,0x44,0x8a,
+      0xc9,0xe8,0xc8,0xe7,0x9a,0x6a,0x73,0x4a,0x61,0xc7,0xf4,0xc6,
+      0x65,0xe9,0x72,0xe6,0x71,0x91,0x93,0xa6,0xda,0x92,0x85,0x62,
+      0xf3,0xc5,0xb2,0xa4,0x84,0xba,0x64,0xa5,0xb3,0xd2,0x81,0xe5,
+      0xd3,0xaa,0xc4,0xca,0xf2,0xb1,0xe4,0xd1,0x83,0x63,0xea,0xc3,
+      0xe2,0x82,0xf1,0xa3,0xc2,0xa1,0xc1,0xe3,0xa2,0xe1,0xff,0xff  }
+  };
+
+  init_decoder();
+  make_decoder ( first_tree[clippedTableNum], 0);
+  second_decode = free_decode;
+  make_decoder (second_tree[clippedTableNum], 0);
+}
+
diff --git a/converter/other/cameratopam/decode.h b/converter/other/cameratopam/decode.h
new file mode 100644
index 00000000..b0addc82
--- /dev/null
+++ b/converter/other/cameratopam/decode.h
@@ -0,0 +1,22 @@
+struct decode {
+  struct decode *branch[2];
+  int leaf;
+}; 
+
+extern struct decode * free_decode;
+extern struct decode first_decode[2048];
+extern struct decode * second_decode;
+
+void 
+init_decoder(void);
+
+void 
+crw_init_tables(unsigned int const table);
+
+const int * 
+make_decoder_int (const int * const source, 
+                  int         const level);
+
+unsigned char * 
+make_decoder(const unsigned char * const source, 
+             int                   const level);
diff --git a/converter/other/cameratopam/dng.c b/converter/other/cameratopam/dng.c
new file mode 100644
index 00000000..44d45b02
--- /dev/null
+++ b/converter/other/cameratopam/dng.c
@@ -0,0 +1,73 @@
+#include <string.h>
+#include "global_variables.h"
+
+#include "dng.h"
+
+void 
+dng_coeff (double cc[4][4], double cm[4][3], double xyz[3])
+{
+  static const double rgb_xyz[3][3] = {     /* RGB from XYZ */
+    {  3.240479, -1.537150, -0.498535 },
+    { -0.969256,  1.875992,  0.041556 },
+    {  0.055648, -0.204043,  1.057311 } };
+#if 0
+  static const double xyz_rgb[3][3] = {     /* XYZ from RGB */
+    { 0.412453, 0.357580, 0.180423 },
+    { 0.212671, 0.715160, 0.072169 },
+    { 0.019334, 0.119193, 0.950227 } };
+#endif
+  double cam_xyz[4][3], xyz_cam[3][4], invert[3][6], num;
+  int i, j, k;
+
+  memset (cam_xyz, 0, sizeof cam_xyz);
+  for (i=0; i < colors; i++)
+    for (j=0; j < 3; j++)
+      for (k=0; k < colors; k++)
+    cam_xyz[i][j] += cc[i][k] * cm[k][j] * xyz[j];
+
+  for (i=0; i < colors; i++) {
+    for (num=j=0; j < 3; j++)
+      num += cam_xyz[i][j];
+    for (j=0; j < 3; j++)
+      cam_xyz[i][j] /= num;
+    pre_mul[i] = 1 / num;
+  }
+  for (i=0; i < 3; i++) {
+    for (j=0; j < 6; j++)
+      invert[i][j] = j == i+3;
+    for (j=0; j < 3; j++)
+      for (k=0; k < colors; k++)
+    invert[i][j] += cam_xyz[k][i] * cam_xyz[k][j];
+  }
+  for (i=0; i < 3; i++) {
+    num = invert[i][i];
+    for (j=0; j < 6; j++)       /* Normalize row i */
+      invert[i][j] /= num;
+    for (k=0; k < 3; k++) {     /* Subtract it from other rows */
+      if (k==i) continue;
+      num = invert[k][i];
+      for (j=0; j < 6; j++)
+    invert[k][j] -= invert[i][j] * num;
+    }
+  }
+  memset (xyz_cam, 0, sizeof xyz_cam);
+  for (i=0; i < 3; i++)
+    for (j=0; j < colors; j++)
+      for (k=0; k < 3; k++)
+    xyz_cam[i][j] += invert[i][k+3] * cam_xyz[j][k];
+
+  memset (coeff, 0, sizeof coeff);
+  for (i=0; i < 3; i++)
+    for (j=0; j < colors; j++)
+      for (k=0; k < 3; k++)
+    coeff[i][j] += rgb_xyz[i][k] * xyz_cam[k][j];
+
+  for (num=j=0; j < colors; j++)
+    num += coeff[1][j];
+  for (i=0; i < 3; i++) {
+    for (j=0; j < colors; j++)
+      coeff[i][j] /= num;
+  }
+  use_coeff = 1;
+}
+
diff --git a/converter/other/cameratopam/dng.h b/converter/other/cameratopam/dng.h
new file mode 100644
index 00000000..01e7e0a5
--- /dev/null
+++ b/converter/other/cameratopam/dng.h
@@ -0,0 +1,2 @@
+void 
+dng_coeff (double cc[4][4], double cm[4][3], double xyz[3]);
diff --git a/converter/other/cameratopam/foveon.c b/converter/other/cameratopam/foveon.c
new file mode 100644
index 00000000..0198940c
--- /dev/null
+++ b/converter/other/cameratopam/foveon.c
@@ -0,0 +1,790 @@
+/* This code is licensed to the public by its copyright owners under GPL. */
+
+#define _XOPEN_SOURCE   /* get M_PI */
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <float.h>
+#include <math.h>
+
+#include "mallocvar.h"
+#include "pm.h"
+#include "global_variables.h"
+#include "decode.h"
+#include "foveon.h"
+
+#if HAVE_INT64
+   typedef int64_t INT64;
+   static bool const have64BitArithmetic = true;
+#else
+   /* We define INT64 to something that lets the code compile, but we
+      should not execute any INT64 code, because it will get the wrong
+      result.
+   */
+   typedef int INT64;
+   static bool const have64BitArithmetic = false;
+#endif
+
+
+#define FORC3 for (c=0; c < 3; c++)
+#define FORC4 for (c=0; c < colors; c++)
+
+
+
+static char *  
+foveon_gets(int    const offset, 
+            char * const str, 
+            int    const len) {
+
+    unsigned int i;
+    fseek (ifp, offset, SEEK_SET);
+    for (i=0; i < len-1; ++i) {
+        /* It certains seems wrong that we're reading a 16 bit integer
+           and assigning it to char, but that's what Dcraw does.
+        */
+        unsigned short val;
+        pm_readlittleshortu(ifp, &val);
+        str[i] = val;
+        if (str[i] == 0)
+            break;
+    }
+    str[i] = 0;
+    return str;
+}
+
+
+
+void 
+parse_foveon(FILE * const ifp) {
+    long fliplong;
+    long pos;
+    long magic;
+    long junk;
+    long entries;
+
+    fseek (ifp, 36, SEEK_SET);
+    pm_readlittlelong(ifp, &fliplong);
+    flip = fliplong;
+    fseek (ifp, -4, SEEK_END);
+    pm_readlittlelong(ifp, &pos);
+    fseek (ifp, pos, SEEK_SET);
+    pm_readlittlelong(ifp, &magic);
+    if (magic != 0x64434553) 
+        return; /* SECd */
+    pm_readlittlelong(ifp, &junk);
+    pm_readlittlelong(ifp, &entries);
+    while (entries--) {
+        long off, len, tag;
+        long save;
+        long pent;
+        int i, poff[256][2];
+        char name[64];
+        long sec_;
+
+        pm_readlittlelong(ifp, &off);
+        pm_readlittlelong(ifp, &len);
+        pm_readlittlelong(ifp, &tag);
+            
+        save = ftell(ifp);
+        fseek (ifp, off, SEEK_SET);
+        pm_readlittlelong(ifp, &sec_);
+        if (sec_ != (0x20434553 | (tag << 24))) return;
+        switch (tag) {
+        case 0x47414d49:          /* IMAG */
+            if (data_offset) 
+                break;
+            data_offset = off + 28;
+            fseek (ifp, 12, SEEK_CUR);
+            {
+                long wlong, hlong;
+                pm_readlittlelong(ifp, &wlong);
+                pm_readlittlelong(ifp, &hlong);
+                raw_width  = wlong;
+                raw_height = hlong;
+            }
+            break;
+        case 0x464d4143:          /* CAMF */
+            meta_offset = off + 24;
+            meta_length = len - 28;
+            if (meta_length > 0x20000)
+                meta_length = 0x20000;
+            break;
+        case 0x504f5250:          /* PROP */
+            pm_readlittlelong(ifp, &junk);
+            pm_readlittlelong(ifp, &pent);
+            fseek (ifp, 12, SEEK_CUR);
+            off += pent*8 + 24;
+            if (pent > 256) pent=256;
+            for (i=0; i < pent*2; i++) {
+                long x;
+                pm_readlittlelong(ifp, &x);
+                poff[0][i] = off + x*2;
+            }
+            for (i=0; i < pent; i++) {
+                foveon_gets (poff[i][0], name, 64);
+                if (!strcmp (name, "CAMMANUF"))
+                    foveon_gets (poff[i][1], make, 64);
+                if (!strcmp (name, "CAMMODEL"))
+                    foveon_gets (poff[i][1], model, 64);
+                if (!strcmp (name, "WB_DESC"))
+                    foveon_gets (poff[i][1], model2, 64);
+                if (!strcmp (name, "TIME"))
+                    timestamp = atoi (foveon_gets (poff[i][1], name, 64));
+            }
+        }
+        fseek (ifp, save, SEEK_SET);
+    }
+    is_foveon = 1;
+}
+
+
+
+void  
+foveon_coeff(bool * const useCoeffP,
+             float        coeff[3][4]) {
+
+    static const float foveon[3][3] =
+    { {  1.4032, -0.2231, -0.1016 },
+      { -0.5263,  1.4816,  0.0170 },
+      { -0.0112,  0.0183,  0.9113 } };
+
+    int i, j;
+
+    for (i=0; i < 3; ++i)
+        for (j=0; j < 3; ++j)
+            coeff[i][j] = foveon[i][j];
+    *useCoeffP = 1;
+}
+
+
+
+static void  
+foveon_decoder (unsigned int const huff[1024], 
+                unsigned int const code) {
+
+    struct decode *cur;
+    int len;
+    unsigned int code2;
+
+    cur = free_decode++;
+    if (free_decode > first_decode+2048) {
+        pm_error ("decoder table overflow");
+    }
+    if (code) {
+        unsigned int i;
+        for (i=0; i < 1024; i++) {
+            if (huff[i] == code) {
+                cur->leaf = i;
+                return;
+            }
+        }
+    }
+    if ((len = code >> 27) > 26) 
+        return;
+    code2 = (len+1) << 27 | (code & 0x3ffffff) << 1;
+
+    cur->branch[0] = free_decode;
+    foveon_decoder (huff, code2);
+    cur->branch[1] = free_decode;
+    foveon_decoder (huff, code2+1);
+}
+
+
+
+static void  
+foveon_load_camf() {
+    unsigned int i, val;
+    long key;
+
+    fseek (ifp, meta_offset, SEEK_SET);
+    pm_readlittlelong(ifp, &key);
+    fread (meta_data, 1, meta_length, ifp);
+    for (i=0; i < meta_length; i++) {
+        key = (key * 1597 + 51749) % 244944;
+        assert(have64BitArithmetic);
+        val = key * (INT64) 301593171 >> 24;
+        meta_data[i] ^= ((((key << 8) - val) >> 1) + val) >> 17;
+    }
+}
+
+
+
+void  
+foveon_load_raw() {
+
+    struct decode *dindex;
+    short diff[1024], pred[3];
+    unsigned int huff[1024], bitbuf=0;
+    int row, col, bit=-1, c, i;
+
+    assert(have64BitArithmetic);
+
+    for (i=0; i < 1024; ++i)
+        pm_readlittleshort(ifp, &diff[i]);
+
+    for (i=0; i < 1024; ++i) {
+        long x;
+        pm_readlittlelong(ifp, &x);
+        huff[i] = x;
+    }
+    init_decoder();
+    foveon_decoder (huff, 0);
+
+    for (row=0; row < height; row++) {
+        long junk;
+        memset (pred, 0, sizeof pred);
+        if (!bit) 
+            pm_readlittlelong(ifp, &junk);
+        for (col=bit=0; col < width; col++) {
+            FORC3 {
+                for (dindex=first_decode; dindex->branch[0]; ) {
+                    if ((bit = (bit-1) & 31) == 31)
+                        for (i=0; i < 4; i++)
+                            bitbuf = (bitbuf << 8) + fgetc(ifp);
+                    dindex = dindex->branch[bitbuf >> bit & 1];
+                }
+                pred[c] += diff[dindex->leaf];
+            }
+            FORC3 image[row*width+col][c] = pred[c];
+        }
+    }
+    foveon_load_camf();
+    maximum = clip_max = 0xffff;
+}
+
+
+
+static int
+sget4(char const s[]) {
+    return s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24;
+}
+
+
+
+static char *  
+foveon_camf_param (const char * const block, 
+                   const char * const param) {
+    unsigned idx, num;
+    char *pos, *cp, *dp;
+
+    for (idx=0; idx < meta_length; idx += sget4(pos+8)) {
+        pos = meta_data + idx;
+        if (strncmp (pos, "CMb", 3)) break;
+        if (pos[3] != 'P') continue;
+        if (strcmp (block, pos+sget4(pos+12))) continue;
+        cp = pos + sget4(pos+16);
+        num = sget4(cp);
+        dp = pos + sget4(cp+4);
+        while (num--) {
+            cp += 8;
+            if (!strcmp (param, dp+sget4(cp)))
+                return dp+sget4(cp+4);
+        }
+    }
+    return NULL;
+}
+
+
+
+static void *  
+foveon_camf_matrix (int                dim[3], 
+                    const char * const name) {
+
+    unsigned i, idx, type, ndim, size, *mat;
+    char *pos, *cp, *dp;
+
+    for (idx=0; idx < meta_length; idx += sget4(pos+8)) {
+        pos = meta_data + idx;
+        if (strncmp (pos, "CMb", 3)) break;
+        if (pos[3] != 'M') continue;
+        if (strcmp (name, pos+sget4(pos+12))) continue;
+        dim[0] = dim[1] = dim[2] = 1;
+        cp = pos + sget4(pos+16);
+        type = sget4(cp);
+        if ((ndim = sget4(cp+4)) > 3) break;
+        dp = pos + sget4(cp+8);
+        for (i=ndim; i--; ) {
+            cp += 12;
+            dim[i] = sget4(cp);
+        }
+        if ((size = dim[0]*dim[1]*dim[2]) > meta_length/4) break;
+        MALLOCARRAY(mat, size);
+        if (mat == NULL)
+            pm_error("Unable to allocate memory for size=%d", size);
+        for (i=0; i < size; i++)
+            if (type && type != 6)
+                mat[i] = sget4(dp + i*4);
+            else
+                mat[i] = sget4(dp + i*2) & 0xffff;
+        return mat;
+    }
+    pm_message ("'%s' matrix not found!", name);
+    return NULL;
+}
+
+
+
+static int  
+foveon_fixed (void *       const ptr, 
+              int          const size, 
+              const char * const name) {
+    void *dp;
+    int dim[3];
+
+    dp = foveon_camf_matrix (dim, name);
+    if (!dp) return 0;
+    memcpy (ptr, dp, size*4);
+    free (dp);
+    return 1;
+}
+
+static float  foveon_avg (unsigned short *pix, int range[2], float cfilt)
+{
+    int i;
+    float val, min=FLT_MAX, max=-FLT_MAX, sum=0;
+
+    for (i=range[0]; i <= range[1]; i++) {
+        sum += val = 
+            (short)pix[i*4] + ((short)pix[i*4]-(short)pix[(i-1)*4]) * cfilt;
+        if (min > val) min = val;
+        if (max < val) max = val;
+    }
+    return (sum - min - max) / (range[1] - range[0] - 1);
+}
+
+static short *foveon_make_curve (double max, double mul, double filt)
+{
+    short *curve;
+    int size;
+    unsigned int i;
+    double x;
+
+    size = 4*M_PI*max / filt;
+    MALLOCARRAY(curve, size+1);
+    if (curve == NULL)
+        pm_error("Out of memory for %d-element curve array", size);
+
+    curve[0] = size;
+    for (i=0; i < size; ++i) {
+        x = i*filt/max/4;
+        curve[i+1] = (cos(x)+1)/2 * tanh(i*filt/mul) * mul + 0.5;
+    }
+    return curve;
+}
+
+static void foveon_make_curves
+(short **curvep, float dq[3], float div[3], float filt)
+{
+    double mul[3], max=0;
+    int c;
+
+    FORC3 mul[c] = dq[c]/div[c];
+    FORC3 if (max < mul[c]) max = mul[c];
+    FORC3 curvep[c] = foveon_make_curve (max, mul[c], filt);
+}
+
+static int  foveon_apply_curve (short *curve, int i)
+{
+    if (abs(i) >= (unsigned short)curve[0]) return 0;
+    return i < 0 ? -(unsigned short)curve[1-i] : (unsigned short)curve[1+i];
+}
+
+void  
+foveon_interpolate(float coeff[3][4]) {
+
+    static const short hood[] = { 
+        -1,-1, -1,0, -1,1, 0,-1, 0,1, 1,-1, 1,0, 1,1 };
+    short *pix, prev[3], *curve[8], (*shrink)[3];
+    float cfilt=0.8, ddft[3][3][2], ppm[3][3][3];
+    float cam_xyz[3][3], correct[3][3], last[3][3], trans[3][3];
+    float chroma_dq[3], color_dq[3], diag[3][3], div[3];
+    float (*black)[3], (*sgain)[3], (*sgrow)[3];
+    float fsum[3], val, frow, num;
+    int row, col, c, i, j, diff, sgx, irow, sum, min, max, limit;
+    int dim[3], dscr[2][2], (*smrow[7])[3], total[4], ipix[3];
+    int work[3][3], smlast, smred, smred_p=0, dev[3];
+    int satlev[3], keep[4], active[4];
+    unsigned *badpix;
+    double dsum=0, trsum[3];
+    char str[128], *cp;
+
+    foveon_fixed (dscr, 4, "DarkShieldColRange");
+    foveon_fixed (ppm[0][0], 27, "PostPolyMatrix");
+    foveon_fixed (ddft[1][0], 12, "DarkDrift");
+    foveon_fixed (&cfilt, 1, "ColumnFilter");
+    foveon_fixed (satlev, 3, "SaturationLevel");
+    foveon_fixed (keep, 4, "KeepImageArea");
+    foveon_fixed (active, 4, "ActiveImageArea");
+    foveon_fixed (chroma_dq, 3, "ChromaDQ");
+    foveon_fixed (color_dq, 3,
+                  foveon_camf_param ("IncludeBlocks", "ColorDQ") ?
+                  "ColorDQ" : "ColorDQCamRGB");
+
+    if (!(cp = foveon_camf_param ("WhiteBalanceIlluminants", model2)))
+    { pm_message ( "Invalid white balance \"%s\"", model2);
+    return; }
+    foveon_fixed (cam_xyz, 9, cp);
+    foveon_fixed (correct, 9,
+                  foveon_camf_param ("WhiteBalanceCorrections", model2));
+    memset (last, 0, sizeof last);
+    for (i=0; i < 3; i++)
+        for (j=0; j < 3; j++)
+            FORC3 last[i][j] += correct[i][c] * cam_xyz[c][j];
+
+    sprintf (str, "%sRGBNeutral", model2);
+    if (foveon_camf_param ("IncludeBlocks", str))
+        foveon_fixed (div, 3, str);
+    else {
+#define LAST(x,y) last[(i+x)%3][(c+y)%3]
+        for (i=0; i < 3; i++)
+            FORC3 diag[c][i] = LAST(1,1)*LAST(2,2) - LAST(1,2)*LAST(2,1);
+#undef LAST
+        FORC3 div[c] = 
+            diag[c][0]*0.3127 + diag[c][1]*0.329 + diag[c][2]*0.3583;
+    }
+    num = 0;
+    FORC3 if (num < div[c]) num = div[c];
+    FORC3 div[c] /= num;
+
+    memset (trans, 0, sizeof trans);
+    for (i=0; i < 3; i++)
+        for (j=0; j < 3; j++)
+            FORC3 trans[i][j] += coeff[i][c] * last[c][j] * div[j];
+    FORC3 trsum[c] = trans[c][0] + trans[c][1] + trans[c][2];
+    dsum = (6*trsum[0] + 11*trsum[1] + 3*trsum[2]) / 20;
+    for (i=0; i < 3; i++)
+        FORC3 last[i][c] = trans[i][c] * dsum / trsum[i];
+    memset (trans, 0, sizeof trans);
+    for (i=0; i < 3; i++)
+        for (j=0; j < 3; j++)
+            FORC3 trans[i][j] += (i==c ? 32 : -1) * last[c][j] / 30;
+
+    foveon_make_curves (curve, color_dq, div, cfilt);
+    FORC3 chroma_dq[c] /= 3;
+    foveon_make_curves (curve+3, chroma_dq, div, cfilt);
+    FORC3 dsum += chroma_dq[c] / div[c];
+    curve[6] = foveon_make_curve (dsum, dsum, cfilt);
+    curve[7] = foveon_make_curve (dsum*2, dsum*2, cfilt);
+
+    sgain = foveon_camf_matrix (dim, "SpatialGain");
+    if (!sgain) return;
+    sgrow = calloc (dim[1], sizeof *sgrow);
+    sgx = (width + dim[1]-2) / (dim[1]-1);
+
+    black = calloc (height, sizeof *black);
+    for (row=0; row < height; row++) {
+        for (i=0; i < 6; i++)
+            ddft[0][0][i] = ddft[1][0][i] +
+                row / (height-1.0) * (ddft[2][0][i] - ddft[1][0][i]);
+        FORC3 black[row][c] =
+            ( foveon_avg (image[row*width]+c, dscr[0], cfilt) +
+              foveon_avg (image[row*width]+c, dscr[1], cfilt) * 3
+              - ddft[0][c][0] ) / 4 - ddft[0][c][1];
+    }
+    memcpy (black, black+8, sizeof *black*8);
+    memcpy (black+height-11, black+height-22, 11*sizeof *black);
+    memcpy (last, black, sizeof last);
+
+    for (row=1; row < height-1; row++) {
+        FORC3 if (last[1][c] > last[0][c]) {
+            if (last[1][c] > last[2][c])
+                black[row][c] = 
+                    (last[0][c] > last[2][c]) ? last[0][c]:last[2][c];
+        } else
+            if (last[1][c] < last[2][c])
+                black[row][c] = 
+                    (last[0][c] < last[2][c]) ? last[0][c]:last[2][c];
+        memmove (last, last+1, 2*sizeof last[0]);
+        memcpy (last[2], black[row+1], sizeof last[2]);
+    }
+    FORC3 black[row][c] = (last[0][c] + last[1][c])/2;
+    FORC3 black[0][c] = (black[1][c] + black[3][c])/2;
+
+    val = 1 - exp(-1/24.0);
+    memcpy (fsum, black, sizeof fsum);
+    for (row=1; row < height; row++)
+        FORC3 fsum[c] += black[row][c] =
+            (black[row][c] - black[row-1][c])*val + black[row-1][c];
+    memcpy (last[0], black[height-1], sizeof last[0]);
+    FORC3 fsum[c] /= height;
+    for (row = height; row--; )
+        FORC3 last[0][c] = black[row][c] =
+            (black[row][c] - fsum[c] - last[0][c])*val + last[0][c];
+
+    memset (total, 0, sizeof total);
+    for (row=2; row < height; row+=4)
+        for (col=2; col < width; col+=4) {
+            FORC3 total[c] += (short) image[row*width+col][c];
+            total[3]++;
+        }
+    for (row=0; row < height; row++)
+        FORC3 black[row][c] += fsum[c]/2 + total[c]/(total[3]*100.0);
+
+    for (row=0; row < height; row++) {
+        for (i=0; i < 6; i++)
+            ddft[0][0][i] = ddft[1][0][i] +
+                row / (height-1.0) * (ddft[2][0][i] - ddft[1][0][i]);
+        pix = (short*)image[row*width];
+        memcpy (prev, pix, sizeof prev);
+        frow = row / (height-1.0) * (dim[2]-1);
+        if ((irow = frow) == dim[2]-1) irow--;
+        frow -= irow;
+        for (i=0; i < dim[1]; i++)
+            FORC3 sgrow[i][c] = sgain[ irow   *dim[1]+i][c] * (1-frow) +
+                sgain[(irow+1)*dim[1]+i][c] *    frow;
+        for (col=0; col < width; col++) {
+            FORC3 {
+                diff = pix[c] - prev[c];
+                prev[c] = pix[c];
+                ipix[c] = pix[c] + floor ((diff + (diff*diff >> 14)) * cfilt
+                                          - ddft[0][c][1] 
+                                          - ddft[0][c][0] 
+                                            * ((float) col/width - 0.5)
+                                          - black[row][c] );
+            }
+            FORC3 {
+                work[0][c] = ipix[c] * ipix[c] >> 14;
+                work[2][c] = ipix[c] * work[0][c] >> 14;
+                work[1][2-c] = ipix[(c+1) % 3] * ipix[(c+2) % 3] >> 14;
+            }
+            FORC3 {
+                for (val=i=0; i < 3; i++)
+                    for (  j=0; j < 3; j++)
+                        val += ppm[c][i][j] * work[i][j];
+                ipix[c] = floor ((ipix[c] + floor(val)) *
+                                 ( sgrow[col/sgx  ][c] * (sgx - col%sgx) +
+                                   sgrow[col/sgx+1][c] * (col%sgx) ) / sgx / 
+                                 div[c]);
+                if (ipix[c] > 32000) ipix[c] = 32000;
+                pix[c] = ipix[c];
+            }
+            pix += 4;
+        }
+    }
+    free (black);
+    free (sgrow);
+    free (sgain);
+
+    if ((badpix = foveon_camf_matrix (dim, "BadPixels"))) {
+        for (i=0; i < dim[0]; i++) {
+            col = (badpix[i] >> 8 & 0xfff) - keep[0];
+            row = (badpix[i] >> 20       ) - keep[1];
+            if ((unsigned)(row-1) > height-3 || (unsigned)(col-1) > width-3)
+                continue;
+            memset (fsum, 0, sizeof fsum);
+            for (sum=j=0; j < 8; j++)
+                if (badpix[i] & (1 << j)) {
+                    FORC3 fsum[c] += 
+                        image[(row+hood[j*2])*width+col+hood[j*2+1]][c];
+                    sum++;
+                }
+            if (sum) FORC3 image[row*width+col][c] = fsum[c]/sum;
+        }
+        free (badpix);
+    }
+
+    /* Array for 5x5 Gaussian averaging of red values */
+    smrow[6] = calloc (width*5, sizeof **smrow);
+    if (smrow[6] == NULL)
+        pm_error("Out of memory");
+    for (i=0; i < 5; i++)
+        smrow[i] = smrow[6] + i*width;
+
+    /* Sharpen the reds against these Gaussian averages */
+    for (smlast=-1, row=2; row < height-2; row++) {
+        while (smlast < row+2) {
+            for (i=0; i < 6; i++)
+                smrow[(i+5) % 6] = smrow[i];
+            pix = (short*)image[++smlast*width+2];
+            for (col=2; col < width-2; col++) {
+                smrow[4][col][0] =
+                    (pix[0]*6 + (pix[-4]+pix[4])*4 + pix[-8]+pix[8] + 8) >> 4;
+                pix += 4;
+            }
+        }
+        pix = (short*)image[row*width+2];
+        for (col=2; col < width-2; col++) {
+            smred = ( 6 *  smrow[2][col][0]
+                      + 4 * (smrow[1][col][0] + smrow[3][col][0])
+                      +      smrow[0][col][0] + smrow[4][col][0] + 8 ) >> 4;
+            if (col == 2)
+                smred_p = smred;
+            i = pix[0] + ((pix[0] - ((smred*7 + smred_p) >> 3)) >> 3);
+            if (i > 32000) i = 32000;
+            pix[0] = i;
+            smred_p = smred;
+            pix += 4;
+        }
+    }
+
+    /* Adjust the brighter pixels for better linearity */
+    FORC3 {
+        i = satlev[c] / div[c];
+        if (maximum > i) maximum = i;
+    }
+    clip_max = maximum;
+    limit = maximum * 9 >> 4;
+    for (pix=(short*)image[0]; pix < (short *) image[height*width]; pix+=4) {
+        if (pix[0] <= limit || pix[1] <= limit || pix[2] <= limit)
+            continue;
+        min = max = pix[0];
+        for (c=1; c < 3; c++) {
+            if (min > pix[c]) min = pix[c];
+            if (max < pix[c]) max = pix[c];
+        }
+        i = 0x4000 - ((min - limit) << 14) / limit;
+        i = 0x4000 - (i*i >> 14);
+        i = i*i >> 14;
+        FORC3 pix[c] += (max - pix[c]) * i >> 14;
+    }
+    /*
+      Because photons that miss one detector often hit another,
+      the sum R+G+B is much less noisy than the individual colors.
+      So smooth the hues without smoothing the total.
+    */
+    for (smlast=-1, row=2; row < height-2; row++) {
+        while (smlast < row+2) {
+            for (i=0; i < 6; i++)
+                smrow[(i+5) % 6] = smrow[i];
+            pix = (short*)image[++smlast*width+2];
+            for (col=2; col < width-2; col++) {
+                FORC3 smrow[4][col][c] = (pix[c-4]+2*pix[c]+pix[c+4]+2) >> 2;
+                pix += 4;
+            }
+        }
+        pix = (short*)image[row*width+2];
+        for (col=2; col < width-2; col++) {
+            FORC3 dev[c] = -foveon_apply_curve (curve[7], pix[c] -
+                                                ((smrow[1][col][c] + 
+                                                  2*smrow[2][col][c] + 
+                                                  smrow[3][col][c]) >> 2));
+            sum = (dev[0] + dev[1] + dev[2]) >> 3;
+            FORC3 pix[c] += dev[c] - sum;
+            pix += 4;
+        }
+    }
+    for (smlast=-1, row=2; row < height-2; row++) {
+        while (smlast < row+2) {
+            for (i=0; i < 6; i++)
+                smrow[(i+5) % 6] = smrow[i];
+            pix = (short*)image[++smlast*width+2];
+            for (col=2; col < width-2; col++) {
+                FORC3 smrow[4][col][c] =
+                    (pix[c-8]+pix[c-4]+pix[c]+pix[c+4]+pix[c+8]+2) >> 2;
+                pix += 4;
+            }
+        }
+        pix = (short*)image[row*width+2];
+        for (col=2; col < width-2; col++) {
+            for (total[3]=375, sum=60, c=0; c < 3; c++) {
+                for (total[c]=i=0; i < 5; i++)
+                    total[c] += smrow[i][col][c];
+                total[3] += total[c];
+                sum += pix[c];
+            }
+            if (sum < 0) sum = 0;
+            j = total[3] > 375 ? (sum << 16) / total[3] : sum * 174;
+            FORC3 pix[c] += foveon_apply_curve (curve[6],
+                                                ((j*total[c] + 0x8000) >> 16) 
+                                                - pix[c]);
+            pix += 4;
+        }
+    }
+
+    /* Transform the image to a different colorspace */
+    for (pix=(short*)image[0]; pix < (short *) image[height*width]; pix+=4) {
+        FORC3 pix[c] -= foveon_apply_curve (curve[c], pix[c]);
+        sum = (pix[0]+pix[1]+pix[1]+pix[2]) >> 2;
+        FORC3 pix[c] -= foveon_apply_curve (curve[c], pix[c]-sum);
+        FORC3 {
+            for (dsum=i=0; i < 3; i++)
+                dsum += trans[c][i] * pix[i];
+            if (dsum < 0)  dsum = 0;
+            if (dsum > 24000) dsum = 24000;
+            ipix[c] = dsum + 0.5;
+        }
+        FORC3 pix[c] = ipix[c];
+    }
+
+    /* Smooth the image bottom-to-top and save at 1/4 scale */
+    MALLOCARRAY(shrink, (width/4) * (height/4));
+    if (shrink == NULL)
+        pm_error("Out of memory allocating 1/4 scale array");
+
+    for (row = height/4; row > 0; --row) {
+        for (col=0; col < width/4; ++col) {
+            ipix[0] = ipix[1] = ipix[2] = 0;
+            for (i=0; i < 4; i++)
+                for (j=0; j < 4; j++)
+                    FORC3 ipix[c] += image[(row*4+i)*width+col*4+j][c];
+            FORC3
+                if (row+2 > height/4)
+                    shrink[row*(width/4)+col][c] = ipix[c] >> 4;
+                else
+                    shrink[row*(width/4)+col][c] =
+                        (shrink[(row+1)*(width/4)+col][c]*1840 + 
+                         ipix[c]*141 + 2048) >> 12;
+        }
+    }
+    /* From the 1/4-scale image, smooth right-to-left */
+    for (row=0; row < (height & ~3); ++row) {
+        ipix[0] = ipix[1] = ipix[2] = 0;
+        if ((row & 3) == 0)
+            for (col = width & ~3 ; col--; )
+                FORC3 smrow[0][col][c] = ipix[c] =
+                    (shrink[(row/4)*(width/4)+col/4][c]*1485 + 
+                     ipix[c]*6707 + 4096) >> 13;
+
+        /* Then smooth left-to-right */
+        ipix[0] = ipix[1] = ipix[2] = 0;
+        for (col=0; col < (width & ~3); col++)
+            FORC3 smrow[1][col][c] = ipix[c] =
+                (smrow[0][col][c]*1485 + ipix[c]*6707 + 4096) >> 13;
+
+        /* Smooth top-to-bottom */
+        if (row == 0)
+            memcpy (smrow[2], smrow[1], sizeof **smrow * width);
+        else
+            for (col=0; col < (width & ~3); col++)
+                FORC3 smrow[2][col][c] =
+                    (smrow[2][col][c]*6707 + smrow[1][col][c]*1485 + 4096) 
+                        >> 13;
+
+        /* Adjust the chroma toward the smooth values */
+        for (col=0; col < (width & ~3); col++) {
+            for (i=j=30, c=0; c < 3; c++) {
+                i += smrow[2][col][c];
+                j += image[row*width+col][c];
+            }
+            j = (j << 16) / i;
+            for (sum=c=0; c < 3; c++) {
+                ipix[c] = foveon_apply_curve(
+                    curve[c+3],
+                    ((smrow[2][col][c] * j + 0x8000) >> 16) - 
+                    image[row*width+col][c]);
+                sum += ipix[c];
+            }
+            sum >>= 3;
+            FORC3 {
+                i = image[row*width+col][c] + ipix[c] - sum;
+                if (i < 0) i = 0;
+                image[row*width+col][c] = i;
+            }
+        }
+    }
+    free (shrink);
+    free (smrow[6]);
+    for (i=0; i < 8; i++)
+        free (curve[i]);
+
+    /* Trim off the black border */
+    active[1] -= keep[1];
+    active[3] -= 2;
+    i = active[2] - active[0];
+    for (row = 0; row < active[3]-active[1]; row++)
+        memcpy (image[row*i], image[(row+active[1])*width+active[0]],
+                i * sizeof *image);
+    width = i;
+    height = row;
+}
diff --git a/converter/other/cameratopam/foveon.h b/converter/other/cameratopam/foveon.h
new file mode 100644
index 00000000..57be2244
--- /dev/null
+++ b/converter/other/cameratopam/foveon.h
@@ -0,0 +1,14 @@
+#include "pm.h"
+
+void 
+parse_foveon(FILE * const ifp);
+
+void  
+foveon_interpolate(float coeff[3][4]);
+
+void 
+foveon_load_raw(void);
+
+void  
+foveon_coeff(bool * const useCoeffP,
+             float        coeff[3][4]);
diff --git a/converter/other/cameratopam/global_variables.h b/converter/other/cameratopam/global_variables.h
new file mode 100644
index 00000000..c8732d5a
--- /dev/null
+++ b/converter/other/cameratopam/global_variables.h
@@ -0,0 +1,55 @@
+/* These are unfortunately global variables used by various modules.
+
+   It would be nice to clean up this code and get rid of all of these.
+*/
+#include <stdio.h>
+#include <time.h>
+#include "pm_c_util.h"
+
+extern FILE * ifp;
+extern int flip;
+extern int data_offset;
+extern int raw_width;
+extern int raw_height;
+extern int height;
+extern int width;
+extern char * meta_data;
+extern int meta_offset;
+extern int meta_length;
+extern char make[64];
+extern char model[70];
+extern char model2[64];
+extern time_t timestamp;
+extern int is_foveon;
+extern int is_dng;
+extern int is_canon;
+extern unsigned short (*image)[4];
+extern int maximum;
+extern int clip_max;
+extern short order;
+extern int zero_after_ff;
+extern int shrink;
+extern int iwidth;
+extern unsigned int filters;
+extern int black;
+extern unsigned short white[8][8];
+extern int top_margin;
+extern int left_margin;
+extern int fuji_width;
+extern int tiff_samples;
+extern int tiff_data_compression;
+extern int kodak_data_compression;
+extern int fuji_secondary;
+extern int use_coeff;
+extern int use_gamma;
+extern int xmag;
+extern int ymag;
+extern float cam_mul[4];
+#define camera_red  cam_mul[0]
+#define camera_blue cam_mul[2]
+extern float pre_mul[4];
+extern int colors;
+extern unsigned short curve[0x1000];
+extern float coeff[3][4];
+extern int use_secondary;
+extern bool verbose;
diff --git a/converter/other/cameratopam/identify.c b/converter/other/cameratopam/identify.c
new file mode 100644
index 00000000..a101c8ad
--- /dev/null
+++ b/converter/other/cameratopam/identify.c
@@ -0,0 +1,1183 @@
+#define _BSD_SOURCE   /* Make sure strcasecmp() is in string.h */
+#include <string.h>
+
+#include "pm.h"
+
+#include "global_variables.h"
+#include "util.h"
+#include "foveon.h"
+#include "canon.h"
+#include "dng.h"
+#include "ljpeg.h"
+#include "camera.h"
+
+#include "identify.h"
+
+
+#if HAVE_INT64
+   static bool const have64BitArithmetic = true;
+#else
+   static bool const have64BitArithmetic = false;
+#endif
+
+
+static loadRawFn load_raw;
+
+/* This does the same as the function of the same name in the GNU C library */
+static const char *memmem_internal (const char *haystack, size_t haystacklen,
+                     const char *needle, size_t needlelen)
+{
+  const char *c;
+  for (c = haystack; c <= haystack + haystacklen - needlelen; c++)
+    if (!memcmp (c, needle, needlelen))
+      return c;
+  return NULL;
+}
+
+/*
+   Thanks to Adobe for providing these excellent CAM -> XYZ matrices!
+ */
+static void 
+adobe_coeff()
+{
+  static const struct {
+    const char *prefix;
+    short trans[12];
+  } table[] = {
+    { "Canon EOS D2000C",
+    { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } },
+    { "Canon EOS D30",
+    { 9805,-2689,-1312,-5803,13064,3068,-2438,3075,8775 } },
+    { "Canon EOS D60",
+    { 6188,-1341,-890,-7168,14489,2937,-2640,3228,8483 } },
+    { "Canon EOS 10D",
+    { 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } },
+    { "Canon EOS 20D",
+    { 6599,-537,-891,-8071,15783,2424,-1983,2234,7462 } },
+    { "Canon EOS-1Ds Mark II",
+    { 6517,-602,-867,-8180,15926,2378,-1618,1771,7633 } },
+    { "Canon EOS-1D Mark II",
+    { 6264,-582,-724,-8312,15948,2504,-1744,1919,8664 } },
+    { "Canon EOS-1DS",
+    { 4374,3631,-1743,-7520,15212,2472,-2892,3632,8161 } },
+    { "Canon EOS-1D",
+    { 6906,-278,-1017,-6649,15074,1621,-2848,3897,7611 } },
+    { "Canon EOS",
+    { 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } },
+    { "Canon PowerShot 600",
+    { -3822,10019,1311,4085,-157,3386,-5341,10829,4812,-1969,10969,1126 } },
+    { "Canon PowerShot A50",
+    { -5300,9846,1776,3436,684,3939,-5540,9879,6200,-1404,11175,217 } },
+    { "Canon PowerShot A5",
+    { -4801,9475,1952,2926,1611,4094,-5259,10164,5947,-1554,10883,547 } },
+    { "Canon PowerShot G1",
+    { -4778,9467,2172,4743,-1141,4344,-5146,9908,6077,-1566,11051,557 } },
+    { "Canon PowerShot G2",
+    { 9087,-2693,-1049,-6715,14382,2537,-2291,2819,7790 } },
+    { "Canon PowerShot G3",
+    { 9212,-2781,-1073,-6573,14189,2605,-2300,2844,7664 } },
+    { "Canon PowerShot G5",
+    { 9757,-2872,-933,-5972,13861,2301,-1622,2328,7212 } },
+    { "Canon PowerShot G6",
+    { 9877,-3775,-871,-7613,14807,3072,-1448,1305,7485 } },
+    { "Canon PowerShot Pro1",
+    { 10062,-3522,-999,-7643,15117,2730,-765,817,7323 } },
+    { "Canon PowerShot Pro70",
+    { -4155,9818,1529,3939,-25,4522,-5521,9870,6610,-2238,10873,1342 } },
+    { "Canon PowerShot Pro90",
+    { -4963,9896,2235,4642,-987,4294,-5162,10011,5859,-1770,11230,577 } },
+    { "Canon PowerShot S30",
+    { 10566,-3652,-1129,-6552,14662,2006,-2197,2581,7670 } },
+    { "Canon PowerShot S40",
+    { 8510,-2487,-940,-6869,14231,2900,-2318,2829,9013 } },
+    { "Canon PowerShot S45",
+    { 8163,-2333,-955,-6682,14174,2751,-2077,2597,8041 } },
+    { "Canon PowerShot S50",
+    { 8882,-2571,-863,-6348,14234,2288,-1516,2172,6569 } },
+    { "Canon PowerShot S70",
+    { 9976,-3810,-832,-7115,14463,2906,-901,989,7889 } },
+    { "Contax N Digital",
+    { 7777,1285,-1053,-9280,16543,2916,-3677,5679,7060 } },
+    { "EPSON R-D1",
+    { 6827,-1878,-732,-8429,16012,2564,-704,592,7145 } },
+    { "FUJIFILM FinePix E550",
+    { 11044,-3888,-1120,-7248,15168,2208,-1531,2277,8069 } },
+    { "FUJIFILM FinePix F700",
+    { 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } },
+    { "FUJIFILM FinePix S20Pro",
+    { 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } },
+    { "FUJIFILM FinePix S2Pro",
+    { 12492,-4690,-1402,-7033,15423,1647,-1507,2111,7697 } },
+    { "FUJIFILM FinePix S3Pro",
+    { 11807,-4612,-1294,-8927,16968,1988,-2120,2741,8006 } },
+    { "FUJIFILM FinePix S5000",
+    { 8754,-2732,-1019,-7204,15069,2276,-1702,2334,6982 } },
+    { "FUJIFILM FinePix S5100",
+    { 11940,-4431,-1255,-6766,14428,2542,-993,1165,7421 } },
+    { "FUJIFILM FinePix S7000",
+    { 10190,-3506,-1312,-7153,15051,2238,-2003,2399,7505 } },
+    { "Kodak DCS315C",
+    { 17523,-4827,-2510,756,8546,-137,6113,1649,2250 } },
+    { "Kodak DCS330C",
+    { 20620,-7572,-2801,-103,10073,-396,3551,-233,2220 } },
+    { "KODAK DCS420",
+    { 10868,-1852,-644,-1537,11083,484,2343,628,2216 } },
+    { "KODAK DCS460",
+    { 10592,-2206,-967,-1944,11685,230,2206,670,1273 } },
+    { "KODAK EOSDCS1",
+    { 10592,-2206,-967,-1944,11685,230,2206,670,1273 } },
+    { "KODAK EOSDCS3B",
+    { 9898,-2700,-940,-2478,12219,206,1985,634,1031 } },
+    { "Kodak DCS520C",
+    { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } },
+    { "Kodak DCS560C",
+    { 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 } },
+    { "Kodak DCS620C",
+    { 23617,-10175,-3149,-2054,11749,-272,2586,-489,3453 } },
+    { "Kodak DCS620X",
+    { 13095,-6231,154,12221,-21,-2137,895,4602,2258 } },
+    { "Kodak DCS660C",
+    { 18244,-6351,-2739,-791,11193,-521,3711,-129,2802 } },
+    { "Kodak DCS720X",
+    { 11775,-5884,950,9556,1846,-1286,-1019,6221,2728 } },
+    { "Kodak DCS760C",
+    { 16623,-6309,-1411,-4344,13923,323,2285,274,2926 } },
+    { "Kodak DCS Pro SLR",
+    { 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } },
+    { "Kodak DCS Pro 14nx",
+    { 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } },
+    { "Kodak DCS Pro 14",
+    { 7791,3128,-776,-8588,16458,2039,-2455,4006,6198 } },
+    { "Kodak ProBack645",
+    { 16414,-6060,-1470,-3555,13037,473,2545,122,4948 } },
+    { "Kodak ProBack",
+    { 21179,-8316,-2918,-915,11019,-165,3477,-180,4210 } },
+    { "LEICA DIGILUX 2",
+    { 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 } },
+    { "Leaf Valeo",
+    { 8236,1746,-1314,-8251,15953,2428,-3673,5786,5771 } },
+    { "Minolta DiMAGE 5",
+    { 8983,-2942,-963,-6556,14476,2237,-2426,2887,8014 } },
+    { "Minolta DiMAGE 7",
+    { 9144,-2777,-998,-6676,14556,2281,-2470,3019,7744 } },
+    { "Minolta DiMAGE A1",
+    { 9274,-2547,-1167,-8220,16323,1943,-2273,2720,8340 } },
+    { "MINOLTA DiMAGE A200",
+    { 8560,-2487,-986,-8112,15535,2771,-1209,1324,7743 } },
+    { "Minolta DiMAGE A2",
+    { 9097,-2726,-1053,-8073,15506,2762,-966,981,7763 } },
+    { "MINOLTA DYNAX 7D",
+    { 10239,-3104,-1099,-8037,15727,2451,-927,925,6871 } },
+    { "NIKON D100",
+    { 5915,-949,-778,-7516,15364,2282,-1228,1337,6404 } },
+    { "NIKON D1H",
+    { 7577,-2166,-926,-7454,15592,1934,-2377,2808,8606 } },
+    { "NIKON D1X",
+    { 7620,-2173,-966,-7604,15843,1805,-2356,2811,8439 } },
+    { "NIKON D1",
+    { 7559,-2130,-965,-7611,15713,1972,-2478,3042,8290 } },
+    { "NIKON D2H",
+    { 5710,-901,-615,-8594,16617,2024,-2975,4120,6830 } },
+    { "NIKON D70",
+    { 7732,-2422,-789,-8238,15884,2498,-859,783,7330 } },
+    { "NIKON E995", /* copied from E5000 */
+    { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
+    { "NIKON E2500",
+    { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
+    { "NIKON E4500",
+    { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
+    { "NIKON E5000",
+    { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
+    { "NIKON E5400",
+    { 9349,-2987,-1001,-7919,15766,2266,-2098,2680,6839 } },
+    { "NIKON E5700",
+    { -5368,11478,2368,5537,-113,3148,-4969,10021,5782,778,9028,211 } },
+    { "NIKON E8400",
+    { 7842,-2320,-992,-8154,15718,2599,-1098,1342,7560 } },
+    { "NIKON E8700",
+    { 8489,-2583,-1036,-8051,15583,2643,-1307,1407,7354 } },
+    { "NIKON E8800",
+    { 7971,-2314,-913,-8451,15762,2894,-1442,1520,7610 } },
+    { "OLYMPUS C5050",
+    { 10508,-3124,-1273,-6079,14294,1901,-1653,2306,6237 } },
+    { "OLYMPUS C5060",
+    { 10445,-3362,-1307,-7662,15690,2058,-1135,1176,7602 } },
+    { "OLYMPUS C70",
+    { 10793,-3791,-1146,-7498,15177,2488,-1390,1577,7321 } },
+    { "OLYMPUS C80",
+    { 8606,-2509,-1014,-8238,15714,2703,-942,979,7760 } },
+    { "OLYMPUS E-10",
+    { 12745,-4500,-1416,-6062,14542,1580,-1934,2256,6603 } },
+    { "OLYMPUS E-1",
+    { 11846,-4767,-945,-7027,15878,1089,-2699,4122,8311 } },
+    { "OLYMPUS E-20",
+    { 13173,-4732,-1499,-5807,14036,1895,-2045,2452,7142 } },
+    { "OLYMPUS E-300",
+    { 7828,-1761,-348,-5788,14071,1830,-2853,4518,6557 } },
+    { "PENTAX *ist D",
+    { 9651,-2059,-1189,-8881,16512,2487,-1460,1345,10687 } },
+    { "Panasonic DMC-LC1",
+    { 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 } },
+    { "SONY DSC-F828",
+    { 7924,-1910,-777,-8226,15459,2998,-1517,2199,6818,-7242,11401,3481 } },
+    { "SONY DSC-V3",
+    { 9877,-3775,-871,-7613,14807,3072,-1448,1305,7485 } }
+  };
+  double cc[4][4], cm[4][3], xyz[] = { 1,1,1 };
+  char name[130];
+  int i, j;
+
+  for (i=0; i < 4; i++)
+    for (j=0; j < 4; j++)
+      cc[i][j] = i == j;
+  sprintf (name, "%s %s", make, model);
+  for (i=0; i < sizeof table / sizeof *table; i++)
+    if (!strncmp (name, table[i].prefix, strlen(table[i].prefix))) {
+      for (j=0; j < 12; j++)
+    cm[0][j] = table[i].trans[j];
+      dng_coeff (cc, cm, xyz);
+      break;
+    }
+}
+
+/*
+   Identify which camera created this file, and set global variables
+   accordingly.  Return nonzero if the file cannot be decoded.
+ */
+int
+identify(FILE *       const ifp,
+         bool         const use_secondary,
+         bool         const use_camera_rgb,
+         float        const red_scale,
+         float        const blue_scale,
+         unsigned int const four_color_rgb,
+         const char * const inputFileName,
+         loadRawFn *  const loadRawFnP)
+{
+  char head[32];
+  char * c;
+  unsigned hlen, fsize, i;
+  static const struct {
+    int fsize;
+    char make[12], model[15], withjpeg;
+  } table[] = {
+    {    62464, "Kodak",    "DC20"       ,0 },
+    {   124928, "Kodak",    "DC20"       ,0 },
+    {   311696, "ST Micro", "STV680 VGA" ,0 },  /* SPYz */
+    {   787456, "Creative", "PC-CAM 600" ,0 },
+    {  2465792, "NIKON",    "E950"       ,1 },  /* or E800,E700 */
+    {  2940928, "NIKON",    "E2100"      ,1 },  /* or E2500 */
+    {  4771840, "NIKON",    "E990"       ,1 },  /* or E995 */
+    {  4775936, "NIKON",    "E3700"      ,1 },  /* or Optio 33WR */
+    {  5869568, "NIKON",    "E4300"      ,1 },  /* or DiMAGE Z2 */
+    {  5865472, "NIKON",    "E4500"      ,0 },
+    {  1976352, "CASIO",    "QV-2000UX"  ,0 },
+    {  3217760, "CASIO",    "QV-3*00EX"  ,0 },
+    {  6218368, "CASIO",    "QV-5700"    ,0 },
+    {  7530816, "CASIO",    "QV-R51"     ,1 },
+    {  7684000, "CASIO",    "QV-4000"    ,0 },
+    {  7753344, "CASIO",    "EX-Z55"     ,1 },
+    {  9313536, "CASIO",    "EX-P600"    ,1 },
+    { 10979200, "CASIO",    "EX-P700"    ,1 },
+    {  3178560, "PENTAX",   "Optio S"    ,1 },  /*  8-bit */
+    {  4841984, "PENTAX",   "Optio S"    ,1 },  /* 12-bit */
+    {  6114240, "PENTAX",   "Optio S4"   ,1 },  /* or S4i */
+    { 12582980, "Sinar",    ""           ,0 } };
+  static const char *corp[] =
+    { "Canon", "NIKON", "EPSON", "Kodak", "OLYMPUS", "PENTAX",
+      "MINOLTA", "Minolta", "Konica", "CASIO" };
+  float tmp;
+
+  /*  What format is this file?  Set make[] if we recognize it. */
+
+  raw_height = raw_width = fuji_width = flip = 0;
+  make[0] = model[0] = model2[0] = 0;
+  memset (white, 0, sizeof white);
+  timestamp = tiff_samples = 0;
+  data_offset = meta_length = tiff_data_compression = 0;
+  zero_after_ff = is_dng = fuji_secondary = filters = 0;
+  black = is_foveon = use_coeff = 0;
+  use_gamma = xmag = ymag = 1;
+  for (i=0; i < 4; i++) {
+    cam_mul[i] = 1 & i;
+    pre_mul[i] = 1;
+  }
+  colors = 3;
+  for (i=0; i < 0x1000; i++) curve[i] = i;
+  maximum = 0xfff;
+#ifdef USE_LCMS
+  profile_length = 0;
+#endif
+
+  order = get2(ifp);
+  hlen = get4(ifp);
+  fseek (ifp, 0, SEEK_SET);
+  fread (head, 1, 32, ifp);
+  fseek (ifp, 0, SEEK_END);
+  fsize = ftell(ifp);
+  if ((c = (char*)memmem_internal(head, 32, "MMMMRawT", 8))) {
+    strcpy (make, "Phase One");
+    data_offset = c - head;
+    fseek (ifp, data_offset + 8, SEEK_SET);
+    fseek (ifp, get4(ifp) + 136, SEEK_CUR);
+    raw_width = get4(ifp);
+    fseek (ifp, 12, SEEK_CUR);
+    raw_height = get4(ifp);
+  } else if (order == 0x4949 || order == 0x4d4d) {
+    if (!memcmp (head+6, "HEAPCCDR", 8)) {
+      data_offset = hlen;
+      parse_ciff(ifp, hlen, fsize - hlen);
+    } else {
+      parse_tiff(ifp, 0);
+      if (!strncmp(make,"NIKON",5) && filters == 0)
+    make[0] = 0;
+    }
+  } else if (!memcmp (head, "\0MRM", 4))
+    parse_minolta(ifp);
+    else if (!memcmp (head, "\xff\xd8\xff\xe1", 4) &&
+         !memcmp (head+6, "Exif", 4)) {
+    fseek (ifp, 4, SEEK_SET);
+    fseek (ifp, 4 + get2(ifp), SEEK_SET);
+    if (fgetc(ifp) != 0xff)
+      parse_tiff(ifp, 12);
+  } else if (!memcmp (head, "BM", 2)) {
+    data_offset = 0x1000;
+    order = 0x4949;
+    fseek (ifp, 38, SEEK_SET);
+    if (get4(ifp) == 2834 && get4(ifp) == 2834) {
+      strcpy (model, "BMQ");
+      flip = 3;
+      goto nucore;
+    }
+  } else if (!memcmp (head, "BR", 2)) {
+    strcpy (model, "RAW");
+nucore:
+    strcpy (make, "Nucore");
+    order = 0x4949;
+    fseek (ifp, 10, SEEK_SET);
+    data_offset += get4(ifp);
+    get4(ifp);
+    raw_width  = get4(ifp);
+    raw_height = get4(ifp);
+    if (model[0] == 'B' && raw_width == 2597) {
+      raw_width++;
+      data_offset -= 0x1000;
+    }
+  } else if (!memcmp (head+25, "ARECOYK", 7)) {
+    strcpy (make, "Contax");
+    strcpy (model,"N Digital");
+    fseek (ifp, 60, SEEK_SET);
+    camera_red  = get4(ifp);
+    camera_red /= get4(ifp);
+    camera_blue = get4(ifp);
+    camera_blue = get4(ifp) / camera_blue;
+  } else if (!memcmp (head, "FUJIFILM", 8)) {
+    long data_offset_long;
+    fseek (ifp, 84, SEEK_SET);
+    parse_tiff(ifp, get4(ifp)+12);
+    fseek (ifp, 100, SEEK_SET);
+    pm_readbiglong(ifp, &data_offset_long);
+    data_offset = data_offset_long;
+  } else if (!memcmp (head, "DSC-Image", 9))
+    parse_rollei(ifp);
+  else if (!memcmp (head, "FOVb", 4))
+    parse_foveon(ifp);
+  else
+      for (i=0; i < sizeof table / sizeof *table; i++)
+          if (fsize == table[i].fsize) {
+              strcpy (make,  table[i].make );
+              strcpy (model, table[i].model);
+              if (table[i].withjpeg)
+                  parse_external_jpeg(inputFileName);
+          }
+  parse_mos(ifp, 8);
+  parse_mos(ifp, 3472);
+
+  for (i=0; i < sizeof corp / sizeof *corp; i++)
+    if (strstr (make, corp[i]))     /* Simplify company names */
+    strcpy (make, corp[i]);
+  if (!strncmp (make, "KODAK", 5))
+    make[16] = model[16] = 0;
+  c = make + strlen(make);      /* Remove trailing spaces */
+  while (*--c == ' ') *c = 0;
+  c = model + strlen(model);
+  while (*--c == ' ') *c = 0;
+  i = strlen(make);         /* Remove make from model */
+  if (!strncmp (model, make, i++))
+    memmove (model, model+i, 64-i);
+  make[63] = model[63] = model2[63] = 0;
+
+  if (make[0] == 0) {
+    pm_message ("unrecognized file format.");
+    return 1;
+  }
+
+/*  File format is OK.  Do we know this camera? */
+/*  Start with some useful defaults:           */
+
+  top_margin = left_margin = 0;
+  if ((raw_height | raw_width) < 0)
+       raw_height = raw_width  = 0;
+  height = raw_height;
+  width  = raw_width;
+  if (fuji_width) {
+    width = height + fuji_width;
+    height = width - 1;
+    ymag = 1;
+  }
+  load_raw = NULL;
+  if (is_dng) {
+    strcat (model, " DNG");
+    if (!filters)
+      colors = tiff_samples;
+    if (tiff_data_compression == 1)
+      load_raw = adobe_dng_load_raw_nc;
+    if (tiff_data_compression == 7)
+      load_raw = adobe_dng_load_raw_lj;
+    goto dng_skip;
+  }
+
+/*  We'll try to decode anything from Canon or Nikon. */
+
+  if (!filters) filters = 0x94949494;
+  if ((is_canon = !strcmp(make,"Canon")))
+    load_raw = memcmp (head+6,"HEAPCCDR",8) ?
+        lossless_jpeg_load_raw : canon_compressed_load_raw;
+  if (!strcmp(make,"NIKON"))
+    load_raw = nikon_is_compressed() ?
+    nikon_compressed_load_raw : nikon_load_raw;
+
+/* Set parameters based on camera name (for non-DNG files). */
+
+  if (is_foveon) {
+    if (!have64BitArithmetic)
+      pm_error("This program was built without 64 bit arithmetic "
+               "capability and the Foveon format requires it.");
+    if (height*2 < width) ymag = 2;
+    if (width < height) xmag = 2;
+    filters = 0;
+    load_raw = foveon_load_raw;
+    foveon_coeff(&use_coeff, coeff);
+  } else if (!strcmp(model,"PowerShot 600")) {
+    height = 613;
+    width  = 854;
+    colors = 4;
+    filters = 0xe1e4e1e4;
+    load_raw = canon_600_load_raw;
+  } else if (!strcmp(model,"PowerShot A5") ||
+         !strcmp(model,"PowerShot A5 Zoom")) {
+    height = 773;
+    width  = 960;
+    raw_width = 992;
+    colors = 4;
+    filters = 0x1e4e1e4e;
+    load_raw = canon_a5_load_raw;
+  } else if (!strcmp(model,"PowerShot A50")) {
+    height =  968;
+    width  = 1290;
+    raw_width = 1320;
+    colors = 4;
+    filters = 0x1b4e4b1e;
+    load_raw = canon_a5_load_raw;
+  } else if (!strcmp(model,"PowerShot Pro70")) {
+    height = 1024;
+    width  = 1552;
+    colors = 4;
+    filters = 0x1e4b4e1b;
+    load_raw = canon_a5_load_raw;
+    black = 34;
+  } else if (!strcmp(model,"PowerShot Pro90 IS")) {
+    width  = 1896;
+    colors = 4;
+    filters = 0xb4b4b4b4;
+  } else if (is_canon && raw_width == 2144) {
+    height = 1550;
+    width  = 2088;
+    top_margin  = 8;
+    left_margin = 4;
+    if (!strcmp(model,"PowerShot G1")) {
+      colors = 4;
+      filters = 0xb4b4b4b4;
+    }
+  } else if (is_canon && raw_width == 2224) {
+    height = 1448;
+    width  = 2176;
+    top_margin  = 6;
+    left_margin = 48;
+  } else if (is_canon && raw_width == 2376) {
+    height = 1720;
+    width  = 2312;
+    top_margin  = 6;
+    left_margin = 12;
+  } else if (is_canon && raw_width == 2672) {
+    height = 1960;
+    width  = 2616;
+    top_margin  = 6;
+    left_margin = 12;
+  } else if (is_canon && raw_width == 3152) {
+    height = 2056;
+    width  = 3088;
+    top_margin  = 12;
+    left_margin = 64;
+    maximum = 0xfa0;
+  } else if (is_canon && raw_width == 3160) {
+    height = 2328;
+    width  = 3112;
+    top_margin  = 12;
+    left_margin = 44;
+  } else if (is_canon && raw_width == 3344) {
+    height = 2472;
+    width  = 3288;
+    top_margin  = 6;
+    left_margin = 4;
+  } else if (!strcmp(model,"EOS D2000C")) {
+    filters = 0x61616161;
+    black = curve[200];
+  } else if (!strcmp(model,"EOS-1D")) {
+    raw_height = height = 1662;
+    raw_width  = width  = 2496;
+    data_offset = 288912;
+    filters = 0x61616161;
+  } else if (!strcmp(model,"EOS-1DS")) {
+    raw_height = height = 2718;
+    raw_width  = width  = 4082;
+    data_offset = 289168;
+    filters = 0x61616161;
+  } else if (is_canon && raw_width == 3516) {
+    top_margin  = 14;
+    left_margin = 42;
+    goto canon_cr2;
+  } else if (is_canon && raw_width == 3596) {
+    top_margin  = 12;
+    left_margin = 74;
+    goto canon_cr2;
+  } else if (is_canon && raw_width == 5108) {
+    top_margin  = 13;
+    left_margin = 98;
+    maximum = 0xe80;
+canon_cr2:
+    height = raw_height - top_margin;
+    width  = raw_width - left_margin;
+  } else if (!strcmp(model,"D1")) {
+    camera_red  *= 256/527.0;
+    camera_blue *= 256/317.0;
+  } else if (!strcmp(model,"D1X")) {
+    width  = 4024;
+    ymag = 2;
+  } else if (!strcmp(model,"D100")) {
+    if (tiff_data_compression == 34713 && load_raw == nikon_load_raw)
+      raw_width = (width += 3) + 3;
+  } else if (!strcmp(model,"D2H")) {
+    width  = 2482;
+    left_margin = 6;
+  } else if (!strcmp(model,"D2X")) {
+    width  = 4312;
+    pre_mul[0] = 1.514;
+    pre_mul[2] = 1.727;
+  } else if (fsize == 2465792) {
+    height = 1203;
+    width  = 1616;
+    filters = 0x4b4b4b4b;
+    colors = 4;
+    load_raw = nikon_e950_load_raw;
+    nikon_e950_coeff();
+    pre_mul[0] = 1.18193;
+    pre_mul[2] = 1.16452;
+    pre_mul[3] = 1.17250;
+  } else if (!strcmp(model,"E990")) {
+    if (!timestamp && !nikon_e990()) goto cp_e995;
+    height = 1540;
+    width  = 2064;
+    colors = 4;
+    filters = 0xb4b4b4b4;
+    nikon_e950_coeff();
+    pre_mul[0] = 1.196;
+    pre_mul[1] = 1.246;
+    pre_mul[2] = 1.018;
+  } else if (!strcmp(model,"E995")) {
+cp_e995:
+    strcpy (model, "E995");
+    height = 1540;
+    width  = 2064;
+    colors = 4;
+    filters = 0xe1e1e1e1;
+  } else if (!strcmp(model,"E2100")) {
+    if (!timestamp && !nikon_e2100()) goto cp_e2500;
+    width = 1616;
+    height = 1206;
+    load_raw = nikon_e2100_load_raw;
+    pre_mul[0] = 1.945;
+    pre_mul[2] = 1.040;
+  } else if (!strcmp(model,"E2500")) {
+cp_e2500:
+    strcpy (model, "E2500");
+    width = 1616;
+    height = 1204;
+    colors = 4;
+    filters = 0x4b4b4b4b;
+  } else if (!strcmp(model,"E3700")) {
+    if (!timestamp && pentax_optio33()) goto optio_33wr;
+    height = 1542;
+    width  = 2064;
+    load_raw = nikon_e2100_load_raw;
+    pre_mul[0] = 1.818;
+    pre_mul[2] = 1.618;
+  } else if (!strcmp(model,"Optio 33WR")) {
+optio_33wr:
+    strcpy (make, "PENTAX");
+    strcpy (model,"Optio 33WR");
+    height = 1542;
+    width  = 2064;
+    load_raw = nikon_e2100_load_raw;
+    flip = 1;
+    filters = 0x16161616;
+    pre_mul[0] = 1.331;
+    pre_mul[2] = 1.820;
+  } else if (!strcmp(model,"E4300")) {
+    if (!timestamp && minolta_z2()) goto dimage_z2;
+    height = 1710;
+    width  = 2288;
+    filters = 0x16161616;
+    pre_mul[0] = 508;
+    pre_mul[1] = 256;
+    pre_mul[2] = 322;
+  } else if (!strcmp(model,"DiMAGE Z2")) {
+dimage_z2:
+    strcpy (make, "MINOLTA");
+    strcpy (model,"DiMAGE Z2");
+    height = 1710;
+    width  = 2288;
+    filters = 0x16161616;
+    load_raw = nikon_e2100_load_raw;
+    pre_mul[0] = 508;
+    pre_mul[1] = 256;
+    pre_mul[2] = 450;
+  } else if (!strcmp(model,"E4500")) {
+    height = 1708;
+    width  = 2288;
+    colors = 4;
+    filters = 0xb4b4b4b4;
+  } else if (!strcmp(model,"R-D1")) {
+    tiff_data_compression = 34713;
+    load_raw = nikon_load_raw;
+  } else if (!strcmp(model,"FinePixS2Pro")) {
+    height = 3584;
+    width  = 3583;
+    fuji_width = 2144;
+    filters = 0x61616161;
+    load_raw = fuji_s2_load_raw;
+    black = 128;
+    strcpy (model+7, " S2Pro");
+  } else if (!strcmp(model,"FinePix S3Pro")) {
+    height = 3583;
+    width  = 3584;
+    fuji_width = 2144;
+    if (fsize > 18000000 && use_secondary)
+      data_offset += 4352*2*1444;
+    filters = 0x49494949;
+    load_raw = fuji_s3_load_raw;
+    maximum = 0xffff;
+  } else if (!strcmp(model,"FinePix S5000")) {
+    height = 2499;
+    width  = 2500;
+    fuji_width = 1423;
+    filters = 0x49494949;
+    load_raw = fuji_s5000_load_raw;
+    maximum = 0x3e00;
+  } else if (!strcmp(model,"FinePix S5100") ||
+         !strcmp(model,"FinePix S5500")) {
+    height = 1735;
+    width  = 2304;
+    data_offset += width*10;
+    filters = 0x49494949;
+    load_raw = unpacked_load_raw;
+    maximum = 0xffff;
+  } else if (!strcmp(model,"FinePix E550") ||
+         !strcmp(model,"FinePix F810") ||
+         !strcmp(model,"FinePix S7000")) {
+    height = 3587;
+    width  = 3588;
+    fuji_width = 2047;
+    filters = 0x49494949;
+    load_raw = fuji_s7000_load_raw;
+    maximum = 0x3e00;
+  } else if (!strcmp(model,"FinePix F700") ||
+         !strcmp(model,"FinePix S20Pro")) {
+    height = 2523;
+    width  = 2524;
+    fuji_width = 1440;
+    filters = 0x49494949;
+    load_raw = fuji_f700_load_raw;
+    maximum = 0x3e00;
+  } else if (!strcmp(model,"Digital Camera KD-400Z")) {
+    height = 1712;
+    width  = 2312;
+    raw_width = 2336;
+    data_offset = 4034;
+    fseek (ifp, 2032, SEEK_SET);
+    goto konica_400z;
+  } else if (!strcmp(model,"Digital Camera KD-510Z")) {
+    data_offset = 4032;
+    pre_mul[0] = 1.297;
+    pre_mul[2] = 1.438;
+    fseek (ifp, 2032, SEEK_SET);
+    goto konica_510z;
+  } else if (!strcasecmp(make,"MINOLTA")) {
+    load_raw = unpacked_load_raw;
+    maximum = 0xf7d;
+    if (!strncmp(model,"DiMAGE A",8)) {
+      if (!strcmp(model,"DiMAGE A200")) {
+    filters = 0x49494949;
+    tmp = camera_red;
+    camera_red  = 1 / camera_blue;
+    camera_blue = 1 / tmp;
+      }
+      load_raw = packed_12_load_raw;
+      maximum = model[8] == '1' ? 0xf8b : 0xfff;
+    } else if (!strncmp(model,"ALPHA",5) ||
+           !strncmp(model,"DYNAX",5) ||
+           !strncmp(model,"MAXXUM",6)) {
+      load_raw = packed_12_load_raw;
+      maximum = 0xffb;
+    } else if (!strncmp(model,"DiMAGE G",8)) {
+      if (model[8] == '4') {
+    data_offset = 5056;
+    pre_mul[0] = 1.602;
+    pre_mul[2] = 1.441;
+    fseek (ifp, 2078, SEEK_SET);
+    height = 1716;
+    width  = 2304;
+      } else if (model[8] == '5') {
+    data_offset = 4016;
+    fseek (ifp, 1936, SEEK_SET);
+konica_510z:
+    height = 1956;
+    width  = 2607;
+    raw_width = 2624;
+      } else if (model[8] == '6') {
+    data_offset = 4032;
+    fseek (ifp, 2030, SEEK_SET);
+    height = 2136;
+    width  = 2848;
+      }
+      filters = 0x61616161;
+konica_400z:
+      load_raw = unpacked_load_raw;
+      maximum = 0x3df;
+      order = 0x4d4d;
+      camera_red   = get2(ifp);
+      camera_blue  = get2(ifp);
+      camera_red  /= get2(ifp);
+      camera_blue /= get2(ifp);
+    }
+    if (pre_mul[0] == 1 && pre_mul[2] == 1) {
+      pre_mul[0] = 1.42;
+      pre_mul[2] = 1.25;
+    }
+  } else if (!strcmp(model,"*ist D")) {
+    load_raw = unpacked_load_raw;
+  } else if (!strcmp(model,"*ist DS")) {
+    height--;
+    load_raw = packed_12_load_raw;
+  } else if (!strcmp(model,"Optio S")) {
+    if (fsize == 3178560) {
+      height = 1540;
+      width  = 2064;
+      load_raw = eight_bit_load_raw;
+      camera_red  *= 4;
+      camera_blue *= 4;
+      pre_mul[0] = 1.391;
+      pre_mul[2] = 1.188;
+    } else {
+      height = 1544;
+      width  = 2068;
+      raw_width = 3136;
+      load_raw = packed_12_load_raw;
+      maximum = 0xf7c;
+      pre_mul[0] = 1.137;
+      pre_mul[2] = 1.453;
+    }
+  } else if (!strncmp(model,"Optio S4",8)) {
+    height = 1737;
+    width  = 2324;
+    raw_width = 3520;
+    load_raw = packed_12_load_raw;
+    maximum = 0xf7a;
+    pre_mul[0] = 1.980;
+    pre_mul[2] = 1.570;
+  } else if (!strcmp(model,"STV680 VGA")) {
+    height = 484;
+    width  = 644;
+    load_raw = eight_bit_load_raw;
+    flip = 2;
+    filters = 0x16161616;
+    black = 16;
+    pre_mul[0] = 1.097;
+    pre_mul[2] = 1.128;
+  } else if (!strcmp(make,"Phase One")) {
+    switch (raw_height) {
+      case 2060:
+    strcpy (model, "LightPhase");
+    height = 2048;
+    width  = 3080;
+    top_margin  = 5;
+    left_margin = 22;
+    pre_mul[0] = 1.331;
+    pre_mul[2] = 1.154;
+    break;
+      case 2682:
+    strcpy (model, "H10");
+    height = 2672;
+    width  = 4012;
+    top_margin  = 5;
+    left_margin = 26;
+    break;
+      case 4128:
+    strcpy (model, "H20");
+    height = 4098;
+    width  = 4098;
+    top_margin  = 20;
+    left_margin = 26;
+    pre_mul[0] = 1.963;
+    pre_mul[2] = 1.430;
+    break;
+      case 5488:
+    strcpy (model, "H25");
+    height = 5458;
+    width  = 4098;
+    top_margin  = 20;
+    left_margin = 26;
+    pre_mul[0] = 2.80;
+    pre_mul[2] = 1.20;
+    }
+    filters = top_margin & 1 ? 0x94949494 : 0x49494949;
+    load_raw = phase_one_load_raw;
+    maximum = 0xffff;
+  } else if (!strcmp(model,"Ixpress")) {
+    height = 4084;
+    width  = 4080;
+    filters = 0x49494949;
+    load_raw = ixpress_load_raw;
+    maximum = 0xffff;
+    pre_mul[0] = 1.963;
+    pre_mul[2] = 1.430;
+  } else if (!strcmp(make,"Sinar") && !memcmp(head,"8BPS",4)) {
+    fseek (ifp, 14, SEEK_SET);
+    height = get4(ifp);
+    width  = get4(ifp);
+    filters = 0x61616161;
+    data_offset = 68;
+    load_raw = unpacked_load_raw;
+    maximum = 0xffff;
+  } else if (!strcmp(make,"Leaf")) {
+    if (height > width)
+      filters = 0x16161616;
+    load_raw = unpacked_load_raw;
+    maximum = 0x3fff;
+    strcpy (model, "Valeo");
+    if (raw_width == 2060) {
+      filters = 0;
+      load_raw = leaf_load_raw;
+      maximum = 0xffff;
+      strcpy (model, "Volare");
+    }
+  } else if (!strcmp(model,"DIGILUX 2") || !strcmp(model,"DMC-LC1")) {
+    height = 1928;
+    width  = 2568;
+    data_offset = 1024;
+    load_raw = unpacked_load_raw;
+    maximum = 0xfff0;
+  } else if (!strcmp(model,"E-1")) {
+    filters = 0x61616161;
+    load_raw = unpacked_load_raw;
+    maximum = 0xfff0;
+    black = 1024;
+  } else if (!strcmp(model,"E-10")) {
+    load_raw = unpacked_load_raw;
+    maximum = 0xfff0;
+    black = 2048;
+  } else if (!strncmp(model,"E-20",4)) {
+    load_raw = unpacked_load_raw;
+    maximum = 0xffc0;
+    black = 2560;
+  } else if (!strcmp(model,"E-300")) {
+    width -= 21;
+    load_raw = olympus_e300_load_raw;
+    if (fsize > 15728640) {
+      load_raw = unpacked_load_raw;
+      maximum = 0xfc30;
+    } else
+      black = 62;
+  } else if (!strcmp(make,"OLYMPUS")) {
+    load_raw = olympus_cseries_load_raw;
+    if (!strcmp(model,"C5050Z") ||
+    !strcmp(model,"C8080WZ"))
+      filters = 0x16161616;
+  } else if (!strcmp(model,"N Digital")) {
+    height = 2047;
+    width  = 3072;
+    filters = 0x61616161;
+    data_offset = 0x1a00;
+    load_raw = packed_12_load_raw;
+    maximum = 0xf1e;
+  } else if (!strcmp(model,"DSC-F828")) {
+    width = 3288;
+    left_margin = 5;
+    load_raw = sony_load_raw;
+    filters = 0x9c9c9c9c;
+    colors = 4;
+    black = 491;
+  } else if (!strcmp(model,"DSC-V3")) {
+    width = 3109;
+    left_margin = 59;
+    load_raw = sony_load_raw;
+  } else if (!strcasecmp(make,"KODAK")) {
+    filters = 0x61616161;
+    if (!strcmp(model,"NC2000F")) {
+      width -= 4;
+      left_margin = 1;
+      for (i=176; i < 0x1000; i++)
+    curve[i] = curve[i-1];
+      pre_mul[0] = 1.509;
+      pre_mul[2] = 2.686;
+    } else if (!strcmp(model,"EOSDCS3B")) {
+      width -= 4;
+      left_margin = 2;
+    } else if (!strcmp(model,"EOSDCS1")) {
+      width -= 4;
+      left_margin = 2;
+    } else if (!strcmp(model,"DCS315C")) {
+      black = 8;
+    } else if (!strcmp(model,"DCS330C")) {
+      black = 8;
+    } else if (!strcmp(model,"DCS420")) {
+      width -= 4;
+      left_margin = 2;
+    } else if (!strcmp(model,"DCS460")) {
+      width -= 4;
+      left_margin = 2;
+    } else if (!strcmp(model,"DCS460A")) {
+      width -= 4;
+      left_margin = 2;
+      colors = 1;
+      filters = 0;
+    } else if (!strcmp(model,"DCS520C")) {
+      black = 180;
+    } else if (!strcmp(model,"DCS560C")) {
+      black = 188;
+    } else if (!strcmp(model,"DCS620C")) {
+      black = 180;
+    } else if (!strcmp(model,"DCS620X")) {
+      black = 185;
+    } else if (!strcmp(model,"DCS660C")) {
+      black = 214;
+    } else if (!strcmp(model,"DCS660M")) {
+      black = 214;
+      colors = 1;
+      filters = 0;
+    } else if (!strcmp(model,"DCS760M")) {
+      colors = 1;
+      filters = 0;
+    }
+    switch (tiff_data_compression) {
+    case 0:               /* No compression */
+    case 1:
+        load_raw = kodak_easy_load_raw;
+        break;
+    case 7:               /* Lossless JPEG */
+        load_raw = lossless_jpeg_load_raw;
+    case 32867:
+        break;
+    case 65000:           /* Kodak DCR compression */
+        if (!have64BitArithmetic)
+            pm_error("This program was built without 64 bit arithmetic "
+                     "capability, and Kodak DCR compression requires it.");
+        if (kodak_data_compression == 32803)
+            load_raw = kodak_compressed_load_raw;
+        else {
+            load_raw = kodak_yuv_load_raw;
+            height = (height+1) & -2;
+            width  = (width +1) & -2;
+            filters = 0;
+        }
+        break;
+    default:
+        pm_message ("%s %s uses unrecognized compression method %d.",
+                    make, model, tiff_data_compression);
+        return 1;
+    }
+    if (!strcmp(model,"DC20")) {
+      height = 242;
+      if (fsize < 100000) {
+    width = 249;
+    raw_width = 256;
+      } else {
+    width = 501;
+    raw_width = 512;
+      }
+      data_offset = raw_width + 1;
+      colors = 4;
+      filters = 0x8d8d8d8d;
+      kodak_dc20_coeff (1.0);
+      pre_mul[1] = 1.179;
+      pre_mul[2] = 1.209;
+      pre_mul[3] = 1.036;
+      load_raw = kodak_easy_load_raw;
+    } else if (strstr(model,"DC25")) {
+      strcpy (model, "DC25");
+      height = 242;
+      if (fsize < 100000) {
+    width = 249;
+    raw_width = 256;
+    data_offset = 15681;
+      } else {
+    width = 501;
+    raw_width = 512;
+    data_offset = 15937;
+      }
+      colors = 4;
+      filters = 0xb4b4b4b4;
+      load_raw = kodak_easy_load_raw;
+    } else if (!strcmp(model,"Digital Camera 40")) {
+      strcpy (model, "DC40");
+      height = 512;
+      width = 768;
+      data_offset = 1152;
+      load_raw = kodak_radc_load_raw;
+    } else if (strstr(model,"DC50")) {
+      strcpy (model, "DC50");
+      height = 512;
+      width = 768;
+      data_offset = 19712;
+      load_raw = kodak_radc_load_raw;
+    } else if (strstr(model,"DC120")) {
+      strcpy (model, "DC120");
+      height = 976;
+      width = 848;
+      if (tiff_data_compression == 7)
+    load_raw = kodak_jpeg_load_raw;
+      else
+    load_raw = kodak_dc120_load_raw;
+    }
+  } else if (!strcmp(make,"Rollei")) {
+    switch (raw_width) {
+      case 1316:
+    height = 1030;
+    width  = 1300;
+    top_margin  = 1;
+    left_margin = 6;
+    break;
+      case 2568:
+    height = 1960;
+    width  = 2560;
+    top_margin  = 2;
+    left_margin = 8;
+    }
+    filters = 0x16161616;
+    load_raw = rollei_load_raw;
+    pre_mul[0] = 1.8;
+    pre_mul[2] = 1.3;
+  } else if (!strcmp(model,"PC-CAM 600")) {
+    height = 768;
+    data_offset = width = 1024;
+    filters = 0x49494949;
+    load_raw = eight_bit_load_raw;
+    pre_mul[0] = 1.14;
+    pre_mul[2] = 2.73;
+  } else if (!strcmp(model,"QV-2000UX")) {
+    height = 1208;
+    width  = 1632;
+    data_offset = width * 2;
+    load_raw = eight_bit_load_raw;
+  } else if (!strcmp(model,"QV-3*00EX")) {
+    height = 1546;
+    width  = 2070;
+    raw_width = 2080;
+    load_raw = eight_bit_load_raw;
+  } else if (!strcmp(model,"QV-4000")) {
+    height = 1700;
+    width  = 2260;
+    load_raw = unpacked_load_raw;
+    maximum = 0xffff;
+  } else if (!strcmp(model,"QV-5700")) {
+    height = 1924;
+    width  = 2576;
+    load_raw = casio_qv5700_load_raw;
+  } else if (!strcmp(model,"QV-R51")) {
+    height = 1926;
+    width  = 2576;
+    raw_width = 3904;
+    load_raw = packed_12_load_raw;
+    pre_mul[0] = 1.340;
+    pre_mul[2] = 1.672;
+  } else if (!strcmp(model,"EX-Z55")) {
+    height = 1960;
+    width  = 2570;
+    raw_width = 3904;
+    load_raw = packed_12_load_raw;
+    pre_mul[0] = 1.520;
+    pre_mul[2] = 1.316;
+  } else if (!strcmp(model,"EX-P600")) {
+    height = 2142;
+    width  = 2844;
+    raw_width = 4288;
+    load_raw = packed_12_load_raw;
+    pre_mul[0] = 1.797;
+    pre_mul[2] = 1.219;
+  } else if (!strcmp(model,"EX-P700")) {
+    height = 2318;
+    width  = 3082;
+    raw_width = 4672;
+    load_raw = packed_12_load_raw;
+    pre_mul[0] = 1.758;
+    pre_mul[2] = 1.504;
+  } else if (!strcmp(make,"Nucore")) {
+    filters = 0x61616161;
+    load_raw = unpacked_load_raw;
+    if (width == 2598) {
+      filters = 0x16161616;
+      load_raw = nucore_load_raw;
+      flip = 2;
+    }
+  }
+  if (!use_coeff) adobe_coeff();
+dng_skip:
+  if (!load_raw || !height) {
+    pm_message ("This program cannot handle data from %s %s.",
+                make, model);
+    return 1;
+  }
+#ifdef NO_JPEG
+  if (load_raw == kodak_jpeg_load_raw) {
+    pm_message ("decoder was not linked with libjpeg.");
+    return 1;
+  }
+#endif
+  if (!raw_height) raw_height = height;
+  if (!raw_width ) raw_width  = width;
+  if (use_camera_rgb && colors == 3)
+      use_coeff = 0;
+  if (use_coeff)         /* Apply user-selected color balance */
+    for (i=0; i < colors; i++) {
+      coeff[0][i] *= red_scale;
+      coeff[2][i] *= blue_scale;
+    }
+  if (four_color_rgb && filters && colors == 3) {
+    for (i=0; i < 32; i+=4) {
+      if ((filters >> i & 15) == 9)
+    filters |= 2 << i;
+      if ((filters >> i & 15) == 6)
+    filters |= 8 << i;
+    }
+    colors++;
+    pre_mul[3] = pre_mul[1];
+    if (use_coeff)
+      for (i=0; i < 3; i++)
+    coeff[i][3] = coeff[i][1] /= 2;
+  }
+  fseek (ifp, data_offset, SEEK_SET);
+
+  *loadRawFnP = load_raw;
+
+  return 0;
+}
diff --git a/converter/other/cameratopam/identify.h b/converter/other/cameratopam/identify.h
new file mode 100644
index 00000000..012b807c
--- /dev/null
+++ b/converter/other/cameratopam/identify.h
@@ -0,0 +1,11 @@
+typedef void (*loadRawFn)();
+
+int
+identify(FILE *       const ifp,
+         bool         const use_secondary,
+         bool         const use_camera_rgb,
+         float        const red_scale,
+         float        const blue_scale,
+         unsigned int const four_color_rgb,
+         const char * const inputFileName,
+         loadRawFn *  const loadRawFnP);
diff --git a/converter/other/cameratopam/ljpeg.c b/converter/other/cameratopam/ljpeg.c
new file mode 100644
index 00000000..18423f4f
--- /dev/null
+++ b/converter/other/cameratopam/ljpeg.c
@@ -0,0 +1,141 @@
+#define _BSD_SOURCE    /* Make sure string.h containst strcasecmp() */
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+
+#include "pm.h"
+
+#include "global_variables.h"
+#include "util.h"
+#include "decode.h"
+#include "bayer.h"
+
+#include "ljpeg.h"
+
+
+/*
+   Not a full implementation of Lossless JPEG, just
+   enough to decode Canon, Kodak and Adobe DNG images.
+ */
+
+int  
+ljpeg_start (FILE * ifp, struct jhead *jh)
+{
+  int i, tag, len;
+  unsigned char data[256], *dp;
+
+  init_decoder();
+  for (i=0; i < 4; i++)
+    jh->huff[i] = free_decode;
+  fread (data, 2, 1, ifp);
+  if (data[0] != 0xff || data[1] != 0xd8) return 0;
+  do {
+    fread (data, 2, 2, ifp);
+    tag =  data[0] << 8 | data[1];
+    len = (data[2] << 8 | data[3]) - 2;
+    if (tag <= 0xff00 || len > 255) return 0;
+    fread (data, 1, len, ifp);
+    switch (tag) {
+      case 0xffc3:
+    jh->bits = data[0];
+    jh->high = data[1] << 8 | data[2];
+    jh->wide = data[3] << 8 | data[4];
+    jh->clrs = data[5];
+    break;
+      case 0xffc4:
+    for (dp = data; dp < data+len && *dp < 4; ) {
+      jh->huff[*dp] = free_decode;
+      dp = make_decoder (++dp, 0);
+    }
+    }
+  } while (tag != 0xffda);
+  jh->row = calloc (jh->wide*jh->clrs, 2);
+  if (jh->row == NULL)
+      pm_error("Out of memory in ljpeg_start()");
+  for (i=0; i < 4; i++)
+    jh->vpred[i] = 1 << (jh->bits-1);
+  zero_after_ff = 1;
+  getbits(ifp, -1);
+  return 1;
+}
+
+int 
+ljpeg_diff (struct decode *dindex)
+{
+  int len, diff;
+
+  while (dindex->branch[0])
+    dindex = dindex->branch[getbits(ifp, 1)];
+  diff = getbits(ifp, len = dindex->leaf);
+  if ((diff & (1 << (len-1))) == 0)
+    diff -= (1 << len) - 1;
+  return diff;
+}
+
+void
+ljpeg_row (struct jhead *jh)
+{
+  int col, c, diff;
+  unsigned short *outp=jh->row;
+
+  for (col=0; col < jh->wide; col++)
+    for (c=0; c < jh->clrs; c++) {
+      diff = ljpeg_diff (jh->huff[c]);
+      *outp = col ? outp[-jh->clrs]+diff : (jh->vpred[c] += diff);
+      outp++;
+    }
+}
+
+void  
+lossless_jpeg_load_raw(void)
+{
+  int jwide, jrow, jcol, val, jidx, i, row, col;
+  struct jhead jh;
+  int min=INT_MAX;
+
+  if (!ljpeg_start (ifp, &jh)) return;
+  jwide = jh.wide * jh.clrs;
+
+  for (jrow=0; jrow < jh.high; jrow++) {
+    ljpeg_row (&jh);
+    for (jcol=0; jcol < jwide; jcol++) {
+      val = curve[jh.row[jcol]];
+      jidx = jrow*jwide + jcol;
+      if (raw_width == 5108) {
+    i = jidx / (1680*jh.high);
+    if (i < 2) {
+      row = jidx / 1680 % jh.high;
+      col = jidx % 1680 + i*1680;
+    } else {
+      jidx -= 2*1680*jh.high;
+      row = jidx / 1748;
+      col = jidx % 1748 + 2*1680;
+    }
+      } else if (raw_width == 3516) {
+    row = jidx / 1758;
+    col = jidx % 1758;
+    if (row >= raw_height) {
+      row -= raw_height;
+      col += 1758;
+    }
+      } else {
+    row = jidx / raw_width;
+    col = jidx % raw_width;
+      }
+      if ((unsigned) (row-top_margin) >= height) continue;
+      if ((unsigned) (col-left_margin) < width) {
+    BAYER(row-top_margin,col-left_margin) = val;
+    if (min > val) min = val;
+      } else
+    black += val;
+    }
+  }
+  free (jh.row);
+  if (raw_width > width)
+    black /= (raw_width - width) * height;
+  if (!strcasecmp(make,"KODAK"))
+    black = min;
+}
+
+
diff --git a/converter/other/cameratopam/ljpeg.h b/converter/other/cameratopam/ljpeg.h
new file mode 100644
index 00000000..60832a3d
--- /dev/null
+++ b/converter/other/cameratopam/ljpeg.h
@@ -0,0 +1,17 @@
+struct jhead {
+  int bits, high, wide, clrs, vpred[4];
+  struct decode *huff[4];
+  unsigned short *row;
+};
+
+void  
+lossless_jpeg_load_raw(void);
+
+int  
+ljpeg_start (FILE * ifp, struct jhead *jh);
+
+int 
+ljpeg_diff (struct decode *dindex);
+
+void
+ljpeg_row (struct jhead *jh);
diff --git a/converter/other/cameratopam/util.c b/converter/other/cameratopam/util.c
new file mode 100644
index 00000000..b3ccf7e9
--- /dev/null
+++ b/converter/other/cameratopam/util.c
@@ -0,0 +1,83 @@
+#define _XOPEN_SOURCE   /* Make sure unistd.h contains swab() */
+#include <unistd.h>
+#include <stdio.h>
+
+#include "pm.h"
+#include "global_variables.h"
+#include "util.h"
+
+#ifndef LONG_BITS
+#define LONG_BITS (8 * sizeof(long))
+#endif
+/*
+   Get a 2-byte integer, making no assumptions about CPU byte order.
+   Nor should we assume that the compiler evaluates left-to-right.
+ */
+unsigned short
+get2(FILE * const ifp)
+{
+    unsigned char a, b;
+
+    a = fgetc(ifp);  b = fgetc(ifp);
+
+    if (order == 0x4949)      /* "II" means little-endian */
+        return a | b << 8;
+    else              /* "MM" means big-endian */
+        return a << 8 | b;
+}
+
+/*
+   Same for a 4-byte integer.
+ */
+int
+get4(FILE * const ifp)
+{
+    unsigned char a, b, c, d;
+
+    a = fgetc(ifp);  b = fgetc(ifp);
+    c = fgetc(ifp);  d = fgetc(ifp);
+
+    if (order == 0x4949)
+        return a | b << 8 | c << 16 | d << 24;
+    else
+        return a << 24 | b << 16 | c << 8 | d;
+}
+
+/*
+   Faster than calling get2() multiple times.
+ */
+void
+read_shorts (FILE * const ifp, unsigned short *pixel, int count)
+{
+    fread (pixel, 2, count, ifp);
+    if ((order == 0x4949) == (BYTE_ORDER == BIG_ENDIAN))
+        swab (pixel, pixel, count*2);
+}
+
+/*
+   getbits(-1) initializes the buffer
+   getbits(n) where 0 <= n <= 25 returns an n-bit integer
+ */
+unsigned 
+getbits (FILE * const ifp, int nbits)
+{
+    static unsigned long bitbuf=0;
+    static int vbits=0;
+    unsigned c, ret;
+
+    if (nbits == 0) return 0;
+    if (nbits == -1)
+        ret = bitbuf = vbits = 0;
+    else {
+        ret = bitbuf << (LONG_BITS - vbits) >> (LONG_BITS - nbits);
+        vbits -= nbits;
+    }
+    while (vbits < LONG_BITS - 7) {
+        c = fgetc(ifp);
+        bitbuf = (bitbuf << 8) + c;
+        if (c == 0xff && zero_after_ff)
+            fgetc(ifp);
+        vbits += 8;
+    }
+    return ret;
+}
diff --git a/converter/other/cameratopam/util.h b/converter/other/cameratopam/util.h
new file mode 100644
index 00000000..bf9006d3
--- /dev/null
+++ b/converter/other/cameratopam/util.h
@@ -0,0 +1,16 @@
+#ifndef UTIL_H_INCLUDED
+#define UTIL_H_INCLUDED
+
+unsigned short
+get2(FILE * const ifp);
+
+int
+get4(FILE * const ifp);
+
+void
+read_shorts (FILE * const ifp, unsigned short *pixel, int count);
+
+unsigned int
+getbits (FILE * const ifp, int nbits);
+
+#endif
diff --git a/converter/other/dithers.h b/converter/other/dithers.h
new file mode 100644
index 00000000..1ced833d
--- /dev/null
+++ b/converter/other/dithers.h
@@ -0,0 +1,91 @@
+#ifndef DITHERS_H_INCLUDED
+#define DITHERS_H_INCLUDED
+
+/*
+** dithers.h
+**
+** Here are some dithering matrices.  They are all taken from "Digital
+** Halftoning" by Robert Ulichney, MIT Press, ISBN 0-262-21009-6.
+*/
+
+
+#if 0
+/*
+** Order-6 ordered dithering matrix.  Note that smaller ordered dithers
+** have no advantage over larger ones, so use dither8 instead.
+*/
+static int dither6[8][8] = {
+  {  1, 59, 15, 55,  2, 56, 12, 52 },
+  { 33, 17, 47, 31, 34, 18, 44, 28 },
+  {  9, 49,  5, 63, 10, 50,  6, 60 },
+  { 41, 25, 37, 21, 42, 26, 38, 22 },
+  {  3, 57, 13, 53,  0, 58, 14, 54 },
+  { 35, 19, 45, 29, 32, 16, 46, 30 },
+  { 11, 51,  7, 61,  8, 48,  4, 62 },
+  { 43, 27, 39, 23, 40, 24, 36, 20 }
+  };
+#endif
+
+/* Order-8 ordered dithering matrix. */
+static int dither8[16][16] = {
+  {   1,235, 59,219, 15,231, 55,215,  2,232, 56,216, 12,228, 52,212},
+  { 129, 65,187,123,143, 79,183,119,130, 66,184,120,140, 76,180,116},
+  {  33,193, 17,251, 47,207, 31,247, 34,194, 18,248, 44,204, 28,244},
+  { 161, 97,145, 81,175,111,159, 95,162, 98,146, 82,172,108,156, 92},
+  {   9,225, 49,209,  5,239, 63,223, 10,226, 50,210,  6,236, 60,220},
+  { 137, 73,177,113,133, 69,191,127,138, 74,178,114,134, 70,188,124},
+  {  41,201, 25,241, 37,197, 21,255, 42,202, 26,242, 38,198, 22,252},
+  { 169,105,153, 89,165,101,149, 85,170,106,154, 90,166,102,150, 86},
+  {   3,233, 57,217, 13,229, 53,213,  0,234, 58,218, 14,230, 54,214},
+  { 131, 67,185,121,141, 77,181,117,128, 64,186,122,142, 78,182,118},
+  {  35,195, 19,249, 45,205, 29,245, 32,192, 16,250, 46,206, 30,246},
+  { 163, 99,147, 83,173,109,157, 93,160, 96,144, 80,174,110,158, 94},
+  {  11,227, 51,211,  7,237, 61,221,  8,224, 48,208,  4,238, 62,222},
+  { 139, 75,179,115,135, 71,189,125,136, 72,176,112,132, 68,190,126},
+  {  43,203, 27,243, 39,199, 23,253, 40,200, 24,240, 36,196, 20,254},
+  { 171,107,155, 91,167,103,151, 87,168,104,152, 88,164,100,148, 84} 
+};
+
+/* Order-3 clustered dithering matrix. */
+static int cluster3[6][6] = {
+  {  9,11,10, 8, 6, 7},
+  { 12,17,16, 5, 0, 1},
+  { 13,14,15, 4, 3, 2},
+  {  8, 6, 7, 9,11,10},
+  {  5, 0, 1,12,17,16},
+  {  4, 3, 2,13,14,15}
+};
+
+/* Order-4 clustered dithering matrix. */
+static int cluster4[8][8] = {
+  { 18,20,19,16,13,11,12,15},
+  { 27,28,29,22, 4, 3, 2, 9},
+  { 26,31,30,21, 5, 0, 1,10},
+  { 23,25,24,17, 8, 6, 7,14},
+  { 13,11,12,15,18,20,19,16},
+  {  4, 3, 2, 9,27,28,29,22},
+  {  5, 0, 1,10,26,31,30,21},
+  {  8, 6, 7,14,23,25,24,17}
+};
+
+/* Order-8 clustered dithering matrix. */
+static int cluster8[16][16] = {
+   { 64, 69, 77, 87, 86, 76, 68, 67, 63, 58, 50, 40, 41, 51, 59, 60},
+   { 70, 94,100,109,108, 99, 93, 75, 57, 33, 27, 18, 19, 28, 34, 52},
+   { 78,101,114,116,115,112, 98, 83, 49, 26, 13, 11, 12, 15, 29, 44},
+   { 88,110,123,124,125,118,107, 85, 39, 17,  4,  3,  2,  9, 20, 42},
+   { 89,111,122,127,126,117,106, 84, 38, 16,  5,  0,  1, 10, 21, 43},
+   { 79,102,119,121,120,113, 97, 82, 48, 25,  8,  6,  7, 14, 30, 45},
+   { 71, 95,103,104,105, 96, 92, 74, 56, 32, 24, 23, 22, 31, 35, 53},
+   { 65, 72, 80, 90, 91, 81, 73, 66, 62, 55, 47, 37, 36, 46, 54, 61},
+   { 63, 58, 50, 40, 41, 51, 59, 60, 64, 69, 77, 87, 86, 76, 68, 67},
+   { 57, 33, 27, 18, 19, 28, 34, 52, 70, 94,100,109,108, 99, 93, 75},
+   { 49, 26, 13, 11, 12, 15, 29, 44, 78,101,114,116,115,112, 98, 83},
+   { 39, 17,  4,  3,  2,  9, 20, 42, 88,110,123,124,125,118,107, 85},
+   { 38, 16,  5,  0,  1, 10, 21, 43, 89,111,122,127,126,117,106, 84},
+   { 48, 25,  8,  6,  7, 14, 30, 45, 79,102,119,121,120,113, 97, 82},
+   { 56, 32, 24, 23, 22, 31, 35, 53, 71, 95,103,104,105, 96, 92, 74},
+   { 62, 55, 47, 37, 36, 46, 54, 61, 65, 72, 80, 90, 91, 81, 73, 66}
+};
+
+#endif
diff --git a/converter/other/exif.c b/converter/other/exif.c
new file mode 100644
index 00000000..19f108a7
--- /dev/null
+++ b/converter/other/exif.c
@@ -0,0 +1,1030 @@
+/*--------------------------------------------------------------------------
+  This file contains subroutines for use by Jpegtopnm to handle the
+  EXIF header.
+
+  The code is adapted from the program Jhead by Matthaias Wandel
+  December 1999 - August 2000, and contributed to the public domain.
+  Bryan Henderson adapted it to Netpbm in September 2001.  Bryan
+  added more of Wandel's code, from Jhead 1.9 dated December 2002 in
+  January 2003.
+
+  An EXIF header is a JFIF APP1 marker.  It is generated by some
+  digital cameras and contains information about the circumstances of
+  the creation of the image (camera settings, etc.).
+
+  The EXIF header uses the TIFF format, only it contains only tag
+  values and no actual image.
+
+  Note that the image format called EXIF is simply JFIF with an EXIF
+  header, i.e. a subformat of JFIF.
+
+  See the EXIF specs at http://exif.org (2001.09.01).
+
+--------------------------------------------------------------------------*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <limits.h>
+#include <ctype.h>
+
+#ifdef _WIN32
+    #include <sys/utime.h>
+#else
+    #include <utime.h>
+    #include <sys/types.h>
+    #include <unistd.h>
+    #include <errno.h>
+#endif
+
+#include "pm_c_util.h"
+#include "pm.h"
+#include "nstring.h"
+
+#include "exif.h"
+
+static unsigned char * LastExifRefd;
+static unsigned char * DirWithThumbnailPtrs;
+static double FocalplaneXRes;
+bool HaveXRes;
+static double FocalplaneUnits;
+static int ExifImageWidth;
+static int MotorolaOrder = 0;
+
+typedef struct {
+    unsigned short Tag;
+    const char * Desc;
+}TagTable_t;
+
+
+/* Describes format descriptor */
+static int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
+#define NUM_FORMATS 12
+
+#define FMT_BYTE       1 
+#define FMT_STRING     2
+#define FMT_USHORT     3
+#define FMT_ULONG      4
+#define FMT_URATIONAL  5
+#define FMT_SBYTE      6
+#define FMT_UNDEFINED  7
+#define FMT_SSHORT     8
+#define FMT_SLONG      9
+#define FMT_SRATIONAL 10
+#define FMT_SINGLE    11
+#define FMT_DOUBLE    12
+
+/* Describes tag values */
+
+#define TAG_EXIF_OFFSET       0x8769
+#define TAG_INTEROP_OFFSET    0xa005
+
+#define TAG_MAKE              0x010F
+#define TAG_MODEL             0x0110
+
+#define TAG_ORIENTATION       0x0112
+
+#define TAG_EXPOSURETIME      0x829A
+#define TAG_FNUMBER           0x829D
+
+#define TAG_SHUTTERSPEED      0x9201
+#define TAG_APERTURE          0x9202
+#define TAG_MAXAPERTURE       0x9205
+#define TAG_FOCALLENGTH       0x920A
+
+#define TAG_DATETIME_ORIGINAL 0x9003
+#define TAG_USERCOMMENT       0x9286
+
+#define TAG_SUBJECT_DISTANCE  0x9206
+#define TAG_FLASH             0x9209
+
+#define TAG_FOCALPLANEXRES    0xa20E
+#define TAG_FOCALPLANEUNITS   0xa210
+#define TAG_EXIF_IMAGEWIDTH   0xA002
+#define TAG_EXIF_IMAGELENGTH  0xA003
+
+/* the following is added 05-jan-2001 vcs */
+#define TAG_EXPOSURE_BIAS     0x9204
+#define TAG_WHITEBALANCE      0x9208
+#define TAG_METERING_MODE     0x9207
+#define TAG_EXPOSURE_PROGRAM  0x8822
+#define TAG_ISO_EQUIVALENT    0x8827
+#define TAG_COMPRESSION_LEVEL 0x9102
+
+#define TAG_THUMBNAIL_OFFSET  0x0201
+#define TAG_THUMBNAIL_LENGTH  0x0202
+
+static TagTable_t const TagTable[] = {
+  {   0x100,   "ImageWidth"},
+  {   0x101,   "ImageLength"},
+  {   0x102,   "BitsPerSample"},
+  {   0x103,   "Compression"},
+  {   0x106,   "PhotometricInterpretation"},
+  {   0x10A,   "FillOrder"},
+  {   0x10D,   "DocumentName"},
+  {   0x10E,   "ImageDescription"},
+  {   0x10F,   "Make"},
+  {   0x110,   "Model"},
+  {   0x111,   "StripOffsets"},
+  {   0x112,   "Orientation"},
+  {   0x115,   "SamplesPerPixel"},
+  {   0x116,   "RowsPerStrip"},
+  {   0x117,   "StripByteCounts"},
+  {   0x11A,   "XResolution"},
+  {   0x11B,   "YResolution"},
+  {   0x11C,   "PlanarConfiguration"},
+  {   0x128,   "ResolutionUnit"},
+  {   0x12D,   "TransferFunction"},
+  {   0x131,   "Software"},
+  {   0x132,   "DateTime"},
+  {   0x13B,   "Artist"},
+  {   0x13E,   "WhitePoint"},
+  {   0x13F,   "PrimaryChromaticities"},
+  {   0x156,   "TransferRange"},
+  {   0x200,   "JPEGProc"},
+  {   0x201,   "ThumbnailOffset"},
+  {   0x202,   "ThumbnailLength"},
+  {   0x211,   "YCbCrCoefficients"},
+  {   0x212,   "YCbCrSubSampling"},
+  {   0x213,   "YCbCrPositioning"},
+  {   0x214,   "ReferenceBlackWhite"},
+  {   0x828D,  "CFARepeatPatternDim"},
+  {   0x828E,  "CFAPattern"},
+  {   0x828F,  "BatteryLevel"},
+  {   0x8298,  "Copyright"},
+  {   0x829A,  "ExposureTime"},
+  {   0x829D,  "FNumber"},
+  {   0x83BB,  "IPTC/NAA"},
+  {   0x8769,  "ExifOffset"},
+  {   0x8773,  "InterColorProfile"},
+  {   0x8822,  "ExposureProgram"},
+  {   0x8824,  "SpectralSensitivity"},
+  {   0x8825,  "GPSInfo"},
+  {   0x8827,  "ISOSpeedRatings"},
+  {   0x8828,  "OECF"},
+  {   0x9000,  "ExifVersion"},
+  {   0x9003,  "DateTimeOriginal"},
+  {   0x9004,  "DateTimeDigitized"},
+  {   0x9101,  "ComponentsConfiguration"},
+  {   0x9102,  "CompressedBitsPerPixel"},
+  {   0x9201,  "ShutterSpeedValue"},
+  {   0x9202,  "ApertureValue"},
+  {   0x9203,  "BrightnessValue"},
+  {   0x9204,  "ExposureBiasValue"},
+  {   0x9205,  "MaxApertureValue"},
+  {   0x9206,  "SubjectDistance"},
+  {   0x9207,  "MeteringMode"},
+  {   0x9208,  "LightSource"},
+  {   0x9209,  "Flash"},
+  {   0x920A,  "FocalLength"},
+  {   0x927C,  "MakerNote"},
+  {   0x9286,  "UserComment"},
+  {   0x9290,  "SubSecTime"},
+  {   0x9291,  "SubSecTimeOriginal"},
+  {   0x9292,  "SubSecTimeDigitized"},
+  {   0xA000,  "FlashPixVersion"},
+  {   0xA001,  "ColorSpace"},
+  {   0xA002,  "ExifImageWidth"},
+  {   0xA003,  "ExifImageLength"},
+  {   0xA005,  "InteroperabilityOffset"},
+  {   0xA20B,  "FlashEnergy"},                 /* 0x920B in TIFF/EP */
+  {   0xA20C,  "SpatialFrequencyResponse"},  /* 0x920C    -  - */
+  {   0xA20E,  "FocalPlaneXResolution"},     /* 0x920E    -  - */
+  {   0xA20F,  "FocalPlaneYResolution"},      /* 0x920F    -  - */
+  {   0xA210,  "FocalPlaneResolutionUnit"},  /* 0x9210    -  - */
+  {   0xA214,  "SubjectLocation"},             /* 0x9214    -  - */
+  {   0xA215,  "ExposureIndex"},            /* 0x9215    -  - */
+  {   0xA217,  "SensingMethod"},            /* 0x9217    -  - */
+  {   0xA300,  "FileSource"},
+  {   0xA301,  "SceneType"},
+  {      0, NULL}
+} ;
+
+
+
+/*--------------------------------------------------------------------------
+   Convert a 16 bit unsigned value from file's native byte order
+--------------------------------------------------------------------------*/
+static int Get16u(void * Short)
+{
+    if (MotorolaOrder){
+        return (((unsigned char *)Short)[0] << 8) | 
+            ((unsigned char *)Short)[1];
+    }else{
+        return (((unsigned char *)Short)[1] << 8) | 
+            ((unsigned char *)Short)[0];
+    }
+}
+
+/*--------------------------------------------------------------------------
+   Convert a 32 bit signed value from file's native byte order
+--------------------------------------------------------------------------*/
+static int Get32s(void * Long)
+{
+    if (MotorolaOrder){
+        return  
+            ((( char *)Long)[0] << 24) | (((unsigned char *)Long)[1] << 16) |
+            (((unsigned char *)Long)[2] << 8 ) | 
+            (((unsigned char *)Long)[3] << 0 );
+    }else{
+        return  
+            ((( char *)Long)[3] << 24) | (((unsigned char *)Long)[2] << 16) |
+            (((unsigned char *)Long)[1] << 8 ) | 
+            (((unsigned char *)Long)[0] << 0 );
+    }
+}
+
+/*--------------------------------------------------------------------------
+   Convert a 32 bit unsigned value from file's native byte order
+--------------------------------------------------------------------------*/
+static unsigned Get32u(void * Long)
+{
+    return (unsigned)Get32s(Long) & 0xffffffff;
+}
+
+/*--------------------------------------------------------------------------
+   Display a number as one of its many formats
+--------------------------------------------------------------------------*/
+static void PrintFormatNumber(FILE * const file, 
+                              void * const ValuePtr, 
+                              int const Format, int const ByteCount)
+{
+    switch(Format){
+    case FMT_SBYTE:
+    case FMT_BYTE:      printf("%02x\n",*(unsigned char *)ValuePtr); break;
+    case FMT_USHORT:    fprintf(file, "%d\n",Get16u(ValuePtr));    break;
+    case FMT_ULONG:     
+    case FMT_SLONG:     fprintf(file, "%d\n",Get32s(ValuePtr));    break;
+    case FMT_SSHORT:    
+        fprintf(file, "%hd\n",(signed short)Get16u(ValuePtr));     break;
+    case FMT_URATIONAL:
+    case FMT_SRATIONAL: 
+        fprintf(file, "%d/%d\n",Get32s(ValuePtr), Get32s(4+(char *)ValuePtr));
+        break;
+    case FMT_SINGLE:    
+        fprintf(file, "%f\n",(double)*(float *)ValuePtr);          break;
+    case FMT_DOUBLE:    fprintf(file, "%f\n",*(double *)ValuePtr); break;
+    default: 
+        fprintf(file, "Unknown format %d:", Format);
+        {
+            int a;
+            for (a=0; a < ByteCount && a < 16; ++a)
+                printf("%02x", ((unsigned char *)ValuePtr)[a]);
+        }
+        fprintf(file, "\n");
+    }
+}
+
+
+/*--------------------------------------------------------------------------
+   Evaluate number, be it int, rational, or float from directory.
+--------------------------------------------------------------------------*/
+static double ConvertAnyFormat(void * ValuePtr, int Format)
+{
+    double Value;
+    Value = 0;
+
+    switch(Format){
+        case FMT_SBYTE:     Value = *(signed char *)ValuePtr;  break;
+        case FMT_BYTE:      Value = *(unsigned char *)ValuePtr;        break;
+
+        case FMT_USHORT:    Value = Get16u(ValuePtr);          break;
+        case FMT_ULONG:     Value = Get32u(ValuePtr);          break;
+
+        case FMT_URATIONAL:
+        case FMT_SRATIONAL: 
+            {
+                int Num,Den;
+                Num = Get32s(ValuePtr);
+                Den = Get32s(4+(char *)ValuePtr);
+                if (Den == 0){
+                    Value = 0;
+                }else{
+                    Value = (double)Num/Den;
+                }
+                break;
+            }
+
+        case FMT_SSHORT:    Value = (signed short)Get16u(ValuePtr);  break;
+        case FMT_SLONG:     Value = Get32s(ValuePtr);                break;
+
+        /* Not sure if this is correct (never seen float used in Exif format)
+         */
+        case FMT_SINGLE:    Value = (double)*(float *)ValuePtr;      break;
+        case FMT_DOUBLE:    Value = *(double *)ValuePtr;             break;
+    }
+    return Value;
+}
+
+/*--------------------------------------------------------------------------
+   Process one of the nested EXIF directories.
+--------------------------------------------------------------------------*/
+static void 
+ProcessExifDir(unsigned char *  const ExifData, 
+               unsigned int     const ExifLength,
+               unsigned int     const DirOffset,
+               ImageInfo_t *    const ImageInfoP, 
+               int              const ShowTags,
+               unsigned char ** const LastExifRefdP) {
+
+    unsigned char * const DirStart = ExifData + DirOffset;
+    int de;
+    int a;
+    int NumDirEntries;
+    unsigned ThumbnailOffset = 0;
+    unsigned ThumbnailSize = 0;
+
+    NumDirEntries = Get16u(DirStart);
+    #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))
+
+    {
+        unsigned char * DirEnd;
+        DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries);
+        if (DirEnd+4 > (ExifData+ExifLength)){
+            if (DirEnd+2 == ExifData+ExifLength || 
+                DirEnd == ExifData+ExifLength){
+                /* Version 1.3 of jhead would truncate a bit too much.
+                   This also caught later on as well.
+                */
+            }else{
+                /* Note: Files that had thumbnails trimmed with jhead
+                   1.3 or earlier might trigger this.
+                */
+                pm_message("Illegal directory entry size");
+                return;
+            }
+        }
+        if (DirEnd > LastExifRefd) LastExifRefd = DirEnd;
+    }
+
+    if (ShowTags){
+        pm_message("Directory with %d entries",NumDirEntries);
+    }
+
+    for (de=0;de<NumDirEntries;de++){
+        int Tag, Format, Components;
+        unsigned char * ValuePtr;
+            /* This actually can point to a variety of things; it must
+               be cast to other types when used.  But we use it as a
+               byte-by-byte cursor, so we declare it as a pointer to a
+               generic byte here.  
+            */
+        int ByteCount;
+        unsigned char * DirEntry;
+        DirEntry = DIR_ENTRY_ADDR(DirStart, de);
+
+        Tag = Get16u(DirEntry);
+        Format = Get16u(DirEntry+2);
+        Components = Get32u(DirEntry+4);
+
+        if ((Format-1) >= NUM_FORMATS) {
+            /* (-1) catches illegal zero case as unsigned underflows
+               to positive large.  
+            */
+            pm_message("Illegal number format %d for tag %04x", Format, Tag);
+            continue;
+        }
+        
+        ByteCount = Components * BytesPerFormat[Format];
+
+        if (ByteCount > 4){
+            unsigned OffsetVal;
+            OffsetVal = Get32u(DirEntry+8);
+            /* If its bigger than 4 bytes, the dir entry contains an offset.*/
+            if (OffsetVal+ByteCount > ExifLength){
+                /* Bogus pointer offset and / or bytecount value */
+                pm_message("Illegal pointer offset value in EXIF "
+                           "for tag %04x.  "
+                           "Offset %d bytes %d ExifLen %d\n",
+                           Tag, OffsetVal, ByteCount, ExifLength);
+                continue;
+            }
+            ValuePtr = ExifData+OffsetVal;
+        }else{
+            /* 4 bytes or less and value is in the dir entry itself */
+            ValuePtr = DirEntry+8;
+        }
+
+        if (*LastExifRefdP < ValuePtr+ByteCount){
+            /* Keep track of last byte in the exif header that was
+               actually referenced.  That way, we know where the
+               discardable thumbnail data begins.
+            */
+            *LastExifRefdP = ValuePtr+ByteCount;
+        }
+
+        if (ShowTags){
+            /* Show tag name */
+            for (a=0;;a++){
+                if (TagTable[a].Tag == 0){
+                    fprintf(stderr, "  Unknown Tag %04x Value = ", Tag);
+                    break;
+                }
+                if (TagTable[a].Tag == Tag){
+                    fprintf(stderr, "    %s = ",TagTable[a].Desc);
+                    break;
+                }
+            }
+
+            /* Show tag value. */
+            switch(Format){
+
+                case FMT_UNDEFINED:
+                    /* Undefined is typically an ascii string. */
+
+                case FMT_STRING:
+                    /* String arrays printed without function call
+                       (different from int arrays)
+                    */
+                    {
+                        int NoPrint = 0;
+                        printf("\"");
+                        for (a=0;a<ByteCount;a++){
+                            if (ISPRINT((ValuePtr)[a])){
+                                fprintf(stderr, "%c", (ValuePtr)[a]);
+                                NoPrint = 0;
+                            }else{
+
+                                /* Avoiding indicating too many
+                                   unprintable characters of proprietary
+                                   bits of binary information this
+                                   program may not know how to parse.  
+                                */
+                                if (!NoPrint){
+                                    fprintf(stderr, "?");
+                                    NoPrint = 1;
+                                }
+                            }
+                        }
+                        fprintf(stderr, "\"\n");
+                    }
+                    break;
+
+                default:
+                    /* Handle arrays of numbers later (will there ever be?)*/
+                    PrintFormatNumber(stderr, ValuePtr, Format, ByteCount);
+            }
+        }
+
+        /* Extract useful components of tag */
+        switch(Tag){
+
+            case TAG_MAKE:
+                strncpy(ImageInfoP->CameraMake, (char*)ValuePtr, 31);
+                break;
+
+            case TAG_MODEL:
+                strncpy(ImageInfoP->CameraModel, (char*)ValuePtr, 39);
+                break;
+
+            case TAG_DATETIME_ORIGINAL:
+                strncpy(ImageInfoP->DateTime, (char*)ValuePtr, 19);
+                ImageInfoP->DatePointer = (char*)ValuePtr;
+                break;
+
+            case TAG_USERCOMMENT:
+                /* Olympus has this padded with trailing spaces.
+                   Remove these first. 
+                */
+                for (a=ByteCount;;){
+                    a--;
+                    if (((char*)ValuePtr)[a] == ' '){
+                        ((char*)ValuePtr)[a] = '\0';
+                    }else{
+                        break;
+                    }
+                    if (a == 0) break;
+                }
+
+                /* Copy the comment */
+                if (memcmp(ValuePtr, "ASCII",5) == 0){
+                    for (a=5;a<10;a++){
+                        char c;
+                        c = ((char*)ValuePtr)[a];
+                        if (c != '\0' && c != ' '){
+                            strncpy(ImageInfoP->Comments, (char*)ValuePtr+a, 
+                                    199);
+                            break;
+                        }
+                    }
+                    
+                }else{
+                    strncpy(ImageInfoP->Comments, (char*)ValuePtr, 199);
+                }
+                break;
+
+            case TAG_FNUMBER:
+                /* Simplest way of expressing aperture, so I trust it the most.
+                   (overwrite previously computd value if there is one)
+                   */
+                ImageInfoP->ApertureFNumber = 
+                    (float)ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_APERTURE:
+            case TAG_MAXAPERTURE:
+                /* More relevant info always comes earlier, so only
+                 use this field if we don't have appropriate aperture
+                 information yet. 
+                */
+                if (ImageInfoP->ApertureFNumber == 0){
+                    ImageInfoP->ApertureFNumber = (float)
+                        exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5);
+                }
+                break;
+
+            case TAG_FOCALLENGTH:
+                /* Nice digital cameras actually save the focal length
+                   as a function of how farthey are zoomed in. 
+                */
+
+                ImageInfoP->FocalLength = 
+                    (float)ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_SUBJECT_DISTANCE:
+                /* Inidcates the distacne the autofocus camera is focused to.
+                   Tends to be less accurate as distance increases.
+                */
+                ImageInfoP->Distance = 
+                    (float)ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_EXPOSURETIME:
+                /* Simplest way of expressing exposure time, so I
+                   trust it most.  (overwrite previously computd value
+                   if there is one) 
+                */
+                ImageInfoP->ExposureTime = 
+                    (float)ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_SHUTTERSPEED:
+                /* More complicated way of expressing exposure time,
+                   so only use this value if we don't already have it
+                   from somewhere else.  
+                */
+                if (ImageInfoP->ExposureTime == 0){
+                    ImageInfoP->ExposureTime = (float)
+                        (1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2)));
+                }
+                break;
+
+            case TAG_FLASH:
+                if ((int)ConvertAnyFormat(ValuePtr, Format) & 7){
+                    ImageInfoP->FlashUsed = TRUE;
+                }else{
+                    ImageInfoP->FlashUsed = FALSE;
+                }
+                break;
+
+            case TAG_ORIENTATION:
+                ImageInfoP->Orientation = 
+                    (int)ConvertAnyFormat(ValuePtr, Format);
+                if (ImageInfoP->Orientation < 1 || 
+                    ImageInfoP->Orientation > 8){
+                    pm_message("Undefined rotation value %d", 
+                               ImageInfoP->Orientation);
+                    ImageInfoP->Orientation = 0;
+                }
+                break;
+
+            case TAG_EXIF_IMAGELENGTH:
+            case TAG_EXIF_IMAGEWIDTH:
+                /* Use largest of height and width to deal with images
+                   that have been rotated to portrait format.  
+                */
+                a = (int)ConvertAnyFormat(ValuePtr, Format);
+                if (ExifImageWidth < a) ExifImageWidth = a;
+                break;
+
+            case TAG_FOCALPLANEXRES:
+                HaveXRes = TRUE;
+                FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_FOCALPLANEUNITS:
+                switch((int)ConvertAnyFormat(ValuePtr, Format)){
+                    case 1: FocalplaneUnits = 25.4; break; /* 1 inch */
+                    case 2: 
+                        /* According to the information I was using, 2
+                           means meters.  But looking at the Cannon
+                           powershot's files, inches is the only
+                           sensible value.  
+                        */
+                        FocalplaneUnits = 25.4;
+                        break;
+
+                    case 3: FocalplaneUnits = 10;   break;  /* 1 centimeter*/
+                    case 4: FocalplaneUnits = 1;    break;  /* 1 millimeter*/
+                    case 5: FocalplaneUnits = .001; break;  /* 1 micrometer*/
+                }
+                break;
+
+                /* Remaining cases contributed by: Volker C. Schoech
+                   (schoech@gmx.de)
+                */
+
+            case TAG_EXPOSURE_BIAS:
+                ImageInfoP->ExposureBias = 
+                    (float) ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_WHITEBALANCE:
+                ImageInfoP->Whitebalance = 
+                    (int)ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_METERING_MODE:
+                ImageInfoP->MeteringMode = 
+                    (int)ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_EXPOSURE_PROGRAM:
+                ImageInfoP->ExposureProgram = 
+                    (int)ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_ISO_EQUIVALENT:
+                ImageInfoP->ISOequivalent = 
+                    (int)ConvertAnyFormat(ValuePtr, Format);
+                if ( ImageInfoP->ISOequivalent < 50 ) 
+                    ImageInfoP->ISOequivalent *= 200;
+                break;
+
+            case TAG_COMPRESSION_LEVEL:
+                ImageInfoP->CompressionLevel = 
+                    (int)ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_THUMBNAIL_OFFSET:
+                ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format);
+                DirWithThumbnailPtrs = DirStart;
+                break;
+
+            case TAG_THUMBNAIL_LENGTH:
+                ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format);
+                break;
+
+            case TAG_EXIF_OFFSET:
+            case TAG_INTEROP_OFFSET:
+                {
+                    unsigned int const SubdirOffset  = Get32u(ValuePtr);
+                    if (SubdirOffset >= ExifLength)
+                        pm_message("Illegal exif or interop offset "
+                                   "directory link.  Offset is %u, "
+                                   "but Exif data is only %u bytes.",
+                                   SubdirOffset, ExifLength);
+                    else
+                        ProcessExifDir(ExifData, ExifLength, SubdirOffset, 
+                                       ImageInfoP, ShowTags, LastExifRefdP);
+                    continue;
+                }
+        }
+
+    }
+
+
+    {
+        /* In addition to linking to subdirectories via exif tags,
+           there's also a potential link to another directory at the end
+           of each directory.  This has got to be the result of a
+           committee!  
+        */
+        if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= 
+            ExifData+ExifLength){
+            unsigned int const SubdirOffset =
+                Get32u(DirStart+2+12*NumDirEntries);
+            if (SubdirOffset){
+                unsigned char * const SubdirStart = ExifData + SubdirOffset;
+                if (SubdirStart > ExifData+ExifLength){
+                    if (SubdirStart < ExifData+ExifLength+20){
+                        /* Jhead 1.3 or earlier would crop the whole directory!
+                           As Jhead produces this form of format incorrectness,
+                           I'll just let it pass silently.
+                        */
+                        if (ShowTags) 
+                            printf("Thumbnail removed with "
+                                   "Jhead 1.3 or earlier\n");
+                    }else{
+                        pm_message("Illegal subdirectory link");
+                    }
+                }else{
+                    if (SubdirOffset <= ExifLength)
+                        ProcessExifDir(ExifData, ExifLength, SubdirOffset,
+                                       ImageInfoP, ShowTags, LastExifRefdP);
+                }
+            }
+        }else{
+            /* The exif header ends before the last next directory pointer. */
+        }
+    }
+
+    if (ThumbnailSize && ThumbnailOffset){
+        if (ThumbnailSize + ThumbnailOffset <= ExifLength){
+            /* The thumbnail pointer appears to be valid.  Store it. */
+            ImageInfoP->ThumbnailPointer = ExifData + ThumbnailOffset;
+            ImageInfoP->ThumbnailSize = ThumbnailSize;
+
+            if (ShowTags){
+                fprintf(stderr, "Thumbnail size: %d bytes\n",ThumbnailSize);
+            }
+        }
+    }
+}
+
+
+
+void 
+process_EXIF(unsigned char * const ExifData,
+             unsigned int    const length,
+             ImageInfo_t *   const ImageInfoP, 
+             int             const ShowTags,
+             const char **   const errorP) {
+/*--------------------------------------------------------------------------
+  Interpret an EXIF APP1 marker
+
+  'ExifData' is the actual Exif data; it does not include the
+  "Exif" identifier and length field that often prefix Exif data.
+
+  'length' is the length of the Exif section.
+--------------------------------------------------------------------------*/
+    int FirstOffset;
+    unsigned char * LastExifRefd;
+
+    *errorP = NULL;  /* initial assumption */
+
+    if (ShowTags){
+        fprintf(stderr, "Exif header %d bytes long\n",length);
+    }
+
+    if (memcmp(ExifData+0,"II",2) == 0) {
+        if (ShowTags) 
+            fprintf(stderr, "Exif header in Intel order\n");
+        MotorolaOrder = 0;
+    } else {
+        if (memcmp(ExifData+0, "MM", 2) == 0) {
+            if (ShowTags) 
+                fprintf(stderr, "Exif header in Motorola order\n");
+            MotorolaOrder = 1;
+        } else {
+            asprintfN(errorP, "Invalid alignment marker in Exif "
+                      "data.  First two bytes are '%c%c' (0x%02x%02x) "
+                      "instead of 'II' or 'MM'.", 
+                      ExifData[0], ExifData[1], ExifData[0], ExifData[1]);
+        }
+    }
+    if (!*errorP) {
+        unsigned short const start = Get16u(ExifData + 2);
+        /* Check the next value for correctness. */
+        if (start != 0x002a){
+            asprintfN(errorP, "Invalid Exif header start.  "
+                      "two bytes after the alignment marker "
+                      "should be 0x002a, but is 0x%04x",
+                      start);
+        }
+    }
+    if (!*errorP) {
+        FirstOffset = Get32u(ExifData + 4);
+        if (FirstOffset < 8 || FirstOffset > 16){
+            /* I used to ensure this was set to 8 (website I used
+               indicated its 8) but PENTAX Optio 230 has it set
+               differently, and uses it as offset. (Sept 11 2002)
+                */
+            pm_message("Suspicious offset of first IFD value in Exif header");
+        }
+        
+        ImageInfoP->Comments[0] = '\0';  /* Initial value - null string */
+        
+        HaveXRes = FALSE;  /* Initial assumption */
+        FocalplaneUnits = 0;
+        ExifImageWidth = 0;
+        
+        LastExifRefd = ExifData;
+        DirWithThumbnailPtrs = NULL;
+        
+        ProcessExifDir(ExifData, length, FirstOffset, 
+                       ImageInfoP, ShowTags, &LastExifRefd);
+        
+        /* Compute the CCD width, in millimeters. */
+        if (HaveXRes){
+            ImageInfoP->HaveCCDWidth = 1;
+            ImageInfoP->CCDWidth = 
+                    (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes);
+        } else
+            ImageInfoP->HaveCCDWidth = 0;
+            
+        if (ShowTags){
+            fprintf(stderr, 
+                    "Non-settings part of Exif header: %d bytes\n",
+                    ExifData+length-LastExifRefd);
+        }
+    }
+}
+
+/*--------------------------------------------------------------------------
+   Show the collected image info, displaying camera F-stop and shutter
+   speed in a consistent and legible fashion.
+--------------------------------------------------------------------------*/
+void 
+ShowImageInfo(ImageInfo_t * const ImageInfoP)
+{
+    if (ImageInfoP->CameraMake[0]){
+        fprintf(stderr, "Camera make  : %s\n",ImageInfoP->CameraMake);
+        fprintf(stderr, "Camera model : %s\n",ImageInfoP->CameraModel);
+    }
+    if (ImageInfoP->DateTime[0]){
+        fprintf(stderr, "Date/Time    : %s\n",ImageInfoP->DateTime);
+    }
+    fprintf(stderr, "Resolution   : %d x %d\n",
+            ImageInfoP->Width, ImageInfoP->Height);
+    if (ImageInfoP->Orientation > 1){
+
+        /* Only print orientation if one was supplied, and if its not
+           1 (normal orientation)
+
+           1 - The 0th row is at the visual top of the image
+               and the 0th column is the visual left-hand side.
+           2 - The 0th row is at the visual top of the image
+               and the 0th column is the visual right-hand side.
+           3 - The 0th row is at the visual bottom of the image
+               and the 0th column is the visual right-hand side.
+           4 - The 0th row is at the visual bottom of the image
+               and the 0th column is the visual left-hand side.
+           5 - The 0th row is the visual left-hand side of of the image
+               and the 0th column is the visual top.
+           6 - The 0th row is the visual right-hand side of of the image
+               and the 0th column is the visual top.
+           7 - The 0th row is the visual right-hand side of of the image
+               and the 0th column is the visual bottom.
+           8 - The 0th row is the visual left-hand side of of the image
+               and the 0th column is the visual bottom.
+
+           Note: The descriptions here are the same as the name of the
+           command line option to pass to jpegtran to right the image
+        */
+        static const char * OrientTab[9] = {
+            "Undefined",
+            "Normal",           /* 1 */
+            "flip horizontal",  /* left right reversed mirror */
+            "rotate 180",       /* 3 */
+            "flip vertical",    /* upside down mirror */
+            "transpose",    /* Flipped about top-left <--> bottom-right axis.*/
+            "rotate 90",        /* rotate 90 cw to right it. */
+            "transverse",   /* flipped about top-right <--> bottom-left axis */
+            "rotate 270",       /* rotate 270 to right it. */
+        };
+
+        fprintf(stderr, "Orientation  : %s\n", 
+                OrientTab[ImageInfoP->Orientation]);
+    }
+
+    if (ImageInfoP->IsColor == 0){
+        fprintf(stderr, "Color/bw     : Black and white\n");
+    }
+    if (ImageInfoP->FlashUsed >= 0){
+        fprintf(stderr, "Flash used   : %s\n",
+                ImageInfoP->FlashUsed ? "Yes" :"No");
+    }
+    if (ImageInfoP->FocalLength){
+        fprintf(stderr, "Focal length : %4.1fmm",
+                (double)ImageInfoP->FocalLength);
+        if (ImageInfoP->HaveCCDWidth){
+            fprintf(stderr, "  (35mm equivalent: %dmm)",
+                    (int)
+                    (ImageInfoP->FocalLength/ImageInfoP->CCDWidth*36 + 0.5));
+        }
+        fprintf(stderr, "\n");
+    }
+
+    if (ImageInfoP->HaveCCDWidth){
+        fprintf(stderr, "CCD width    : %2.4fmm\n",
+                (double)ImageInfoP->CCDWidth);
+    }
+
+    if (ImageInfoP->ExposureTime){ 
+        if (ImageInfoP->ExposureTime < 0.010){
+            fprintf(stderr, 
+                    "Exposure time: %6.4f s ",
+                    (double)ImageInfoP->ExposureTime);
+        }else{
+            fprintf(stderr, 
+                    "Exposure time: %5.3f s ",
+                    (double)ImageInfoP->ExposureTime);
+        }
+        if (ImageInfoP->ExposureTime <= 0.5){
+            fprintf(stderr, " (1/%d)",(int)(0.5 + 1/ImageInfoP->ExposureTime));
+        }
+        fprintf(stderr, "\n");
+    }
+    if (ImageInfoP->ApertureFNumber){
+        fprintf(stderr, "Aperture     : f/%3.1f\n",
+                (double)ImageInfoP->ApertureFNumber);
+    }
+    if (ImageInfoP->Distance){
+        if (ImageInfoP->Distance < 0){
+            fprintf(stderr, "Focus dist.  : Infinite\n");
+        }else{
+            fprintf(stderr, "Focus dist.  :%5.2fm\n",
+                    (double)ImageInfoP->Distance);
+        }
+    }
+
+
+
+
+
+    if (ImageInfoP->ISOequivalent){ /* 05-jan-2001 vcs */
+        fprintf(stderr, "ISO equiv.   : %2d\n",(int)ImageInfoP->ISOequivalent);
+    }
+    if (ImageInfoP->ExposureBias){ /* 05-jan-2001 vcs */
+        fprintf(stderr, "Exposure bias:%4.2f\n",
+                (double)ImageInfoP->ExposureBias);
+    }
+        
+    if (ImageInfoP->Whitebalance){ /* 05-jan-2001 vcs */
+        switch(ImageInfoP->Whitebalance) {
+        case 1:
+            fprintf(stderr, "Whitebalance : sunny\n");
+            break;
+        case 2:
+            fprintf(stderr, "Whitebalance : fluorescent\n");
+            break;
+        case 3:
+            fprintf(stderr, "Whitebalance : incandescent\n");
+            break;
+        default:
+            fprintf(stderr, "Whitebalance : cloudy\n");
+        }
+    }
+    if (ImageInfoP->MeteringMode){ /* 05-jan-2001 vcs */
+        switch(ImageInfoP->MeteringMode) {
+        case 2:
+            fprintf(stderr, "Metering Mode: center weight\n");
+            break;
+        case 3:
+            fprintf(stderr, "Metering Mode: spot\n");
+            break;
+        case 5:
+            fprintf(stderr, "Metering Mode: matrix\n");
+            break;
+        }
+    }
+    if (ImageInfoP->ExposureProgram){ /* 05-jan-2001 vcs */
+        switch(ImageInfoP->ExposureProgram) {
+        case 2:
+            fprintf(stderr, "Exposure     : program (auto)\n");
+            break;
+        case 3:
+            fprintf(stderr, "Exposure     : aperture priority (semi-auto)\n");
+            break;
+        case 4:
+            fprintf(stderr, "Exposure     : shutter priority (semi-auto)\n");
+            break;
+        }
+    }
+    if (ImageInfoP->CompressionLevel){ /* 05-jan-2001 vcs */
+        switch(ImageInfoP->CompressionLevel) {
+        case 1:
+            fprintf(stderr, "Jpeg Quality  : basic\n");
+            break;
+        case 2:
+            fprintf(stderr, "Jpeg Quality  : normal\n");
+            break;
+        case 4:
+            fprintf(stderr, "Jpeg Quality  : fine\n");
+            break;
+       }
+    }
+
+         
+
+    /* Print the comment. Print 'Comment:' for each new line of comment. */
+    if (ImageInfoP->Comments[0]){
+        int a,c;
+        fprintf(stderr, "Comment      : ");
+        for (a=0;a<MAX_COMMENT;a++){
+            c = ImageInfoP->Comments[a];
+            if (c == '\0') break;
+            if (c == '\n'){
+                /* Do not start a new line if the string ends with a cr */
+                if (ImageInfoP->Comments[a+1] != '\0'){
+                    fprintf(stderr, "\nComment      : ");
+                }else{
+                    fprintf(stderr, "\n");
+                }
+            }else{
+                putc(c, stderr);
+            }
+        }
+        fprintf(stderr, "\n");
+    }
+
+    fprintf(stderr, "\n");
+}
+
+
+
+
diff --git a/converter/other/exif.h b/converter/other/exif.h
new file mode 100644
index 00000000..e5825e12
--- /dev/null
+++ b/converter/other/exif.h
@@ -0,0 +1,56 @@
+#ifndef EXIF_H_INCLUDED
+#define EXIF_H_INCLUDED
+
+#define MAX_COMMENT 2000
+
+#ifdef _WIN32
+    #define PATH_MAX _MAX_PATH
+#endif
+
+/*--------------------------------------------------------------------------
+  This structure stores Exif header image elements in a simple manner
+  Used to store camera data as extracted from the various ways that it can be
+  stored in an exif header
+--------------------------------------------------------------------------*/
+typedef struct {
+    char  CameraMake   [32];
+    char  CameraModel  [40];
+    char  DateTime     [20];
+    int   Height, Width;
+    int   Orientation;
+    int   IsColor;
+    int   FlashUsed;
+    float FocalLength;
+    float ExposureTime;
+    float ApertureFNumber;
+    float Distance;
+    int   HaveCCDWidth;  /* boolean */
+    float CCDWidth;
+    float ExposureBias;
+    int   Whitebalance;
+    int   MeteringMode;
+    int   ExposureProgram;
+    int   ISOequivalent;
+    int   CompressionLevel;
+    char  Comments[MAX_COMMENT];
+
+    unsigned char * ThumbnailPointer;  /* Pointer at the thumbnail */
+    unsigned ThumbnailSize;     /* Size of thumbnail. */
+
+    char * DatePointer;
+}ImageInfo_t;
+
+
+/* Prototypes for exif.c functions. */
+
+void 
+process_EXIF(unsigned char * const ExifSection, 
+             unsigned int    const length,
+             ImageInfo_t *   const ImageInfoP, 
+             int             const ShowTags,
+             const char **   const errorP);
+
+void 
+ShowImageInfo(ImageInfo_t * const ImageInfoP);
+
+#endif
diff --git a/converter/other/fiasco/Makefile b/converter/other/fiasco/Makefile
new file mode 100644
index 00000000..0dd945ed
--- /dev/null
+++ b/converter/other/fiasco/Makefile
@@ -0,0 +1,59 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/other/fiasco
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+INCLUDES = \
+	-I$(SRCDIR)/$(SUBDIR)/codec -I$(SRCDIR)/$(SUBDIR)/input \
+	-I$(SRCDIR)/$(SUBDIR)/output -I$(SRCDIR)/$(SUBDIR)/lib \
+
+BINARIES = pnmtofiasco fiascotopnm
+
+MERGEBINARIES = $(BINARIES)
+
+SCRIPTS =
+
+all: $(BINARIES)
+
+FIASCOLIBS = codec/libfiasco_codec.a \
+	     input/libfiasco_input.a \
+	     output/libfiasco_output.a \
+	     lib/libfiasco_lib.a 
+
+COMMON_OBJECTS = binerror.o getopt.o getopt1.o params.o
+
+OBJECTS = $(BINARIES:%=%.o) $(COMMON_OBJECTS)
+
+MERGE_OBJECTS = $(BINARIES:%=%.o2) $(COMMON_OBJECTS)  $(FIASCOLIBS)
+
+SUBDIRS = codec input output lib
+
+include $(SRCDIR)/Makefile.common
+
+$(BINARIES):%:%.o $(COMMON_OBJECTS) $(FIASCOLIBS) $(NETPBMLIB) \
+   $(LIBOPT)
+	$(LD) $(LDFLAGS) -o $@ $< $(COMMON_OBJECTS) \
+	$(shell $(LIBOPT) $(FIASCOLIBS) $(NETPBMLIB)) $(MATHLIB) $(LDLIBS) \
+	$(RPATH) $(LADD)
+
+codec/libfiasco_codec.a: $(BUILDDIR)/$(SUBDIR)/codec FORCE
+	$(MAKE) -C codec -f $(SRCDIR)/$(SUBDIR)/codec/Makefile \
+		libfiasco_codec.a SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR)
+
+input/libfiasco_input.a: $(BUILDDIR)/$(SUBDIR)/input FORCE
+	$(MAKE) -C input -f $(SRCDIR)/$(SUBDIR)/input/Makefile \
+		libfiasco_input.a SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR)
+
+output/libfiasco_output.a: $(BUILDDIR)/$(SUBDIR)/output FORCE
+	$(MAKE) -C output -f $(SRCDIR)/$(SUBDIR)/output/Makefile \
+		libfiasco_output.a SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR)
+
+lib/libfiasco_lib.a: $(BUILDDIR)/$(SUBDIR)/lib FORCE
+	$(MAKE) -C lib -f $(SRCDIR)/$(SUBDIR)/lib/Makefile \
+		libfiasco_lib.a SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR)
+
+
diff --git a/converter/other/fiasco/README.netpbm b/converter/other/fiasco/README.netpbm
new file mode 100644
index 00000000..4f4019ad
--- /dev/null
+++ b/converter/other/fiasco/README.netpbm
@@ -0,0 +1,41 @@
+The 'fiasco' subdirectory of the Netpbm source tree is primarily a copy
+of the source distribution of the Fiasco package by Ullrich Hafner.
+Bryan Henderson adapted fiasco-1.0 on July 6, 2000 to netpbm, and then
+merged in all the updates from fiasco-1.3 on February 9, 2001.
+
+The changes are:
+
+- Uses Netpbm libraries for input and output of Netpbm format images.
+
+- Works with maxvals other than 255 in Netpbm input images.  This change
+  also makes a minor correction to the maxval 255 case.  Where the Fiasco
+  package multiplies by 16 to convert from 8 bit to 12 bit intensity,
+  the correct factor is 4095/255.
+
+- Does not issue warning when system configuration file not found.
+  The location of that file is a compile-time option in 'fiasco', but
+  fixed at /etc in Netpbm.  The expectation is that Netpbm users will
+  never have a system configuration file.
+
+- Does not fail if basis file small.fco is not found.  The Fiasco code
+  already contained facilities for defaulting to a built-in version of
+  small.fco, but it was disabled by an early check for existence of
+  the basis file as an actual file.  In Netpbm, that check for
+  existence is simply removed.
+
+- Remove WINDOWS config.h configuration macro, which determined whether
+  files would be open with "b" flag (binary).  Use "b" flag unconditionally.
+
+- Rename internal "log2" function to "Log2" to avoid conflict with existing
+  "log2" macro or function.  The original package has conditional compilation
+  to allow it to use the existing log2 when configured for a system that has
+  it.  In Netpbm, we always use the private version.
+
+- Compilation warnings fixed.
+
+- 'bin' subdirectory moved to top level directory.
+
+- man pages for programs moved from doc subdirectory to top level directory.
+
+- man page of pnmpsnr created.
+
diff --git a/converter/other/fiasco/binerror.c b/converter/other/fiasco/binerror.c
new file mode 100644
index 00000000..8a41a214
--- /dev/null
+++ b/converter/other/fiasco/binerror.c
@@ -0,0 +1,143 @@
+/*
+ *  error.c:		Error handling
+ *
+ *  Written by:		Stefan Frank
+ *			Ullrich Hafner
+ *  
+ *  Credits:	Modelled after variable argument routines from Jef
+ *		Poskanzer's pbmplus package. 
+ *
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/03/20 21:29:59 $
+ *  $Author: hafner $
+ *  $Revision: 4.3 $
+ *  $State: Exp $
+ */
+
+#define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+#define _ERROR_C
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#if STDC_HEADERS
+#	include <stdarg.h>
+#	define VA_START(args, lastarg) va_start(args, lastarg)
+#else  /* not STDC_HEADERS */
+#	include <varargs.h>
+#	define VA_START(args, lastarg) va_start(args)
+#endif /* not STDC_HEADERS */
+#include <string.h>
+
+#if HAVE_SETJMP_H
+#	include <setjmp.h>
+#endif /* HAVE_SETJMP_H */
+
+#include "fiasco.h"
+#include "binerror.h"
+
+/*****************************************************************************
+
+			     global variables
+  
+*****************************************************************************/
+
+int   error_line = 0;
+const char *error_file = NULL;
+
+/*****************************************************************************
+
+			     local variables
+  
+*****************************************************************************/
+
+static const char *executable = "(name not initialized)";
+
+/*****************************************************************************
+
+			       public code
+  
+*****************************************************************************/
+
+void
+init_error_handling (const char *name)
+/*
+ *  Initialize filename of executable.
+ *
+ *  No return value.
+ */
+{
+   if (name)
+      executable = strdup (name);
+}
+
+void
+_error (const char *format, ...)
+/*
+ *  Print error message and exit.
+ *
+ *  No return value.
+ */
+{
+   va_list	args;
+
+   VA_START (args, format);
+
+   fprintf (stderr, "%s: %s: line %d:\nError: ",
+	    executable, error_file, error_line);
+#if HAVE_VPRINTF
+   vfprintf (stderr, format, args);
+#elif HAVE_DOPRNT
+   _doprnt (format, args, stderr);
+#endif /* HAVE_DOPRNT */
+   fputc ('\n', stderr);
+   va_end(args);
+
+   exit (1);
+}
+
+void
+_file_error (const char *filename)
+/*
+ *  Print file error message and exit.
+ *
+ *  No return value.
+ */
+{
+   fprintf (stderr, "%s: %s: line %d:\nError: ",
+	    executable, error_file, error_line);
+   perror (filename);
+
+   exit (2);
+}
+
+void 
+_warning (const char *format, ...)
+/*
+ *  Issue a warning and continue execution.
+ *
+ *  No return value.
+ */
+{
+   va_list args;
+
+   VA_START (args, format);
+
+   fprintf (stderr, "%s: %s: line %d:\nWarning: ",
+	    executable, error_file, error_line);
+#if HAVE_VPRINTF
+   vfprintf (stderr, format, args);
+#elif HAVE_DOPRNT
+   _doprnt (format, args, stderr);
+#endif /* HAVE_DOPRNT */
+   fputc ('\n', stderr);
+
+   va_end (args);
+}
diff --git a/converter/other/fiasco/binerror.h b/converter/other/fiasco/binerror.h
new file mode 100644
index 00000000..e7ff43c9
--- /dev/null
+++ b/converter/other/fiasco/binerror.h
@@ -0,0 +1,50 @@
+/*
+ *  error.h
+ *  
+ *  Written by:		Stefan Frank
+ *			Ullrich Hafner
+ *
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/03/20 21:29:59 $
+ *  $Author: hafner $
+ *  $Revision: 4.3 $
+ *  $State: Exp $
+ */
+
+#ifndef _ERROR_H
+#define _ERROR_H
+
+#define error          error_line=__LINE__,error_file=__FILE__,_error
+#define warning        error_line=__LINE__,error_file=__FILE__,_warning
+#define file_error(fn) error_line=__LINE__,error_file=__FILE__,_file_error(fn)
+
+#ifdef _ERROR_C
+#define _EXTERN_TYPE
+#else
+#define _EXTERN_TYPE	extern
+#endif
+
+_EXTERN_TYPE int   error_line;
+_EXTERN_TYPE const char *error_file;
+
+void
+init_error_handling (const char *name);
+void
+_error (const char *format, ...);
+void
+_warning (const char *format, ...);
+void
+_file_error (const char *filename);
+
+#if HAVE_ASSERT_H
+#	include <assert.h>
+#else /* not HAVE_ASSERT_H */
+#	define assert(exp)	{if (!(exp)) error ("Assertion `" #exp " != NULL' failed.");}
+#endif /* not HAVE_ASSERT_H */
+
+#endif /* not _ERROR_H */
+
diff --git a/converter/other/fiasco/buttons.c b/converter/other/fiasco/buttons.c
new file mode 100644
index 00000000..82ed18cd
--- /dev/null
+++ b/converter/other/fiasco/buttons.c
@@ -0,0 +1,510 @@
+/*
+ *  buttons.c:		Draw MWFA player buttons in X11 window	
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/15 17:23:11 $
+ *  $Author: hafner $
+ *  $Revision: 5.2 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#ifndef X_DISPLAY_MISSING
+
+#include <X11/Xlib.h>
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+
+#if STDC_HEADERS
+#	include <stdlib.h>
+#endif /* not STDC_HEADERS */
+
+#include "types.h"
+#include "macros.h"
+
+#include "display.h"
+#include "binerror.h"
+#include "buttons.h"
+
+/*****************************************************************************
+
+			     local variables
+  
+*****************************************************************************/
+
+static const int EVENT_MASK = (KeyPressMask | ButtonPressMask |
+			       ButtonReleaseMask | ExposureMask);
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static void
+draw_progress_bar (x11_info_t *xinfo, binfo_t *binfo, unsigned n,
+		   unsigned n_frames);
+static void
+draw_button (x11_info_t *xinfo, binfo_t *binfo,
+	     buttons_t button, bool_t pressed);
+static void
+draw_control_panel (x11_info_t *xinfo, binfo_t *binfo,
+		    unsigned n, unsigned n_frames);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+binfo_t * 
+init_buttons (x11_info_t *xinfo, unsigned n, unsigned n_frames,
+	      unsigned buttons_height, unsigned progbar_height)
+/*
+ *  Initialize a toolbar with the typical collection of video player
+ *  buttons (pause, play, record, next, etc.) in the window given by 'xinfo'.
+ *  'n' gives the current frame, 'whereas' n_frames is the total number of
+ *  frames of the video stream.
+ *  The size of the button toolbar is given by 'buttons_height',
+ *  the size of the progressbar is given by 'progbar_height'.
+ *
+ *  Return value:
+ *	struct managing the toolbar and progressbar information
+ */
+{
+   XGCValues  values;
+   XEvent     event;
+   Colormap   cmap;
+   XColor     gray, dgray, lgray, red;
+   XColor     graye, dgraye, lgraye, rede;
+   buttons_t  button;			/* counter */
+   binfo_t   *binfo = calloc (1, sizeof (binfo_t));
+
+   if (!binfo)
+      error ("Out of memory.");
+   
+   binfo->width            = xinfo->ximage->width;
+   binfo->height           = buttons_height;
+   binfo->progbar_height   = progbar_height;
+   binfo->record_is_rewind = NO;
+
+   /*
+    *  Generate sub-window for control panel
+    */
+   binfo->window = XCreateSimpleWindow (xinfo->display, xinfo->window,
+					0, xinfo->ximage->height,
+					binfo->width, binfo->height, 0,
+					BlackPixel (xinfo->display,
+						    xinfo->screen),
+					WhitePixel (xinfo->display,
+						    xinfo->screen));
+   XSelectInput(xinfo->display, binfo->window, StructureNotifyMask);
+   XMapWindow (xinfo->display, binfo->window);
+   do
+   {
+      XNextEvent (xinfo->display, &event);
+   }
+   while (event.type != MapNotify || event.xmap.event != binfo->window);
+   XSelectInput (xinfo->display, binfo->window, EVENT_MASK);
+
+   /*
+    *  Generate graphic contexts for different colors.
+    */
+   cmap = DefaultColormap (xinfo->display, xinfo->screen);
+   XAllocNamedColor (xinfo->display, cmap, "#404040", &dgray, &dgraye);
+   XAllocNamedColor (xinfo->display, cmap, "white", &lgray, &lgraye);
+   XAllocNamedColor (xinfo->display, cmap, "#a8a8a8", &gray, &graye);
+   XAllocNamedColor (xinfo->display, cmap, "red", &red, &rede);
+   
+   values.foreground = BlackPixel (xinfo->display, xinfo->screen);
+   values.background = WhitePixel (xinfo->display, xinfo->screen);
+   binfo->gc [BLACK] = XCreateGC (xinfo->display,
+				  RootWindow (xinfo->display, xinfo->screen),
+				  (GCForeground | GCBackground), &values);
+   values.foreground = BlackPixel (xinfo->display, xinfo->screen);
+   values.background = WhitePixel (xinfo->display, xinfo->screen);
+   values.line_width = 3;
+   values.join_style = JoinRound;
+   binfo->gc [THICKBLACK] = XCreateGC (xinfo->display,
+				       RootWindow (xinfo->display,
+						   xinfo->screen),
+				       (GCForeground | GCBackground
+					| GCLineWidth | GCJoinStyle), &values);
+   values.foreground = gray.pixel;
+   values.background = WhitePixel (xinfo->display, xinfo->screen);
+   binfo->gc [NGRAY] = XCreateGC (xinfo->display,
+				  RootWindow (xinfo->display, xinfo->screen),
+				  (GCForeground | GCBackground), &values);
+   values.foreground = lgray.pixel;
+   values.background = WhitePixel (xinfo->display, xinfo->screen);
+   binfo->gc [LGRAY] = XCreateGC (xinfo->display,
+				  RootWindow (xinfo->display, xinfo->screen),
+				  (GCForeground | GCBackground), &values);
+   values.foreground = dgray.pixel;
+   values.background = WhitePixel (xinfo->display, xinfo->screen);
+   binfo->gc [DGRAY] = XCreateGC (xinfo->display,
+				  RootWindow (xinfo->display, xinfo->screen),
+				  (GCForeground | GCBackground), &values);
+   values.foreground = red.pixel;
+   values.background = WhitePixel (xinfo->display, xinfo->screen);
+   binfo->gc [RED]   = XCreateGC (xinfo->display,
+				  RootWindow (xinfo->display, xinfo->screen),
+				  (GCForeground | GCBackground), &values);
+
+   for (button = 0; button < NO_BUTTON; button++)
+      binfo->pressed [button] = NO;
+
+   draw_control_panel (xinfo, binfo, n, n_frames); 
+   
+   return binfo;
+}
+
+void
+wait_for_input (x11_info_t *xinfo)
+/*
+ *  Wait for key press or mouse click in window 'xinfo'.
+ *  Redraw 'image' if event other then ButtonPress or KeyPress occurs.
+ *  Enlarge or reduce size of image by factor 2^'enlarge_factor'.
+ *
+ *  No return value.
+ *
+ *  Side effect:
+ *	program is terminated after key press or mouse click.
+ */
+{
+   bool_t leave_loop = NO;
+   
+   XSelectInput (xinfo->display, xinfo->window, EVENT_MASK);
+
+   while (!leave_loop)
+   {
+      XEvent event;
+
+      XMaskEvent (xinfo->display, EVENT_MASK, &event);
+      switch (event.type)
+      {
+	 case ButtonPress:
+	 case KeyPress:
+	    leave_loop = YES;
+	    break;
+	 default:
+	    display_image (0, 0, xinfo);
+	    break;
+      }
+   }
+}
+
+void
+check_events (x11_info_t *xinfo, binfo_t *binfo, unsigned n, unsigned n_frames)
+/*
+ *  Check the X11 event loop. If the PAUSE buttonin the of panel 'binfo'
+ *  is activated wait until next event occurs.
+ *  Redraw 'image' if event other then ButtonPress or ButtonRelease occurs.
+ *  Enlarge or reduce size of image by factor 2^'enlarge_factor'.
+ *  'n' gives the current frame, 'whereas' n_frames is the total number of
+ *  frames of the video stream.
+ *
+ *  No return values.
+ *
+ *  Side effects:
+ *	status of buttons (binfo->pressed [button]) is changed accordingly.
+ */
+{
+   bool_t leave_eventloop;
+
+   leave_eventloop = (!binfo->pressed [PAUSE_BUTTON]
+		      && binfo->pressed [PLAY_BUTTON])
+		     || (!binfo->pressed [PAUSE_BUTTON]
+			 && binfo->record_is_rewind
+			 && binfo->pressed [RECORD_BUTTON])
+		     || binfo->pressed [RECORD_BUTTON];
+   draw_progress_bar (xinfo, binfo, n, n_frames);
+
+   if (binfo->pressed [PAUSE_BUTTON] && binfo->pressed [PLAY_BUTTON])
+   {
+      XFlush (xinfo->display);
+      draw_button (xinfo, binfo, PLAY_BUTTON, NO); /* clear PLAY mode */
+      XFlush (xinfo->display);
+   }
+   if (binfo->pressed [PAUSE_BUTTON]
+       && binfo->record_is_rewind && binfo->pressed [RECORD_BUTTON])
+   {
+      XFlush (xinfo->display);
+      draw_button (xinfo, binfo, RECORD_BUTTON, NO); /* clear PLAY mode */
+      XFlush (xinfo->display);
+   }
+
+   if (binfo->pressed [STOP_BUTTON])
+   {
+      XFlush (xinfo->display);
+      draw_button (xinfo, binfo, STOP_BUTTON, NO); /* clear STOP button */
+      XFlush (xinfo->display);
+   }
+
+   do
+   {
+      XEvent event;
+      int    button;
+      bool_t wait_release = NO;
+	 
+      
+      if (XCheckMaskEvent (xinfo->display, EVENT_MASK, &event))
+      {
+	 switch (event.type)
+	 {
+	    case ButtonPress:
+	       wait_release = NO;
+	       if (!(binfo->pressed [RECORD_BUTTON] &&
+		     !binfo->record_is_rewind))
+		  for (button = 0; button < NO_BUTTON; button++)
+		  {
+		     int x0, y0, x1, y1; /* button coordinates */
+		  
+		     x0 = button * (binfo->width / NO_BUTTON);
+		     y0 = binfo->progbar_height;
+		     x1 = x0 + binfo->width / NO_BUTTON;
+		     y1 = y0 + binfo->height - binfo->progbar_height - 1;
+		     if (event.xbutton.x > x0 && event.xbutton.x < x1
+			 && event.xbutton.y > y0 && event.xbutton.y < y1) 
+		     {
+			draw_button (xinfo, binfo, button,
+				     !binfo->pressed [button]);
+			wait_release = YES;
+			break;
+		     }
+		  }
+	       break;
+	    case ButtonRelease:
+	       wait_release = NO;
+	       break;
+	    default:
+	       wait_release = NO;
+	       draw_control_panel (xinfo, binfo, n, n_frames);
+	       display_image (0, 0, xinfo);
+	       break;
+	 }
+	 leave_eventloop = !wait_release
+			   && (binfo->pressed [PLAY_BUTTON]
+			       || binfo->pressed [STOP_BUTTON]
+			       || binfo->pressed [RECORD_BUTTON]
+			       || binfo->pressed [QUIT_BUTTON]);
+      }
+   } while (!leave_eventloop);
+
+   if ((binfo->pressed [RECORD_BUTTON] && !binfo->record_is_rewind)
+       && n == n_frames - 1)
+   {
+      binfo->record_is_rewind = YES;
+      draw_button (xinfo, binfo, RECORD_BUTTON, NO);
+   }
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static void
+draw_control_panel (x11_info_t *xinfo, binfo_t *binfo,
+		    unsigned n, unsigned n_frames)
+/*
+ *  Draw control panel 'binfo' with all buttons and progressbar in
+ *  the given 'window'.
+ *  'n' gives the current frame, 'whereas' n_frames is the total number of
+ *  frames of the video stream.
+ *
+ *  No return value.
+ */
+{
+   buttons_t button;
+   
+   XFillRectangle (xinfo->display, binfo->window, binfo->gc [NGRAY],
+		   0, 0, binfo->width, binfo->height);
+   draw_progress_bar (xinfo, binfo, n, n_frames);
+   for (button = 0; button < NO_BUTTON; button++)
+      draw_button (xinfo, binfo, button, binfo->pressed [button]);
+}
+
+static void
+draw_progress_bar (x11_info_t *xinfo, binfo_t *binfo, unsigned n,
+		   unsigned n_frames)
+/*
+ *  Draw progressbar of control panel 'binfo' in the given 'window'.
+ *  'n' gives the current frame, whereas 'n_frames' is the total number of
+ *  frames of the video stream.
+ *
+ *  No return value.
+ */
+{
+   unsigned x, y, width, height;
+
+   x 	  = 2;
+   y 	  = 1;
+   width  = binfo->width - 5;
+   height = binfo->progbar_height - 3;
+   
+   XDrawLine (xinfo->display, binfo->window, binfo->gc [DGRAY],
+	      x, y, x + width, y);
+   XDrawLine (xinfo->display, binfo->window, binfo->gc [DGRAY],
+	      x, y, x, y + height - 1);
+   XDrawLine (xinfo->display, binfo->window, binfo->gc [LGRAY],
+	      x + width, y + 1, x + width, y + height);
+   XDrawLine (xinfo->display, binfo->window, binfo->gc [LGRAY],
+	      x, y + height, x + width, y + height);
+
+   x++; y++; width  -= 2; height -= 2;
+   XFillRectangle (xinfo->display, binfo->window, binfo->gc [NGRAY],
+		   x, y, width, height);
+
+   XFillRectangle (xinfo->display, binfo->window, binfo->gc [BLACK],
+		   x + n * max (1, width / n_frames), y,
+		   max (1, width / n_frames), height);
+}
+
+static void
+draw_button (x11_info_t *xinfo, binfo_t *binfo,
+	     buttons_t button, bool_t pressed)
+/*
+ *  Draw 'button' of control panel 'binfo' in the given 'window'.
+ *  'pressed' indicates whether the button is pressed or not.
+ *
+ *  No return value.
+ */
+{
+   grayscale_t top, bottom;		/* index of GC */
+   unsigned    x, y, width, height;	/* coordinates of button */
+   
+   x 	  = button * (binfo->width / NO_BUTTON);
+   y 	  = binfo->progbar_height;
+   width  = binfo->width / NO_BUTTON;
+   height = binfo->height - binfo->progbar_height - 1;
+   
+   if (width < 4 || height < 4)
+      return;
+   
+   if (pressed)
+   {
+      top    = DGRAY;
+      bottom = LGRAY;
+   }
+   else
+   {
+      top    = LGRAY;
+      bottom = DGRAY;
+   }
+
+   x 	 += 2;
+   width -= 4;
+   
+   XDrawLine (xinfo->display, binfo->window, binfo->gc [top],
+	      x, y, x + width, y);
+   XDrawLine (xinfo->display, binfo->window, binfo->gc [top],
+	      x, y, x, y + height - 1);
+   XDrawLine (xinfo->display, binfo->window, binfo->gc [bottom],
+	      x + width, y + 1, x + width, y + height);
+   XDrawLine (xinfo->display, binfo->window, binfo->gc [bottom],
+	      x, y + height, x + width, y + height);
+
+   x++; y++; width  -= 2; height -= 2;
+   XFillRectangle (xinfo->display, binfo->window, binfo->gc [NGRAY],
+		   x, y, width, height);
+
+   switch (button)
+   {
+      case STOP_BUTTON:
+	 XFillRectangle (xinfo->display, binfo->window, binfo->gc [BLACK],
+			 x + width / 2 - 6, y + height / 2 - 4, 11, 11);
+	 if (pressed && !binfo->pressed [STOP_BUTTON])
+	 {
+	    draw_button (xinfo, binfo, PLAY_BUTTON, NO);
+	    draw_button (xinfo, binfo, PAUSE_BUTTON, NO); 
+	    draw_button (xinfo, binfo, RECORD_BUTTON, NO); 
+	 }
+	 break;
+      case PAUSE_BUTTON:
+	 XFillRectangle (xinfo->display, binfo->window, binfo->gc [BLACK],
+			 x + width / 2 - 6, y + height / 2 - 4, 5, 11);
+	 XFillRectangle (xinfo->display, binfo->window, binfo->gc [BLACK],
+			 x + width / 2 + 1, y + height / 2 - 4, 5, 11);
+	 break;
+      case PLAY_BUTTON:
+	 {
+	    XPoint triangle [3];
+
+	    triangle [0].x = x + width / 2 - 5;
+	    triangle [0].y = y + height / 2 - 5;
+	    triangle [1].x = 10;
+	    triangle [1].y = 6;
+	    triangle [2].x = -10;
+	    triangle [2].y = 6;
+
+	    XFillPolygon (xinfo->display, binfo->window, binfo->gc [BLACK],
+			  triangle, 3, Convex, CoordModePrevious);
+	    if (pressed && !binfo->pressed [PLAY_BUTTON]
+		&& binfo->pressed [RECORD_BUTTON])
+	       draw_button (xinfo, binfo, RECORD_BUTTON, NO);
+	 }
+	 break;
+      case RECORD_BUTTON:
+	 if (!binfo->record_is_rewind)
+	 {
+	    XFillArc (xinfo->display, binfo->window, binfo->gc [RED],
+		      x + width / 2 - 5, y + height / 2 - 5, 11, 11, 0,
+		      360 * 64);
+	    if (pressed && !binfo->pressed [RECORD_BUTTON])
+	    {
+	       draw_button (xinfo, binfo, STOP_BUTTON, YES);
+	       draw_button (xinfo, binfo, PLAY_BUTTON, NO);
+	       draw_button (xinfo, binfo, PAUSE_BUTTON, NO); 
+	    }
+	 }
+	 else
+	 {
+	    XPoint triangle [3];
+
+	    triangle [0].x = x + width / 2 + 5;
+	    triangle [0].y = y + height / 2 - 5;
+	    triangle [1].x = -10;
+	    triangle [1].y = 6;
+	    triangle [2].x = 10;
+	    triangle [2].y = 6;
+
+	    XFillPolygon (xinfo->display, binfo->window, binfo->gc [BLACK],
+			  triangle, 3, Convex, CoordModePrevious);
+	    if (pressed && !binfo->pressed [RECORD_BUTTON]
+		&& binfo->pressed [PLAY_BUTTON])
+	       draw_button (xinfo, binfo, PLAY_BUTTON, NO);
+	 }
+	 break;
+      case QUIT_BUTTON:
+	 {
+	    XPoint triangle [3];
+
+	    triangle [0].x = x + width / 2 - 6;
+	    triangle [0].y = y + height / 2 + 2;
+	    triangle [1].x = 6;
+	    triangle [1].y = -7;
+	    triangle [2].x = 6;
+	    triangle [2].y = 7;
+
+	    XFillPolygon (xinfo->display, binfo->window, binfo->gc [BLACK],
+			  triangle, 3, Convex, CoordModePrevious);
+	    XFillRectangle (xinfo->display, binfo->window, binfo->gc [BLACK],
+			    x + width / 2 - 5, y + height / 2 + 4, 11, 3);
+	 }
+	 break;
+      default:
+	 break;
+   }
+   binfo->pressed [button] = pressed;
+}
+
+#endif /* not X_DISPLAY_MISSING */
diff --git a/converter/other/fiasco/buttons.h b/converter/other/fiasco/buttons.h
new file mode 100644
index 00000000..a09f3423
--- /dev/null
+++ b/converter/other/fiasco/buttons.h
@@ -0,0 +1,50 @@
+/*
+ *  buttons.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:51:17 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _BUTTONS_H
+#define _BUTTONS_H
+
+#ifndef X_DISPLAY_MISSING
+
+typedef enum grayscale_e {BLACK, NGRAY, LGRAY, DGRAY, RED,
+			  THICKBLACK, NO_GC} grayscale_t;
+typedef enum buttons_e {STOP_BUTTON, PLAY_BUTTON, PAUSE_BUTTON, RECORD_BUTTON,
+			QUIT_BUTTON, NO_BUTTON} buttons_t;
+
+typedef struct buttoninfo
+{
+   Window   window;
+   bool_t   pressed [NO_BUTTON];
+   GC	    gc [NO_GC];
+   unsigned width;
+   unsigned height;
+   unsigned progbar_height;
+   bool_t   record_is_rewind;
+} binfo_t;
+
+void
+check_events (x11_info_t *xinfo, binfo_t *binfo, unsigned n,
+	      unsigned n_frames);
+void
+wait_for_input (x11_info_t *xinfo);
+binfo_t * 
+init_buttons (x11_info_t *xinfo, unsigned n, unsigned n_frames,
+	      unsigned buttons_height, unsigned progbar_height);
+
+#endif /* not X_DISPLAY_MISSING */
+
+#endif /* not _BUTTONS_H */
+
diff --git a/converter/other/fiasco/codec/Makefile b/converter/other/fiasco/codec/Makefile
new file mode 100644
index 00000000..9a9d502a
--- /dev/null
+++ b/converter/other/fiasco/codec/Makefile
@@ -0,0 +1,27 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../../..
+  BUILDDIR = $(SRCDIR)
+endif
+FIASCOSUBDIR = converter/other/fiasco
+SUBDIR = $(FIASCOSUBDIR)/codec
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+INCLUDES = -I$(SRCDIR)/$(FIASCOSUBDIR) -I$(SRCDIR)/$(FIASCOSUBDIR)/lib \
+	 -I$(SRCDIR)/$(FIASCOSUBDIR)/input -I$(SRCDIR)/$(FIASCOSUBDIR)/output 
+
+OBJECTS =  approx.o bintree.o coder.o coeff.o \
+           control.o decoder.o dfiasco.o domain-pool.o ip.o motion.o mwfa.o \
+           options.o prediction.o subdivide.o tiling.o wfalib.o
+
+MERGE_OBJECTS = $(OBJECTS)
+
+all: libfiasco_codec.a
+
+include $(SRCDIR)/Makefile.common
+
+libfiasco_codec.a: $(OBJECTS)
+	$(AR) -rc $@ $(OBJECTS)
+	$(RANLIB) $@
+
diff --git a/converter/other/fiasco/codec/approx.c b/converter/other/fiasco/codec/approx.c
new file mode 100644
index 00000000..72e38cbf
--- /dev/null
+++ b/converter/other/fiasco/codec/approx.c
@@ -0,0 +1,702 @@
+/*
+ *  approx.c:		Approximation of range images with matching pursuit
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#include <math.h>
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "cwfa.h"
+#include "ip.h"
+#include "rpf.h"
+#include "domain-pool.h"
+#include "misc.h"
+#include "list.h"
+#include "approx.h"
+#include "coeff.h"
+#include "wfalib.h"
+
+/*****************************************************************************
+
+			     local variables
+  
+*****************************************************************************/
+
+typedef struct mp
+{
+   word_t exclude [MAXEDGES];
+   word_t indices [MAXEDGES + 1];
+   word_t into [MAXEDGES + 1];
+   real_t weight [MAXEDGES];
+   real_t matrix_bits;
+   real_t weights_bits;
+   real_t err;
+   real_t costs;
+} mp_t;
+
+/*****************************************************************************
+
+			     prototypes
+  
+*****************************************************************************/
+
+static void
+orthogonalize (unsigned index, unsigned n, unsigned level, real_t min_norm,
+	       const word_t *domain_blocks, const coding_t *c);
+static void 
+matching_pursuit (mp_t *mp, bool_t full_search, real_t price,
+		  unsigned max_edges, int y_state, const range_t *range,
+		  const domain_pool_t *domain_pool, const coeff_t *coeff,
+		  const wfa_t *wfa, const coding_t *c);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+real_t 
+approximate_range (real_t max_costs, real_t price, int max_edges,
+		   int y_state, range_t *range, domain_pool_t *domain_pool,
+		   coeff_t *coeff, const wfa_t *wfa, const coding_t *c)
+/*
+ *  Approximate image block 'range' by matching pursuit. This functions
+ *  calls the matching pursuit algorithm several times (with different
+ *  parameters) in order to find the best approximation. Refer to function
+ *  'matching_pursuit()' for more details about parameters.
+ *
+ *  Return value:
+ *	approximation costs
+ */
+{
+   mp_t	  mp;
+   bool_t success = NO;
+
+   /*
+    *  First approximation attempt: default matching pursuit algorithm.
+    */
+   mp.exclude [0] = NO_EDGE;
+   matching_pursuit (&mp, c->options.full_search, price, max_edges,
+		     y_state, range, domain_pool, coeff, wfa, c);
+
+   /*
+    *  Next approximation attempt: remove domain block mp->indices [0]
+    *  from domain pool (vector with smallest costs) and run the
+    *  matching pursuit again.
+    */
+   if (c->options.second_domain_block)
+   {
+      mp_t tmp_mp = mp;
+      
+      tmp_mp.exclude [0] = tmp_mp.indices [0];
+      tmp_mp.exclude [1] = NO_EDGE;
+	    
+      matching_pursuit (&tmp_mp, c->options.full_search, price, max_edges,
+			y_state, range, domain_pool, coeff, wfa, c);
+      if (tmp_mp.costs < mp.costs)	/* success */
+      {
+	 success = YES;
+	 mp      = tmp_mp;
+      }
+   }
+
+   /*
+    *  Next approximation attempt: check whether some coefficients have
+    *  been quantized to zero. Vectors causing the underflow are
+    *  removed from the domain pool and then the matching pursuit
+    *  algorithm is run again (until underflow doesn't occur anymore).
+    */
+   if (c->options.check_for_underflow)
+   {
+      int  iteration = -1;
+      mp_t tmp_mp    = mp;
+      
+      do
+      {
+	 int i;
+ 
+	 iteration++;
+	 tmp_mp.exclude [iteration] = NO_EDGE;
+	 
+	 for (i = 0; isdomain (tmp_mp.indices [i]); i++)
+	    if (tmp_mp.weight [i] == 0)
+	    {
+	       tmp_mp.exclude [iteration] = tmp_mp.indices [i];
+	       break;
+	    }
+      
+	 if (isdomain (tmp_mp.exclude [iteration])) /* try again */
+	 {
+	    tmp_mp.exclude [iteration + 1] = NO_EDGE;
+	    
+	    matching_pursuit (&tmp_mp, c->options.full_search, price,
+			      max_edges, y_state, range, domain_pool,
+			      coeff, wfa, c);
+	    if (tmp_mp.costs < mp.costs)	/* success */
+	    {
+	       success = YES;
+	       mp      = tmp_mp;
+	    }
+	 }
+      } while (isdomain (tmp_mp.exclude [iteration])
+	       && iteration < MAXEDGES - 1);
+   }
+
+   /*
+    *  Next approximation attempt: check whether some coefficients have
+    *  been quantized to +/- max-value. Vectors causing the overflow are
+    *  removed from the domain pool and then the matching pursuit
+    *  algorithm is run again (until overflow doesn't occur anymore).
+    */
+   if (c->options.check_for_overflow)
+   {
+      int  iteration = -1;
+      mp_t tmp_mp    = mp;
+      
+      do
+      {
+	 int i;
+ 
+	 iteration++;
+	 tmp_mp.exclude [iteration] = NO_EDGE;
+	 
+	 for (i = 0; isdomain (tmp_mp.indices [i]); i++)
+	 {
+	    rpf_t *rpf = tmp_mp.indices [i] ? coeff->rpf : coeff->dc_rpf;
+	    
+	    if (tmp_mp.weight [i] == btor (rtob (200, rpf), rpf)
+		|| tmp_mp.weight [i] == btor (rtob (-200, rpf), rpf))
+	    {
+	       tmp_mp.exclude [iteration] = tmp_mp.indices [i];
+	       break;
+	    }
+	 }
+      
+	 if (isdomain (tmp_mp.exclude [iteration])) /* try again */
+	 {
+	    tmp_mp.exclude [iteration + 1] = NO_EDGE;
+	    
+	    matching_pursuit (&tmp_mp, c->options.full_search, price,
+			      max_edges, y_state, range, domain_pool,
+			      coeff, wfa, c);
+	    if (tmp_mp.costs < mp.costs)	/* success */
+	    {
+	       success = YES;
+	       mp      = tmp_mp;
+	    }
+	 }
+      } while (isdomain (tmp_mp.exclude [iteration])
+	       && iteration < MAXEDGES - 1);
+   }
+
+   /*
+    *  Finally, check whether the best approximation has costs
+    *  smaller than 'max_costs'.
+    */
+   if (mp.costs < max_costs) 
+   {
+      int    edge;
+      bool_t overflow  = NO;
+      bool_t underflow = NO;
+      int    new_index, old_index;
+
+      new_index = 0;
+      for (old_index = 0; isdomain (mp.indices [old_index]); old_index++)
+	 if (mp.weight [old_index] != 0)
+	 {
+	    rpf_t *rpf = mp.indices [old_index] ? coeff->rpf : coeff->dc_rpf;
+	    
+	    if (mp.weight [old_index] == btor (rtob (200, rpf), rpf)
+		|| mp.weight [old_index] == btor (rtob (-200, rpf), rpf))
+	       overflow = YES;
+	    
+	    mp.indices [new_index] = mp.indices [old_index];
+	    mp.into [new_index]    = mp.into [old_index];
+	    mp.weight [new_index]  = mp.weight [old_index];
+	    new_index++;
+	 }
+	 else
+	    underflow = YES;
+      
+      mp.indices [new_index] = NO_EDGE;
+      mp.into  [new_index]   = NO_EDGE;
+
+      /*
+       *  Update of probability models
+       */
+      {
+	 word_t *domain_blocks = domain_pool->generate (range->level, y_state,
+							wfa,
+							domain_pool->model);
+	 domain_pool->update (domain_blocks, mp.indices,
+			      range->level, y_state, wfa, domain_pool->model);
+	 coeff->update (mp.weight, mp.into, range->level, coeff);
+	 
+	 Free (domain_blocks);
+      }
+      
+      for (edge = 0; isedge (mp.indices [edge]); edge++)
+      {
+	 range->into [edge]   = mp.into [edge];
+	 range->weight [edge] = mp.weight [edge];
+      }
+      range->into [edge]  = NO_EDGE;
+      range->matrix_bits  = mp.matrix_bits;
+      range->weights_bits = mp.weights_bits;
+      range->err          = mp.err;
+   }
+   else
+   {
+      range->into [0] = NO_EDGE;
+      mp.costs	      = MAXCOSTS;
+   }
+   
+   return mp.costs;
+}
+
+/*****************************************************************************
+
+			     local variables
+  
+*****************************************************************************/
+
+static real_t norm_ortho_vector [MAXSTATES];     
+/*
+ *  Square-norm of the i-th vector of the orthogonal basis (OB)
+ *  ||o_i||^2; i = 0, ... ,n
+ */
+static real_t ip_image_ortho_vector [MAXEDGES];
+/* 
+ *  Inner product between the i-th vector of the OB and the given range: 
+ *  <b, o_i>; i = 0, ... ,n 
+ */
+static real_t ip_domain_ortho_vector [MAXSTATES][MAXEDGES];
+/* 
+ *  Inner product between the i-th vector of the OB and the image of domain j: 
+ *  <s_j, o_i>; j = 0, ... , wfa->states; i = 0, ... ,n, 
+ */
+static real_t rem_denominator [MAXSTATES];     
+static real_t rem_numerator [MAXSTATES];
+/*
+ *  At step n of the orthogonalization the comparitive value
+ *  (numerator_i / denominator_i):= <b, o_n>^2 / ||o_n|| ,
+ *  is computed for every domain i,
+ *  where o_n := s_i - \sum(k = 0, ... , n-1) {(<s_i, o_k> / ||o_k||^2) o_k}
+ *  To avoid computing the same values over and over again,
+ *  the constant (remaining) parts of every domain are
+ *  stored in 'rem_numerator' and 'rem_denominator' separately
+ */
+static bool_t used [MAXSTATES];    
+/*
+ *  Shows whether a domain image was already used in a
+ *  linear combination (YES) or not (NO)
+ */
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static void 
+matching_pursuit (mp_t *mp, bool_t full_search, real_t price,
+		  unsigned max_edges, int y_state, const range_t *range,
+		  const domain_pool_t *domain_pool, const coeff_t *coeff,
+		  const wfa_t *wfa, const coding_t *c)
+/*
+ *  Find an approximation of the current 'range' with a linear
+ *  combination of vectors of the 'domain_pool'. The linear
+ *  combination is generated step by step with the matching pursuit
+ *  algorithm.  If flag 'full_search' is set then compute complete set
+ *  of linear combinations with n = {0, ..., 'max_edges'} vectors and
+ *  return the best one. Otherwise abort the computation as soon as
+ *  costs (LC (n + 1)) exceed costs ( LC (n)) and return the
+ *  sub-optimal solution.  'price' is the langrange multiplier
+ *  weighting rate and distortion.  'band' is the current color band
+ *  and 'y_state' the corresponding state in the Y component at same
+ *  pixel position.  'domain_pool' gives the set of available vectors,
+ *  and 'coeff' the model for the linear factors. The number of
+ *  elements in the linear combination is limited by 'max_edges'. In
+ *  'mp', vectors may be specified which should be excluded during the
+ *  approximation.
+ *  
+ *  No return value.
+ *
+ *  Side effects:
+ *	vectors, factors, rate, distortion and costs are stored in 'mp'
+ */
+{
+   unsigned	 n;			/* current vector of the OB */
+   int		 index;			/* best fitting domain image */
+   unsigned	 domain;		/* counter */
+   real_t	 norm;			/* norm of range image */
+   real_t	 additional_bits;	/* bits for mc, nd, and tree */
+   word_t	*domain_blocks;		/* current set of domain images */
+   const real_t  min_norm = 2e-3;	/* lower bound of norm */
+   unsigned 	 best_n   = 0;
+   unsigned	 size 	  = size_of_level (range->level);
+ 
+   /*
+    *  Initialize domain pool and inner product arrays
+    */
+   domain_blocks = domain_pool->generate (range->level, y_state, wfa,
+					  domain_pool->model);
+   for (domain = 0; domain_blocks [domain] >= 0; domain++)
+   {
+      used [domain] = NO;
+      rem_denominator [domain]		/* norm of domain */
+	 = get_ip_state_state (domain_blocks [domain], domain_blocks [domain],
+			       range->level, c);
+      if (rem_denominator [domain] / size < min_norm)
+	 used [domain] = YES;		/* don't use domains with small norm */
+      else
+	 rem_numerator [domain]		/* inner product <s_domain, b> */
+	    = get_ip_image_state (range->image, range->address,
+				  range->level, domain_blocks [domain], c);
+      if (!used [domain] && fabs (rem_numerator [domain]) < min_norm)
+	 used [domain] = YES;
+   }
+
+   /*
+    *  Exclude all domain blocks given in array 'mp->exclude'
+    */
+   for (n = 0; isdomain (mp->exclude [n]); n++)
+      used [mp->exclude [n]] = YES;
+
+   /*
+    *  Compute the approximation costs if 'range' is approximated with
+    *  no linear combination, i.e. the error is equal to the square
+    *  of the image norm and the size of the automaton is determined by
+    *  storing only zero elements in the current matrix row
+    */
+   for (norm = 0, n = 0; n < size; n++)
+      norm += square (c->pixels [range->address * size + n]);
+
+   additional_bits = range->tree_bits + range->mv_tree_bits
+		     + range->mv_coord_bits + range->nd_tree_bits
+		     + range->nd_weights_bits;
+
+   mp->err          = norm;
+   mp->weights_bits = 0;
+   mp->matrix_bits  = domain_pool->bits (domain_blocks, NULL, range->level,
+					 y_state, wfa, domain_pool->model);
+   mp->costs        = (mp->matrix_bits + mp->weights_bits
+		       + additional_bits) * price + mp->err;
+
+   n = 0;
+   do 
+   {
+      /*
+       *  Current approximation is: b = d_0 o_0 + ... + d_(n-1) o_(n-1)
+       *  with corresponding costs 'range->err + range->bits * p'.
+       *  For all remaining state images s_i (used[s_i] == NO) set
+       *  o_n :	= s_i - \sum(k = 0, ... , n-1) {(<s_i, o_k> / ||o_k||^2) o_k}
+       *  and try to beat current costs.
+       *  Choose that vector for the next orthogonalization step,
+       *  which has minimal costs: s_index.
+       *  (No progress is indicated by index == -1)
+       */
+      
+      real_t min_matrix_bits  = 0;
+      real_t min_weights_bits = 0;
+      real_t min_error 	      = 0;
+      real_t min_weight [MAXEDGES];
+      real_t min_costs = full_search ? MAXCOSTS : mp->costs;
+      
+      for (index = -1, domain = 0; domain_blocks [domain] >= 0; domain++) 
+	 if (!used [domain]) 
+	 {
+	    real_t    matrix_bits, weights_bits;
+	    /*
+	     *  To speed up the search through the domain images,
+	     *  the costs of using domain image 'domain' as next vector
+	     *  can be approximated in a first step:
+	     *  improvement of image quality
+	     *	  <= square (rem_numerator[domain]) / rem_denominator[domain]
+	     */
+	    {
+		  word_t   vectors [MAXEDGES + 1];
+		  word_t   states [MAXEDGES + 1];
+		  real_t   weights [MAXEDGES + 1];
+		  unsigned i, k;
+		  
+		  for (i = 0, k = 0; k < n; k++)
+		     if (mp->weight [k] != 0)
+		     {
+			vectors [i] = mp->indices [k];
+			states [i]  = domain_blocks [vectors [i]];
+			weights [i] = mp->weight [k];
+			i++;
+		     }
+		  vectors [i] 	  = domain;
+		  states [i]  	  = domain_blocks [domain];
+		  weights [i] 	  = 0.5;
+		  vectors [i + 1] = -1;
+		  states [i + 1]  = -1;
+
+		  weights_bits = coeff->bits (weights, states, range->level,
+					      coeff);
+		  matrix_bits = domain_pool->bits (domain_blocks, vectors,
+						   range->level, y_state,
+						   wfa, domain_pool->model);
+	    }
+	    if (((matrix_bits + weights_bits + additional_bits) * price +
+		 mp->err -
+		 square (rem_numerator [domain]) / rem_denominator [domain])
+		< min_costs)
+	    {
+	       /*
+		*  1.) Compute the weights (linear factors) c_i of the
+		*  linear combination
+		*  b = c_0 v_0 + ... + c_(n-1) v_(n-1) + c_n v_'domain'
+		*  Use backward substitution to obtain c_i from the linear
+		*  factors of the lin. comb. b = d_0 o_0 + ... + d_n o_n
+		*  of the corresponding orthogonal vectors {o_0, ..., o_n}.
+		*  Vector o_n of the orthogonal basis is obtained by using
+		*  vector 'v_domain' in step n of the Gram Schmidt
+		*  orthogonalization (see above for definition of o_n).
+		*  Recursive formula for the coefficients c_i:
+		*  c_n := <b, o_n> / ||o_n||^2
+		*  for i = n - 1, ... , 0:
+		*  c_i := <b, o_i> / ||o_i||^2 +
+		*          \sum (k = i + 1, ... , n){ c_k <v_k, o_i>
+		*					/ ||o_i||^2 }
+		*  2.) Because linear factors are stored with reduced precision
+		*  factor c_i is rounded with the given precision in step i
+		*  of the recursive formula. 
+		*/
+
+	       unsigned k;		/* counter */
+	       int    	l;		/* counter */
+	       real_t 	m_bits;		/* number of matrix bits to store */
+	       real_t 	w_bits;		/* number of weights bits to store */
+	       real_t 	r [MAXEDGES];	/* rounded linear factors */
+	       real_t 	f [MAXEDGES];	/* linear factors */
+	       int    	v [MAXEDGES];	/* mapping of domains to vectors */
+	       real_t 	costs;		/* current approximation costs */
+	       real_t 	m_err;		/* current approximation error */
+
+	       f [n] = rem_numerator [domain] / rem_denominator [domain];
+	       v [n] = domain;		/* corresponding mapping */
+	       for (k = 0; k < n; k++)
+	       {
+		  f [k] = ip_image_ortho_vector [k] / norm_ortho_vector [k];
+		  v [k] = mp->indices [k];
+	       }
+	    
+	       for (l = n; l >= 0; l--) 
+	       {
+		  rpf_t *rpf = domain_blocks [v [l]]
+			       ? coeff->rpf : coeff->dc_rpf;
+
+		  r [l] = f [l] = btor (rtob (f [l], rpf), rpf);
+		     
+		  for (k = 0; k < (unsigned) l; k++)
+		     f [k] -= f [l] * ip_domain_ortho_vector [v [l]][k]
+			      / norm_ortho_vector [k] ;
+	       } 
+
+	       /*
+		*  Compute the number of output bits of the linear combination
+		*  and store the weights with reduced precision. The
+		*  resulting linear combination is
+		*  b = r_0 v_0 + ... + r_(n-1) v_(n-1) + r_n v_'domain'
+		*/
+	       {
+		  word_t vectors [MAXEDGES + 1];
+		  word_t states [MAXEDGES + 1];
+		  real_t weights [MAXEDGES + 1];
+		  int	 i;
+		  
+		  for (i = 0, k = 0; k <= n; k++)
+		     if (f [k] != 0)
+		     {
+			vectors [i] = v [k];
+			states [i]  = domain_blocks [v [k]];
+			weights [i] = f [k];
+			i++;
+		     }
+		  vectors [i] = -1;
+		  states [i]  = -1;
+
+		  w_bits = coeff->bits (weights, states, range->level, coeff);
+		  m_bits = domain_pool->bits (domain_blocks, vectors,
+					      range->level, y_state,
+					      wfa, domain_pool->model);
+	       }
+	       
+	       /*
+		*  To compute the approximation error, the corresponding
+		*  linear factors of the linear combination 
+		*  b = r_0 o_0 + ... + r_(n-1) o_(n-1) + r_n o_'domain'
+		*  with orthogonal vectors must be computed with following
+		*  formula:
+		*  r_i := r_i +
+		*          \sum (k = i + 1, ... , n) { r_k <v_k, o_i>
+		*					/ ||o_i||^2 }
+		*/
+	       for (l = 0; (unsigned) l <= n; l++)
+	       {
+		  /*
+		   *  compute <v_n, o_n>
+		   */
+		  real_t a;
+
+		  a = get_ip_state_state (domain_blocks [v [l]],
+					  domain_blocks [domain],
+					  range->level, c);
+		  for (k = 0; k < n; k++) 
+		     a -= ip_domain_ortho_vector [v [l]][k]
+			  / norm_ortho_vector [k]
+			  * ip_domain_ortho_vector [domain][k];
+		  ip_domain_ortho_vector [v [l]][n] = a;
+	       }
+	       norm_ortho_vector [n]     = rem_denominator [domain];
+	       ip_image_ortho_vector [n] = rem_numerator [domain];
+ 	    
+	       for (k = 0; k <= n; k++)
+		  for (l = k + 1; (unsigned) l <= n; l++)
+		     r [k] += ip_domain_ortho_vector [v [l]][k] * r [l]
+			      / norm_ortho_vector [k];
+	       /*
+		*  Compute approximation error:
+		*  error := ||b||^2 +
+		*  \sum (k = 0, ... , n){r_k^2 ||o_k||^2 - 2 r_k <b, o_k>}
+		*/
+	       m_err = norm;
+	       for (k = 0; k <= n; k++)
+		  m_err += square (r [k]) * norm_ortho_vector [k]
+			 - 2 * r [k] * ip_image_ortho_vector [k];
+	       if (m_err < 0)		/* TODO: return MAXCOSTS */
+		  warning ("Negative image norm: %f"
+			   " (current domain: %d, level = %d)",
+			   (double) m_err, domain, range->level);
+
+	       costs = (m_bits + w_bits + additional_bits) * price + m_err;
+	       if (costs < min_costs)	/* found a better approximation */
+	       {
+		  index            = domain;
+		  min_costs        = costs;
+		  min_matrix_bits  = m_bits;
+		  min_weights_bits = w_bits;
+		  min_error        = m_err;
+		  for (k = 0; k <= n; k++)
+		     min_weight [k] = f [k];
+	       }
+	    }
+	 }
+      
+      if (index >= 0)			/* found a better approximation */
+      {
+	 if (min_costs < mp->costs)
+	 {
+	    unsigned k;
+	    
+	    mp->costs        = min_costs;
+	    mp->err          = min_error;
+	    mp->matrix_bits  = min_matrix_bits;
+	    mp->weights_bits = min_weights_bits;
+	    
+	    for (k = 0; k <= n; k++)
+	       mp->weight [k] = min_weight [k];
+
+	    best_n = n + 1;
+	 }
+	 
+	 mp->indices [n] = index;
+	 mp->into [n]    = domain_blocks [index];
+
+	 used [index] = YES;
+
+	 /* 
+	  *  Gram-Schmidt orthogonalization step n 
+	  */
+	 orthogonalize (index, n, range->level, min_norm, domain_blocks, c);
+	 n++;
+      }	
+   } 
+   while (n < max_edges && index >= 0);
+
+   mp->indices [best_n] = NO_EDGE;
+   
+   mp->costs = (mp->matrix_bits + mp->weights_bits + additional_bits) * price
+	       + mp->err;
+
+   Free (domain_blocks);
+}
+
+static void
+orthogonalize (unsigned index, unsigned n, unsigned level, real_t min_norm,
+	       const word_t *domain_blocks, const coding_t *c)
+/*
+ *  Step 'n' of the Gram-Schmidt orthogonalization procedure:
+ *  vector 'index' is orthogonalized with respect to the set
+ *  {u_[0], ... , u_['n' - 1]}. The size of the image blocks is given by
+ *  'level'. If the denominator gets smaller than 'min_norm' then
+ *  the corresponding domain is excluded from the list of available
+ *  domain blocks.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	The remainder values (numerator and denominator) of
+ *	all 'domain_blocks' are updated. 
+ */
+{
+   unsigned domain;
+   
+   ip_image_ortho_vector [n] = rem_numerator [index];
+   norm_ortho_vector [n]     = rem_denominator [index];
+
+   /*
+    *  Compute inner products between all domain images and 
+    *  vector n of the orthogonal basis:
+    *  for (i = 0, ... , wfa->states)  
+    *  <s_i, o_n> := <s_i, v_n> -
+    *      \sum (k = 0, ... , n - 1){ <v_n, o_k> <s_i, o_k> / ||o_k||^2}
+    *  Moreover the denominator and numerator parts of the comparitive
+    *  value are updated.
+    */
+   for (domain = 0; domain_blocks [domain] >= 0; domain++) 
+      if (!used [domain]) 
+      {
+	 unsigned k;
+	 real_t   tmp = get_ip_state_state (domain_blocks [index],
+					    domain_blocks [domain], level, c);
+	 
+	 for (k = 0; k < n; k++) 
+	    tmp -= ip_domain_ortho_vector [domain][k] / norm_ortho_vector [k]
+		   * ip_domain_ortho_vector [index][k];
+	 ip_domain_ortho_vector [domain][n] = tmp;
+	 rem_denominator [domain] -= square (tmp) / norm_ortho_vector [n];
+	 rem_numerator [domain]   -= ip_image_ortho_vector [n]
+				     / norm_ortho_vector [n]
+				     * ip_domain_ortho_vector [domain][n] ;
+
+	 /*
+	  *  Exclude vectors with small denominator
+	  */
+	 if (!used [domain]) 
+	    if (rem_denominator [domain] / size_of_level (level) < min_norm) 
+	       used [domain] = YES;
+      }
+}
+
+
+
diff --git a/converter/other/fiasco/codec/approx.h b/converter/other/fiasco/codec/approx.h
new file mode 100644
index 00000000..c54b78c9
--- /dev/null
+++ b/converter/other/fiasco/codec/approx.h
@@ -0,0 +1,30 @@
+/*
+ *  approx.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _APPROX_H
+#define _APPROX_H
+
+#include "types.h"
+#include "cwfa.h"
+#include "domain-pool.h"
+
+real_t 
+approximate_range (real_t max_costs, real_t price, int max_edges,
+		   int y_state, range_t *range, domain_pool_t *domain_pool,
+		   coeff_t *coeff, const wfa_t *wfa, const coding_t *c);
+
+#endif /* not _APPROX_H */
+
diff --git a/converter/other/fiasco/codec/bintree.c b/converter/other/fiasco/codec/bintree.c
new file mode 100644
index 00000000..ddd74e15
--- /dev/null
+++ b/converter/other/fiasco/codec/bintree.c
@@ -0,0 +1,94 @@
+/*
+ *  bintree.c:		Bintree model of WFA tree	
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include <math.h>
+
+#include "bintree.h"
+#include "misc.h"
+#include "cwfa.h"
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+void
+tree_update (bool_t child, unsigned level, tree_t *model)
+/*
+ *  Update tree model of given 'level'.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	tree model is changed.
+ */
+{
+   if (!child)
+      model->total [level]++;
+   else
+   {
+      model->counts [level]++;
+      model->total [level]++;
+   }
+}
+
+real_t
+tree_bits (bool_t child, unsigned level, const tree_t *model)
+/*
+ *  Compute number of bits needed for coding element 'child' of the bintree.
+ *  For each 'level' a different context is used.
+ *
+ *  Return value:
+ *	# bits
+ */
+{
+   real_t prob = model->counts [level] / (real_t) model->total [level];
+
+   return child ? - log2 (prob) : - log2 (1 - prob);
+}
+
+void
+init_tree_model (tree_t *tree_model)
+/*
+ *  Initialize the model for the tree.
+ *  Symbol RANGE is synonymous with the '0' symbol and
+ *  symbol NO_RANGE is synonymous with the '1' symbol of the binary coder.
+ *  The 'count' array counts the number of NO_RANGE symbols.
+ *
+ *  No return value.
+ */
+{
+   unsigned level;
+   unsigned counts_0 [MAXLEVEL] = {20, 17, 15, 10, 5,  4,  3,
+				   2,  1,  1,  1,  1,  1,  1,  1,
+				   1,  1,  1,  1 , 1,  1,  1};
+   unsigned counts_1 [MAXLEVEL] = {1 , 1,  1,  1,  1,  1,  1,
+				   1,  1,  2,  3,  5,  10, 15, 20,
+				   25, 30, 35, 60, 60, 60, 60};
+   
+   for (level = 0; level < MAXLEVEL ; level++) 
+   {
+      tree_model->counts [level] = counts_1 [level];
+      tree_model->total [level]  = counts_0 [level] + counts_1 [level];
+   }
+}
diff --git a/converter/other/fiasco/codec/bintree.h b/converter/other/fiasco/codec/bintree.h
new file mode 100644
index 00000000..cdb80c94
--- /dev/null
+++ b/converter/other/fiasco/codec/bintree.h
@@ -0,0 +1,43 @@
+/*
+ *  bintree.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _BINTREE_H
+#define _BINTREE_H
+
+#include "wfa.h"
+#include "types.h"
+
+typedef struct tree
+/*
+ *  Used for estimating the number of bits needed for storing the
+ *  tree array. For each level a different context is used.
+ *  The binary alphabet consists of the two symbols NO_RANGE and RANGE,
+ *  which indicate whether there exists a tree edge or not.
+ */
+{
+   unsigned counts [MAXLEVEL];		/* # NO_RANGE symbols at given level */
+   unsigned total [MAXLEVEL];		/* total number of symbols at  ''   */
+} tree_t;
+
+real_t
+tree_bits (bool_t child, unsigned level, const tree_t *model);
+void
+init_tree_model (tree_t *tree_model);
+void
+tree_update (bool_t child, unsigned level, tree_t *model);
+
+#endif /* not _BINTREE_H */
+
diff --git a/converter/other/fiasco/codec/coder.c b/converter/other/fiasco/codec/coder.c
new file mode 100644
index 00000000..df878d87
--- /dev/null
+++ b/converter/other/fiasco/codec/coder.c
@@ -0,0 +1,965 @@
+/*
+ *  coder.c:		WFA coder toplevel functions
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include "config.h"
+#include "pnm.h"
+
+#include <math.h>
+#include <ctype.h>
+
+#include <string.h>
+
+#include "nstring.h"
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "fiasco.h"
+
+#include "cwfa.h"
+#include "misc.h"
+#include "control.h"
+#include "bintree.h"
+#include "subdivide.h"
+#include "read.h"
+#include "write.h"
+#include "image.h"
+#include "mwfa.h"
+#include "list.h"
+#include "decoder.h"
+#include "motion.h"
+#include "wfalib.h"
+#include "domain-pool.h"
+#include "coeff.h"
+#include "coder.h"
+#include "rpf.h"
+
+/*****************************************************************************
+
+				global variables
+  
+*****************************************************************************/
+
+const real_t MAXCOSTS =	1e20;
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static coding_t *
+alloc_coder (char const * const *inputname, const c_options_t *options,
+	     wfa_info_t *wi);
+static void
+free_coder (coding_t *c);
+static char *
+get_input_image_name (char const * const *templptr, unsigned ith_image);
+static void
+video_coder (char const * const *image_template, bitfile_t *output,
+	     wfa_t *wfa, coding_t *c);
+static void 
+frame_coder (wfa_t *wfa, coding_t *c, bitfile_t *output);
+static void
+print_statistics (char c, real_t costs, const wfa_t *wfa, const image_t *image,
+		  const range_t *range);
+static frame_type_e
+pattern2type (unsigned frame, const char *pattern);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+int
+fiasco_coder (char const * const *inputname, const char *outputname,
+	      float quality, const fiasco_c_options_t *options)
+/*
+ *  FIASCO coder.
+ *  Encode image or video frames given by the array of filenames `inputname'
+ *  and write to the outputfile `outputname'.
+ *  If 'inputname' = NULL or
+ *     'inputname [0]' == NULL or
+ *     'inputname [0]' == "-", read standard input.
+ *  If 'outputname' == NULL or "-", write on standard output.
+ *  'quality' defines the approximation quality and is 1 (worst) to 100 (best).
+ *
+ *  Return value:
+ *	1 on success
+ *	0 otherwise
+ */
+{
+   try
+   {
+      char const * const default_input [] = {"-", NULL};
+      fiasco_c_options_t *default_options = NULL;
+      const c_options_t  *cop;
+      char const * const *template;
+      
+      /*
+       *  Check parameters
+       */
+      if (!inputname || !inputname [0] || streq (inputname [0], "-"))
+	 template = default_input;
+      else
+	 template = inputname;
+      
+      if (quality <= 0)
+      {
+	 set_error (_("Compression quality has to be positive."));
+	 return 0;
+      }
+      else if (quality >= 100)
+      {
+	 warning (_("Quality typically is 1 (worst) to 100 (best).\n"
+		    "Be prepared for a long running time."));
+      }
+
+      if (options)
+      {
+	 cop = cast_c_options ((fiasco_c_options_t *) options);
+	 if (!cop)
+	    return 0;
+      }
+      else
+      {
+	 default_options = fiasco_c_options_new ();
+	 cop 		 = cast_c_options (default_options);
+      }
+
+   /*
+    *  Open output stream and initialize WFA
+    */
+      {
+	 bitfile_t *output = open_bitfile (outputname, "FIASCO_DATA",
+					   WRITE_ACCESS);
+	 if (!output)
+	 {
+	    set_error (_("Can't write outputfile `%s'.\n%s"),
+		       outputname ? outputname : "<stdout>",
+		       get_system_error ());
+	    if (default_options)
+	       fiasco_c_options_delete (default_options);
+	    return 0;
+	 }
+	 else
+	 {
+	    wfa_t    *wfa = alloc_wfa (YES);
+	    coding_t *c   = alloc_coder (template, cop, wfa->wfainfo);
+	 
+	    read_basis (cop->basis_name, wfa);
+	    append_basis_states (wfa->basis_states, wfa, c);
+	 
+	    c->price = 128 * 64 / quality;
+	 
+	    video_coder (template, output, wfa, c);
+	 
+	    close_bitfile (output);
+	    free_wfa (wfa);
+	    free_coder (c);
+	 
+	    if (default_options)
+	       fiasco_c_options_delete (default_options);
+	 }
+      }
+      return 1;
+   }
+   catch
+   {
+      return 0;
+   }
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static coding_t *
+alloc_coder (char const * const *inputname, const c_options_t *options,
+	     wfa_info_t *wi)
+/*
+ *  Coder structure constructor.
+ *  Allocate memory for the FIASCO coder structure and
+ *  fill in default values specified by 'options'.
+ *
+ *  Return value:
+ *	pointer to the new coder structure or NULL on error
+ */
+{
+   coding_t *c = NULL;
+   
+   /*
+    *  Check whether all specified image frames are readable and of same type
+    */
+   {
+      char     *filename;
+      int   	width, w = 0, height, h = 0;
+      bool_t	color, c = NO;
+      unsigned 	n;
+      
+      for (n = 0; (filename = get_input_image_name (inputname, n)); n++)
+      {
+          FILE *file;
+          xelval maxval;
+          int format;
+          if (filename == NULL)
+              file = stdin;
+          else
+              file = pm_openr(filename);
+          pnm_readpnminit(file, &width, &height, &maxval, &format);
+          color = (PNM_FORMAT_TYPE(format) == PPM_FORMAT) ? TRUE: FALSE;
+
+          pm_close(file);
+	 if (n)
+	 {
+	    if (w != width || h != height || c != color)
+	    {
+	       set_error (_("Format of image frame `%s' doesn't match."),
+			  filename ? filename : "<stdin>");
+	       return NULL;
+	    }
+	 }
+	 else
+	 {
+	    w = width;
+	    h = height;
+	    c = color;
+	 }
+	 Free (filename);
+      }
+      wi->frames = n;
+      wi->width  = w;
+      wi->height = h;
+      wi->color  = c;
+   }
+
+   /*
+    *  Levels ...
+    */
+   {
+      unsigned lx, ly;
+      
+      lx = (unsigned) (log2 (wi->width - 1) + 1);
+      ly = (unsigned) (log2 (wi->height - 1) + 1);
+      
+      wi->level = max (lx, ly) * 2 - ((ly == lx + 1) ? 1 : 0);
+   }
+   
+   c = Calloc (1, sizeof (coding_t));
+
+   c->options 	      	   = *options;
+   c->options.lc_min_level = max (options->lc_min_level, 3);
+   c->options.lc_max_level = min (options->lc_max_level, wi->level - 1);
+
+   c->tiling = alloc_tiling (options->tiling_method,
+			     options->tiling_exponent, wi->level);
+
+   if (wi->frames > 1 && c->tiling->exponent > 0)
+   {
+      c->tiling->exponent = 0;
+      warning (_("Image tiling valid only with still image compression."));
+   }
+
+   if (c->options.lc_max_level >= wi->level - c->tiling->exponent)
+   {
+      message ("'max_level' changed from %d to %d due to image tiling level.",
+	       c->options.lc_max_level, wi->level - c->tiling->exponent - 1);
+      c->options.lc_max_level = wi->level - c->tiling->exponent - 1;
+   }
+   
+   if (c->options.lc_min_level > c->options.lc_max_level)
+      c->options.lc_min_level = c->options.lc_max_level;
+   
+   /*
+    *  p_min_level, p_max_level min and max level for ND/MC prediction
+    *  [p_min_level, p_max_level] must be a subset of [min_level, max_level] !
+    */
+   wi->p_min_level = max (options->p_min_level, c->options.lc_min_level);
+   wi->p_max_level = min (options->p_max_level, c->options.lc_max_level);
+   if (wi->p_min_level > wi->p_max_level)
+      wi->p_min_level = wi->p_max_level;
+
+   c->options.images_level = min (c->options.images_level,
+				  c->options.lc_max_level - 1);
+   
+   c->products_level  = max (0, ((signed int) c->options.lc_max_level
+				 - (signed int) c->options.images_level - 1));
+   c->pixels 	      = Calloc (size_of_level (c->options.lc_max_level),
+				sizeof (real_t));
+   c->images_of_state = Calloc (MAXSTATES, sizeof (real_t *));
+   c->ip_images_state = Calloc (MAXSTATES, sizeof (real_t *));
+   c->ip_states_state = Calloc (MAXSTATES * MAXLEVEL, sizeof (real_t *));
+   
+   debug_message ("Imageslevel :%d, Productslevel :%d",
+		  c->options.images_level, c->products_level);
+   debug_message ("Memory : (%d + %d + %d * 'states') * 'states' + %d",
+		  size_of_tree (c->options.images_level) * 4,
+		  size_of_tree (c->products_level) * 4,
+		  (c->options.lc_max_level - c->options.images_level),
+		  size_of_level (c->options.lc_max_level));
+   
+   /*
+    *  Domain pools ...
+    */
+   c->domain_pool   = NULL;
+   c->d_domain_pool = NULL;
+
+   /*
+    *  Coefficients model ...
+    */
+   c->coeff   = NULL;
+   c->d_coeff = NULL;
+
+   /*
+    *  Max. number of states and edges
+    */
+   wi->max_states   	   = max (min (options->max_states, MAXSTATES), 1);
+   c->options.max_elements = max (min (options->max_elements, MAXEDGES), 1);
+
+   /*
+    *  Title and comment strings
+    */
+   wi->title   = strdup (options->title);
+   wi->comment = strdup (options->comment);
+   
+   /*
+    *  Reduced precision format
+    */
+   wi->rpf
+      = alloc_rpf (options->rpf_mantissa, options->rpf_range);
+   wi->dc_rpf
+      = alloc_rpf (options->dc_rpf_mantissa, options->dc_rpf_range);
+   wi->d_rpf
+      = alloc_rpf (options->d_rpf_mantissa, options->d_rpf_range);
+   wi->d_dc_rpf
+      = alloc_rpf (options->d_dc_rpf_mantissa, options->d_dc_rpf_range);
+   
+   /*
+    *  Color image options ...
+    */
+   wi->chroma_max_states = max (1, options->chroma_max_states);
+
+   /*
+    *  Set up motion compensation struct.
+    *  p_min_level, p_max_level are also used for ND prediction
+    */
+   wi->search_range   = options->search_range;
+   wi->fps 	      = options->fps;
+   wi->half_pixel     = options->half_pixel_prediction;
+   wi->cross_B_search = options->half_pixel_prediction;
+   wi->B_as_past_ref  = options->B_as_past_ref;
+   wi->smoothing      = options->smoothing;
+   
+   c->mt = alloc_motion (wi);
+
+   return c;
+}
+
+static void
+free_coder (coding_t *c)
+/*
+ *  Coder struct destructor:
+ *  Free memory of 'coder' struct.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	structure 'coder' is discarded.
+ */
+{
+   free_tiling (c->tiling);
+   free_motion (c->mt);
+   
+   Free (c->pixels);
+   Free (c->images_of_state);
+   Free (c->ip_images_state);
+   Free (c->ip_states_state);
+   Free (c);
+}
+
+static char *
+get_input_image_name (char const * const *templptr, unsigned ith_image)
+/*
+ *  Construct the i-th image-name using templates.
+ *  If the template contains a '[' it must be of the form
+ *  "prefix[start-end{+,-}step]suffix"
+ *  where "{+,-}step" is optional.
+ *  Leading zeros of "start" are significant.
+ *
+ *  Example:
+ *   "image0[12-01-1].pgm" yields image012.pgm, image011.pgm, ..., image001.pgm
+ *
+ *  Return value:
+ *      ptr to name of image 'ith_image' or NULL if ith_image is out of range.
+ */
+{
+   while (*templptr)
+   {
+      const char *template = *templptr++;
+      char       *s;
+
+      if (!(s = strchr (template, '['))) /* no template, just a filename */
+      {
+	 if (ith_image == 0)
+	    return strdup (template);
+	 else
+	    ith_image--;
+      }
+      else				/* template parser */
+      {
+	 unsigned  n_digits;		/* # of digits in image name no. */
+	 char 	  *s2;
+	 char 	  *suffix;		/* characters after template end */
+	 char  	   prefix [MAXSTRLEN];	/* chars up to the template start */
+	 unsigned  first;		/* first image number */
+	 unsigned  last;		/* last image number */
+	 int  	   image_num;		/* current image number */
+	 int   	   increment = 1;
+	 int 	   dummy;
+
+	 strcpy (prefix, template);
+	 prefix [s - template] = '\0';
+   
+	 for (s2 = ++s, n_digits = 0; ISDIGIT (*s2); s2++, n_digits++)
+	    ;
+	 if (sscanf (s, "%d", &dummy) == 0 || dummy < 0)
+	    error ("Input name template conversion failure.\n"
+		   "Check spelling of template.");
+	 first = (unsigned) dummy;
+	 
+	 if (*s2++ != '-')
+	    error ("Input name template conversion failure.\n"
+		   "Check spelling of template.");
+   
+	 for (s = s2; ISDIGIT (*s2); s2++)
+	    ;
+	 if (sscanf (s, "%d", &dummy) == 0 || dummy < 0)
+	    error ("Input name template conversion failure.\n"
+		   "Check spelling of template.");
+	 last = (unsigned) dummy;
+	 
+	 if (*s2 == '+' || *s2 == '-') 
+	 {
+	    for (s = s2++; ISDIGIT (*s2); s2++)
+	       ;
+	    if (sscanf (s, "%d", &increment) == 0)
+	       error ("Input name template conversion failure.\n"
+		      "Check spelling of template.");
+	 } 	 
+	 if (*s2 != ']')
+	    error ("Input name template conversion failure.\n"
+		   "Check spelling of template.");
+	 suffix = s2 + 1;
+   
+	 image_num = first + increment * ith_image;
+	 if (image_num < 0)
+	    error ("Input name template conversion failure.\n"
+		   "Check spelling of template.");
+	 
+	 if ((increment >  0 && (unsigned) image_num > last) || 
+	     (increment <= 0 && (unsigned) image_num < last))
+	 {
+	    /* TODO: check this */
+	    ith_image -= (last - first) / increment + 1;
+	 }
+	 else
+	 {
+	    char formatstr [MAXSTRLEN]; /* format string for image filename */
+	    char image_name [MAXSTRLEN]; /* image file name to be composed */
+	    
+	    strcpy (formatstr, "%s%0?d%s");
+	    formatstr [4] = '0' + (char) n_digits;
+	    sprintf (image_name, formatstr, prefix, image_num, suffix);
+	    return strdup (image_name);
+	 }
+      }
+   }
+   return NULL;
+}	
+
+static void
+video_coder (char const * const *image_template, bitfile_t *output,
+	     wfa_t *wfa, coding_t *c)
+/*
+ *  Toplevel function to encode a sequence of video frames specified
+ *  by 'image_template'. The output is written to stream 'output'.
+ *  Coding options are given by 'c'.
+ *
+ *  No return value.
+ */
+{
+   unsigned  display;			/* picture number in display order */
+   int	     future_display;		/* number of future reference */
+   int	     frame;			/* current frame number */
+   char	    *image_name;		/* image name of current frame */
+   image_t  *reconst 	  = NULL; 	/* decoded reference image */
+   bool_t    future_frame = NO;		/* YES if last frame was in future */
+   
+   debug_message ("Generating %d WFA's ...", wfa->wfainfo->frames);
+
+   future_display = -1;
+   frame          = -1;
+   display        = 0;
+
+   while ((image_name = get_input_image_name (image_template, display)))
+   {
+      frame_type_e type;		/* current frame type: I, B, P */
+      
+      /*
+       *  Determine type of next frame.
+       *  Skip already coded frames (future reference!)
+       */
+      if (display == 0 && !c->options.reference_filename)
+	 type = I_FRAME;		/* Force first frame to be intra */
+      else
+	 type = pattern2type (display, c->options.pattern);
+      
+      if (type != I_FRAME && c->options.reference_filename)
+	 /* Load reference from disk */
+      {
+	 debug_message ("Reading reference frame `%s'.",
+			c->options.reference_filename);
+	 reconst 	 = read_image (c->options.reference_filename);
+	 c->options.reference_filename = NULL;
+      }
+      if ((int) display == future_display) /* Skip already coded future ref */
+      {				
+	 display++;
+	 continue;
+      }	  
+      else if (type == B_FRAME && (int) display > future_display) 
+      {
+	 unsigned i = display;
+	 /*
+	  *  Search for future reference
+	  */
+	 while (type == B_FRAME)
+	 {
+	    char *name;			/* image name of future frame */
+
+	    i++;
+	    name = get_input_image_name (image_template, i);
+	    
+	    if (!name)			/* Force last valid frame to be 'P' */
+	    {
+	       future_display = i - 1;
+	       type = P_FRAME;
+	    }
+	    else
+	    {
+	       future_display = i;    
+	       image_name     = name;
+	       type 	      = pattern2type (i, c->options.pattern);
+	    }
+	    frame = future_display;
+	 }
+      }
+      else
+      {
+	 frame = display;
+	 display++;
+      }
+
+      debug_message ("Coding \'%s\' [%c-frame].", image_name,
+		     type == I_FRAME ? 'I' : (type == P_FRAME ? 'P' : 'B'));
+       
+      /*
+       *  Depending on current frame type update past and future frames
+       *  which are needed as reference frames.
+       */
+      c->mt->frame_type = type;
+      if (type == I_FRAME)
+      {
+	 if (c->mt->past)		/* discard past frame */
+	    free_image (c->mt->past);
+	 c->mt->past = NULL;
+	 if (c->mt->future)		/* discard future frame */
+	    free_image (c->mt->future);
+	 c->mt->future = NULL;
+	 if (reconst)			/* discard current frame */
+	    free_image (reconst);
+	 reconst = NULL;
+      }
+      else if (type == P_FRAME)
+      {
+	 if (c->mt->past)		/* discard past frame */
+	    free_image (c->mt->past);
+	 c->mt->past = reconst;		/* past frame <- current frame */
+	 reconst    = NULL;
+	 if (c->mt->future)		/* discard future frame */
+	    free_image (c->mt->future);
+	 c->mt->future = NULL;
+      }
+      else				/* B_FRAME */
+      {
+	 if (future_frame)		/* last frame was future frame */
+	 {
+	    if (c->mt->future)		/* discard future frame */
+	       free_image (c->mt->future);
+	    c->mt->future = reconst;	/* future frame <- current frame */
+	    reconst      = NULL;
+	 }
+	 else
+	 {
+	    if (wfa->wfainfo->B_as_past_ref == YES)
+	    {
+	       if (c->mt->past)		/* discard past frame */
+		  free_image (c->mt->past);
+	       c->mt->past = reconst;	/* past frame <- current frame */
+	       reconst    = NULL;
+	    }
+	    else
+	    {
+	       if (reconst)		/* discard current frame */
+		  free_image (reconst);
+	       reconst = NULL;
+	    }
+	 }
+      }
+
+      /*
+       *  Start WFA coding of current frame
+       */
+      future_frame   = frame == future_display;
+      c->mt->number   = frame;
+      c->mt->original = read_image (image_name);
+      if (c->tiling->exponent && type == I_FRAME) 
+	 perform_tiling (c->mt->original, c->tiling);
+
+      frame_coder (wfa, c, output);
+
+      /*
+       *  Regenerate image:
+       *  1. Compute approximation of WFA ranges (real image bintree order)
+       *  2. Generate byte image in rasterscan order
+       *  3. Apply motion compensation
+       */
+      reconst = decode_image (wfa->wfainfo->width, wfa->wfainfo->height,
+			      FORMAT_4_4_4, NULL, wfa);
+
+      if (type != I_FRAME)
+	 restore_mc (0, reconst, c->mt->past, c->mt->future, wfa);
+
+      if (c->mt->original)
+	 free_image (c->mt->original);
+      c->mt->original = NULL;
+      
+      remove_states (wfa->basis_states, wfa); /* Clear WFA structure */
+   }
+
+   if (reconst)
+      free_image (reconst);
+   if (c->mt->future)
+      free_image (c->mt->future);
+   if (c->mt->past)
+      free_image (c->mt->past);
+   if (c->mt->original)
+      free_image (c->mt->original);
+}
+
+static frame_type_e
+pattern2type (unsigned frame, const char *pattern)
+{
+    int tmp = TOUPPER (pattern [frame % strlen (pattern)]);
+    frame_type_e retval;
+
+    switch (tmp)
+    {
+    case 'I':
+        retval = I_FRAME;
+        break;
+    case 'B':
+        retval = B_FRAME;
+        break;
+    case 'P':
+        retval = P_FRAME;
+        break;
+    default:
+        error ("Frame type %c not valid. Choose one of I,B or P.", tmp);
+    }
+    return retval;
+}
+
+static void 
+frame_coder (wfa_t *wfa, coding_t *c, bitfile_t *output)
+/*
+ * 
+ *  WFA Coding of next frame.  All important coding parameters are
+ *  stored in 'c'.  The generated 'wfa' is written to stream 'output'
+ *  immediately after coding.
+ *
+ *  No return value.
+ */
+{
+   unsigned state;
+   range_t  range;			/* first range == the entire image */
+   real_t   costs;			/* total costs (minimized quantity) */
+   unsigned bits;			/* number of bits written on disk */
+   clock_t  ptimer;
+   
+   prg_timer (&ptimer, START);
+
+   bits = bits_processed (output);
+   
+   init_tree_model (&c->tree);
+   init_tree_model (&c->p_tree);
+
+   c->domain_pool
+      = alloc_domain_pool (c->options.id_domain_pool,
+			   wfa->wfainfo->max_states,
+			   c->options.max_elements, wfa);
+   c->d_domain_pool
+      = alloc_domain_pool ((c->options.prediction
+			    || c->mt->frame_type != I_FRAME)
+			   ? c->options.id_d_domain_pool : "constant",
+			   wfa->wfainfo->max_states,
+			   c->options.max_elements, wfa);
+
+   c->coeff   = alloc_coeff_model (c->options.id_rpf_model,
+				   wfa->wfainfo->rpf,
+				   wfa->wfainfo->dc_rpf,
+				   c->options.lc_min_level,
+				   c->options.lc_max_level);
+   c->d_coeff = alloc_coeff_model (c->options.id_d_rpf_model,
+				   wfa->wfainfo->d_rpf,
+				   wfa->wfainfo->d_dc_rpf,
+				   c->options.lc_min_level,
+				   c->options.lc_max_level);
+
+   if (!c->mt->original->color)		/* grayscale image */
+   {
+      memset (&range, 0, sizeof (range_t));
+      range.level = wfa->wfainfo->level;
+
+      costs = subdivide (MAXCOSTS, GRAY, RANGE, &range, wfa, c,
+			 c->options.prediction || c->mt->frame_type != I_FRAME,
+			 NO);
+      if (c->options.progress_meter != FIASCO_PROGRESS_NONE)
+	 message ("");
+
+      if (isrange (range.tree))		/* entire image is approx. by lc? */
+	 error ("No root state generated!");
+      else
+	 wfa->root_state = range.tree;
+
+      print_statistics ('\0', costs, wfa, c->mt->original, &range);
+   }
+   else
+   {
+      int     YCb_node = -1;
+      int     tree [3];			/* 3 root states of each color comp. */
+      color_e band;
+      
+      /*
+       *  When compressing color images, the three color components (YCbCr) 
+       *  are copied into a large image:
+       *  [  Y  Cr ]
+       *  [  Cb 0  ]
+       *  I.e. the color components of an image are processed in a row.
+       *  After all components are compressed, virtual states are generated
+       *  to describe the large image.
+       */
+      for (band = first_band (YES); band <= last_band (YES) ; band++)
+      {
+	 debug_message ("Encoding color component %d", band);
+	 tree [band] = RANGE;
+	 if (band == Cb)
+	 {
+	    unsigned min_level;
+
+	    c->domain_pool->chroma (wfa->wfainfo->chroma_max_states, wfa,
+				    c->domain_pool->model);
+	    /*
+	     *  Don't use a finer partioning for the chrominancy bands than for
+	     *  the luminancy band.
+	     */
+	    for (min_level = MAXLEVEL, state = wfa->basis_states;
+		 state < wfa->states; state++)
+	    {
+	       unsigned lincomb, label;
+	       
+	       for (lincomb = 0, label = 0; label < MAXLABELS; label++)
+		  lincomb += isrange (wfa->tree [state][label]) ? 1 : 0;
+	       if (lincomb)
+		  min_level = min (min_level,
+				   (unsigned) (wfa->level_of_state [state]
+					       - 1));
+	    }
+	    c->options.lc_min_level = min_level;
+	    if (c->mt->frame_type != I_FRAME) /* subtract mc of luminance */
+	       subtract_mc (c->mt->original, c->mt->past, c->mt->future, wfa);
+	 }
+
+	 memset (&range, 0, sizeof (range_t));
+	 range.level = wfa->wfainfo->level;
+	 
+	 costs = subdivide (MAXCOSTS, band, tree [Y], &range, wfa, c,
+			    c->mt->frame_type != I_FRAME && band == Y, NO);
+	 if (c->options.progress_meter != FIASCO_PROGRESS_NONE)
+	    message ("");
+	 {
+	    char colors [] = {'Y', 'B', 'R'};
+	    
+	    print_statistics (colors [band], costs, wfa,
+			      c->mt->original, &range);
+	 }
+	 
+	 if (isrange (range.tree))	/* whole image is approx. by a l.c. */
+	    error ("No root state generated for color component %d!", band);
+	 else
+	    tree[band] = range.tree;
+	 
+	 if (band == Cb)
+	 {
+	    wfa->tree [wfa->states][0] = tree[Y];
+	    wfa->tree [wfa->states][1] = tree[Cb];
+	    YCb_node = wfa->states;
+	    append_state (YES, compute_final_distribution (wfa->states, wfa),
+			  wfa->wfainfo->level + 1, wfa, c);
+	 }
+      }
+      /*
+       *  generate two virtual states (*) 
+       *
+       *              *
+       *            /   \
+       *           +     *
+       *          / \   /  
+       *         Y   CbCr 
+       */
+      wfa->tree [wfa->states][0] = tree[Cr];
+      wfa->tree [wfa->states][1] = RANGE;
+      append_state (YES, compute_final_distribution (wfa->states, wfa),
+		    wfa->wfainfo->level + 1, wfa, c);
+      wfa->tree[wfa->states][0] = YCb_node;
+      wfa->tree[wfa->states][1] = wfa->states - 1;
+      append_state (YES, compute_final_distribution (wfa->states, wfa),
+		    wfa->wfainfo->level + 2, wfa, c);
+
+      wfa->root_state = wfa->states - 1;
+   }
+
+   for (state = wfa->basis_states; state < MAXSTATES; state++)
+   {
+      unsigned level;
+      
+      if (c->images_of_state [state])
+      {
+	 Free (c->images_of_state [state]);
+	 c->images_of_state [state] = NULL;
+      }
+      if (c->ip_images_state [state])
+      {
+	 Free (c->ip_images_state [state]);
+	 c->ip_images_state [state] = NULL;
+      }
+      for (level = c->options.images_level + 1;
+	   level <= c->options.lc_max_level;
+	   level++)
+	 if (c->ip_states_state [state][level])
+	 {
+	    Free (c->ip_states_state [state][level]);
+	    c->ip_states_state [state][level] = NULL;
+	 }
+      
+   }
+   
+   locate_delta_images (wfa);
+   write_next_wfa (wfa, c, output);
+   
+   bits = bits_processed (output) - bits;
+   debug_message ("Total number of bits written: %d (%d bytes, %5.3f bpp)",
+		  bits, bits >> 3,
+		  bits / (double) (c->mt->original->height
+				   * c->mt->original->width));
+   debug_message ("Total encoding time (real): %d sec",
+		  prg_timer (&ptimer, STOP) / 1000);
+
+   c->domain_pool->free (c->domain_pool);
+   c->d_domain_pool->free (c->d_domain_pool);
+
+   c->coeff->free (c->coeff);
+   c->d_coeff->free (c->d_coeff);
+}
+
+static void
+print_statistics (char c, real_t costs, const wfa_t *wfa, const image_t *image,
+		  const range_t *range)
+{
+   unsigned max_level, min_level, state, label, lincomb;
+   
+   for (max_level = 0, min_level = MAXLEVEL, state = wfa->basis_states;
+	state < wfa->states; state++)
+   {
+      for (lincomb = 0, label = 0; label < MAXLABELS; label++)
+	 lincomb += isrange(wfa->tree[state][label]) ? 1 : 0;
+	 
+      if (lincomb)
+      {
+	 max_level = max (max_level,
+			  (unsigned) (wfa->level_of_state [state] - 1));
+	 min_level = min (min_level,
+			  (unsigned) (wfa->level_of_state [state] - 1));
+      }
+   }
+   debug_message ("Image partitioning: maximum level %d , minimum level %d",
+		  max_level, min_level);
+   debug_message ("WFA contains %d states (%d basis states).",
+		  wfa->states, wfa->basis_states);
+   debug_message ("Estimated error: %.2f (RMSE: %.2f, PSNR: %.2f dB).",
+		  (double) range->err,
+		  sqrt (range->err / image->width / image->height),
+		  10 * log ( 255.0 * 255.0 /
+			     (range->err / image->width / image->height))
+		  / log (10.0));
+   debug_message ("Estimated filesize: %.0f bits (%.0f bytes).",
+		  (double) (range->tree_bits + range->matrix_bits
+			    + range->weights_bits
+			    + range->mv_tree_bits + range->mv_coord_bits
+			    + range->nd_tree_bits + range->nd_weights_bits),
+		  (double) (range->tree_bits + range->matrix_bits
+			    + range->weights_bits + range->mv_tree_bits
+			    + range->mv_coord_bits + range->nd_tree_bits
+			    + range->nd_weights_bits) / 8);
+   if (c)
+      debug_message ("(%cT: %.0f, %cM: %.0f, %cW: %.0f, %cMC: %.0f, "
+		     "%cMV: %.0f, %cNT: %.0f, %cNW: %.0f.)",
+		     c, (double) range->tree_bits,
+		     c, (double) range->matrix_bits,
+		     c, (double) range->weights_bits,
+		     c, (double) range->mv_tree_bits,
+		     c, (double) range->mv_coord_bits,
+		     c, (double) range->nd_tree_bits,
+		     c, (double) range->nd_weights_bits);
+   else
+      debug_message ("(T: %.0f, M: %.0f, W: %.0f, MC: %.0f, MV: %.0f, "
+		     "NT: %.0f, NW: %.0f.)",
+		     (double) range->tree_bits,
+		     (double) range->matrix_bits,
+		     (double) range->weights_bits,
+		     (double) range->mv_tree_bits,
+		     (double) range->mv_coord_bits,
+		     (double) range->nd_tree_bits,
+		     (double) range->nd_weights_bits);
+   debug_message ("Total costs : %.2f", (double) costs);
+}
diff --git a/converter/other/fiasco/codec/coder.h b/converter/other/fiasco/codec/coder.h
new file mode 100644
index 00000000..c6f4bb7c
--- /dev/null
+++ b/converter/other/fiasco/codec/coder.h
@@ -0,0 +1,24 @@
+/*
+ *  coder.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _CODER_H
+#define _CODER_H
+
+#include "types.h"
+#include "cwfa.h"
+
+#endif /* not _CODER_H */
+
diff --git a/converter/other/fiasco/codec/coeff.c b/converter/other/fiasco/codec/coeff.c
new file mode 100644
index 00000000..0cd64f17
--- /dev/null
+++ b/converter/other/fiasco/codec/coeff.c
@@ -0,0 +1,369 @@
+/*
+ *  coeff.c:		Matching pursuit coefficients probability model
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include <string.h>
+#include "config.h"
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "rpf.h"
+#include "misc.h"
+#include "coeff.h"
+
+/*
+ *  Coefficient model interface:
+ *  Implementing the coefficients model interface requires the
+ *  following steps: 
+ *  - Add a constructor that initializes the coeff_t structure
+ *  - Allocate new model with default_alloc() 
+ *  - Fill the c_array_t coeff_models[] array with constructor and name
+ *  - Write code for methods bits() and update()
+ *  - Either use default functions for remaining methods or override them
+ *  The new model is automatically registered at the command line.
+ */
+
+/*****************************************************************************
+		  uniform distribution coefficients model
+*****************************************************************************/
+
+static coeff_t *
+alloc_uniform_coeff_model (rpf_t *rpf, rpf_t *dc_rpf,
+			   unsigned min_level, unsigned max_level);
+static void
+uniform_update (const real_t *used_coeff, const word_t *used_states,
+		unsigned level, coeff_t *coeff);
+static real_t
+uniform_bits (const real_t *used_coeff, const word_t *used_states,
+	      unsigned level, const coeff_t *coeff);
+
+/*****************************************************************************
+			  default functions
+*****************************************************************************/
+
+static void
+default_model_free (void *model);
+static void *
+default_model_duplicate (const coeff_t *coeff, const void *model);
+static void
+default_free (coeff_t *coeff);
+static coeff_t *
+default_alloc (rpf_t *rpf, rpf_t *dc_rpf,
+	       unsigned min_level, unsigned max_level);
+
+/*****************************************************************************
+		adaptive arithmetic coding model
+*****************************************************************************/
+
+static coeff_t *
+alloc_aac_coeff_model (rpf_t *rpf, rpf_t *dc_rpf,
+		       unsigned min_level, unsigned max_level);
+static void
+aac_model_free (void *model);
+static void *
+aac_model_alloc (const coeff_t *coeff);
+static void *
+aac_model_duplicate (const coeff_t *coeff, const void *model);
+static void
+aac_update (const real_t *used_coeff, const word_t *used_states,
+	    unsigned level, coeff_t *coeff);
+static real_t
+aac_bits (const real_t *used_coeff, const word_t *used_states,
+	  unsigned level, const coeff_t *coeff);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+typedef struct c_array
+{
+   const char *identifier;
+   coeff_t    *(*function) (rpf_t *rpf, rpf_t *dc_rpf,
+			    unsigned min_level, unsigned max_level);
+} c_array_t;
+
+c_array_t coeff_models[] = {{"adaptive", alloc_aac_coeff_model},
+			    {"uniform",	 alloc_uniform_coeff_model},
+			    {NULL,	 NULL}};
+
+coeff_t *
+alloc_coeff_model (const char *coeff_model_name, rpf_t *rpf, rpf_t *dc_rpf,
+		   unsigned min_level, unsigned max_level)
+/*
+ *  Allocate a new coefficients model which is identified by the string
+ *  'coeff_model_name'.  'rpf' and 'dc_rpf' define the reduced
+ *  precision formats the should be used to quantize normal and DC
+ *  components, respectively. 'min_level' and 'max_level' define the
+ *  range of range approximations.
+ * 
+ *  Return value:
+ *	pointer to the allocated coefficients model
+ *
+ *  Note:
+ *      Refer to 'coeff.h' for a short description of the member functions.  */
+{
+   unsigned n;
+   
+   for (n = 0; coeff_models [n].identifier; n++) /* step through all id's */
+      if (strcaseeq (coeff_models [n].identifier, coeff_model_name)) 
+	 return coeff_models [n].function (rpf, dc_rpf, min_level, max_level);
+
+   warning ("Can't initialize coefficients model '%s'. "
+	    "Using default value '%s'.",
+	    coeff_model_name, coeff_models [0].identifier);
+
+   return coeff_models [0].function (rpf, dc_rpf, min_level, max_level);
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+/*****************************************************************************
+		  uniform distribution coefficients model
+*****************************************************************************/
+
+static coeff_t *
+alloc_uniform_coeff_model (rpf_t *rpf, rpf_t *dc_rpf,
+			   unsigned min_level, unsigned max_level)
+/*
+ *  Underlying probability model: uniform distribution.
+ *  I.e. each coefficient is written as such with
+ *  (1 + exponent_bits + mantissa_bits) bits.
+ */
+{
+   coeff_t *coeff = default_alloc (rpf, dc_rpf, min_level, max_level);
+
+   coeff->bits   = uniform_bits;
+   coeff->update = uniform_update;
+   
+   return coeff;
+}
+
+static real_t
+uniform_bits (const real_t *used_coeff, const word_t *used_states,
+	      unsigned level, const coeff_t *coeff)
+{
+   unsigned edge;
+   real_t   bits = 0;			/* #bits to store coefficients */
+   
+   for (edge = 0; isedge (used_states [edge]); edge++)
+   {
+      rpf_t *rpf = used_states [edge] ? coeff->rpf : coeff->dc_rpf;
+      
+      bits += rpf->mantissa_bits + 1;
+   }
+
+   return bits;
+}
+
+static void
+uniform_update (const real_t *used_coeff, const word_t *used_states,
+		unsigned level, coeff_t *coeff)
+{
+   return;				/* nothing to do */
+}
+
+/*****************************************************************************
+		adaptive arithmetic coding  model
+*****************************************************************************/
+
+typedef struct aac_model
+{
+   word_t *counts;
+   word_t *totals;
+} aac_model_t;
+
+static coeff_t *
+alloc_aac_coeff_model (rpf_t *rpf, rpf_t *dc_rpf,
+		       unsigned min_level, unsigned max_level)
+/*
+ *  Underlying probability model: adaptive arithmetic coding using
+ *  the level of a range as context.
+ */
+{
+   coeff_t *coeff = default_alloc (rpf, dc_rpf, min_level, max_level);
+   
+   coeff->bits            = aac_bits;
+   coeff->update          = aac_update;
+   coeff->model_free      = aac_model_free;
+   coeff->model_duplicate = aac_model_duplicate;
+   coeff->model		  = aac_model_alloc (coeff);
+   
+   return coeff;
+}
+
+static real_t
+aac_bits (const real_t *used_coeff, const word_t *used_states,
+	  unsigned level, const coeff_t *coeff)
+{
+   real_t	bits  = 0;		/* # bits to store coefficients */
+   unsigned	edge;
+   int		state;
+   word_t      *counts;
+   aac_model_t *model = (aac_model_t *) coeff->model;
+
+   counts = model->counts
+	    + (1 << (1 + coeff->dc_rpf->mantissa_bits))
+	    + ((level - coeff->min_level)
+	       * (1 << (1 + coeff->rpf->mantissa_bits)));
+   
+   for (edge = 0; isedge (state = used_states [edge]); edge++)
+      if (state)
+	 bits -= log2 (counts [rtob (used_coeff [edge], coeff->rpf)]
+		       / (real_t) model->totals [level
+						- coeff->min_level + 1]);
+      else
+	 bits -= log2 (model->counts [rtob (used_coeff [edge], coeff->dc_rpf)]
+		       / (real_t) model->totals [0]);
+   
+   return bits;
+}
+
+static void
+aac_update (const real_t *used_coeff, const word_t *used_states,
+	    unsigned level, coeff_t *coeff)
+{
+   unsigned	edge;
+   int		state;
+   word_t      *counts;
+   aac_model_t *model = (aac_model_t *) coeff->model;
+
+   counts = model->counts
+	    + (1 << (1 + coeff->dc_rpf->mantissa_bits))
+	    + ((level - coeff->min_level)
+	       * (1 << (1 + coeff->rpf->mantissa_bits)));
+
+   for (edge = 0; isedge (state = used_states [edge]); edge++)
+      if (state)
+      {
+	 counts [rtob (used_coeff [edge], coeff->rpf)]++;
+	 model->totals [level - coeff->min_level + 1]++;
+      }
+      else
+      {
+	 model->counts [rtob (used_coeff [edge], coeff->dc_rpf)]++;
+	 model->totals [0]++;
+      }
+}
+
+static void *
+aac_model_duplicate (const coeff_t *coeff, const void *model)
+{
+   aac_model_t *src = (aac_model_t *) model;
+   aac_model_t *dst = aac_model_alloc (coeff);
+
+   memcpy (dst->counts, src->counts,
+	   sizeof (word_t) * ((coeff->max_level - coeff->min_level + 1)
+			      * (1 << (1 + coeff->rpf->mantissa_bits))
+			      + (1 << (1 + coeff->dc_rpf->mantissa_bits))));
+   memcpy (dst->totals, src->totals,
+	   sizeof (word_t) * (coeff->max_level - coeff->min_level + 1 + 1));
+   
+   return dst;
+}
+
+static void *
+aac_model_alloc (const coeff_t *coeff)
+{
+   aac_model_t *model;
+   unsigned	size = (coeff->max_level - coeff->min_level + 1)
+		       * (1 << (1 + coeff->rpf->mantissa_bits))
+		       + (1 << (1 + coeff->dc_rpf->mantissa_bits));
+   
+   model 	 = Calloc (1, sizeof (aac_model_t));
+   model->counts = Calloc (size, sizeof (word_t));
+   model->totals = Calloc (coeff->max_level - coeff->min_level + 1 + 1,
+			   sizeof (word_t));
+   /*
+    *  Initialize model
+    */
+   {
+      unsigned  n;
+      word_t   *ptr = model->counts;
+      
+      for (n = size; n; n--)
+	 *ptr++ = 1;
+      model->totals [0] = 1 << (1 + coeff->dc_rpf->mantissa_bits);
+      for (n = coeff->min_level; n <= coeff->max_level; n++)
+	 model->totals [n - coeff->min_level + 1]
+	    = 1 << (1 + coeff->rpf->mantissa_bits);
+   }
+   
+   return (void *) model;
+}
+
+static void
+aac_model_free (void *model)
+{
+   aac_model_t	*aac_model = (aac_model_t *) model;
+
+   if (aac_model)
+   {
+      Free (aac_model->counts);
+      Free (aac_model->totals);
+      Free (aac_model);
+   }
+}
+
+/*****************************************************************************
+				default functions
+*****************************************************************************/
+
+static coeff_t *
+default_alloc (rpf_t *rpf, rpf_t *dc_rpf,
+	       unsigned min_level, unsigned max_level)
+{
+   coeff_t *coeff = Calloc (1, sizeof (coeff_t));
+
+   coeff->rpf 	      	  = rpf;
+   coeff->dc_rpf       	  = dc_rpf;
+   coeff->min_level	  = min_level;
+   coeff->max_level	  = max_level;
+   coeff->model	      	  = NULL;
+   coeff->bits	      	  = NULL;
+   coeff->update      	  = NULL;
+   coeff->free	      	  = default_free;
+   coeff->model_free  	  = default_model_free;
+   coeff->model_duplicate = default_model_duplicate;
+   
+   return coeff;
+}
+
+static void
+default_free (coeff_t *coeff)
+{
+   coeff->model_free (coeff->model);
+   Free (coeff);
+}
+
+static void *
+default_model_duplicate (const coeff_t *coeff, const void *model)
+{
+   return NULL;
+}
+
+static void
+default_model_free (void *model)
+{
+   if (model)
+      Free (model);
+}
diff --git a/converter/other/fiasco/codec/coeff.h b/converter/other/fiasco/codec/coeff.h
new file mode 100644
index 00000000..118cd0fc
--- /dev/null
+++ b/converter/other/fiasco/codec/coeff.h
@@ -0,0 +1,61 @@
+/*
+ *  coeff.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _COEFF_H
+#define _COEFF_H
+
+#include "types.h"
+#include "rpf.h"
+#include "wfa.h"
+
+typedef struct coeff
+{
+   rpf_t    *rpf;			/* reduced precision format */
+   rpf_t    *dc_rpf;			/* RPF of DC (state 0) component */
+   unsigned min_level, max_level;	/* allocate memory for [min,..,max] */
+   void	    *model;			/* generic pointer to prob. model */
+   real_t (*bits) (const real_t *used_coeff, const word_t *used_domains,
+		   unsigned level, const struct coeff *coeff);
+   /*
+    *  Compute bit-rate of a range approximation with coefficients given by
+    *  -1 terminated list 'used_domains'.
+    */
+   void   (*update) (const real_t *used_coeff, const word_t *used_domains,
+		       unsigned level, struct coeff *coeff);
+   /*
+    *  Update the probability model according to the chosen approximation.
+    *  (given by the -1 terminated list 'used_domains').
+    */
+   void	  (*free) (struct coeff *coeff);
+   /*
+    *  Discard the given coefficients struct.
+    */
+   void   (*model_free) (void *model);
+   /*
+    *  Free given probability model.
+    */
+   void   *(*model_duplicate) (const struct coeff *coeff, const void *model);
+   /*
+    *  Duplicate the given probability model (i.e. alloc and copy).
+    */
+} coeff_t;
+
+coeff_t *
+alloc_coeff_model (const char *coeff_model_name, rpf_t *rpf, rpf_t *dc_rpf,
+		   unsigned min_level, unsigned max_level);
+
+#endif /* not _COEFF_H */
+
diff --git a/converter/other/fiasco/codec/control.c b/converter/other/fiasco/codec/control.c
new file mode 100644
index 00000000..9af9928b
--- /dev/null
+++ b/converter/other/fiasco/codec/control.c
@@ -0,0 +1,276 @@
+/*
+ *  control.c:		Control unit of WFA structure
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#if HAVE_STRING_H
+#	include <string.h>
+#else /* not HAVE_STRING_H */
+#	include <strings.h>
+#endif /* not HAVE_STRING_H */
+ 
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "cwfa.h"
+#include "ip.h"
+#include "misc.h"
+#include "wfalib.h"
+#include "control.h"
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static void 
+clear_or_alloc (real_t **ptr, size_t size);
+static void 
+compute_images (unsigned from, unsigned to, const wfa_t *wfa, coding_t *c);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+void    
+append_state (bool_t auxiliary_state, real_t final, unsigned level_of_state,
+	      wfa_t *wfa, coding_t *c)
+/*
+ *  Append a 'wfa' state. If 'auxiliary_state' == YES then
+ *  allocate memory for inner products and state images.  'final' is
+ *  the final distribution of the new state. 'level_of_state' is the
+ *  level of the subimage which is represented by this state
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	The WFA information are updated in structure 'wfa'
+ *	State images are computed and inner products are cleared (in 'c')
+ */
+{
+   wfa->final_distribution [wfa->states] = final;
+   wfa->level_of_state [wfa->states]     = level_of_state;
+   
+   if (!auxiliary_state)
+   {
+      unsigned level;
+
+      wfa->domain_type [wfa->states] = USE_DOMAIN_MASK;
+
+      /*
+       *  Allocate memory for inner products and for state images
+       */
+      clear_or_alloc (&c->images_of_state [wfa->states],
+		      size_of_tree (c->options.images_level));
+	 
+      for (level = c->options.images_level + 1;
+	   level <= c->options.lc_max_level; level++)
+	 clear_or_alloc (&c->ip_states_state [wfa->states][level],
+			 wfa->states + 1);
+
+      clear_or_alloc (&c->ip_images_state [wfa->states],
+		      size_of_tree (c->products_level));
+
+      /*
+       *  Compute the images of the current state at level 0,..,'imageslevel'
+       */
+      
+      c->images_of_state [wfa->states][0] = final;
+      compute_images (wfa->states, wfa->states, wfa, c);  
+
+      /*
+       *  Compute the inner products between the current state and the
+       *  old states 0,...,'states'-1
+       */ 
+      
+      compute_ip_states_state (wfa->states, wfa->states, wfa, c);
+   }
+   else
+   {
+      unsigned level;
+      
+      wfa->domain_type [wfa->states] = 0;
+	    
+      /*
+       *  Free the allocated memory
+       */
+      if (c->images_of_state [wfa->states] != NULL)
+      {
+	 Free (c->images_of_state [wfa->states]);
+	 c->images_of_state [wfa->states] = NULL;
+      }
+      for (level = 0; level <= c->options.lc_max_level; level++)
+	 if (c->ip_states_state [wfa->states][level])
+	 {
+	    Free (c->ip_states_state [wfa->states][level]);
+	    c->ip_states_state [wfa->states][level] = NULL;
+	 }
+      if (c->ip_images_state [wfa->states])
+      {
+	 Free (c->ip_images_state [wfa->states]);
+	 c->ip_images_state [wfa->states] = NULL;
+      }
+   }
+   
+   wfa->states++;
+   if (wfa->states >= MAXSTATES) 
+      error ("Maximum number of states reached!");
+}	
+ 
+void 
+append_basis_states (unsigned basis_states, wfa_t *wfa, coding_t *c)
+/*
+ *  Append the WFA basis states 0, ... , ('basis_states' - 1).
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	The WFA information are updated in structure 'wfa'
+ *	State images and inner products are computed (in 'c')
+ */
+{
+   unsigned level, state;
+
+   /*
+    *  Allocate memory
+    */
+
+   for (state = 0; state < basis_states; state++)
+   {
+      clear_or_alloc (&c->images_of_state [state],
+		      size_of_tree (c->options.images_level));
+
+      for (level = c->options.images_level + 1;
+	   level <= c->options.lc_max_level; level++)
+	 clear_or_alloc (&c->ip_states_state [state][level], state + 1);
+
+      clear_or_alloc (&c->ip_images_state [state],
+		      size_of_tree (c->products_level));
+
+      c->images_of_state [state][0] = wfa->final_distribution [state];
+      wfa->level_of_state [state]   = -1;
+   }
+   
+   compute_images (0, basis_states - 1, wfa, c);  
+   compute_ip_states_state (0, basis_states - 1, wfa, c);
+   wfa->states = basis_states;
+   
+   if (wfa->states >= MAXSTATES) 
+      error ("Maximum number of states reached!");
+}	
+ 
+void 
+append_transitions (unsigned state, unsigned label, const real_t *weight,
+		    const word_t *into, wfa_t *wfa)
+/*
+ *  Append the 'wfa' transitions (given by the arrays 'weight' and 'into')
+ *  of the range ('state','label').
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	new 'wfa' edges are appended
+ */
+{
+   unsigned edge;
+
+   wfa->y_column [state][label] = 0;
+   for (edge = 0; isedge (into [edge]); edge++)
+   {
+      append_edge (state, into [edge], weight [edge], label, wfa);
+      if (into [edge] == wfa->y_state [state][label])
+	 wfa->y_column [state][label] = 1;
+   }
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static void 
+compute_images (unsigned from, unsigned to, const wfa_t *wfa, coding_t *c)
+/*
+ *  Computes the images of the given states 'from', ... , 'to' 
+ *  at level 0,...,'c->imagelevel'.
+ *  Uses the fact that each state image is a linear combination of state
+ *  images, i.e. s_i := c_0 s_0 + ... + c_i s_i.
+ */
+{
+   unsigned label, level, state;
+   
+   /*
+    *  Compute the images Phi(state)
+    *  #		level = 0
+    *  ##		level = 1
+    *  ####		level = 2
+    *  ########    	level = 3
+    *  ...
+    *  ########...##    level = imageslevel
+    */
+   
+   for (level = 1; level <= c->options.images_level; level++)
+      for (state = from; state <= to; state++)
+	 for (label = 0; label < MAXLABELS; label++)
+	 {
+	    real_t   *dst, *src;
+	    unsigned  edge;
+	    int	      domain;		/* current domain */
+	    
+	    if (ischild (domain = wfa->tree[state][label]))
+	    {
+	       dst = c->images_of_state [state] + address_of_level (level) +
+		     label * size_of_level (level - 1);
+	       src = c->images_of_state [domain]
+		     + address_of_level (level - 1);
+	       memcpy (dst, src, size_of_level (level - 1) * sizeof (real_t));
+	    }
+	    for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
+		 edge++)
+	    {
+	       unsigned n;
+	       real_t 	weight = wfa->weight [state][label][edge];
+	       
+	       dst = c->images_of_state [state] + address_of_level (level) +
+		     label * size_of_level (level - 1);
+	       src = c->images_of_state [domain]
+		     + address_of_level (level - 1);
+		  
+	       for (n = size_of_level (level - 1); n; n--)
+		  *dst++ += *src++ * weight;
+	    }
+	 }
+
+}
+
+static void 
+clear_or_alloc (real_t **ptr, size_t size)
+/*
+ *  if *ptr == NULL 	allocate memory with Calloc 
+ *  otherwise 		fill the real_t-array ptr[] with 0
+ */
+{
+   if (*ptr == NULL) 
+      *ptr = Calloc (size, sizeof (real_t));
+   else 
+      memset (*ptr, 0, size * sizeof (real_t));
+    
+}
diff --git a/converter/other/fiasco/codec/control.h b/converter/other/fiasco/codec/control.h
new file mode 100644
index 00000000..f601d8b8
--- /dev/null
+++ b/converter/other/fiasco/codec/control.h
@@ -0,0 +1,33 @@
+/*
+ *  control.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _CONTROL_H
+#define _CONTROL_H
+
+#include "cwfa.h"
+#include "types.h"
+
+void 
+append_transitions (unsigned state, unsigned label, const real_t *weight,
+		    const word_t *into, wfa_t *wfa);
+void 
+append_basis_states (unsigned basis_states, wfa_t *wfa, coding_t *c);
+void    
+append_state (bool_t auxiliary_state, real_t final, unsigned level_of_state,
+	      wfa_t *wfa, coding_t *c);
+
+#endif /* not _CONTROL_H */
+
diff --git a/converter/other/fiasco/codec/cwfa.h b/converter/other/fiasco/codec/cwfa.h
new file mode 100644
index 00000000..9c4e7fee
--- /dev/null
+++ b/converter/other/fiasco/codec/cwfa.h
@@ -0,0 +1,107 @@
+/*
+ *  cwfa.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/10/28 17:39:30 $
+ *  $Author: hafner $
+ *  $Revision: 5.3 $
+ *  $State: Exp $
+ */
+
+#ifndef _CWFA_H
+#define _CWFA_H
+
+#include "wfa.h"
+#include "types.h"
+#include "tiling.h"
+#include "rpf.h"
+#include "domain-pool.h"
+#include "bintree.h"
+#include "coeff.h"
+#include "list.h"
+#include "wfalib.h"
+#include "options.h"
+
+extern const real_t MAXCOSTS;
+
+typedef struct motion
+{
+   image_t	 *original;		/* Current image */
+   image_t	 *past;			/* Preceeding image */
+   image_t	 *future;		/* Succeeding image */
+   frame_type_e	  frame_type;		/* frame type: B_, P_ I_FRAME */
+   unsigned	  number;		/* display number of frame */
+   real_t        *xbits;		/* # bits for mv x-component */
+   real_t        *ybits;		/* # bits for mv y-component */
+   real_t       **mc_forward_norms; 	/* norms of mcpe */
+   real_t       **mc_backward_norms; 	/* norms of mcpe */
+} motion_t;
+
+typedef struct range
+/*
+ *  Information about current range in the original image.
+ *  Approximation data (error, encoding bits, approximation type and factors
+ *  of the linear combination) are also saved.
+ */ 
+{
+   unsigned global_address;             /* We need absolute image addresses
+				           for distance calculations. */
+   unsigned x, y;			/* Coordinates of upper left corner */
+   unsigned image;			/* Position in the tree */
+   unsigned address;			/* Address of the pixel data */
+   unsigned level;			/* Level of the range */
+   real_t   weight [MAXEDGES + 1];	/* coeff. of the approximation */
+   word_t   into [MAXEDGES + 1];	/* used domains of the approximation */
+   int	    tree;			/* == domain : range is approximated
+					   with new state 'domain'
+					   == RANGE  :
+					   with a linear comb. */
+   real_t   err;			/* approximation error */
+   real_t   tree_bits;			/* # bits to encode tree */
+   real_t   matrix_bits;		/* # bits to encode matrices */
+   real_t   weights_bits;		/* # bits to encode weights */
+   mv_t	    mv;				/* motion vector */
+   real_t   mv_tree_bits;		/* # bits to encode mv tree */
+   real_t   mv_coord_bits;		/* # bits to encode mv coordinates */
+   real_t   nd_tree_bits;		/* # bits to encode nd tree */
+   real_t   nd_weights_bits;		/* # bits to encode nd factors */
+   bool_t   prediction;			/* range is predicted? */
+} range_t;
+
+typedef struct coding
+/*
+ *  All parameters and variables that must be accessible through the coding
+ *  process.
+ */
+{
+   real_t     	   price;		/* determines quality of approx. */
+   real_t   	 **images_of_state;	/* image of state i at level
+					   0, ... , imageslevel */
+   real_t   *(*ip_states_state)[MAXLEVEL]; /* inner products between state i
+					      and states 0, ... , i
+					      at all image levels */
+   real_t   	 **ip_images_state;	/* inner products between all
+					   ranges and state i */
+   real_t    	  *pixels;		/* current image pixels stored in tree
+					   order (only leaves are stored) */
+   unsigned   	   products_level;	/* inner products are stored up to
+					   this level */
+   tiling_t   	  *tiling;		/* tiling of the entire image */
+   tree_t     	   tree;		/* probability model */
+   tree_t     	   p_tree;		/* prediction probability model */
+   motion_t   	  *mt;			/* motion compensation information */
+   coeff_t   	  *coeff;
+   coeff_t   	  *d_coeff;
+   domain_pool_t  *domain_pool;
+   domain_pool_t  *d_domain_pool;
+   c_options_t     options;		/* global options */
+} coding_t;
+
+#endif /* not _CWFA_H */
+
diff --git a/converter/other/fiasco/codec/decoder.c b/converter/other/fiasco/codec/decoder.c
new file mode 100644
index 00000000..9474c1e7
--- /dev/null
+++ b/converter/other/fiasco/codec/decoder.c
@@ -0,0 +1,1532 @@
+/*
+ *  decode.c:		Decoding of an image represented by a WFA
+ *
+ *  Written by:		Ullrich Hafner
+ *			Michael Unger
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+ 
+/*
+ *  $Date: 2000/10/22 10:44:48 $
+ *  $Author: hafner $
+ *  $Revision: 5.3 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#if HAVE_STRING_H
+#	include <string.h>
+#else /* not HAVE_STRING_H */
+#	include <strings.h>
+#endif /* not HAVE_STRING_H */
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "wfa.h"
+#include "image.h"
+#include "misc.h"
+#include "motion.h"
+#include "read.h"
+#include "wfalib.h"
+#include "decoder.h"
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static void
+compute_state_images (unsigned frame_level, word_t **simg,
+		      const u_word_t *offset, const wfa_t *wfa);
+static void
+free_state_images (unsigned max_level, bool_t color, word_t **state_image,
+		   u_word_t *offset, const unsigned *root_state,
+		   unsigned range_state, format_e format, const wfa_t *wfa);
+static void
+alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
+		    const unsigned *root_state, unsigned range_state,
+		    unsigned max_level, format_e format, const wfa_t *wfa);
+static void
+compute_actual_size (unsigned luminance_root,
+		     unsigned *width, unsigned *height, const wfa_t *wfa);
+static void
+enlarge_image (int enlarge_factor, format_e format, unsigned y_root,
+	       wfa_t *wfa);
+static word_t *
+duplicate_state_image (const word_t *domain, unsigned offset, unsigned level);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+video_t *
+alloc_video (bool_t store_wfa)
+/*
+ *  Video struct constructor:
+ *  Initialize video structure and allocate memory for current, past
+ *  and future WFA if flag 'store_wfa' is TRUE.
+ *
+ *  Return value:
+ *	pointer to the new video structure
+ */
+{
+   video_t *video = Calloc (1, sizeof (video_t));
+   
+   video->future_display = -1;
+   video->display        = 0;
+
+   video->future = video->sfuture = video->past
+		 = video->frame   = video->sframe = NULL;
+
+   if (store_wfa)
+   {
+      video->wfa        = alloc_wfa (NO);
+      video->wfa_past   = alloc_wfa (NO);
+      video->wfa_future = alloc_wfa (NO);
+   }
+   else
+      video->wfa = video->wfa_past = video->wfa_future = NULL;
+
+   return video;
+}
+
+void
+free_video (video_t *video)
+/*
+ *  Video struct destructor:
+ *  Free memory of given 'video' struct.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'video' struct is discarded.
+ */
+{
+   if (video->past)
+      free_image (video->past);
+   if (video->future)
+      free_image (video->future);
+   if (video->sfuture)
+      free_image (video->sfuture);
+   if (video->frame)
+      free_image (video->frame);
+   if (video->sframe)
+      free_image (video->sframe);
+   if (video->wfa)
+      free_wfa (video->wfa);
+   if (video->wfa_past)
+      free_wfa (video->wfa_past);
+   if (video->wfa_future)
+      free_wfa (video->wfa_future);
+
+   Free (video);
+}
+
+image_t *
+get_next_frame (bool_t store_wfa, int enlarge_factor,
+		int smoothing, const char *reference_frame,
+		format_e format, video_t *video, dectimer_t *timer,
+		wfa_t *orig_wfa, bitfile_t *input)
+/*
+ *  Get next frame of the WFA 'video' from stream 'input'.
+ *  'orig_wfa' is the constant part of the WFA used by all frames.
+ *  Depending on values of 'enlarge_factor' and 'smoothing' enlarge and
+ *  smooth image, respectively. 
+ *  If 'store_wfa' is TRUE, then store WFA structure of reference frames
+ *  (used by analysis tool xwfa).
+ *  If 'reference_frame' is not NULL, then load image 'reference_frame'
+ *  from disk.
+ *  'format' gives the color format to be used (either 4:2:0 or 4:4:4).
+ *  If 'timer' is not NULL, then accumulate running time statistics. 
+ *
+ *  Return value:
+ *	pointer to decoded frame
+ *
+ *  Side effects:
+ *	'video' and 'timer' struct are modified.
+ */
+{
+   image_t *frame 			  = NULL; /* current frame */
+   image_t *sframe 			  = NULL; /* current smoothed frame */
+   bool_t   current_frame_is_future_frame = NO;
+
+   if (video->future_display == video->display)	 
+   {
+      /*
+       *  Future frame is already computed since it has been used
+       *  as reference frame. So just return the stored frame.
+       */
+      if (video->frame) /* discard current frame */
+	 free_image (video->frame);
+      video->frame  = video->future;
+      video->future = NULL;
+
+      if (video->sframe) /* discard current (smoothed) frame */
+	 free_image (video->sframe);
+      video->sframe  = video->sfuture;
+      video->sfuture = NULL;
+
+      if (store_wfa)
+	 copy_wfa (video->wfa, video->wfa_future);
+
+      video->display++;
+
+      if (!store_wfa)
+	 video->wfa = NULL;
+   }
+   else
+   {
+      do				/* compute next frame(s) */
+      {
+	 unsigned      frame_number;	/* current frame number */
+	 clock_t       ptimer;
+	 unsigned int  stop_timer [3];
+	 wfa_t	      *tmp_wfa = NULL;
+	 
+	 if (!store_wfa)
+	    video->wfa = orig_wfa;
+	 else
+	 {
+	    tmp_wfa = alloc_wfa (NO);
+	    copy_wfa (tmp_wfa, video->wfa);
+	    copy_wfa (video->wfa, orig_wfa);
+	 }
+   
+	 /*
+	  *  First step: read WFA from disk
+	  */
+	 prg_timer (&ptimer, START);
+	 frame_number = read_next_wfa (video->wfa, input);
+	 stop_timer [0] = prg_timer (&ptimer, STOP);
+	 if (timer)
+	 {
+	    timer->input [video->wfa->frame_type] += stop_timer [0];
+	    timer->frames [video->wfa->frame_type]++;
+	 }
+      
+	 /*
+	  *  Read reference frame from disk if required
+	  *  (i.e., 1st frame is of type B or P)
+	  */
+	 if (video->display == 0 && video->wfa->frame_type != I_FRAME)
+	 {
+	    if (!reference_frame)
+	       error ("First frame is %c-frame but no "
+		      "reference frame is given.",
+		      video->wfa->frame_type == B_FRAME ? 'B' : 'P');
+
+	    video->frame  = read_image (reference_frame);
+	    video->sframe = NULL;
+	 }
+   
+	 /*
+	  *  Depending on current frame type update past and future frames
+	  */
+	 if (video->wfa->frame_type == I_FRAME)
+	 {
+	    if (video->past)		/* discard past frame */
+	       free_image (video->past);
+	    video->past = NULL;
+	    if (video->future)		/* discard future frame */
+	       free_image (video->future);
+	    video->future = NULL;
+	    if (video->sfuture)		/* discard (smoothed) future frame */
+	       free_image (video->sfuture);
+	    video->sfuture = NULL;
+	    if (video->frame)		/* discard current frame */
+	       free_image (video->frame);
+	    video->frame = NULL;
+	    if (video->sframe)		/* discard current (smoothed) frame */
+	       free_image (video->sframe);
+	    video->sframe = NULL;
+	 }
+	 else if (video->wfa->frame_type == P_FRAME)
+	 {
+	    if (video->past)		/* discard past frame */
+	       free_image (video->past);
+	    video->past = video->frame;	/* past <- current frame */
+	    video->frame = NULL;
+	    if (video->sframe)		/* discard current (smoothed) frame */
+	       free_image (video->sframe);
+	    video->sframe = NULL;
+	    if (store_wfa)
+	       copy_wfa (video->wfa_past, tmp_wfa);
+	    if (video->future)		/* discard future frame */
+	       free_image (video->future);
+	    video->future = NULL;
+	    if (video->sfuture)		/* discard (smoothed) future frame */
+	       free_image (video->sfuture);
+	    video->sfuture = NULL;
+	 }
+	 else				/* B_FRAME */
+	 {
+	    if (current_frame_is_future_frame)
+	    {
+	       if (video->future)	/* discard future frame */
+		  free_image (video->future);
+	       video->future = frame;	/* future <- current frame */
+	       if (video->sfuture)	/* discard (smoothed) future frame */
+		  free_image (video->sfuture);
+	       video->sfuture = sframe;	/* future <- current (smoothed) */
+	       if (store_wfa)
+		  copy_wfa (video->wfa_future, tmp_wfa);
+	       if (video->frame)	/* discard current frame */
+		  free_image (video->frame);
+	       video->frame = NULL;
+	       if (video->sframe)	/* discard current (smoothed) frame */
+		  free_image (video->sframe);
+	       video->sframe = NULL;
+	       frame  = NULL;
+	       sframe = NULL;
+	    }
+	    else
+	    {
+	       if (video->wfa->wfainfo->B_as_past_ref == YES)
+	       {
+		  if (video->past)	/* discard past frame */
+		     free_image (video->past);
+		  video->past  = video->frame; /* past <- current frame */
+		  video->frame = NULL;
+		  if (video->sframe)	/* discard current (smoothed) frame */
+		     free_image (video->sframe);
+		  video->sframe = NULL;
+		  if (store_wfa)
+		     copy_wfa (video->wfa_past, tmp_wfa);
+	       }
+	       else
+	       {
+		  if (video->frame)	/* discard current */
+		     free_image (video->frame);
+		  video->frame = NULL;
+		  if (video->sframe)	/* discard current (smoothed) frame */
+		     free_image (video->sframe);
+		  video->sframe = NULL;
+	       }
+	    }
+	 }
+	 if (tmp_wfa)
+	    free_wfa (tmp_wfa);
+	 
+	 current_frame_is_future_frame = NO;
+	 /*
+	  *  Second step: decode image
+	  *  Optionally enlarge image if specified by option 'enlarge_factor'.
+	  */
+	 {
+	    unsigned orig_width, orig_height;
+
+	    stop_timer [0] = stop_timer [1] = stop_timer [2] = 0;
+	 
+	    enlarge_image (enlarge_factor, format,
+			   (video->wfa->wfainfo->color
+			    && format == FORMAT_4_2_0)
+			   ? video->wfa->tree [video->wfa->tree [video->wfa->root_state][0]][0] : -1, video->wfa);
+
+	    if (enlarge_factor > 0)
+	    {
+	       orig_width  = video->wfa->wfainfo->width  << enlarge_factor;
+	       orig_height = video->wfa->wfainfo->height << enlarge_factor; 
+	    }
+	    else
+	    { 
+	       orig_width  = video->wfa->wfainfo->width  >> - enlarge_factor;
+	       orig_height = video->wfa->wfainfo->height >> - enlarge_factor;
+	       if (orig_width & 1)
+		  orig_width++;
+	       if (orig_height & 1)
+		  orig_height++;
+	    }
+	 
+	    frame = decode_image (orig_width, orig_height, format,
+				  timer != NULL ? stop_timer : NULL,
+				  video->wfa);
+	    if (timer)
+	    {
+	       timer->preprocessing [video->wfa->frame_type] += stop_timer [0];
+	       timer->decoder [video->wfa->frame_type]       += stop_timer [1];
+	       timer->cleanup [video->wfa->frame_type]       += stop_timer [2];
+	    }
+	 }
+
+	 /*
+	  *  Third step: restore motion compensation
+	  */
+	 if (video->wfa->frame_type != I_FRAME)
+	 {
+	    prg_timer (&ptimer, START);
+	    restore_mc (enlarge_factor, frame, video->past, video->future,
+			video->wfa);
+	    stop_timer [0] = prg_timer (&ptimer, STOP);
+	    if (timer)
+	       timer->motion [video->wfa->frame_type] += stop_timer [0];
+	 }
+
+	 /*
+	  *  Fourth step: smooth image along partitioning borders
+	  */
+	 prg_timer (&ptimer, START);
+	 if (smoothing < 0)	/* smoothing not changed by user */
+	    smoothing = video->wfa->wfainfo->smoothing;
+	 if (smoothing > 0 && smoothing <= 100)
+	 {
+	    sframe = clone_image (frame);
+	    smooth_image (smoothing, video->wfa, sframe);
+	 }
+	 else
+	    sframe = NULL;
+	 
+	 stop_timer [0] = prg_timer (&ptimer, STOP);
+	 if (timer)
+	    timer->smooth [video->wfa->frame_type] += stop_timer [0];
+
+	 if (frame_number == video->display)
+	 {
+	    video->display++;
+	    video->frame  = frame;
+	    video->sframe = sframe;
+	    frame         = NULL;
+	    sframe        = NULL;
+	 }
+	 else if (frame_number > video->display)
+	 {
+	    video->future_display 	  = frame_number;
+	    current_frame_is_future_frame = YES;
+	 }
+      
+	 if (!store_wfa)
+	    remove_states (video->wfa->basis_states, video->wfa);
+      } while (!video->frame);
+
+      if (!store_wfa)
+	 video->wfa = NULL;
+   }
+   
+   return video->sframe ? video->sframe : video->frame;
+}
+
+image_t *
+decode_image (unsigned orig_width, unsigned orig_height, format_e format,
+	      unsigned *dec_timer, const wfa_t *wfa)
+/*
+ *  Compute image which is represented by the given 'wfa'.
+ *  'orig_width'x'orig_height' gives the resolution of the image at
+ *  coding time. Use 4:2:0 subsampling or 4:4:4 'format' for color images.
+ *  If 'dec_timer' is given, accumulate running time statistics. 
+ *  
+ *  Return value:
+ *	pointer to decoded image
+ *
+ *  Side effects:
+ *	'*dectimer' is changed if 'dectimer' != NULL.
+ */
+{
+   unsigned   root_state [3];		/* root of bintree for each band */
+   unsigned   width, height;		/* computed image size */
+   image_t   *frame;			/* regenerated frame */
+   word_t   **images;			/* pointer to array of pointers
+					   to state images */
+   u_word_t  *offsets;			/* pointer to array of state image
+					   offsets */
+   unsigned   max_level;		/* max. level of state with approx. */
+   unsigned   state;
+   clock_t    ptimer;
+
+   prg_timer (&ptimer, START);
+
+   /*
+    *  Compute root of bintree for each color band
+    */
+   if (wfa->wfainfo->color)
+   {
+      root_state [Y]  = wfa->tree [wfa->tree [wfa->root_state][0]][0];
+      root_state [Cb] = wfa->tree [wfa->tree [wfa->root_state][0]][1];
+      root_state [Cr] = wfa->tree [wfa->tree [wfa->root_state][1]][0];
+   }
+   else
+      root_state [GRAY] = wfa->root_state;
+
+   /*
+    *  Compute maximum level of a linear combination
+    */
+   for (max_level = 0, state = wfa->basis_states; state < wfa->states; state++)
+      if (isedge (wfa->into [state][0][0]) || isedge (wfa->into [state][1][0]))
+	 max_level = max (max_level, wfa->level_of_state [state]);
+   
+
+   /*
+    *  Allocate frame buffer for decoded image
+    */
+   compute_actual_size (format == FORMAT_4_2_0 ? root_state [Y] : MAXSTATES,
+			&width, &height, wfa);
+   width  = max (width, orig_width);
+   height = max (height, orig_height);
+   frame = alloc_image (width, height, wfa->wfainfo->color, format);
+   
+   /*
+    *  Allocate buffers for intermediate state images
+    */
+   if (wfa->wfainfo->color)
+   {
+      wfa->level_of_state [wfa->root_state]               = 128;
+      wfa->level_of_state [wfa->tree[wfa->root_state][0]] = 128;
+      wfa->level_of_state [wfa->tree[wfa->root_state][1]] = 128;
+   }
+   alloc_state_images (&images, &offsets, frame, root_state, 0, max_level, 
+		       format, wfa);
+
+   if (dec_timer)
+      dec_timer [0] += prg_timer (&ptimer, STOP);
+
+   /*
+    *  Decode all state images, forming the complete image.
+    */
+   prg_timer (&ptimer, START);
+   compute_state_images (max_level, images, offsets, wfa);
+   if (dec_timer)
+      dec_timer [1] += prg_timer (&ptimer, STOP);
+
+   /*
+    *  Cleanup buffers used for intermediate state images
+    */
+   prg_timer (&ptimer, START);
+   free_state_images (max_level, frame->color, images, offsets, root_state, 0,
+		      format, wfa);
+   
+   /*
+    *  Crop decoded image if the image size differs.
+    */
+   if (orig_width != width || orig_height != height)
+   {
+      frame->height = orig_height;	
+      frame->width  = orig_width;	
+      if (orig_width != width)		
+      {
+	 color_e   band;		/* current color band */
+	 word_t	  *src, *dst;		/* source and destination pointers */
+	 unsigned  y;			/* current row */
+	 
+	 for (band  = first_band (frame->color);
+	      band <= last_band (frame->color); band++)
+	 {
+	    src = dst = frame->pixels [band];
+	    for (y = orig_height; y; y--)
+	    {
+	       memmove (dst, src, orig_width * sizeof (word_t));
+	       dst += orig_width;
+	       src += width;
+	    }
+	    if (format == FORMAT_4_2_0 && band == Y)
+	    {
+	       orig_width  >>= 1;
+	       orig_height >>= 1;
+	       width       >>= 1;
+	    }
+	 }
+      }
+   }
+   if (dec_timer)
+      dec_timer [2] += prg_timer (&ptimer, STOP);
+
+   return frame;
+}
+
+image_t *
+decode_state (unsigned state, unsigned level, wfa_t *wfa)
+/*
+ *  Decode 'state' image of 'wfa' at given 'level'.
+ *
+ *  Return value.
+ *	pointer to decoded state image
+ *
+ *  Side effects:
+ *	'wfa' states > 'state' are removed.  
+ */
+{
+   word_t  *domains [2];
+   image_t *img = Calloc (1, sizeof (image_t));
+
+   /*
+    *  Generate a new state with a 1.0 transition to 'state'
+    */
+   remove_states (state + 1, wfa);
+   append_edge (state + 1, state, 1.0, 0, wfa);
+   wfa->states = state + 2;
+
+   img->color  = NO;
+   img->width  = width_of_level (level);
+   img->height = height_of_level (level);
+   img->format = FORMAT_4_4_4;
+   img->pixels [GRAY] = decode_range (state + 1, 0, level, domains, wfa);
+
+   /*
+    *  Copy decoded range to the frame buffer
+    */
+   {
+      word_t   *src, *dst;
+      unsigned	y;
+	    
+      src = domains [0];
+      dst = img->pixels [GRAY];
+      for (y = img->height; y; y--)
+      {
+	 memcpy (dst, src, width_of_level (level) * sizeof (word_t));
+	 src += width_of_level (level);
+	 dst += img->width;
+      }
+      Free (domains [0]);
+   }
+
+   return img;
+}
+
+word_t *
+decode_range (unsigned range_state, unsigned range_label, unsigned range_level,
+	      word_t **domain, wfa_t *wfa)
+/*
+ *  Compute 'wfa' image of range (identified by 'state' and 'label')
+ *  at 'range_level (works as function decode_image()).
+ *
+ *  Return value:
+ *	pointer to the pixels in SHORT format
+ *
+ *  Side effects:
+ *	if 'domain' != NULL then also the domain blocks
+ *	of the corresponding range blocks are generated
+ *      and returned in domain[]
+ *	'wfa->level_of_state []' is changed
+ */
+{
+   unsigned   root_state [3];		/* dummy (for alloc_state_images) */
+   image_t   *state_image;		/* regenerated state image */
+   word_t   **images;			/* pointer to array of pointers
+					   to state images */
+   u_word_t  *offsets;			/* pointer to array of state image
+					   offsets */
+   word_t    *range;
+
+   enlarge_image (range_level - (wfa->level_of_state [range_state] - 1),
+		  FORMAT_4_4_4, -1, wfa);
+   root_state [0] = range_state;
+   state_image    = alloc_image (width_of_level (range_level + 1),
+				 height_of_level (range_level + 1),
+				 NO, FORMAT_4_4_4);
+   alloc_state_images (&images, &offsets, state_image, NULL, range_state,
+		       range_level + 1, NO, wfa);
+   compute_state_images (range_level + 1, images, offsets, wfa);
+
+   range = Calloc (size_of_level (range_level), sizeof (word_t));
+
+   if ((range_level & 1) == 0)		/* square image */
+   {
+      memcpy (range,
+	      images [range_state + (range_level + 1) * wfa->states]
+	      + range_label * size_of_level (range_level),
+	      size_of_level (range_level) * sizeof (word_t));
+   }
+   else					/* rectangle */
+   {
+      word_t   *src, *dst;
+      unsigned  y;
+      
+      src = images [range_state + (range_level + 1) * wfa->states]
+	    + range_label * width_of_level (range_level);
+      dst = range;
+      for (y = height_of_level (range_level); y; y--)
+      {
+	 memcpy (dst, src, width_of_level (range_level) * sizeof (word_t));
+	 dst += width_of_level (range_level);
+	 src += width_of_level (range_level + 1);
+      }
+   }
+
+   if (domain != NULL)			/* copy domain images */
+   {
+      int      s;			/* domain state */
+      unsigned edge;			/* counter */
+		
+      if (ischild (s = wfa->tree [range_state][range_label]))
+	 *domain++ = duplicate_state_image (images [s + (range_level)
+						   * wfa->states],
+					    offsets [s + (range_level)
+						    * wfa->states],
+					    range_level);
+      for (edge = 0; isedge (s = wfa->into[range_state][range_label][edge]);
+	   edge++)
+	 *domain++ = duplicate_state_image (images [s + (range_level)
+						   * wfa->states],
+					    offsets [s + (range_level)
+						    * wfa->states],
+					    range_level);
+      *domain = NULL;
+   }
+   
+   free_state_images (range_level + 1, NO, images, offsets, NULL, range_state,
+		      NO, wfa);
+   free_image (state_image);
+   
+   return range;
+}
+
+void
+smooth_image (unsigned sf, const wfa_t *wfa, image_t *image)
+/*
+ *  Smooth 'image' along the partitioning boundaries of the 'wfa'
+ *  with factor 's'.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	pixel values of the 'image' are modified with respect to 's'
+ */
+{
+   int	    is, inegs;			/* integer factors of s and 1 - s*/
+   unsigned state;			
+   unsigned img_width  = image->width;
+   unsigned img_height = image->height;
+   real_t   s 	       = 1.0 - sf / 200.0;
+
+   if (s < 0.5 || s >= 1)		/* value out of range */
+      return;
+
+   is 	 = s * 512 + .5;		/* integer representation of s */
+   inegs = (1 - s) * 512 + .5;		/* integer representation of 1 - s */
+   
+   for (state = wfa->basis_states;
+	state < (wfa->wfainfo->color
+		 ? wfa->tree [wfa->root_state][0]
+		 : wfa->states); state++)
+   {
+      word_t   *bptr   = image->pixels [Y]; /* pointer to right or
+					       lower line */
+      unsigned  level  = wfa->level_of_state[state]; /* level of state image */
+      unsigned  width  = width_of_level (level); /* size of state image */
+      unsigned  height = height_of_level (level); /* size of state image */
+      
+      if (wfa->y [state][1] >= img_height || wfa->x [state][1] >= img_width)
+	 continue;			/* outside visible area */
+	 
+      if (level % 2)			/* horizontal smoothing */
+      {
+	 unsigned  i;			/* line counter */
+	 word_t   *img1;		/* pointer to left or upper line */
+	 word_t   *img2;		/* pointer to right or lower line */
+
+	 img1 = bptr + (wfa->y [state][1] - 1) * img_width
+		+ wfa->x [state][1];
+	 img2 = bptr + wfa->y [state][1] * img_width + wfa->x [state][1];
+	 
+	 for (i = min (width, img_width - wfa->x [state][1]); i;
+	      i--, img1++, img2++)
+	 {
+	    int tmp = *img1;
+	    
+#ifdef HAVE_SIGNED_SHIFT
+	    *img1 = (((is * tmp) >> 10) << 1)
+		    + (((inegs * (int) *img2) >> 10) << 1);
+	    *img2 = (((is * (int) *img2) >> 10) << 1)
+		    + (((inegs * tmp) >> 10) << 1);
+#else /* not HAVE_SIGNED_SHIFT */
+	    *img1 = (((is * tmp) / 1024) * 2)
+		    + (((inegs * (int) *img2) / 1024) * 2);
+	    *img2 = (((is * (int) *img2) / 1024) * 2)
+		    + (((inegs * tmp) / 1024) *2);
+#endif /* not HAVE_SIGNED_SHIFT */
+	 }
+      }
+      else				/* vertical smoothing */
+      {
+	 unsigned  i;			/* line counter */
+	 word_t   *img1;		/* pointer to left or upper line */
+	 word_t   *img2;		/* pointer to right or lower line */
+
+	 img1 = bptr + wfa->y [state][1] * img_width + wfa->x [state][1] - 1;
+	 img2 = bptr + wfa->y [state][1] * img_width + wfa->x [state][1];
+	 
+	 for (i = min (height, img_height - wfa->y [state][1]); i;
+	      i--, img1 += img_width, img2 += img_width)
+	 {
+	    int tmp = *img1;
+	    
+#ifdef HAVE_SIGNED_SHIFT
+	    *img1 = (((is * tmp) >> 10) << 1)
+		    + (((inegs * (int) *img2) >> 10) << 1);
+	    *img2 = (((is * (int) *img2) >> 10) << 1)
+		    + (((inegs * tmp) >> 10) << 1);
+#else /* not HAVE_SIGNED_SHIFT */
+	    *img1 = (((is * tmp) / 1024) * 2)
+		    + (((inegs * (int) *img2) / 1024) * 2);
+	    *img2 = (((is * (int) *img2) / 1024) * 2)
+		    + (((inegs * tmp) / 1024) *2);
+#endif /* not HAVE_SIGNED_SHIFT */
+	 }
+      }
+   }
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static void
+enlarge_image (int enlarge_factor, format_e format, unsigned y_root,
+	       wfa_t *wfa)
+/*
+ *  Enlarge or reduce size of state images by factor 2^'enlarge_factor'.
+ *  Use 4:2:0 subsampling if specified by 'format', else use 4:4:4 format.
+ *  'wfa' root state of the first chroma band is given by 'y_root' + 1.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	coordinates of ranges and motion blocks in the WFA structure 'wfa'
+ *	are modified.
+ */
+{
+   
+   if (enlarge_factor != 0 || format == FORMAT_4_2_0)
+   {
+      unsigned state;
+
+      if (enlarge_factor == 0)
+      {
+	 state 		= y_root + 1;
+	 enlarge_factor = -1;
+      }
+      else
+	 state = wfa->basis_states;
+      
+      for (; state < wfa->states; state++)
+      {
+	 unsigned label, n;
+	 
+	 wfa->level_of_state [state]
+	    = max (wfa->level_of_state [state] + enlarge_factor * 2, 0);
+
+	 for (label = 0; label < MAXLABELS; label++)
+	    if (enlarge_factor > 0)
+	    {
+	       wfa->x [state][label] <<= enlarge_factor;
+	       wfa->y [state][label] <<= enlarge_factor;
+	       for (n = enlarge_factor; n; n--)
+	       {
+		  wfa->mv_tree [state][label].fx *= 2;
+		  wfa->mv_tree [state][label].fy *= 2;
+		  wfa->mv_tree [state][label].bx *= 2;
+		  wfa->mv_tree [state][label].by *= 2;
+	       }
+	    }
+	    else				/* enlarge_factor < 0 */
+	    {
+	       wfa->x [state][label] >>= - enlarge_factor;
+	       wfa->y [state][label] >>= - enlarge_factor;
+	       for (n = - enlarge_factor; n; n--)
+	       {
+		  wfa->mv_tree [state][label].fx /= 2;
+		  wfa->mv_tree [state][label].fy /= 2;
+		  wfa->mv_tree [state][label].bx /= 2;
+		  wfa->mv_tree [state][label].by /= 2;
+	       }
+	    }
+	 if (format == FORMAT_4_2_0 && state == y_root)
+	    enlarge_factor--;
+      }
+   }
+}
+
+static void
+compute_actual_size (unsigned luminance_root,
+		     unsigned *width, unsigned *height, const wfa_t *wfa)
+/*
+ *  Compute actual size of the frame represented by the given 'wfa'.
+ *  (The reconstructed frame may get larger than the original due
+ *   to the bintree partitioning.)
+ *  If 'luminance_root' < MAXSTATES then the size of chroma ranges (4:2:0).
+ *
+ *  Return values:
+ *	actual 'width' and 'height' of the decoded frame.
+ */
+{
+   unsigned x = 0, y = 0;		/* maximum coordinates */
+   unsigned state;			/* counter */
+   
+   for (state = wfa->basis_states; state < wfa->states; state++)
+      if (isedge (wfa->into [state][0][0]) || isedge (wfa->into [state][1][0]))
+      {
+	 unsigned mult = state > luminance_root ? 2 : 1;
+	 
+	 x = max ((wfa->x [state][0]
+		   + width_of_level (wfa->level_of_state [state])) * mult, x);
+	 y = max ((wfa->y [state][0]
+		   + height_of_level (wfa->level_of_state [state])) * mult, y);
+      }
+
+   if (x & 1)				/* ensure that image size is even */
+      x++;
+   if (y & 1)
+      y++;
+   *width  = x;
+   *height = y;
+}
+
+static void
+alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
+		    const unsigned *root_state, unsigned range_state,
+		    unsigned max_level, format_e format, const wfa_t *wfa)
+/*
+ *  Generate list of 'wfa' state images which have to be computed for
+ *  each level to obtain the decoded 'frame'. 'root_state[]' denotes the
+ *  state images of the three color bands.
+ *  'max_level' fives the max. level of a linear combination.
+ *  Memory is allocated for every required state image.
+ *  Use 4:2:0 subsampling or 4:4:4 'format' for color images.
+ *  If 'range_state' > 0 then rather compute image of 'range_state' than 
+ *  image of 'wfa->root_state'.
+ *
+ *  Return values:
+ *	'*images'	Pointer to array of state image pointers
+ *	'*offsets'	Pointer to array of state image offsets.
+ *
+ *  Side effects:
+ *	The arrays given above are filled with useful values.
+ */
+{
+   word_t   **simg;			/* ptr to list of state image ptr's */
+   u_word_t  *offs;			/* ptr to list of offsets */
+   unsigned   level;			/* counter */
+   
+   simg	= Calloc (wfa->states * (max_level + 1), sizeof (word_t *));
+   offs	= Calloc (wfa->states * (max_level + 1), sizeof (u_word_t));
+
+   /*
+    *  Initialize buffers for those state images which are at 'max_level'.
+    */
+   if (range_state > 0)			/* a range is given */
+   {
+      simg [range_state + max_level * wfa->states] = frame->pixels [GRAY];
+      offs [range_state + max_level * wfa->states] = frame->width;
+   }
+   else
+   {
+      unsigned state;
+
+      for (state = wfa->basis_states; state <= root_state [Y]; state++)
+	 if (wfa->level_of_state [state] == max_level)
+	 {
+	    simg [state + max_level * wfa->states]
+	       = (frame->pixels [Y] + wfa->y [state][0] * frame->width
+		  + wfa->x [state][0]);
+	    offs [state + max_level * wfa->states] = frame->width;
+	 }
+      if (frame->color)
+      {
+	 unsigned width = format == FORMAT_4_2_0 ?
+			  (frame->width >> 1) : frame->width;
+	 for (; state < wfa->states; state++)
+	    if (wfa->level_of_state [state] == max_level)
+	    {
+	       simg [state + max_level * wfa->states]
+		  = (frame->pixels [state > root_state [Cb] ? Cr : Cb]
+		     + wfa->y [state][0] * width + wfa->x [state][0]);
+	       offs [state + max_level * wfa->states] = width;
+	    }
+      }
+   }
+   
+   /*
+    *  Generate list of state images which must be computed at each level
+    */
+   for (level = max_level; level > 0; level--)
+   {
+      int      child, domain;
+      unsigned state, label, edge;
+      
+      /*
+       *  Range approximation with child. 
+       */
+      for (state = 1; state < (range_state > 0 ?
+			       range_state + 1 : wfa->states); state++)
+	 if (simg [state + level * wfa->states])
+	    for (label = 0; label < MAXLABELS; label++)
+	       if (ischild (child = wfa->tree[state][label]))
+	       {
+		  if (isedge (wfa->into[state][label][0]))
+		  {
+		     /*
+		      *  Allocate new image block.
+		      */
+		     simg [child + (level - 1) * wfa->states]
+			= Calloc (size_of_level (level - 1), sizeof (word_t));
+		     offs [child + (level - 1) * wfa->states]
+			= width_of_level (level - 1);
+		  }
+		  else
+		  {
+		     /*
+		      *  Use image block and offset of parent.
+		      */
+		     if (level & 1)	/* split vertically */
+		     {
+			simg [child + (level - 1) * wfa->states]
+			   = (simg [state + level * wfa->states]
+			      + label * (height_of_level (level - 1)
+					 * offs [state
+						+ level * wfa->states]));
+		     }
+		     else		/* split horizontally */
+		     {
+			simg [child + (level - 1) * wfa->states]
+			   = (simg [state + level * wfa->states]
+			      + label * width_of_level (level - 1));
+		     }
+		     offs [child + (level - 1) * wfa->states]
+			= offs [state + level * wfa->states];
+		  }
+	       }
+      /*
+       *  Range approximation with linear combination 
+       */
+      for (state = 1; state < (range_state > 0 ?
+			       range_state + 1 : wfa->states); state++)
+	 if (simg [state + level * wfa->states])
+	    for (label = 0; label < MAXLABELS; label++)
+	       for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
+		    edge++)
+	       {
+		  if (domain > 0	/* don't allocate memory for state 0 */
+		      && !simg [domain + (level - 1) * wfa->states])
+		  {
+		     simg [domain + (level - 1) * wfa->states]
+			= Calloc (size_of_level (level - 1), sizeof (word_t));
+		     offs [domain + (level - 1) * wfa->states]
+			= width_of_level (level - 1);
+		  }
+	       }
+      
+   }
+
+   *images  = simg;
+   *offsets = offs;
+}
+
+static void
+free_state_images (unsigned max_level, bool_t color, word_t **state_image,
+		   u_word_t *offset, const unsigned *root_state,
+		   unsigned range_state, format_e format, const wfa_t *wfa)
+/*
+ *  Free memory of state images.
+ *  For more details refer to the inverse function 'alloc_state_images()'.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	arrays 'state_image' and 'offset' are discarded.
+ */
+{
+   word_t   marker;			/* ptr is required as a marker */
+   unsigned level;
+
+   if (range_state > 0)
+   {
+      state_image [range_state + max_level * wfa->states] = &marker;
+   }
+   else
+   {
+      unsigned state;
+      
+      /*
+       *  Initialize state image array with states at 'max_level'
+       */
+      for (state = wfa->basis_states; state <= root_state [Y]; state++)
+	 if (wfa->level_of_state [state] == max_level)
+	    state_image [state + max_level * wfa->states] = &marker;
+
+      if (color)
+      {
+	 if (format == FORMAT_4_2_0)
+	    level = max_level - 2;
+	 else
+	    level = max_level;
+      
+	 for (; state < wfa->states; state++)
+	    if (wfa->level_of_state [state] == level)
+	       state_image [state + level * wfa->states] = &marker;
+      }
+   }
+   
+   for (level = max_level; level > 0; level--)
+   {
+      int      domain, child;
+      unsigned state, label, edge;
+      /*
+       *  Range approximation with child. 
+       */
+      for (state = 1; state < (range_state > 0 ?
+			       range_state + 1 : wfa->states); state++)
+	 if (state_image [state + level * wfa->states])
+	    for (label = 0; label < MAXLABELS; label++)
+	       if (ischild (child = wfa->tree[state][label]))
+	       {
+		  if (isedge (wfa->into[state][label][0])
+		      && (state_image [child + (level - 1) * wfa->states]
+			  != &marker))
+		     Free (state_image [child + (level - 1) * wfa->states]);
+		  state_image [child + (level - 1) * wfa->states] = &marker;
+	       }
+      /*
+       *  Range approximation with linear combination 
+       */
+      for (state = 1; state < (range_state > 0 ?
+			       range_state + 1 : wfa->states);
+	   state++)
+	 if (state_image [state + level * wfa->states])
+	    for (label = 0; label < MAXLABELS; label++)
+	       for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
+		    edge++)
+		  if (domain > 0	
+		      && (state_image [domain + (level - 1) * wfa->states]
+			  != NULL)
+		      && (state_image [domain + (level - 1) * wfa->states]
+			  != &marker))
+		  {
+		     Free (state_image [domain + (level - 1) * wfa->states]);
+		     state_image [domain + (level - 1) * wfa->states]
+			= &marker;
+		  }
+   }
+   Free (state_image);
+   Free (offset);
+}
+
+static void
+compute_state_images (unsigned max_level, word_t **simg,
+		      const u_word_t *offset, const wfa_t *wfa)
+/*
+ *  Compute all state images of the 'wfa' at level {1, ... , 'max_level'}
+ *  which are marked in the array 'simg' (offsets of state images
+ *  are given by 'offset').
+ *
+ *  Warning: Several optimizations are used in this function making 
+ *  it difficult to understand.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	state images (given by pointers in the array 'state_image')
+ *	are computed.
+ */
+{
+   unsigned level, state;
+     
+   /*
+    *  Copy one-pixel images in case state_image pointer != &final distr.
+    */
+
+   for (state = 1; state < wfa->states; state++)
+      if (simg [state] != NULL)		/* compute image at level 0 */
+	 *simg [state] = (int) (wfa->final_distribution[state] * 8 + .5) * 2;
+
+   /*
+    *  Compute images of states
+    *  Integer arithmetics are used rather than floating point operations.
+    *  'weight' gives the weight in integer notation
+    *  'src', 'dst', and 'idst' are pointers to the source and
+    *  destination pixels (short or integer format), respectively.
+    *  Short format : one operation per register (16 bit mode). 
+    *  Integer format : two operations per register (32 bit mode). 
+    *  'src_offset', 'dst_offset', and 'dst_offset' give the number of
+    *  pixels which have to be omitted when jumping to the next image row.
+    */
+   for (level = 1; level <= max_level; level++) 
+   {
+      unsigned label;
+      unsigned width  = width_of_level (level - 1);
+      unsigned height = height_of_level (level - 1);
+      
+      for (state = 1; state < wfa->states; state++)
+	 if (simg [state + level * wfa->states] != NULL)
+	    for (label = 0; label < MAXLABELS; label++)
+	       if (isedge (wfa->into [state][label][0]))
+	       {
+		  unsigned  edge;
+		  int       domain;
+		  word_t   *range;	/* address of current range */
+		  bool_t    prediction_used; /* ND prediction found ? */
+
+		  /*
+		   *  Compute address of range image
+		   */
+		  if (level & 1)	/* split vertically */
+		  {
+		     range = simg [state + level * wfa->states]
+			     + label * (height_of_level (level - 1)
+					* offset [state
+						 + level * wfa->states]);
+		  }
+		  else			/* split horizontally */
+		  {
+		     range = simg [state + level * wfa->states]
+			     + label * width_of_level (level - 1);
+		  }
+
+		  /*
+		   *  Generate the state images by adding the corresponding 
+		   *  weighted state images:
+		   *  subimage [label] =
+		   *       weight_1 * image_1 + ... + weight_n * image_n
+		   */
+		  if (!ischild (domain = wfa->tree[state][label]))
+		     prediction_used = NO;
+		  else
+		  {
+		     unsigned  y;
+		     word_t   *src;
+		     word_t   *dst;
+		     unsigned  src_offset;
+		     unsigned  dst_offset;
+
+		     prediction_used = YES;
+		     /*
+		      *  Copy child image
+		      */
+		     src        = simg [domain + (level - 1) * wfa->states];
+		     src_offset = offset [domain + (level - 1) * wfa->states] ;
+		     dst        = range;
+		     dst_offset	= offset [state + level * wfa->states];
+		     for (y = height; y; y--)
+		     {
+			memcpy (dst, src, width * sizeof (word_t));
+			src += src_offset;
+			dst += dst_offset;
+		     }
+		  }
+
+		  if (!prediction_used
+		      && isedge (domain = wfa->into[state][label][0]))
+		  {
+		     /*
+		      *  If prediction is not used then the range is
+		      *  filled with the first domain. No addition is needed.
+		      */
+		     edge = 0;
+		     if (domain != 0)
+		     {
+			int	  weight;
+			word_t 	 *src;
+			unsigned  src_offset;
+
+			src        = simg [domain + ((level - 1)
+						     * wfa->states)];
+			src_offset = offset [domain + ((level - 1)
+						       * wfa->states)] - width;
+			weight     = wfa->int_weight [state][label][edge];
+			
+			if (width == 1)	/* can't add two-pixels in a row */
+			{
+			   word_t   *dst;
+			   unsigned  dst_offset;
+			   
+			   dst        = range;
+			   dst_offset = offset [state + level * wfa->states]
+					- width;
+#ifdef HAVE_SIGNED_SHIFT
+			   *dst++ = ((weight * (int) *src++) >> 10) << 1;
+#else 					/* not HAVE_SIGNED_SHIFT */
+			   *dst++ = ((weight * (int) *src++) / 1024) * 2;
+#endif /* not HAVE_SIGNED_SHIFT */
+			   if (height == 2) 
+			   {
+			      src += src_offset;
+			      dst += dst_offset;
+#ifdef HAVE_SIGNED_SHIFT
+			      *dst++ = ((weight * (int) *src++) >> 10) << 1;
+#else /* not HAVE_SIGNED_SHIFT */
+			      *dst++ = ((weight * (int) *src++) / 1024) * 2;
+#endif /* not HAVE_SIGNED_SHIFT */
+			   }
+			}
+			else
+			{
+			   unsigned  y;
+			   int 	    *idst;
+			   unsigned  idst_offset;
+			   
+			   idst        = (int *) range;
+			   idst_offset = (offset [state + level * wfa->states]
+					  - width) / 2;
+			   for (y = height; y; y--)
+			   {
+			      int *comp_dst = idst + (width >> 1);
+			      
+			      for (; idst != comp_dst; )
+ 			      {
+				 int tmp; /* temp. value of adjacent pixels */
+#ifdef HAVE_SIGNED_SHIFT
+#	ifndef WORDS_BIGENDIAN
+                                 tmp = (((weight * (int) src [1]) >> 10) << 17)
+				       | (((weight * (int) src [0]) >> 9)
+					  & 0xfffe);
+#	else /* not WORDS_BIGENDIAN */
+                                 tmp = (((weight * (int) src [0]) >> 10) << 17)
+				       | (((weight * (int) src [1]) >> 9)
+					  & 0xfffe);
+#	endif /* not WORDS_BIGENDIAN */
+#else /* not HAVE_SIGNED_SHIFT */
+#	ifndef WORDS_BIGENDIAN
+                                 tmp = (((weight * (int) src [1]) / 1024)
+					* 131072)
+				       | (((weight * (int) src [0])/ 512)
+					  & 0xfffe);
+#	else /* not WORDS_BIGENDIAN */
+                                 tmp = (((weight * (int) src [0]) / 1024)
+					* 131072)
+				       | (((weight * (int) src [1]) / 512)
+					  & 0xfffe);
+#	endif /* not WORDS_BIGENDIAN */
+#endif /* not HAVE_SIGNED_SHIFT */
+				 src    +=  2;
+				 *idst++ = tmp & 0xfffefffe;
+			      }
+			      src  += src_offset;
+			      idst += idst_offset;
+			   }
+			}
+		     }
+		     else
+		     {
+			int weight = (int) (wfa->weight[state][label][edge]
+					    * wfa->final_distribution[0]
+					    * 8 + .5) * 2;
+			/*
+			 *  Range needs domain 0
+			 *  (the constant function f(x, y) = 1),
+			 *  hence a faster algorithm is used.
+			 */
+			if (width == 1)	/* can't add two-pixels in a row */
+			{
+			   word_t   *dst;
+			   unsigned  dst_offset;
+			   
+			   dst        = range;
+			   dst_offset = offset [state + level * wfa->states]
+					- width;
+			   
+			   *dst++ = weight;
+			   if (height == 2)
+			   {
+			      dst += dst_offset;
+			      *dst++ = weight;
+			   }
+			}
+			else
+			{
+			   unsigned  x, y;
+			   int 	    *idst;
+			   unsigned  idst_offset;
+			   
+			   weight      = (weight * 65536) | (weight & 0xffff);
+			   idst	       = (int *) range;
+			   idst_offset = offset [state + level * wfa->states]
+					 / 2;
+			   for (x = width >> 1; x; x--)
+			      *idst++ = weight & 0xfffefffe;
+			   idst += (offset [state + level * wfa->states]
+				    - width) / 2;
+
+			   for (y = height - 1; y; y--)
+			   {
+			      memcpy (idst, idst - idst_offset,
+				      width * sizeof (word_t));
+			      idst += idst_offset;
+			   }
+			}
+		     }
+		     edge = 1;
+		  }
+		  else
+		     edge = 0;
+		  
+		  /*
+		   *  Add remaining weighted domain images to current range
+		   */
+		  for (; isedge (domain = wfa->into[state][label][edge]);
+		       edge++)
+		  {
+		     if (domain != 0)
+		     {
+			word_t 	 *src;
+			unsigned  src_offset;
+			int	  weight;
+
+			src        = simg [domain + (level - 1) * wfa->states];
+			src_offset = offset [domain + ((level - 1)
+						       * wfa->states)] - width;
+			weight     = wfa->int_weight [state][label][edge];
+			
+			if (width == 1)	/* can't add two-pixels in a row */
+			{
+			   word_t   *dst;
+			   unsigned  dst_offset;
+			   
+			   dst        = range;
+			   dst_offset = offset [state + level * wfa->states]
+					- width;
+
+#ifdef HAVE_SIGNED_SHIFT
+			   *dst++ += ((weight * (int) *src++) >> 10) << 1;
+#else /* not HAVE_SIGNED_SHIFT */
+			   *dst++ += ((weight * (int) *src++) / 1024) * 2;
+#endif /* not HAVE_SIGNED_SHIFT */
+			   if (height == 2) 
+			   {
+			      src += src_offset;
+			      dst += dst_offset;
+#ifdef HAVE_SIGNED_SHIFT
+			      *dst++ += ((weight * (int) *src++) >> 10) << 1;
+#else /* not HAVE_SIGNED_SHIFT */
+			      *dst++ += ((weight * (int) *src++) / 1024) * 2;
+#endif /* not HAVE_SIGNED_SHIFT */
+			   }
+			}
+			else
+			{
+			   int 	    *idst;
+			   unsigned  idst_offset;
+			   unsigned  y;
+			   
+			   idst        = (int *) range;
+			   idst_offset = (offset [state + level * wfa->states]
+					  - width) / 2;
+			   
+			   for (y = height; y; y--)
+			   {
+			      int *comp_dst = idst + (width >> 1);
+			      
+			      for (; idst != comp_dst;)
+ 			      {
+				 int tmp; /* temp. value of adjacent pixels */
+#ifdef HAVE_SIGNED_SHIFT
+#	ifndef WORDS_BIGENDIAN
+                                 tmp = (((weight * (int) src [1]) >> 10) << 17)
+				       | (((weight * (int) src [0]) >> 9)
+					  & 0xfffe);
+#	else /* not WORDS_BIGENDIAN */
+                                 tmp = (((weight * (int)src [0]) >> 10) << 17)
+				       | (((weight * (int)src [1]) >> 9)
+					  & 0xfffe);
+#	endif /* not WORDS_BIGENDIAN */
+#else /* not HAVE_SIGNED_SHIFT */
+#	ifndef WORDS_BIGENDIAN
+                                 tmp = (((weight * (int) src [1]) / 1024)
+					* 131072)
+				       | (((weight * (int) src [0])/ 512)
+					  & 0xfffe);
+#	else /* not WORDS_BIGENDIAN */
+                                 tmp = (((weight * (int) src [0]) / 1024)
+					* 131072)
+				       | (((weight * (int) src [1])/ 512)
+					  & 0xfffe);
+#	endif /* not WORDS_BIGENDIAN */
+#endif /* not HAVE_SIGNED_SHIFT */
+				 src +=  2;
+				 *idst = (*idst + tmp) & 0xfffefffe;
+				 idst++;
+			      }
+			      src  += src_offset;
+			      idst += idst_offset;
+			   }
+			}
+		     }
+		     else
+		     {
+			int weight = (int) (wfa->weight[state][label][edge]
+					    * wfa->final_distribution[0]
+					    * 8 + .5) * 2;
+			/*
+			 *  Range needs domain 0
+			 *  (the constant function f(x, y) = 1),
+			 *  hence a faster algorithm is used.
+			 */
+			if (width == 1)	/* can't add two-pixels in a row */
+			{
+			   word_t   *dst;
+			   unsigned  dst_offset;
+			   
+			   dst        = range;
+			   dst_offset = offset [state + level * wfa->states]
+					- width;
+			   
+			   *dst++ += weight;
+			   if (height == 2)
+			   {
+			      dst    += dst_offset;
+			      *dst++ += weight;
+			   }
+			}
+			else
+			{
+			   int 	    *idst;
+			   unsigned  idst_offset;
+			   unsigned  y;
+			   
+			   weight      = (weight * 65536) | (weight & 0xffff);
+			   idst	       = (int *) range;
+			   idst_offset = (offset [state + level * wfa->states]
+					  - width) /2;
+			   
+			   for (y = height; y; y--)
+			   {
+			      int *comp_dst = idst + (width >> 1);
+			      
+			      for (; idst != comp_dst; )
+			      {
+				 *idst = (*idst + weight) & 0xfffefffe;
+                                 idst++;
+			      }
+			      idst += idst_offset;
+			   }
+			}
+		     }
+		  }
+	       } 
+   }
+}
+
+static word_t *
+duplicate_state_image (const word_t *domain, unsigned offset, unsigned level)
+/*
+ *  Allocate new memory block 'pixels' and copy pixel values of 'domain' 
+ *  (size and pixel offset are given by 'level' and 'offset')
+ *  to the lock 'pixels'.
+ *
+ *  Return value:
+ *	pointer to the new domain block
+ */
+{
+   word_t *dst, *pixels;
+   int	   y, n;
+
+   dst = pixels = Calloc (size_of_level (level), sizeof (word_t));
+
+   if (domain)
+      for (y = height_of_level (level); y; y--)
+      {
+	 memcpy (dst, domain, width_of_level (level) * sizeof (word_t));
+	 dst    += width_of_level (level);
+	 domain += offset;
+      }
+   else					/* state 0 */
+      for (n = size_of_level (level); n; n--)
+	 *dst++ = (int) (128 * 8 + .5) * 2;
+
+   return pixels;
+}
diff --git a/converter/other/fiasco/codec/decoder.h b/converter/other/fiasco/codec/decoder.h
new file mode 100644
index 00000000..8cd211e0
--- /dev/null
+++ b/converter/other/fiasco/codec/decoder.h
@@ -0,0 +1,70 @@
+/*
+ *  decode.h
+ *		
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/10/22 10:44:48 $
+ *  $Author: hafner $
+ *  $Revision: 5.3 $
+ *  $State: Exp $
+ */
+
+#ifndef _DECODE_H
+#define _DECODE_H
+
+#include "types.h"
+#include "image.h"
+#include "wfa.h"
+
+typedef struct video
+{
+   unsigned  future_display;		/* number of a future frame */
+   unsigned  display;			/* current display number */
+   image_t  *frame;			/* current frame */
+   image_t  *sframe;			/* current smoothed frame */
+   image_t  *future;			/* future reference */
+   image_t  *sfuture;			/* future (smmothed) reference */
+   image_t  *past ;			/* past reference */
+   wfa_t    *wfa;			/* current wfa */
+   wfa_t    *wfa_future;		/* future wfa */
+   wfa_t    *wfa_past;			/* past wfa */
+} video_t;
+
+typedef struct dectimer
+{
+   unsigned int	input [3];
+   unsigned int	preprocessing [3];
+   unsigned int	decoder [3];
+   unsigned int	cleanup [3];
+   unsigned int	motion [3];
+   unsigned int	smooth [3];
+   unsigned int	display [3];
+   unsigned int	frames [3];
+} dectimer_t;
+
+image_t *
+get_next_frame (bool_t store_wfa, int enlarge_factor,
+		int smoothing, const char *reference_frame,
+		format_e format, video_t *video, dectimer_t *timer,
+		wfa_t *orig_wfa, bitfile_t *input);
+image_t *
+decode_image (unsigned orig_width, unsigned orig_height, format_e format,
+	      unsigned *dec_timer, const wfa_t *wfa);
+word_t *
+decode_range (unsigned range_state, unsigned range_label, unsigned range_level,
+	      word_t **domain, wfa_t *wfa);
+image_t *
+decode_state (unsigned state, unsigned level, wfa_t *wfa);
+void
+smooth_image (unsigned sf, const wfa_t *wfa, image_t *image);
+video_t *
+alloc_video (bool_t store_wfa);
+void
+free_video (video_t *video);
+
+#endif /* not _DECODE_H */
diff --git a/converter/other/fiasco/codec/dfiasco.c b/converter/other/fiasco/codec/dfiasco.c
new file mode 100644
index 00000000..1cdfc672
--- /dev/null
+++ b/converter/other/fiasco/codec/dfiasco.c
@@ -0,0 +1,398 @@
+/*
+ *  dfiasco.c:		Decoder public interface
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+ 
+/*
+ *  $Date: 2000/10/28 17:39:30 $
+ *  $Author: hafner $
+ *  $Revision: 5.7 $
+ *  $State: Exp $
+ */
+
+#include <string.h>
+
+#include "config.h"
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "dfiasco.h"
+#include "wfa.h"
+#include "read.h"
+#include "misc.h"
+#include "bit-io.h"
+#include "decoder.h"
+#include "options.h"
+#include "wfalib.h"
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static dfiasco_t *
+cast_dfiasco (fiasco_decoder_t *dfiasco);
+static void
+free_dfiasco (dfiasco_t *dfiasco);
+static dfiasco_t *
+alloc_dfiasco (wfa_t *wfa, video_t *video, bitfile_t *input,
+	       int enlarge_factor, int smoothing, format_e image_format);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+fiasco_decoder_t *
+fiasco_decoder_new (const char *filename, const fiasco_d_options_t *options)
+{
+   try
+   {
+      bitfile_t        	 *input;	/* pointer to WFA FIASCO stream */
+      wfa_t            	 *wfa;		/* wfa structure */
+      video_t          	 *video;	/* information about decoder state */
+      const d_options_t  *dop;		/* decoder additional options */
+      dfiasco_t	       	 *dfiasco;	/* decoder internal state */
+      fiasco_decoder_t 	 *decoder;	/* public interface to decoder */
+      fiasco_d_options_t *default_options = NULL;
+
+      if (options)
+      {
+	 dop = cast_d_options ((fiasco_d_options_t *) options);
+	 if (!dop)
+	    return NULL;
+      }
+      else
+      {
+	 default_options = fiasco_d_options_new ();
+	 dop 		 = cast_d_options (default_options);
+      }
+      
+      wfa   = alloc_wfa (NO);
+      video = alloc_video (NO);
+      input = open_wfa (filename, wfa->wfainfo);
+      read_basis (wfa->wfainfo->basis_name, wfa);
+
+      decoder 	       	   = Calloc (1, sizeof (fiasco_decoder_t));
+      decoder->delete  	   = fiasco_decoder_delete;
+      decoder->write_frame = fiasco_decoder_write_frame;
+      decoder->get_frame   = fiasco_decoder_get_frame;
+      decoder->get_length  = fiasco_decoder_get_length;
+      decoder->get_rate    = fiasco_decoder_get_rate;
+      decoder->get_width   = fiasco_decoder_get_width;
+      decoder->get_height  = fiasco_decoder_get_height;
+      decoder->get_title   = fiasco_decoder_get_title;
+      decoder->get_comment = fiasco_decoder_get_comment;
+      decoder->is_color    = fiasco_decoder_is_color;
+
+      decoder->private = dfiasco
+		       = alloc_dfiasco (wfa, video, input,
+					dop->magnification,
+					dop->smoothing,
+					dop->image_format);
+   
+      if (default_options)
+	 fiasco_d_options_delete (default_options);
+      if (dfiasco->enlarge_factor >= 0)
+      {
+	 int 	       n;
+	 unsigned long pixels = wfa->wfainfo->width * wfa->wfainfo->height;
+
+	 for (n = 1; n <= (int) dfiasco->enlarge_factor; n++)
+	 {
+	    if (pixels << (n << 1) > 2048 * 2048)
+	    {
+	       set_error (_("Magnifaction factor `%d' is too large. "
+			    "Maximium value is %d."),
+			  dfiasco->enlarge_factor, max (0, n - 1));
+	       fiasco_decoder_delete (decoder);
+	       return NULL;
+	    }
+	 }
+      }
+      else
+      {
+	 int n;
+
+	 for (n = 0; n <= (int) - dfiasco->enlarge_factor; n++)
+	 {
+	    if (wfa->wfainfo->width >> n < 32
+		|| wfa->wfainfo->height >> n < 32)
+	    {
+	       set_error (_("Magnifaction factor `%d' is too small. "
+			    "Minimum value is %d."),
+			  dfiasco->enlarge_factor, - max (0, n - 1));
+	       fiasco_decoder_delete (decoder);
+	       return NULL;
+	    }
+	 }
+      }
+      return (fiasco_decoder_t *) decoder;
+   }
+   catch
+   {
+      return NULL;
+   }
+}
+
+int
+fiasco_decoder_write_frame (fiasco_decoder_t *decoder,
+			    const char *filename)
+{
+   dfiasco_t *dfiasco = cast_dfiasco (decoder);
+   
+   if (!dfiasco)
+      return 0;
+   else
+   {
+      try
+      {
+	 image_t *frame = get_next_frame (NO, dfiasco->enlarge_factor,
+					  dfiasco->smoothing, NULL,
+					  FORMAT_4_4_4, dfiasco->video, NULL,
+					  dfiasco->wfa, dfiasco->input);
+	 write_image (filename, frame);
+      }
+      catch
+      {
+	 return 0;
+      }
+      return 1;
+   }
+}
+
+fiasco_image_t *
+fiasco_decoder_get_frame (fiasco_decoder_t *decoder)
+{
+   dfiasco_t *dfiasco = cast_dfiasco (decoder);
+   
+   if (!dfiasco)
+      return NULL;
+   else
+   {
+      try
+      {
+	 fiasco_image_t *image = Calloc (1, sizeof (fiasco_image_t));
+	 image_t 	*frame = get_next_frame (NO, dfiasco->enlarge_factor,
+						 dfiasco->smoothing, NULL,
+						 dfiasco->image_format,
+						 dfiasco->video, NULL,
+						 dfiasco->wfa, dfiasco->input);
+
+	 frame->reference_count++;	/* for motion compensation */
+	 image->private    = frame;
+	 image->delete     = fiasco_image_delete;
+	 image->get_width  = fiasco_image_get_width;
+	 image->get_height = fiasco_image_get_height;
+	 image->is_color   = fiasco_image_is_color;
+	 
+	 return image;
+      }
+      catch
+      {
+	 return NULL;
+      }
+   }
+}
+
+unsigned
+fiasco_decoder_get_length (fiasco_decoder_t *decoder)
+{
+   dfiasco_t *dfiasco = cast_dfiasco (decoder);
+   
+   if (!dfiasco)
+      return 0;
+   else
+      return dfiasco->wfa->wfainfo->frames;
+}
+
+unsigned
+fiasco_decoder_get_rate (fiasco_decoder_t *decoder)
+{
+   dfiasco_t *dfiasco = cast_dfiasco (decoder);
+   
+   if (!dfiasco)
+      return 0;
+   else
+      return dfiasco->wfa->wfainfo->fps;
+}
+
+unsigned
+fiasco_decoder_get_width (fiasco_decoder_t *decoder)
+{
+   dfiasco_t *dfiasco = cast_dfiasco (decoder);
+
+   if (!dfiasco)
+      return 0;
+   else
+   {
+      unsigned width;
+      
+      if (dfiasco->enlarge_factor >= 0)
+	 width = dfiasco->wfa->wfainfo->width << dfiasco->enlarge_factor;
+      else
+	 width = dfiasco->wfa->wfainfo->width >> - dfiasco->enlarge_factor;
+      
+      return width & 1 ? width + 1 : width;
+   }
+}
+
+unsigned
+fiasco_decoder_get_height (fiasco_decoder_t *decoder)
+{
+   dfiasco_t *dfiasco = cast_dfiasco (decoder);
+
+   if (!dfiasco)
+      return 0;
+   else
+   {
+      unsigned height;
+      
+      if (dfiasco->enlarge_factor >= 0)
+	 height = dfiasco->wfa->wfainfo->height << dfiasco->enlarge_factor;
+      else
+	 height = dfiasco->wfa->wfainfo->height >> - dfiasco->enlarge_factor;
+
+      return height & 1 ? height + 1 : height;
+   }
+}
+
+const char *
+fiasco_decoder_get_title (fiasco_decoder_t *decoder)
+{
+   dfiasco_t *dfiasco = cast_dfiasco (decoder);
+
+   if (!dfiasco)
+      return NULL;
+   else
+      return dfiasco->wfa->wfainfo->title;
+}
+
+const char *
+fiasco_decoder_get_comment (fiasco_decoder_t *decoder)
+{
+   dfiasco_t *dfiasco = cast_dfiasco (decoder);
+
+   if (!dfiasco)
+      return NULL;
+   else
+      return dfiasco->wfa->wfainfo->comment;
+}
+
+int
+fiasco_decoder_is_color (fiasco_decoder_t *decoder)
+{
+   dfiasco_t *dfiasco = cast_dfiasco (decoder);
+
+   if (!dfiasco)
+      return 0;
+   else
+      return dfiasco->wfa->wfainfo->color;
+}
+
+int
+fiasco_decoder_delete (fiasco_decoder_t *decoder)
+{
+   dfiasco_t *dfiasco = cast_dfiasco (decoder);
+   
+   if (!dfiasco)
+      return 1;
+   
+   try
+   {
+      free_wfa (dfiasco->wfa);
+      free_video (dfiasco->video);
+      close_bitfile (dfiasco->input);
+      strcpy (dfiasco->id, " ");
+      free_dfiasco(dfiasco);
+      Free (decoder);
+   }
+   catch
+   {
+      return 0;
+   }
+
+   return 1;
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static dfiasco_t *
+alloc_dfiasco (wfa_t *wfa, video_t *video, bitfile_t *input,
+	       int enlarge_factor, int smoothing, format_e image_format)
+/*
+ *  FIASCO decoder constructor:
+ *  Initialize decoder structure.
+ *
+ *  Return value:
+ *	pointer to the new decoder structure
+ */
+{
+   dfiasco_t *dfiasco = Calloc (1, sizeof (dfiasco_t));
+
+   strcpy (dfiasco->id, "DFIASCO");
+   
+   dfiasco->wfa 	   = wfa;
+   dfiasco->video 	   = video;
+   dfiasco->input 	   = input;
+   dfiasco->enlarge_factor = enlarge_factor;
+   dfiasco->smoothing  	   = smoothing;
+   dfiasco->image_format   = image_format;
+   
+   return dfiasco;
+}
+
+static void
+free_dfiasco (dfiasco_t *dfiasco)
+/*
+ *  FIASCO decoder destructor:
+ *  Free memory of given 'decoder' struct.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'video' struct is discarded.
+ */
+{
+   Free (dfiasco);
+}
+
+static dfiasco_t *
+cast_dfiasco (fiasco_decoder_t *dfiasco)
+/*
+ *  Cast pointer `dfiasco' to type dfiasco_t.
+ *  Check whether `dfiasco' is a valid object of type dfiasco_t.
+ *
+ *  Return value:
+ *	pointer to dfiasco_t struct on success
+ *      NULL otherwise
+ */
+{
+   dfiasco_t *this = (dfiasco_t *) dfiasco->private;
+   if (this)
+   {
+      if (!streq (this->id, "DFIASCO"))
+      {
+	 set_error (_("Parameter `dfiasco' doesn't match required type."));
+	 return NULL;
+      }
+   }
+   else
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "dfiasco");
+   }
+
+   return this;
+}
diff --git a/converter/other/fiasco/codec/dfiasco.h b/converter/other/fiasco/codec/dfiasco.h
new file mode 100644
index 00000000..bcc3c7f9
--- /dev/null
+++ b/converter/other/fiasco/codec/dfiasco.h
@@ -0,0 +1,38 @@
+/*
+ *  dfiasco.h
+ *		
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/07/15 18:00:53 $
+ *  $Author: hafner $
+ *  $Revision: 5.2 $
+ *  $State: Exp $
+ */
+
+#ifndef _DFIASCO_H
+#define _DFIASCO_H
+
+#include "types.h"
+#include "bit-io.h"
+#include "decoder.h"
+#include "image.h"
+#include "wfa.h"
+
+typedef struct dfiasco
+{
+   char       id [8];
+   wfa_t     *wfa;
+   video_t   *video;
+   bitfile_t *input;
+   int	      enlarge_factor;
+   int        smoothing;
+   format_e   image_format;
+} dfiasco_t;
+
+#endif /* not _DFIASCO_H */
+
diff --git a/converter/other/fiasco/codec/domain-pool.c b/converter/other/fiasco/codec/domain-pool.c
new file mode 100644
index 00000000..09f854a6
--- /dev/null
+++ b/converter/other/fiasco/codec/domain-pool.c
@@ -0,0 +1,986 @@
+/*
+ *  domain-pool.c:  Domain pool management (probability model)
+ *
+ *  Written by:     Ullrich Hafner
+ *      
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#include <math.h>
+
+#if STDC_HEADERS
+#   include <stdlib.h>
+#endif /* not STDC_HEADERS */
+
+#if HAVE_STRING_H
+#   include <string.h>
+#else /* not HAVE_STRING_H */
+#   include <strings.h>
+#endif /* not HAVE_STRING_H */
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "misc.h"
+#include "cwfa.h"
+#include "wfalib.h"
+#include "domain-pool.h"
+
+/*
+ *  Domain pool model interface:
+ *  Implementing the domain pool model interface requires the
+ *  following steps: 
+ *  - Add a constructor that initializes the domain_pool_t structure
+ *  - Allocate new model with default_alloc() 
+ *  - Fill the dp_array_t domain_pools array with constructor and name
+ *  - Write code for methods bits() and generate()
+ *  - Either use default functions for remaining methods or override them
+ *  The new model is automatically registered at the command line.
+ */
+
+/*****************************************************************************
+                                          
+                  local variables
+                  
+*****************************************************************************/
+
+static real_t *matrix_0 = NULL;
+static real_t *matrix_1 = NULL;
+
+/*****************************************************************************
+                        non-adaptive domain pool
+*****************************************************************************/
+
+static void
+qac_chroma (unsigned max_domains, const wfa_t *wfa, void *model);
+static bool_t
+qac_append (unsigned new_state, unsigned level, const wfa_t *wfa, void *model);
+static void
+qac_update (const word_t *domains, const word_t *used_domains,
+            unsigned level, int y_state, const wfa_t *wfa, void *model);
+static real_t
+qac_bits (const word_t *domains, const word_t *used_domains,
+          unsigned level, int y_state, const wfa_t *wfa, const void *model);
+static word_t *
+qac_generate (unsigned level, int y_state, const wfa_t *wfa,
+              const void *model);
+static void *
+qac_model_duplicate (const void *src);
+static void
+qac_model_free (void *model);
+static void *
+qac_model_alloc (unsigned max_domains);
+static domain_pool_t *
+alloc_qac_domain_pool (unsigned max_domains, unsigned max_edges,
+                       const wfa_t *wfa);
+
+/*****************************************************************************
+              run length encoding pool
+*****************************************************************************/
+
+static domain_pool_t *
+alloc_rle_no_chroma_domain_pool (unsigned max_domains, unsigned max_edges,
+                                 const wfa_t *wfa);
+static void
+rle_chroma (unsigned max_domains, const wfa_t *wfa, void *model);
+static bool_t
+rle_append (unsigned new_state, unsigned level, const wfa_t *wfa, void *model);
+static void
+rle_update (const word_t *domains, const word_t *used_domains,
+            unsigned level, int y_state, const wfa_t *wfa, void *model);
+static real_t
+rle_bits (const word_t *domains, const word_t *used_domains,
+          unsigned level, int y_state, const wfa_t *wfa, const void *model);
+static word_t *
+rle_generate (unsigned level, int y_state, const wfa_t *wfa,
+              const void *model);
+static void *
+rle_model_duplicate (const void *src);
+static void
+rle_model_free (void *model);
+static void *
+rle_model_alloc (unsigned max_domains);
+static domain_pool_t *
+alloc_rle_domain_pool (unsigned max_domains, unsigned max_edges,
+                       const wfa_t *wfa);
+
+/*****************************************************************************
+              const domain pool
+*****************************************************************************/
+
+static real_t
+const_bits (const word_t *domains, const word_t *used_domains,
+            unsigned level, int y_state, const wfa_t *wfa, const void *model);
+static word_t *
+const_generate (unsigned level, int y_state, const wfa_t *wfa,
+                const void *model);
+static domain_pool_t *
+alloc_const_domain_pool (unsigned max_domains, unsigned max_edges,
+                         const wfa_t *wfa);
+
+/*****************************************************************************
+              basis domain pool
+*****************************************************************************/
+
+static domain_pool_t *
+alloc_basis_domain_pool (unsigned max_domains, unsigned max_edges,
+                         const wfa_t *wfa);
+
+/*****************************************************************************
+              uniform distribution pool
+*****************************************************************************/
+
+static real_t
+uniform_bits (const word_t *domains, const word_t *used_domains,
+              unsigned level, int y_state, const wfa_t *wfa,
+              const void *model);
+static word_t *
+uniform_generate (unsigned level, int y_state, const wfa_t *wfa,
+                  const void *model);
+static domain_pool_t *
+alloc_uniform_domain_pool (unsigned max_domains, unsigned max_edges,
+                           const wfa_t *wfa);
+
+/*****************************************************************************
+              default functions
+*****************************************************************************/
+
+static void
+init_matrix_probabilities (void);
+static void
+default_chroma (unsigned max_domains, const wfa_t *wfa, void *model);
+static bool_t
+default_append (unsigned new_state, unsigned level,
+                const wfa_t *wfa, void *model);
+static void
+default_update (const word_t *domains, const word_t *used_domains,
+                unsigned level, int y_state, const wfa_t *wfa, void *model);
+static void
+default_free (domain_pool_t *pool);
+static void
+default_model_free (void *model);
+static void *
+default_model_alloc (unsigned max_domains);
+static void *
+default_model_duplicate (const void *src);
+static domain_pool_t *
+default_alloc (void);
+
+/*****************************************************************************
+
+                public code
+  
+*****************************************************************************/
+
+typedef struct dp_array
+{
+    const char       *identifier;
+    domain_pool_t *(*function) (unsigned max_domains, unsigned max_edges,
+                                const wfa_t *wfa);
+} dp_array_t;
+
+dp_array_t const domain_pools[] = {{"adaptive", alloc_qac_domain_pool},
+                                   {"constant",   alloc_const_domain_pool}, 
+                                   {"basis",      alloc_basis_domain_pool},
+                                   {"uniform",    alloc_uniform_domain_pool},
+                                   {"rle",        alloc_rle_domain_pool},
+                                   {"rle-no-chroma",  alloc_rle_no_chroma_domain_pool},
+                                   {NULL,     NULL}};
+
+domain_pool_t *
+alloc_domain_pool (const char *domain_pool_name, unsigned max_domains,
+                   unsigned max_edges, const wfa_t *wfa)
+/*
+ *  Allocate a new domain pool identified by the string
+ *  'domain_pool_name'.  Maximum number of domain images (each one
+ *  represented by one state of the given 'wfa') is specified by
+ *  'max_domains'. 
+ * 
+ *  Return value:
+ *  pointer to the allocated domain pool
+ *
+ *  Note:
+ *      refer to 'domain-pool.h' for a short description of the member functions.
+ */
+{
+    unsigned n;
+   
+    if (!max_domains)
+    {
+        warning ("Can't generate empty domain pool. "
+                 "Using at least DC component.");
+        max_domains = 1;
+    }
+   
+    for (n = 0; domain_pools [n].identifier; n++) /* step through all id's */
+        if (strcaseeq (domain_pools [n].identifier, domain_pool_name)) 
+            return domain_pools [n].function (max_domains, max_edges, wfa);
+
+    warning ("Can't initialize domain pool '%s'. Using default value '%s'.",
+             domain_pool_name, domain_pools [0].identifier);
+
+    return domain_pools [0].function (max_domains, max_edges, wfa);
+}
+
+/*****************************************************************************
+
+                private code
+  
+*****************************************************************************/
+
+/*****************************************************************************
+              adaptive domain pool
+*****************************************************************************/
+
+typedef struct qac_model
+{
+    word_t   *index;         /* probability of domains */
+    word_t   *states;            /* mapping states -> domains */
+    u_word_t  y_index;           /* pointer to prob of Y domain */
+    u_word_t  n;             /* number of domains in the pool */
+    u_word_t  max_domains;       /* max. number of domains */
+} qac_model_t;
+
+static domain_pool_t *
+alloc_qac_domain_pool (unsigned max_domains, unsigned max_edges,
+                       const wfa_t *wfa)
+/*
+ *  Domain pool with state images {0, ..., 'max_domains').
+ *  Underlying probability model: quasi arithmetic coding of columns.
+ */
+{
+    domain_pool_t *pool;
+    unsigned   state;
+   
+    pool                  = default_alloc ();
+    pool->model           = qac_model_alloc (max_domains);
+    pool->generate        = qac_generate;
+    pool->bits            = qac_bits;
+    pool->update          = qac_update;
+    pool->append          = qac_append;
+    pool->chroma      = qac_chroma;
+    pool->model_free      = qac_model_free;
+    pool->model_duplicate = qac_model_duplicate;
+   
+    for (state = 0; state < wfa->basis_states; state++)
+        if (usedomain (state, wfa))
+            qac_append (state, -1, wfa, pool->model);
+
+    return pool;
+}
+
+static void *
+qac_model_alloc (unsigned max_domains)
+{
+    qac_model_t *model;
+
+    init_matrix_probabilities ();
+
+    model          = Calloc (1, sizeof (qac_model_t));
+    model->index       = Calloc (max_domains, sizeof (word_t));
+    model->states      = Calloc (max_domains, sizeof (word_t));
+    model->y_index     = 0;
+    model->n       = 0;
+    model->max_domains = max_domains;
+
+    return model;
+}
+
+static void
+qac_model_free (void *model)
+{
+    Free (((qac_model_t *) model)->index);
+    Free (((qac_model_t *) model)->states);
+    Free (model);
+}
+
+static void *
+qac_model_duplicate (const void *src)
+{
+    qac_model_t       *qdst;
+    const qac_model_t *qsrc = (qac_model_t *) src;
+
+    qdst      = qac_model_alloc (qsrc->max_domains);
+    qdst->y_index = qsrc->y_index;
+    qdst->n       = qsrc->n;
+   
+    memcpy (qdst->index, qsrc->index, qsrc->n * sizeof (word_t));
+    memcpy (qdst->states, qsrc->states, qsrc->n * sizeof (word_t));
+
+    return qdst;
+}
+
+static word_t *
+qac_generate (unsigned level, int y_state, const wfa_t *wfa, const void *model)
+{
+    word_t      *domains;
+    unsigned n;
+    qac_model_t *qac_model     = (qac_model_t *) model;
+    bool_t   y_state_is_domain = NO;
+
+    if (y_state >= 0 && !usedomain (y_state, wfa)) /* don't use y-state */
+        y_state = -1;
+   
+    domains = Calloc (qac_model->n + 2, sizeof (word_t));
+
+    memcpy (domains, qac_model->states, qac_model->n * sizeof (word_t));
+
+    for (n = 0; n < qac_model->n; n++)
+        if (domains [n] == y_state)   /* match */
+            y_state_is_domain = YES;       
+
+    if (y_state_is_domain)
+        domains [qac_model->n] = -1;  /* end marker */
+    else
+    {
+        domains [qac_model->n]     = y_state; /* additional y-state */
+        domains [qac_model->n + 1] = -1;  /* end marker */
+    }
+
+    return domains;
+}
+
+static real_t
+qac_bits (const word_t *domains, const word_t *used_domains,
+          unsigned level, int y_state, const wfa_t *wfa, const void *model)
+{
+    int      domain;         /* counter */
+    real_t   bits      = 0;      /* bit rate R */
+    qac_model_t *qac_model = (qac_model_t *) model; /* probability model */
+
+    if (y_state >= 0 && !usedomain (y_state, wfa)) /* don't use y-state */
+        y_state = -1;
+
+    for (domain = 0; domain < qac_model->n; domain++)
+        if (qac_model->states [domain] != y_state)
+            bits += matrix_0 [qac_model->index [domain]];
+    if (y_state >= 0)
+        bits += matrix_0 [qac_model->y_index];
+   
+    if (used_domains != NULL)
+    {
+        unsigned edge;
+      
+        for (edge = 0; isedge (domain = used_domains [edge]); edge++)
+            if (domains [domain] == y_state)
+            {
+                bits -= matrix_0 [qac_model->y_index];
+                bits += matrix_1 [qac_model->y_index];
+            }
+            else
+            {
+                bits -= matrix_0 [qac_model->index [domain]];
+                bits += matrix_1 [qac_model->index [domain]];
+            }
+    } 
+   
+    return bits;
+}
+
+static void
+qac_update (const word_t *domains, const word_t *used_domains,
+            unsigned level, int y_state, const wfa_t *wfa, void *model)
+{
+    int      domain;
+    unsigned edge;
+    bool_t   used_y_state      = NO;
+    qac_model_t *qac_model     = (qac_model_t *) model;
+    bool_t   y_state_is_domain = NO;
+   
+    if (y_state >= 0 && !usedomain (y_state, wfa)) /* don't use y-state */
+        y_state = -1;
+
+    for (domain = 0; domain < qac_model->n; domain++)
+    {
+        qac_model->index [domain]++;  /* mark domains unused. */
+        if (qac_model->states [domain] == y_state) /* match */
+            y_state_is_domain = YES;       
+    }
+   
+    for (edge = 0; isedge (domain = used_domains [edge]); edge++)
+        if (domains [domain] == y_state) /* chroma coding */
+        {
+            if (y_state_is_domain)
+                qac_model->index [domain]--; /* undo */
+            qac_model->y_index >>= 1;
+            used_y_state = YES;
+        }
+        else              /* luminance coding */
+        {
+            qac_model->index [used_domains [edge]]--; /* undo */
+            qac_model->index [used_domains [edge]] >>= 1;      
+        }
+
+    if (y_state >= 0 && !used_y_state)
+        qac_model->y_index++;     /* update y-state model */
+   
+    for (domain = 0; domain < qac_model->n; domain++)
+        if (qac_model->index [domain] > 1020) /* check for overflow */
+            qac_model->index [domain] = 1020; 
+    if (qac_model->y_index > 1020)   /* check for overflow */
+        qac_model->y_index = 1020; 
+}
+
+static bool_t
+qac_append (unsigned new_state, unsigned level, const wfa_t *wfa, void *model)
+{
+    qac_model_t  *qac_model = (qac_model_t *) model; /* probability model */
+   
+    if (qac_model->n >= qac_model->max_domains)
+        return NO;            /* don't use state in domain pool */
+    else
+    {
+        qac_model->index [qac_model->n]
+            = qac_model->n > 0 ? qac_model->index [qac_model->n - 1] : 0;
+        qac_model->states [qac_model->n] = new_state;
+        qac_model->n++;
+
+        return YES;           /* state will be used in domain pool */
+    }
+}
+
+static void
+qac_chroma (unsigned max_domains, const wfa_t *wfa, void *model)
+{
+    qac_model_t *qac_model = (qac_model_t *) model; /* probability model */
+   
+    if (max_domains < qac_model->n)  /* choose most probable domains */
+    {
+        word_t   *domains;
+        unsigned  n, new, old;
+        word_t   *states = Calloc (max_domains, sizeof (word_t));
+        word_t   *index  = Calloc (max_domains, sizeof (word_t));
+   
+        domains = compute_hits (wfa->basis_states, wfa->states - 1,
+                                max_domains, wfa);
+        for (n = 0; n < max_domains && domains [n] >= 0; n++)
+            states [n] = domains [n];
+        max_domains = min (max_domains, n);
+        Free (domains);
+
+        for (old = 0, new = 0; new < max_domains && old < qac_model->n; old++)
+            if (qac_model->states [old] == states [new])
+                index [new++] = qac_model->index [old];
+
+        Free (qac_model->states);
+        Free (qac_model->index);
+        qac_model->states      = states;
+        qac_model->index       = index;
+        qac_model->n           = max_domains;
+        qac_model->max_domains = max_domains;
+    }
+    qac_model->y_index     = 0;
+    qac_model->max_domains = qac_model->n;
+}
+
+/*****************************************************************************
+              const domain pool
+*****************************************************************************/
+
+static domain_pool_t *
+alloc_const_domain_pool (unsigned max_domains, unsigned max_edges,
+                         const wfa_t *wfa)
+/*
+ *  Domain pool with state image 0 (constant function f(x, y) = 1).
+ *  No probability model is used.
+ */
+{
+    domain_pool_t *pool;
+   
+    pool           = default_alloc ();   
+    pool->generate = const_generate;
+    pool->bits     = const_bits;
+   
+    return pool;
+}
+
+static word_t *
+const_generate (unsigned level, int y_state, const wfa_t *wfa,
+                const void *model)
+{
+    word_t *domains = Calloc (2, sizeof (word_t));
+   
+    domains [0] = 0;
+    domains [1] = -1;
+   
+    return domains;
+}
+
+static real_t
+const_bits (const word_t *domains, const word_t *used_domains,
+            unsigned level, int y_state, const wfa_t *wfa, const void *model)
+{
+    return 0;                /* 0 bits,
+                                either we have a lc or not */
+}
+
+/*****************************************************************************
+                basis domain pool
+*****************************************************************************/
+
+static domain_pool_t *
+alloc_basis_domain_pool (unsigned max_domains, unsigned max_edges,
+                         const wfa_t *wfa)
+/*
+ *  Domain pool with state images {0, ..., 'basis_states' - 1).
+ *  Underlying probability model: quasi arithmetic coding of columns.
+ *  I.e. domain pool = qac_domainpool ('max_domains' == wfa->basis_states)
+ */
+{
+    return alloc_qac_domain_pool (wfa->basis_states, max_edges, wfa);
+}
+
+/*****************************************************************************
+              uniform-distribution pool
+*****************************************************************************/
+
+static domain_pool_t *
+alloc_uniform_domain_pool (unsigned max_domains, unsigned max_edges,
+                           const wfa_t *wfa)
+/*
+ *  Domain pool with state images {0, ..., 'max_domains').
+ *  Underlying probability model: uniform distribution.
+ */
+{
+    domain_pool_t *pool;
+   
+    pool           = default_alloc ();   
+    pool->generate = uniform_generate;
+    pool->bits     = uniform_bits;
+   
+    return pool;
+}
+
+static word_t *
+uniform_generate (unsigned level, int y_state, const wfa_t *wfa,
+                  const void *model)
+{
+    unsigned  state, n;
+    word_t   *domains = Calloc (wfa->states + 1, sizeof (word_t));
+
+    for (state = 0, n = 0; state < wfa->states; state++)
+        if (usedomain (state, wfa))
+            domains [n++] = state;
+    domains [n] = -1;
+   
+    return domains;
+}
+ 
+static real_t
+uniform_bits (const word_t *domains, const word_t *used_domains,
+              unsigned level, int y_state, const wfa_t *wfa, const void *model)
+{
+    unsigned state, n;
+    real_t   bits = 0;
+   
+    for (state = 0, n = 0; state < wfa->states; state++)
+        if (usedomain (state, wfa))
+            n++;
+
+    bits = - n * log2 ((n - 1) / (real_t) n);
+
+    if (used_domains != NULL)
+    {
+        int edge;
+      
+        for (edge = 0; isedge (used_domains [edge]); edge++)
+            bits -= log2 (1.0 / n);
+    }
+
+    return bits;
+}
+
+/*****************************************************************************
+              run length encoding pool
+*****************************************************************************/
+
+typedef struct rle_model
+{
+    word_t   count [MAXEDGES + 1];
+    u_word_t total;
+    u_word_t n;
+    u_word_t max_domains;
+    u_word_t y_index;        /* pointer to prob of Y domain */
+    word_t      *states;         /* mapping states -> domains */
+    qac_model_t *domain_0;
+} rle_model_t;
+
+static domain_pool_t *
+alloc_rle_domain_pool (unsigned max_domains, unsigned max_edges,
+                       const wfa_t *wfa)
+/*
+ *  Domain pool with state images {0, ..., 'max_domains').
+ *  Underlying probability model: rle 
+ */
+{
+    domain_pool_t *pool;
+    unsigned   state;
+   
+    pool                  = default_alloc ();    
+    pool->model           = rle_model_alloc (max_domains);
+    pool->model_free      = rle_model_free;
+    pool->model_duplicate = rle_model_duplicate;
+    pool->generate        = rle_generate;
+    pool->update          = rle_update;
+    pool->bits            = rle_bits;
+    pool->append          = rle_append;
+    pool->chroma          = rle_chroma;
+
+    for (state = 0; state < wfa->basis_states; state++)
+        if (usedomain (state, wfa))
+            rle_append (state, -1, wfa, pool->model);
+
+    return pool;
+}
+
+static void *
+rle_model_alloc (unsigned max_domains)
+{
+    unsigned m;
+    rle_model_t *model = Calloc (1, sizeof (rle_model_t));
+   
+    for (m = model->total = 0; m < MAXEDGES + 1; m++, model->total++)
+        model->count [m] = 1;
+
+    model->domain_0    = qac_model_alloc (1);
+    model->states      = Calloc (max_domains, sizeof (word_t));
+    model->n       = 0;
+    model->y_index     = 0;
+    model->max_domains = max_domains;
+   
+    return model;
+}
+
+static void
+rle_model_free (void *model)
+{
+    qac_model_free (((rle_model_t *) model)->domain_0);
+    Free (((rle_model_t *) model)->states);
+    Free (model);
+}
+
+static void *
+rle_model_duplicate (const void *src)
+{
+    const rle_model_t *rle_src = (rle_model_t *) src;
+    rle_model_t       *model   = Calloc (1, sizeof (rle_model_t));
+
+    model->domain_0    = qac_model_duplicate (rle_src->domain_0);
+    model->n       = rle_src->n;
+    model->max_domains = rle_src->max_domains;
+    model->states      = Calloc (model->max_domains, sizeof (word_t));
+    model->total       = rle_src->total;
+    model->y_index     = rle_src->y_index;
+   
+    memcpy (model->states, rle_src->states,
+            model->max_domains * sizeof (word_t));
+    memcpy (model->count, rle_src->count,
+            (MAXEDGES + 1) * sizeof (word_t));
+   
+    return model;
+}
+
+static word_t *
+rle_generate (unsigned level, int y_state, const wfa_t *wfa, const void *model)
+{
+    word_t      *domains;
+    unsigned n;
+    rle_model_t *rle_model     = (rle_model_t *) model;
+    bool_t   y_state_is_domain = NO;
+   
+    if (y_state >= 0 && !usedomain (y_state, wfa)) /* don't use y-state */
+        y_state = -1;
+   
+    domains = Calloc (rle_model->n + 2, sizeof (word_t));
+
+    memcpy (domains, rle_model->states, rle_model->n * sizeof (word_t));
+
+    for (n = 0; n < rle_model->n; n++)
+        if (domains [n] == y_state)   /* match */
+            y_state_is_domain = YES;       
+
+    if (y_state_is_domain)
+        domains [rle_model->n] = -1;  /* end marker */
+    else
+    {
+        domains [rle_model->n]     = y_state; /* additional y-state */
+        domains [rle_model->n + 1] = -1;  /* end marker */
+    }
+
+    return domains;
+}
+
+static real_t
+rle_bits (const word_t *domains, const word_t *used_domains,
+          unsigned level, int y_state, const wfa_t *wfa, const void *model)
+{
+    unsigned edge;
+    unsigned n     = 0;
+    real_t   bits      = 0;
+    word_t   sorted [MAXEDGES + 1];
+    rle_model_t *rle_model = (rle_model_t *) model;
+    unsigned last;
+    int      into;
+   
+    if (y_state >= 0 && !usedomain (y_state, wfa)) /* don't use y-state */
+        y_state = -1;
+
+    if (used_domains)
+    {
+        word_t domain;
+      
+        if (y_state >= 0)
+            bits += matrix_0 [rle_model->y_index];
+      
+        for (edge = n = 0; isedge (domain = used_domains [edge]); edge++)
+            if (domains [domain] != y_state)
+                sorted [n++] = used_domains [edge];
+            else
+            {
+                bits -= matrix_0 [rle_model->y_index];
+                bits += matrix_1 [rle_model->y_index];
+            }
+      
+        if (n > 1)
+            qsort (sorted, n, sizeof (word_t), sort_asc_word);
+    }
+
+    bits = - log2 (rle_model->count [n] / (real_t) rle_model->total);
+    if (used_domains && n && sorted [0] == 0)
+    {
+        word_t array0 [2] = {0, NO_EDGE};
+        bits += qac_bits (array0, array0, level, y_state, wfa, rle_model->domain_0);
+    }
+    else
+    {
+        word_t array0 [2] = {NO_EDGE};
+        bits += qac_bits (array0, array0, level, y_state, wfa, rle_model->domain_0);
+    }
+   
+    last = 1;
+    for (edge = 0; edge < n; edge++)
+        if ((into = sorted [edge]) && rle_model->n - 1 - last)
+        {
+            bits += bits_bin_code (into - last, rle_model->n - 1 - last);
+            last  = into + 1;
+        }
+   
+    return bits;
+}
+
+static void
+rle_update (const word_t *domains, const word_t *used_domains,
+            unsigned level, int y_state, const wfa_t *wfa, void *model)
+{
+    rle_model_t *rle_model  = (rle_model_t *) model;
+    bool_t   state_0    = NO, state_y = NO;
+    word_t   array0 [2] = {0, NO_EDGE};
+    unsigned     edge = 0;
+   
+    if (y_state >= 0 && !usedomain (y_state, wfa)) /* don't use y-state */
+        y_state = -1;
+
+    if (used_domains)
+    {
+        word_t   domain;
+      
+        for (edge = 0; isedge (domain = used_domains [edge]); edge++)
+            if (domains [domain] == 0)
+                state_0 = YES;
+            else if (domains [domain] == y_state)
+                state_y = YES;
+    }
+   
+    rle_model->count [edge]++;
+    rle_model->total++;
+
+    qac_update (array0, array0 + (state_0 ? 0 : 1), level, y_state, wfa,
+                rle_model->domain_0);
+
+    if (state_y)
+        rle_model->y_index >>= 1;
+    else
+        rle_model->y_index++;
+    if (rle_model->y_index > 1020)   /* check for overflow */
+        rle_model->y_index = 1020; 
+}
+
+static bool_t
+rle_append (unsigned new_state, unsigned level, const wfa_t *wfa, void *model)
+{
+    rle_model_t *rle_model = (rle_model_t *) model; /* probability model */
+   
+    if (rle_model->n >= rle_model->max_domains)
+        return NO;            /* don't use state in domain pool */
+    else
+    {
+        rle_model->states [rle_model->n] = new_state;
+        rle_model->n++;
+
+        if (new_state == 0)
+        {
+            assert (rle_model->n == 1);
+            qac_append (0, -1, wfa, rle_model->domain_0);
+        }
+      
+        return YES;           /* state will be used in domain pool */
+    }
+}
+
+static void
+rle_chroma (unsigned max_domains, const wfa_t *wfa, void *model)
+{
+    rle_model_t *rle_model = (rle_model_t *) model; /* probability model */
+   
+    if (max_domains < rle_model->n)  /* choose most probable domains */
+    {
+        unsigned  n;
+        word_t   *states  = Calloc (max_domains, sizeof (word_t));
+        word_t   *domains = compute_hits (wfa->basis_states, wfa->states - 1,
+                                          max_domains, wfa);
+      
+        for (n = 0; n < max_domains && domains [n] >= 0; n++)
+            states [n] = domains [n];
+
+        assert (states [0] == 0);
+        max_domains = min (max_domains, n);
+        Free (domains);
+
+        Free (rle_model->states);
+        rle_model->states = states;
+        rle_model->n      = max_domains;
+    }
+    rle_model->y_index     = 0;
+    rle_model->max_domains = rle_model->n;
+}
+
+/*****************************************************************************
+         run length encoding pool no special chroma pool
+*****************************************************************************/
+
+static domain_pool_t *
+alloc_rle_no_chroma_domain_pool (unsigned max_domains, unsigned max_edges,
+                                 const wfa_t *wfa)
+/*
+ *  Domain pool with state images {0, ..., 'max_domains').
+ *  Underlying probability model: rle 
+ *  Domain pool is not changed for chroma bands
+ */
+{
+    domain_pool_t *pool = alloc_rle_domain_pool (max_domains, max_edges, wfa);
+   
+    pool->chroma = default_chroma;
+
+    return pool;
+}
+
+/*****************************************************************************
+              default functions (see domain-pool.h)
+*****************************************************************************/
+
+static domain_pool_t *
+default_alloc (void)
+{
+    domain_pool_t *pool;
+
+    pool                  = Calloc (1, sizeof (domain_pool_t));
+    pool->model           = default_model_alloc(0);
+    pool->generate        = NULL;
+    pool->bits            = NULL;
+    pool->update          = default_update;
+    pool->append          = default_append;
+    pool->chroma          = default_chroma;
+    pool->free            = default_free;
+    pool->model_free      = default_model_free;
+    pool->model_duplicate = default_model_duplicate;
+   
+    return pool;
+}
+
+static void *
+default_model_duplicate (const void *src)
+{
+    return NULL;
+}
+
+static void *
+default_model_alloc (unsigned max_domains)
+{
+    return NULL;
+}
+
+static void
+default_model_free (void *model)
+{
+    if (model)
+        Free (model);
+}
+
+static void
+default_free (domain_pool_t *pool)
+{
+    pool->model_free (pool->model);
+    Free (pool);
+}
+
+static void
+default_update (const word_t *domains, const word_t *used_domains,
+                unsigned level, int y_state, const wfa_t *wfa, void *model)
+{
+    return;              /* nothing to do */
+}
+
+static bool_t
+default_append (unsigned new_state, unsigned level,
+                const wfa_t *wfa, void *model)
+{
+    return YES;              /* use every state in lin comb */
+}
+
+static void
+default_chroma (unsigned max_domains, const wfa_t *wfa, void *model)
+{
+    return;              /* don't alter domain pool */
+}
+
+static void
+init_matrix_probabilities (void)
+/*
+ *  Compute the information contents of matrix element '0' and '1' for
+ *  each possible probability index 0, ... ,  1023. These values are
+ *  obtained from the probability model in the quasi arithmetic
+ *  coding module.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *  local arrays matrix_0 and matrix_1 are initialized if not already done.
+ */
+{
+    if (matrix_0 == NULL || matrix_1 == NULL)
+    {
+        unsigned index;           
+        unsigned n, exp;
+      
+        matrix_0 = Calloc (1 << (MAX_PROB + 1), sizeof (real_t));
+        matrix_1 = Calloc (1 << (MAX_PROB + 1), sizeof (real_t));
+   
+        for (index = 0, n = MIN_PROB; n <= MAX_PROB; n++)
+            for (exp = 0; exp < (unsigned) 1 << n; exp++, index++)
+            {
+                matrix_1 [index] = -log2 (1 / (real_t) (1 << n));
+                matrix_0 [index] = -log2 (1 - 1 / (real_t) (1 << n));
+            }
+    }
+}
diff --git a/converter/other/fiasco/codec/domain-pool.h b/converter/other/fiasco/codec/domain-pool.h
new file mode 100644
index 00000000..d1488779
--- /dev/null
+++ b/converter/other/fiasco/codec/domain-pool.h
@@ -0,0 +1,75 @@
+/*
+ *  domain-pool.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _DOMAIN_POOL_H
+#define _DOMAIN_POOL_H
+
+#include "cwfa.h"
+#include "types.h"
+
+typedef struct domain_pool
+{
+   void	  *model;			/* probability model */
+   word_t *(*generate) (unsigned level, int y_state, const wfa_t *wfa,
+			const void  *model);
+   /*
+    *  Generate set of domain images which may be used for an approximation.
+    *  Use parameters 'level', 'y_state' and 'wfa' to make the decision.
+    */
+   real_t (*bits) (const word_t *domains, const word_t *used_domains,
+		   unsigned level, int y_state, const wfa_t *wfa,
+		   const void *model);
+   /*
+    *  Compute bit-rate of a range approximation with domains given by
+    *  the -1 terminated list 'used_domains'.
+    */
+   void	  (*update) (const word_t *domains, const word_t *used_domains,
+		     unsigned level, int y_state, const wfa_t *wfa,
+		     void *model);
+   /*
+    *  Update the probability model according to the chosen approximation.
+    *  (given by the -1 terminated list 'used_domains').
+    */
+   bool_t (*append) (unsigned state, unsigned level, const wfa_t *wfa,
+		     void *model);
+   /*
+    *  Try to append a new state to the domain pool.
+    */
+   void	  (*chroma) (unsigned max_domains, const wfa_t *wfa, void *model);
+   /*
+    *  Derive a new domain pool that will be used for chroma channel
+    *  coding 
+    */
+   void   (*free) (struct domain_pool *pool);
+   /*
+    *  Discard the given domain pool struct.
+    */
+   void   (*model_free)	(void *model);
+   /*
+    *  Free given probability model.
+    */
+   void   *(*model_duplicate) (const void *src);
+   /*
+    *  Duplicate the given probability model (i.e. alloc and copy).
+    */
+} domain_pool_t;
+
+domain_pool_t *
+alloc_domain_pool (const char *domain_pool_name, unsigned max_domains,
+		   unsigned max_edges, const wfa_t *wfa);
+
+#endif /* not _DOMAIN_POOL_H */
+
diff --git a/converter/other/fiasco/codec/ip.c b/converter/other/fiasco/codec/ip.c
new file mode 100644
index 00000000..caa97baf
--- /dev/null
+++ b/converter/other/fiasco/codec/ip.c
@@ -0,0 +1,324 @@
+/*
+ *  ip.c:		Computation of inner products
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "cwfa.h"
+#include "control.h"
+#include "ip.h"
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static real_t 
+standard_ip_image_state (unsigned address, unsigned level, unsigned domain,
+			 const coding_t *c);
+static real_t 
+standard_ip_state_state (unsigned domain1, unsigned domain2, unsigned level,
+			 const coding_t *c);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+real_t 
+get_ip_image_state (unsigned image, unsigned address, unsigned level,
+		    unsigned domain, const coding_t *c)
+/*
+ *  Return value:
+ *	Inner product between 'image' ('address') and
+ *      'domain' at given 'level' 
+ */
+{
+   if (level <= c->options.images_level)
+   {
+      /*
+       *  Compute the inner product in the standard way by multiplying 
+       *  the pixel-values of the given domain and range image.
+       */ 
+      return standard_ip_image_state (address, level, domain, c);
+   }
+   else 
+   {
+      /*
+       *  Use the already computed inner products stored in 'ip_images_states'
+       */
+      return c->ip_images_state [domain][image];
+   }
+}
+
+void 
+compute_ip_images_state (unsigned image, unsigned address, unsigned level,
+			 unsigned n, unsigned from,
+			 const wfa_t *wfa, coding_t *c)
+/*
+ *  Compute the inner products between all states
+ *  'from', ... , 'wfa->max_states' and the range images 'image'
+ *  (and childs) up to given level.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	inner product tables 'c->ip_images_states' are updated
+ */ 
+{
+   if (level > c->options.images_level) 
+   {
+      unsigned state, label;
+
+      if (level > c->options.images_level + 1)	/* recursive computation */
+	 compute_ip_images_state (MAXLABELS * image + 1, address * MAXLABELS,
+				  level - 1, MAXLABELS * n, from, wfa, c);
+      
+      /*
+       *  Compute inner product <f, Phi_i>
+       */
+      for (label = 0; label < MAXLABELS; label++)
+	 for (state = from; state < wfa->states; state++)
+	    if (need_image (state, wfa))
+	    {
+	       unsigned  edge, count;
+	       int     	 domain;
+	       real_t 	*dst, *src;
+	       
+	       if (ischild (domain = wfa->tree [state][label]))
+	       {
+		  if (level > c->options.images_level + 1)
+		  {
+		     dst = c->ip_images_state [state] + image;
+		     src = c->ip_images_state [domain]
+			   + image * MAXLABELS + label + 1;
+		     for (count = n; count; count--, src += MAXLABELS)
+			*dst++ += *src;
+		  }
+		  else
+		  {
+		     unsigned newadr = address * MAXLABELS + label;
+		     
+		     dst = c->ip_images_state [state] + image;
+		     
+		     for (count = n; count; count--, newadr += MAXLABELS)
+			*dst++ += standard_ip_image_state (newadr, level - 1,
+							   domain, c);
+		  }
+	       }
+	       for (edge = 0; isedge (domain = wfa->into [state][label][edge]);
+		    edge++)
+	       {
+		  real_t weight = wfa->weight [state][label][edge];
+		  
+		  if (level > c->options.images_level + 1)
+		  {
+		     dst = c->ip_images_state [state] + image;
+		     src = c->ip_images_state [domain]
+			   + image * MAXLABELS + label + 1;
+		     for (count = n; count; count--, src += MAXLABELS)
+			*dst++ += *src * weight;
+		  }
+		  else
+		  {
+		     unsigned newadr = address * MAXLABELS + label;
+
+		     dst = c->ip_images_state [state] + image;
+		     
+		     for (count = n; count; count--, newadr += MAXLABELS)
+			*dst++ += weight *
+				  standard_ip_image_state (newadr, level - 1,
+							   domain, c);
+		  }
+	       }
+	    }
+   }
+}
+
+real_t 
+get_ip_state_state (unsigned domain1, unsigned domain2, unsigned level,
+		    const coding_t *c)
+/*
+ *  Return value:
+ *	Inner product between 'domain1' and 'domain2' at given 'level'.
+ */
+{
+   if (level <= c->options.images_level)
+   {
+      /*
+       *  Compute the inner product in the standard way by multiplying 
+       *  the pixel-values of both state-images
+       */ 
+      return standard_ip_state_state (domain1, domain2, level, c);
+   }
+   else 
+   {
+      /*
+       *  Use already computed inner products stored in 'ip_images_states'
+       */
+      if (domain2 < domain1)
+	 return c->ip_states_state [domain1][level][domain2];
+      else
+	 return c->ip_states_state [domain2][level][domain1];
+   }
+}
+
+void 
+compute_ip_states_state (unsigned from, unsigned to,
+			 const wfa_t *wfa, coding_t *c)
+/*
+ *  Computes the inner products between the current state 'state1' and the
+ *  old states 0,...,'state1'-1
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	inner product tables 'c->ip_states_state' are computed.
+ */ 
+{
+   unsigned level;
+   unsigned state1, state2;
+
+   /*
+    *  Compute inner product <Phi_state1, Phi_state2>
+    */
+
+   for (level = c->options.images_level + 1;
+	level <= c->options.lc_max_level; level++)
+      for (state1 = from; state1 <= to; state1++)
+	 for (state2 = 0; state2 <= state1; state2++) 
+	    if (need_image (state2, wfa))
+	    {
+	       unsigned	label;
+	       real_t	ip = 0;
+	       
+	       for (label = 0; label < MAXLABELS; label++)
+	       {
+		  int	   domain1, domain2;
+		  unsigned edge1, edge2;
+		  real_t   sum, weight2;
+		  
+		  if (ischild (domain1 = wfa->tree [state1][label]))
+		  {
+		     sum = 0;
+		     if (ischild (domain2 = wfa->tree [state2][label]))
+			sum = get_ip_state_state (domain1, domain2,
+						  level - 1, c);
+		     
+		     for (edge2 = 0;
+			  isedge (domain2 = wfa->into [state2][label][edge2]);
+			  edge2++)
+		     {
+			weight2 = wfa->weight [state2][label][edge2];
+			sum += weight2 * get_ip_state_state (domain1, domain2,
+							     level - 1, c);
+		     }
+		     ip += sum;
+		  }
+		  for (edge1 = 0;
+		       isedge (domain1 = wfa->into [state1][label][edge1]);
+		       edge1++)
+		  {
+		     real_t weight1 = wfa->weight [state1][label][edge1];
+		     
+		     sum = 0;
+		     if (ischild (domain2 = wfa->tree [state2][label]))
+			sum = get_ip_state_state (domain1, domain2,
+						  level - 1, c);
+		     
+		     for (edge2 = 0;
+			  isedge (domain2 = wfa->into [state2][label][edge2]);
+			  edge2++)
+		     {
+			weight2 = wfa->weight [state2][label][edge2];
+			sum += weight2 * get_ip_state_state (domain1, domain2,
+							     level - 1, c);
+		     }
+		     ip += weight1 * sum;
+		  }
+	       }
+	       c->ip_states_state [state1][level][state2] = ip;
+	    }
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static real_t 
+standard_ip_image_state (unsigned address, unsigned level, unsigned domain,
+			 const coding_t *c)
+/*
+ *  Returns the inner product between the subimage 'address' and the
+ *  state image 'domain' at given 'level'.  The stored state images
+ *  and the image tree are used to compute the inner product in the
+ *  standard way by multiplying the corresponding pixel values.
+ *
+ *  Return value:
+ *	computed inner product
+ */
+{
+   unsigned i;
+   real_t   ip = 0, *imageptr, *stateptr;
+
+   if (level > c->options.images_level)
+      error ("Level %d not supported.", level);
+   
+   imageptr = &c->pixels [address * size_of_level (level)];
+
+   stateptr = c->images_of_state [domain] + address_of_level (level);
+   
+   for (i = size_of_level (level); i; i--)
+      ip += *imageptr++ * *stateptr++;
+
+   return ip;
+}
+
+static real_t 
+standard_ip_state_state (unsigned domain1, unsigned domain2, unsigned level,
+			 const coding_t *c)
+/*
+ *  Returns the inner product between the subimage 'address' and the
+ *  state image 'state' at given 'level'.  The stored state images are
+ *  used to compute the inner product in the standard way by
+ *  multiplying the corresponding pixel values.
+ *
+ *  Return value:
+ *	computed inner product
+ */
+{
+   unsigned i;
+   real_t   ip = 0, *state1ptr, *state2ptr;
+
+   if (level > c->options.images_level)
+      error ("Level %d not supported.", level);
+
+   state1ptr = c->images_of_state [domain1] + address_of_level (level);
+   state2ptr = c->images_of_state [domain2] + address_of_level (level);
+   
+   for (i = size_of_level (level); i; i--)
+      ip += *state1ptr++ * *state2ptr++;
+
+   return ip;
+}
+
diff --git a/converter/other/fiasco/codec/ip.h b/converter/other/fiasco/codec/ip.h
new file mode 100644
index 00000000..e5e4dd65
--- /dev/null
+++ b/converter/other/fiasco/codec/ip.h
@@ -0,0 +1,37 @@
+/*
+ *  ip.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _IP_H
+#define _IP_H
+
+#include "cwfa.h"
+
+void 
+compute_ip_states_state (unsigned from, unsigned to,
+			 const wfa_t *wfa, coding_t *c);
+real_t 
+get_ip_state_state (unsigned domain1, unsigned domain2, unsigned level,
+		    const coding_t *c);
+void 
+compute_ip_images_state (unsigned image, unsigned address, unsigned level,
+			 unsigned n, unsigned from,
+			 const wfa_t *wfa, coding_t *c);
+real_t 
+get_ip_image_state (unsigned image, unsigned address, unsigned level,
+		    unsigned domain, const coding_t *c);
+
+#endif /* not _IP_H */
+
diff --git a/converter/other/fiasco/codec/motion.c b/converter/other/fiasco/codec/motion.c
new file mode 100644
index 00000000..92951281
--- /dev/null
+++ b/converter/other/fiasco/codec/motion.c
@@ -0,0 +1,338 @@
+/*
+ *  motion.c:		Motion compensation code	
+ *
+ *  Written by:		Ullrich Hafner
+ *			Michael Unger
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#if HAVE_STRING_H
+#	include <string.h>
+#else /* not HAVE_STRING_H */
+#	include <strings.h>
+#endif /* not HAVE_STRING_H */
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "image.h"
+#include "misc.h"
+#include "motion.h"
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+void
+restore_mc (int enlarge_factor, image_t *image, const image_t *past,
+	    const image_t *future, const wfa_t *wfa)
+/*
+ *  Restore motion compensated prediction of 'image' represented by 'wfa'.
+ *  If 'enlarge_factor' != 0 then enlarge image by given amount.
+ *  Reference frames are given by 'past' and 'future'.
+ *
+ *  No return values.
+ */
+{
+   unsigned  state, label;
+   unsigned  root_state;
+   word_t   *mcblock1, *mcblock2;	/* MC blocks */
+
+#define FX(v) ((image->format == FORMAT_4_2_0) && band != Y ? ((v) / 2) : v)
+   
+   mcblock1 = Calloc (size_of_level (max ((int) wfa->wfainfo->p_max_level
+					  + 2 * enlarge_factor, 0)),
+		      sizeof (word_t));
+   mcblock2 = Calloc (size_of_level (max ((int) wfa->wfainfo->p_max_level
+					  + 2 * enlarge_factor, 0)),
+		      sizeof (word_t));
+
+   if (!image->color)
+      root_state = wfa->root_state;
+   else
+      root_state  = wfa->tree [wfa->tree [wfa->root_state][0]][0];
+   
+   for (state = wfa->basis_states; state <= root_state; state++)
+      for (label = 0; label < MAXLABELS; label++)
+	 if (wfa->mv_tree[state][label].type != NONE)
+	 {
+	    color_e band;
+	    unsigned level  = wfa->level_of_state [state] - 1;
+	    unsigned width  = width_of_level (level);
+	    unsigned height = height_of_level (level);
+	    unsigned offset = image->width - width;
+	    
+	    switch (wfa->mv_tree [state][label].type)
+	    {
+	       case FORWARD:
+		  for (band  = first_band (image->color);
+		       band <= last_band (image->color); band++)
+		  {
+		     extract_mc_block (mcblock1, FX (width), FX (height),
+				       past->pixels [band], FX (past->width),
+				       wfa->wfainfo->half_pixel,
+				       FX (wfa->x [state][label]),
+				       FX (wfa->y [state][label]),
+				       FX (wfa->mv_tree [state][label].fx),
+				       FX (wfa->mv_tree [state][label].fy));
+		     {
+			word_t   *mc1;	/* current pixel in MC block */
+			word_t 	 *orig;	/* current pixel in original image */
+			unsigned  x, y;	/* pixel coordinates */
+			
+			mc1  = mcblock1;
+			orig = (word_t *) image->pixels [band]
+			       + FX (wfa->x[state][label])
+			       + FX (wfa->y[state][label]) * FX (image->width);
+		     
+			for (y = FX (height); y; y--)
+			{
+			   for (x = FX (width); x; x--)
+			      *orig++ += *mc1++;
+
+			   orig += FX (offset);
+			}
+		     }
+		  }
+		  break;
+	       case BACKWARD:
+		  for (band  = first_band (image->color);
+		       band <= last_band (image->color); band++)
+		  {
+		     extract_mc_block (mcblock1, FX (width), FX (height),
+				       future->pixels [band],
+				       FX (future->width),
+				       wfa->wfainfo->half_pixel,
+				       FX (wfa->x [state][label]),
+				       FX (wfa->y [state][label]),
+				       FX (wfa->mv_tree [state][label].bx),
+				       FX (wfa->mv_tree [state][label].by));
+		     {
+			word_t   *mc1;	/* current pixel in MC block 1 */
+			word_t   *orig;	/* current pixel in original image */
+			unsigned  x, y;	/* pixel coordinates */
+			
+			mc1  = mcblock1;
+			orig = (word_t *) image->pixels [band]
+			       + FX (wfa->x[state][label])
+			       + FX (wfa->y[state][label]) * FX (image->width);
+		     
+			for (y = FX (height); y; y--)
+			{
+			   for (x = FX (width); x; x--)
+			      *orig++ += *mc1++;
+
+			   orig += FX (offset);
+			}
+		     }
+		  }
+		  break;
+	       case INTERPOLATED:
+		  for (band  = first_band (image->color);
+		       band <= last_band (image->color); band++)
+		  {
+		     extract_mc_block (mcblock1, FX (width), FX (height),
+				       past->pixels [band], FX (past->width),
+				       wfa->wfainfo->half_pixel,
+				       FX (wfa->x[state][label]),
+				       FX (wfa->y[state][label]),
+				       FX (wfa->mv_tree[state][label].fx),
+				       FX (wfa->mv_tree[state][label].fy));
+		     extract_mc_block (mcblock2, FX (width), FX (height),
+				       future->pixels [band],
+				       FX (future->width),
+				       wfa->wfainfo->half_pixel,
+				       FX (wfa->x[state][label]),
+				       FX (wfa->y[state][label]),
+				       FX (wfa->mv_tree[state][label].bx),
+				       FX (wfa->mv_tree[state][label].by));
+		     {
+			word_t   *mc1;	/* current pixel in MC block 1 */
+			word_t   *mc2;	/* current pixel in MC block 1 */
+			word_t   *orig;	/* current pixel in original image */
+			unsigned  x, y;	/* pixel coordinates */
+			
+			mc1  = mcblock1;
+			mc2  = mcblock2;
+			orig = (word_t *) image->pixels [band]
+			       + FX (wfa->x[state][label])
+			       + FX (wfa->y[state][label]) * FX (image->width);
+			
+			for (y = FX (height); y; y--)
+			{
+			   for (x = FX (width); x; x--)
+#ifdef HAVE_SIGNED_SHIFT
+			      *orig++ += (*mc1++ + *mc2++) >> 1;
+#else /* not HAVE_SIGNED_SHIFT */
+			   *orig++ += (*mc1++ + *mc2++) / 2;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+			   orig += FX (offset);
+			}
+		     }
+		  }
+		  break;
+	       default:
+		  break;
+	    }
+	 }
+
+   if (image->color)
+   {
+      unsigned	  n;
+      word_t	 *ptr;
+      static int *clipping = NULL;
+      unsigned	  shift    = image->format == FORMAT_4_2_0 ? 2 : 0;
+
+      if (!clipping)			/* initialize clipping table */
+      {
+	 int i;
+	    
+	 clipping = Calloc (256 * 3, sizeof (int));
+	 for (i = -128; i < 128; i++)
+	    clipping [256 + i + 128] = i;
+	 for (i = 0; i < 256; i++)
+	    clipping [i] = clipping [256];
+	 for (i = 512; i < 512 + 256; i++)
+	    clipping [i] = clipping [511];
+	 clipping += 256 + 128;
+      }
+	 
+      ptr = image->pixels [Cb];
+      for (n = (image->width * image->height) >> shift; n; n--, ptr++)
+#ifdef HAVE_SIGNED_SHIFT
+	 *ptr = clipping [*ptr >> 4] << 4;
+#else /* not HAVE_SIGNED_SHIFT */
+	 *ptr = clipping [*ptr / 16] * 16;
+#endif /* not HAVE_SIGNED_SHIFT */
+      ptr = image->pixels [Cr];
+      for (n = (image->width * image->height) >> shift; n; n--, ptr++)
+#ifdef HAVE_SIGNED_SHIFT
+	*ptr = clipping [*ptr >> 4] << 4;
+#else /* not HAVE_SIGNED_SHIFT */
+        *ptr = clipping [*ptr / 16] * 16;
+#endif /* not HAVE_SIGNED_SHIFT */
+   }
+   
+   Free (mcblock1);
+   Free (mcblock2);
+}
+
+void
+extract_mc_block (word_t *mcblock, unsigned width, unsigned height,
+		  const word_t *reference, unsigned ref_width,
+		  bool_t half_pixel, unsigned xo, unsigned yo,
+		  unsigned mx, unsigned my)
+/*
+ *  Extract motion compensation image 'mcblock' of size 'width'x'height'
+ *  from 'reference' image (width is given by 'ref_width').
+ *  Coordinates of reference block are given by ('xo' + 'mx', 'yo' + 'my').
+ *  Use 'half_pixel' precision if specified.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'mcblock[]'	MCPE block is filled with reference pixels 
+ */
+{
+   if (!half_pixel)			/* Fullpixel precision */
+   {
+      const word_t *rblock;		/* pointer to reference image */
+      unsigned	    y;			/* current row */
+      
+      rblock  = reference + (yo + my) * ref_width + (xo + mx);
+      for (y = height; y; y--) 
+      {
+	 memcpy (mcblock, rblock, width * sizeof (word_t));
+
+	 mcblock += width;
+	 rblock  += ref_width;
+      }
+   }
+   else					/* Halfpixel precision */
+   {
+      unsigned	    x, y;		/* current coordinates */
+      unsigned	    offset;		/* remaining pixels in row */
+      const word_t *rblock;		/* pointer to reference image */
+      const word_t *ryblock;		/* pointer to next line */
+      const word_t *rxblock;		/* pointer to next column */
+      const word_t *rxyblock;		/* pointer to next column & row */
+   
+      rblock   = reference + (yo + my / 2) * ref_width + (xo + mx / 2);
+      ryblock  = rblock + ref_width;	/* pixel in next row */
+      rxblock  = rblock + 1;		/* pixel in next column */
+      rxyblock = ryblock + 1;		/* pixel in next row & column */
+      offset   = ref_width - width;
+      
+      if ((mx & 1) == 0)
+      {
+	 if ((my & 1) == 0)		/* Don't use halfpixel refinement */
+	    for (y = height; y; y--) 
+	    {
+	       memcpy (mcblock, rblock, width * sizeof (word_t));
+	       
+	       mcblock += width;
+	       rblock  += ref_width;
+	    }
+	 else				/* Halfpixel in y direction */
+	    for (y = height; y; y--) 
+	    {
+	       for (x = width; x; x--)
+#ifdef HAVE_SIGNED_SHIFT
+		  *mcblock++ = (*rblock++ + *ryblock++) >> 1;
+#else /* not HAVE_SIGNED_SHIFT */
+		  *mcblock++ = (*rblock++ + *ryblock++) / 2;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+	       rblock  += offset;
+	       ryblock += offset;
+	    }
+      }
+      else
+      {
+	 if ((my & 1) == 0)		/* Halfpixel in x direction */
+	    for (y = height; y; y--) 
+	    {
+	       for (x = width; x; x--)
+#ifdef HAVE_SIGNED_SHIFT
+		  *mcblock++ = (*rblock++ + *rxblock++) >> 1;
+#else /* not HAVE_SIGNED_SHIFT */
+		  *mcblock++ = (*rblock++ + *rxblock++) / 2;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+	       rblock  += offset;
+	       rxblock += offset;
+	    }
+	 else				/* Halfpixel in xy direction */
+	    for (y = height; y; y--) 
+	    {
+	       for (x = width; x; x--)
+#ifdef HAVE_SIGNED_SHIFT
+		  *mcblock++ = (*rblock++ + *rxblock++
+				+ *ryblock++ + *rxyblock++) >> 2;
+#else /* not HAVE_SIGNED_SHIFT */
+		  *mcblock++ = (*rblock++ + *rxblock++
+				+ *ryblock++ + *rxyblock++) / 4;
+#endif /* not HAVE_SIGNED_SHIFT */
+	       rblock   += offset;
+	       ryblock  += offset;
+	       rxblock  += offset;
+	       rxyblock += offset;
+	    }
+      }
+   }
+}
diff --git a/converter/other/fiasco/codec/motion.h b/converter/other/fiasco/codec/motion.h
new file mode 100644
index 00000000..2ea382f7
--- /dev/null
+++ b/converter/other/fiasco/codec/motion.h
@@ -0,0 +1,35 @@
+/*
+ *  motion.h
+ *
+ *  Written by:		Ullrich Hafner
+ *			Michael Unger
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _MOTION_H
+#define _MOTION_H
+
+#include "wfa.h"
+#include "types.h"
+#include "image.h"
+
+void
+restore_mc (int enlarge_factor, image_t *image, const image_t *past,
+	    const image_t *future, const wfa_t *wfa);
+void
+extract_mc_block (word_t *mcblock, unsigned width, unsigned height,
+		  const word_t *reference, unsigned ref_width,
+		  bool_t half_pixel, unsigned xo, unsigned yo,
+		  unsigned mx, unsigned my);
+
+#endif /* not _MOTION_H */
+
diff --git a/converter/other/fiasco/codec/mwfa.c b/converter/other/fiasco/codec/mwfa.c
new file mode 100644
index 00000000..6f0af8be
--- /dev/null
+++ b/converter/other/fiasco/codec/mwfa.c
@@ -0,0 +1,864 @@
+/*
+ *  mwfa.c:		Initialization of MWFA coder
+ *
+ *  Written by:		Michael Unger
+ *			Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+
+#if HAVE_STRING_H
+#	include <string.h>
+#else /* not HAVE_STRING_H */
+#	include <strings.h>
+#endif /* not HAVE_STRING_H */
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "misc.h"
+#include "cwfa.h"
+#include "image.h"
+#include "mvcode.h"
+#include "motion.h"
+#include "mwfa.h"
+
+
+static const unsigned local_range = 6;
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static void
+get_mcpe (word_t *mcpe, const image_t *original,
+	  unsigned x0, unsigned y0, unsigned width, unsigned height,
+	  const word_t *mcblock1, const word_t *mcblock2);
+static real_t
+mcpe_norm (const image_t *original, unsigned x0, unsigned y0, unsigned width,
+	   unsigned height, const word_t *mcblock1, const word_t *mcblock2);
+static real_t 
+find_best_mv (real_t price, const image_t *original, const image_t *reference,
+	      unsigned x0, unsigned y0, unsigned width, unsigned height,
+	      real_t *bits, int *mx, int *my, const real_t *mc_norms,
+	      const wfa_info_t *wi, const motion_t *mt);
+static real_t
+find_second_mv (real_t price, const image_t *original,
+		const image_t *reference, const word_t *mcblock1,
+		unsigned xr, unsigned yr, unsigned width, unsigned height,
+		real_t *bits, int *mx, int *my, const wfa_info_t *wi,
+		const motion_t *mt);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+motion_t *
+alloc_motion (const wfa_info_t *wi)
+/*
+ *  Motion structure constructor.
+ *  Allocate memory for the motion structure and
+ *  fill in default values specified by 'wi'.
+ *
+ *  Return value:
+ *	pointer to the new option structure or NULL on error
+ */
+{
+   int	     dx;			/* motion vector coordinate */
+   unsigned  level;
+   unsigned range_size = wi->half_pixel
+			 ? square (wi->search_range)
+			 : square (2 * wi->search_range);
+   motion_t *mt        = Calloc (1, sizeof (motion_t));
+   
+   mt->original = NULL;
+   mt->past     = NULL;
+   mt->future   = NULL;
+   mt->xbits 	= Calloc (2 * wi->search_range, sizeof (real_t));
+   mt->ybits 	= Calloc (2 * wi->search_range, sizeof (real_t));
+
+   for (dx = -wi->search_range; dx < (int) wi->search_range; dx++)
+   {
+      mt->xbits [dx + wi->search_range]
+	 = mt->ybits [dx + wi->search_range]
+	 = mv_code_table [dx + wi->search_range][1];
+   }
+   
+   mt->mc_forward_norms = Calloc (MAXLEVEL, sizeof (real_t *));
+   mt->mc_backward_norms = Calloc (MAXLEVEL, sizeof (real_t *));
+   
+   for (level = wi->p_min_level; level <= wi->p_max_level; level++)
+   {
+      mt->mc_forward_norms  [level] = Calloc (range_size, sizeof (real_t));
+      mt->mc_backward_norms [level] = Calloc (range_size, sizeof (real_t));
+   }
+
+   return mt;
+}
+
+void
+free_motion (motion_t *mt)
+/*
+ *  Motion struct destructor:
+ *  Free memory of 'motion' struct.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	structure 'motion' is discarded.
+ */
+{
+   unsigned level;
+
+   Free (mt->xbits);
+   Free (mt->ybits);
+   for (level = 0; level < MAXLEVEL; level++)
+   {
+      if (mt->mc_forward_norms [level])
+	 Free (mt->mc_forward_norms [level]);
+      if (mt->mc_backward_norms [level])
+	 Free (mt->mc_backward_norms [level]);
+   }
+   Free (mt->mc_forward_norms);
+   Free (mt->mc_backward_norms);
+   Free (mt);
+}
+
+void
+subtract_mc (image_t *image, const image_t *past, const image_t *future,
+	     const wfa_t *wfa)
+/*
+ *  Subtract motion compensation from chrom channels of 'image'.
+ *  Reference frames are given by 'past' and 'future'.
+ *
+ *  No return values.
+ */
+{
+   unsigned  state, label;
+   word_t   *mcblock1 = Calloc (size_of_level (wfa->wfainfo->p_max_level),
+				sizeof (word_t));
+   word_t   *mcblock2 = Calloc (size_of_level (wfa->wfainfo->p_max_level),
+				sizeof (word_t));
+
+   for (state = wfa->basis_states; state < wfa->states; state++)
+      for (label = 0; label < MAXLABELS; label++)
+	 if (wfa->mv_tree [state][label].type != NONE)
+	 {
+	    color_e  band;		/* current color band */
+	    unsigned width, height;	/* size of mcblock */
+	    unsigned offset;		/* remaining pixels in original */
+	    
+	    width  = width_of_level (wfa->level_of_state [state] - 1);
+	    height = height_of_level (wfa->level_of_state [state] - 1);
+	    offset = image->width - width;
+	    
+	    switch (wfa->mv_tree [state][label].type)
+	    {
+	       case FORWARD:
+		  for (band  = first_band (image->color) + 1;
+		       band <= last_band (image->color); band++)
+		  {
+		     unsigned  y;	/* row of block */
+		     word_t   *mc1;	/* pixel in MC block 1 */
+		     word_t   *orig;	/* pixel in original image */
+		     
+		     extract_mc_block (mcblock1, width, height,
+				       past->pixels [band], past->width,
+				       wfa->wfainfo->half_pixel,
+				       wfa->x [state][label],
+				       wfa->y [state][label],
+				       (wfa->mv_tree [state][label].fx / 2)
+				       * 2,
+				       (wfa->mv_tree [state][label].fy / 2)
+				       * 2);
+		     mc1  = mcblock1;
+		     orig = image->pixels [band] + wfa->x [state][label]
+			    + wfa->y [state][label] * image->width;
+		     
+ 		     for (y = height; y; y--)
+		     {
+			unsigned x;	/* column of block */
+			
+			for (x = width; x; x--)
+			   *orig++ -= *mc1++;
+
+			orig += offset;
+		     }
+		  }
+		  break;
+	       case BACKWARD:
+		  for (band  = first_band (image->color) + 1;
+		       band <= last_band (image->color); band++)
+		  {
+		     unsigned  y;	/* row of block */
+		     word_t   *mc1;	/* pixel in MC block 1 */
+		     word_t   *orig;	/* pixel in original image */
+		     
+		     extract_mc_block (mcblock1, width, height,
+				       future->pixels [band], future->width,
+				       wfa->wfainfo->half_pixel,
+				       wfa->x [state][label],
+				       wfa->y [state][label],
+				       (wfa->mv_tree [state][label].bx / 2)
+				       * 2,
+				       (wfa->mv_tree [state][label].by / 2)
+				       * 2);
+		     mc1  = mcblock1;
+		     orig = image->pixels [band] + wfa->x [state][label]
+			    + wfa->y [state][label] * image->width;
+		     
+		     for (y = height; y; y--)
+		     {
+			unsigned x;	/* column of block */
+			
+			for (x = width; x; x--)
+			   *orig++ -= *mc1++;
+
+			orig += offset;
+		     }
+		  }
+		  break;
+	       case INTERPOLATED:
+		  for (band  = first_band (image->color) + 1;
+		       band <= last_band (image->color); band++)
+		  {
+		     unsigned  y;	/* row of block */
+		     word_t   *mc1;	/* pixel in MC block 1 */
+		     word_t   *mc2;	/* pixel in MC block 2 */
+		     word_t   *orig;	/* pixel in original image */
+		     
+		     extract_mc_block (mcblock1, width, height,
+				       past->pixels [band], past->width,
+				       wfa->wfainfo->half_pixel,
+				       wfa->x [state][label],
+				       wfa->y [state][label],
+				       (wfa->mv_tree[state][label].fx / 2)
+				       * 2,
+				       (wfa->mv_tree[state][label].fy / 2)
+				       * 2);
+		     extract_mc_block (mcblock2, width, height,
+				       future->pixels [band], future->width,
+				       wfa->wfainfo->half_pixel,
+				       wfa->x [state][label],
+				       wfa->y [state][label],
+				       (wfa->mv_tree[state][label].bx / 2)
+				       * 2,
+				       (wfa->mv_tree[state][label].by / 2)
+				       * 2);
+		     mc1  = mcblock1;
+		     mc2  = mcblock2;
+		     orig = image->pixels [band] + wfa->x [state][label]
+			    + wfa->y [state][label] * image->width;
+		     
+		     for (y = height; y; y--)
+		     {
+			unsigned x;	/* column of block */
+			
+			for (x = width; x; x--)
+			   *orig++ -= (*mc1++ + *mc2++) / 2;
+
+			orig += offset;
+		     }
+		  }
+		  break;
+	       default:
+		  break;
+	    }
+	 }
+
+   Free (mcblock1);
+   Free (mcblock2);
+}
+
+void
+find_P_frame_mc (word_t *mcpe, real_t price, range_t *range,
+		 const wfa_info_t *wi, const motion_t *mt)
+/*
+ *  Determine best motion vector for P-frame.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *            range->mvt_bits  (# of mv-tree bits)
+ *            range->mvxybits  (# of bits for vector components)
+ *            mt->mcpe         (MCPE in scan-order)
+ */
+{
+   unsigned  width   = width_of_level (range->level);
+   unsigned  height  = height_of_level (range->level);
+   word_t   *mcblock = Calloc (width * height, sizeof (word_t));
+   
+   range->mv_tree_bits = 1;
+   range->mv.type      = FORWARD;
+
+   /*
+    *  Find best matching forward prediction
+    */
+   find_best_mv (price, mt->original, mt->past, range->x, range->y,
+		 width, height, &range->mv_coord_bits, &range->mv.fx,
+		 &range->mv.fy, mt->mc_forward_norms [range->level], wi, mt);
+
+   /*
+    *  Compute MCPE
+    */
+   extract_mc_block (mcblock, width, height, mt->past->pixels [GRAY],
+		     mt->past->width, wi->half_pixel, range->x, range->y,
+		     range->mv.fx, range->mv.fy);
+   get_mcpe (mcpe, mt->original, range->x, range->y, width, height,
+	     mcblock, NULL);
+
+   Free (mcblock);
+}
+
+void
+find_B_frame_mc (word_t *mcpe, real_t price, range_t *range,
+		 const wfa_info_t *wi, const motion_t *mt)
+/*
+ *  Determines best motion compensation for B-frame.
+ *  Steps:
+ *         1)  find best forward motion vector
+ *         2)  find best backward motion vector
+ *         3)  try both motion vectors together (interpolation)
+ *         4)  choose best mode (FORWARD, BACKWARD or INTERPOLATED)
+ *  Bitcodes:
+ *    FORWARD      000
+ *    BACKWARD     001
+ *    INTERPOLATED  01
+ *  
+ *  Return values:
+ *            range->mvt_bits  (# of mv-tree bits)
+ *            range->mvxybits  (# of bits for vector components)
+ *            mt->mcpe         (MCPE in scan-order)
+ */
+{
+   mc_type_e  mctype;			/* type of motion compensation */
+   real_t     forward_costs;		/* costs of FORWARD mc */
+   real_t     backward_costs;		/* costs of BACKWARD mc */
+   real_t     interp_costs;		/* costs of INTERPOLATED mc */
+   real_t     forward_bits;		/* bits for FORWARD mc */
+   real_t     backward_bits;		/* bits for BACKWARD mc */
+   real_t     interp_bits;		/* bits for INTERPOLATED mc */
+   int	      fx,  fy;			/* coordinates FORWARD mc */
+   int	      bx,  by;			/* coordinates BACKWARD mc */
+   int	      ifx, ify;			/* coordinates forw. INTERPOLATED mc */
+   int	      ibx, iby;			/* coordinates back. INTERPOLATED mc */
+   unsigned   width    = width_of_level (range->level);
+   unsigned   height   = height_of_level (range->level);
+   word_t    *mcblock1 = Calloc (width * height, sizeof (word_t));
+   word_t    *mcblock2 = Calloc (width * height, sizeof (word_t));
+   
+   /*
+    *  Forward interpolation: use past frame as reference
+    */
+   forward_costs = find_best_mv (price, mt->original, mt->past,
+				 range->x, range->y, width, height,
+				 &forward_bits, &fx, &fy,
+				 mt->mc_forward_norms [range->level], wi, mt)
+		   + 3 * price; /* code 000 */
+
+   /*
+    *  Backward interpolation: use future frame as reference
+    */
+   backward_costs = find_best_mv (price, mt->original, mt->future,
+				  range->x, range->y, width, height,
+				  &backward_bits, &bx, &by,
+				  mt->mc_backward_norms [range->level], wi, mt)
+		    + 3 * price; /* code 001 */
+
+   /*
+    *  Bidirectional interpolation: use both past and future frame as reference
+    */
+   if (wi->cross_B_search) 
+   {
+      real_t icosts1;			/* costs interpolation alternative 1 */
+      real_t icosts2;			/* costs interpolation alternative 2 */
+      real_t ibackward_bits;		/* additional bits alternative 1 */
+      real_t iforward_bits;		/* additional bits alternative 1 */
+      
+      /*
+       *  Alternative 1: keep forward mv and vary backward mv locally
+       */
+      extract_mc_block (mcblock1, width, height, mt->past->pixels [GRAY],
+			mt->past->width, wi->half_pixel,
+			range->x, range->y, fx, fy);
+
+      ibx = bx;				/* start with backward coordinates */
+      iby = by;
+      icosts1 = find_second_mv (price, mt->original, mt->future,
+				mcblock1, range->x, range->y, width, height,
+				&ibackward_bits, &ibx, &iby, wi, mt)
+		+ (forward_bits + 2) * price; /* code 01 */
+
+      /*
+       *  Alternative 2: Keep backward mv and vary forward mv locally
+       */
+      extract_mc_block (mcblock1, width, height, mt->future->pixels [GRAY],
+			mt->future->width, wi->half_pixel,
+			range->x, range->y, bx, by);
+
+      ifx = fx;
+      ify = fy;
+      icosts2 = find_second_mv (price, mt->original, mt->past,
+				mcblock1, range->x, range->y, width, height,
+				&iforward_bits, &ifx, &ify, wi, mt)
+		+ (backward_bits + 2) * price; /* code 01 */
+      
+      /*
+       *  Choose best alternative
+       */
+      if (icosts1 < icosts2)
+      {
+	 ifx 	      = fx;
+	 ify 	      = fy;
+	 interp_bits  = forward_bits + ibackward_bits;
+	 interp_costs = icosts1;
+      }
+      else
+      {
+	 ibx 	      = bx;
+	 iby 	      = by;
+	 interp_bits  = iforward_bits + backward_bits;
+	 interp_costs = icosts2;
+      }
+   }
+   else					/* local exhaustive search */
+   {
+      /*
+       *  Keep forward and backward mv due to time constraints
+       */
+
+      ifx = fx;
+      ify = fy;
+      ibx = bx;
+      iby = by;
+      interp_bits = forward_bits + backward_bits;
+
+      extract_mc_block (mcblock1, width, height, mt->past->pixels [GRAY],
+			mt->past->width, wi->half_pixel,
+			range->x, range->y, fx, fy);
+      extract_mc_block (mcblock2, width, height, mt->future->pixels [GRAY],
+			mt->future->width, wi->half_pixel,
+			range->x, range->y, bx, by);
+      interp_costs = mcpe_norm (mt->original, range->x, range->y,
+				width, height, mcblock1, mcblock2)
+		     + (interp_bits + 2) * price; /* code 01 */
+   }
+
+   /*
+    *  Choose alternative with smallest costs
+    */
+   if (forward_costs <= interp_costs)
+   {
+      if (forward_costs <= backward_costs)
+	 mctype = FORWARD;
+      else
+	 mctype = BACKWARD;
+   }
+   else
+   {
+      if (backward_costs <= interp_costs)
+	 mctype = BACKWARD;
+      else
+	 mctype = INTERPOLATED;
+   }
+
+   switch (mctype)
+   {
+      case FORWARD:
+	 range->mv_tree_bits  = 3;
+	 range->mv_coord_bits = forward_bits;
+	 range->mv.type       = FORWARD;
+	 range->mv.fx         = fx;
+	 range->mv.fy         = fy;
+	 extract_mc_block (mcblock1, width, height, mt->past->pixels [GRAY],
+			   mt->past->width, wi->half_pixel,
+			   range->x, range->y, range->mv.fx, range->mv.fy);
+	 get_mcpe (mcpe, mt->original, range->x, range->y, width, height,
+		   mcblock1, NULL);
+	 break;
+      case BACKWARD:
+	 range->mv_tree_bits  = 3;
+	 range->mv_coord_bits = backward_bits;
+	 range->mv.type       = BACKWARD;
+	 range->mv.bx         = bx;
+	 range->mv.by         = by;
+	 extract_mc_block (mcblock1, width, height, mt->future->pixels [GRAY],
+			   mt->future->width, wi->half_pixel,
+			   range->x, range->y, range->mv.bx, range->mv.by);
+	 get_mcpe (mcpe, mt->original, range->x, range->y, width, height,
+		   mcblock1, NULL);
+	 break;
+      case INTERPOLATED:
+	 range->mv_tree_bits  = 2;
+	 range->mv_coord_bits = interp_bits;
+	 range->mv.type       = INTERPOLATED;
+	 range->mv.fx         = ifx;
+	 range->mv.fy         = ify;
+	 range->mv.bx         = ibx;
+	 range->mv.by         = iby;
+	 extract_mc_block (mcblock1, width, height, mt->past->pixels [GRAY],
+			   mt->past->width, wi->half_pixel,
+			   range->x, range->y, range->mv.fx, range->mv.fy);
+	 extract_mc_block (mcblock2, width, height, mt->future->pixels [GRAY],
+			   mt->future->width, wi->half_pixel,
+			   range->x, range->y, range->mv.bx, range->mv.by);
+	 get_mcpe (mcpe, mt->original, range->x, range->y, width, height,
+		   mcblock1, mcblock2);
+	 break;
+      default:
+	 break;
+   }
+
+   Free (mcblock1);
+   Free (mcblock2);
+}
+
+void
+fill_norms_table (unsigned x0, unsigned y0, unsigned level,
+		  const wfa_info_t *wi, motion_t *mt)
+/*
+ *  Compute norms of difference images for all possible displacements
+ *  in 'mc_forward_norm' and 'mc_backward_norm'.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'mt->mc_backward_norms' are computed
+ *	'mt->mc_forward_norms' are computed 
+ */
+{
+   int	     mx, my;			/* coordinates of motion vector */
+   unsigned  sr;			/* mv search range +-'sr' pixels */
+   unsigned  index   = 0;		/* index of motion vector */
+   unsigned  width   = width_of_level (level);
+   unsigned  height  = height_of_level (level);
+   word_t   *mcblock = Calloc (width * height, sizeof (word_t));
+
+   sr = wi->half_pixel ? wi->search_range / 2 :  wi->search_range;
+   
+   for (my = -sr; my < (int) sr; my++)
+      for (mx = -sr; mx < (int) sr; mx++, index++)
+      {
+	  if ((int) x0 + mx < 0 ||	/* block outside visible area */
+	      x0 + mx + width > mt->original->width || 
+	      (int) y0 + my < 0 ||
+	      y0 + my + height > mt->original->height)
+	  {
+	     mt->mc_forward_norms [level][index]  = 0.0;
+	     mt->mc_backward_norms [level][index] = 0.0;
+	  }
+	  else
+	  {
+	     extract_mc_block (mcblock, width, height, mt->past->pixels [GRAY],
+			       mt->past->width, wi->half_pixel,
+			       x0, y0, mx, my);
+	     mt->mc_forward_norms [level][index]
+		= mcpe_norm (mt->original, x0, y0, width, height,
+			     mcblock, NULL);
+
+	     if (mt->frame_type == B_FRAME)
+	     {
+		extract_mc_block (mcblock, width, height,
+				  mt->future->pixels [GRAY],
+				  mt->future->width, wi->half_pixel,
+				  x0, y0, mx, my);
+		mt->mc_backward_norms[level][index]
+		   = mcpe_norm (mt->original, x0, y0, width, height,
+				mcblock, NULL); 
+	     }
+	  }
+       }
+
+   Free (mcblock);
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static void
+get_mcpe (word_t *mcpe, const image_t *original, unsigned x0, unsigned y0,
+	  unsigned width, unsigned height, const word_t *mcblock1,
+	  const word_t *mcblock2)
+/*
+ *  Compute MCPE image 'original' - reference. The reference is either
+ *  composed of 'mcblock1' or of ('mcblock1' + 'mcblock2') / 2 (if
+ *  'mcblock2' != NULL).  Coordinates of original block are given by
+ *  'x0', 'y0', 'width', and 'height'.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'mcpe []' is filled with the delta image
+ */
+{
+   const word_t	*oblock;		/* pointer to original image */
+
+   assert (mcpe);
+   
+   oblock = original->pixels [GRAY] + y0 * original->width + x0;
+
+   if (mcblock2 != NULL)		/* interpolated prediction */
+   {
+      unsigned x, y;			/* current coordinates */
+      
+      for (y = height; y; y--) 
+      {
+	 for (x = width; x; x--)
+	    *mcpe++ = *oblock++ - (*mcblock1++ + *mcblock2++) / 2;
+
+	 oblock += original->width - width;
+      }
+   }
+   else					/* forward or backward prediction */
+   {
+      unsigned x, y;			/* current coordinates */
+      
+      for (y = height; y; y--) 
+      {
+	 for (x = width; x; x--)
+	    *mcpe++ = *oblock++ - *mcblock1++;
+      
+	 oblock += original->width - width;
+      }
+   }
+}
+
+static real_t
+mcpe_norm (const image_t *original, unsigned x0, unsigned y0, unsigned width,
+	   unsigned height, const word_t *mcblock1, const word_t *mcblock2)
+/*
+ *  Compute norm of motion compensation prediction error.
+ *  Coordinates of 'original' block are given by ('x0', 'y0')
+ *  and 'width', 'height'.
+ *  Reference blocks are stored in 'mcimage1' and 'mcimage2'.
+ *
+ *  Return value:
+ *	square of norm of difference image
+ */
+{
+   unsigned  n;
+   real_t    norm = 0;
+   word_t   *mcpe = Calloc (width * height, sizeof (word_t));
+   word_t   *ptr  = mcpe;
+   
+   get_mcpe (mcpe, original, x0, y0, width, height, mcblock1, mcblock2);
+
+   for (n = height * width; n; n--, ptr++) 
+      norm += square (*ptr / 16);
+   
+   Free (mcpe);
+   
+   return norm;
+}
+
+static real_t 
+find_best_mv (real_t price, const image_t *original, const image_t *reference,
+	      unsigned x0, unsigned y0, unsigned width, unsigned height,
+	      real_t *bits, int *mx, int *my, const real_t *mc_norms,
+	      const wfa_info_t *wi, const motion_t *mt)
+/*
+ *  Find best matching motion vector in image 'reference' to predict
+ *  the block ('x0', 'y0') of size 'width'x'height in image 'original'.
+ *
+ *  Return values:
+ *	prediction costs
+ *
+ *  Side effects:
+ *	'mx', 'my'		coordinates of motion vector
+ *	'bits'			number of bits to encode mv
+ */
+{
+   unsigned sr;				/* mv search range +/- 'sr' pixels */
+   unsigned index;			/* index of motion vector */
+   int 	    x, y;			/* coordinates of motion vector */
+   real_t   costs;			/* costs arising if mv is chosen */
+   real_t   mincosts = MAXCOSTS;	/* best costs so far  */
+   unsigned bitshift;			/* half_pixel coordinates multiplier */
+   
+   *mx = *my = 0;
+
+   /*
+    *  Find best fitting motion vector:
+    *  Use exhaustive search in the interval x,y +- sr (no halfpixel accuracy)
+    *					  or x,y +- sr/2  (halfpixel accuracy)
+    */
+   sr 	    = wi->half_pixel ? wi->search_range / 2 :  wi->search_range;
+   bitshift = (wi->half_pixel ? 2 : 1);	/* bit0 reserved for halfpixel pred. */
+   
+   for (index = 0, y = -sr; y < (int) sr; y++)
+      for (x = -sr; x < (int) sr; x++, index++)
+	 if ((int) x0 + x >= 0 && (int) y0 + y >= 0 &&	
+	     x0 + x + width  <= original->width && 
+	     y0 + y + height <= original->height)
+	 {
+	    /*
+	     *  Block is inside visible area.
+	     *  Compare current costs with 'mincosts'
+	     */
+	    costs = mc_norms [index]
+		    + (mt->xbits [(x + sr) * bitshift]
+		       + mt->ybits [(y + sr) * bitshift]) * price;
+
+	     if (costs < mincosts)
+	     {
+		mincosts = costs;
+		*mx      = x * bitshift;
+		*my      = y * bitshift;
+	     }
+	 }
+
+   /*
+    *  Halfpixel prediction:
+    *  Compare all nine combinations (-1, y), (0, y), (+1, y) for y = -1,0,+1
+    */
+   if (wi->half_pixel)
+   {
+      int	rx, ry;			/* halfpixel refinement */
+      unsigned	bestrx, bestry;		/* coordinates of best mv */
+      word_t   *mcblock = Calloc (width * height, sizeof (word_t));
+      
+      bestrx = bestry = 0;
+      for (rx = -1; rx <= 1; rx++)
+	 for (ry = -1; ry <= 1; ry++)
+	 {
+	    /*
+	     *  Check if the new motion vector is in allowed area
+	     */
+	    if (rx == 0 && ry == 0)	/* already tested */
+	       continue;
+	    if ((int) x0 + (*mx / 2) + rx < 0 || /* outside visible area */
+		x0 + (*mx / 2) + rx + width > original->width ||
+		(int) y0 + (*my / 2) + ry < 0 || 
+		y0 + (*my / 2) + ry + height > original->height)
+	       continue;
+	    if (*mx + rx < (int) -sr || *mx + rx >= (int) sr ||
+		*my + ry < (int) -sr || *my + ry >= (int) sr) 
+	       continue;		/* out of bounds */
+
+	    /*
+	     *  Compute costs of new motion compensation
+	     */
+	    extract_mc_block (mcblock, width, height,
+			      reference->pixels [GRAY],
+			      reference->width, wi->half_pixel,
+			      x0, y0, *mx + rx, *my + ry);
+	    costs = mcpe_norm (mt->original, x0, y0, width, height, mcblock,
+			       NULL)
+		    + (mt->xbits [*mx + rx + sr * bitshift]
+		       + mt->ybits [*my + ry + sr * bitshift]) * price;
+	    if (costs < mincosts)
+	    {
+	       bestrx   = rx;
+	       bestry   = ry;
+	       mincosts = costs;
+	    }
+	 }
+
+      *mx += bestrx;
+      *my += bestry;
+
+      Free (mcblock);
+   } /* halfpixel */
+	     
+   *bits = mt->xbits [*mx + sr * bitshift] + mt->ybits [*my + sr * bitshift];
+
+   return mincosts;
+}
+
+static real_t
+find_second_mv (real_t price, const image_t *original,
+		const image_t *reference, const word_t *mcblock1,
+		unsigned xr, unsigned yr, unsigned width, unsigned height,
+		real_t *bits, int *mx, int *my, const wfa_info_t *wi,
+		const motion_t *mt)
+/*
+ *  Search local area (*mx,*my) for best additional mv.
+ *  Overwrite mt->tmpblock.
+ *  TODO check sr = search_range
+ *
+ *  Return values:
+ *	prediction costs
+ *
+ *  Side effects:
+ *	'mx','my'	coordinates of mv
+ *      'bits'		number of bits to encode mv
+ */
+{
+   real_t    mincosts = MAXCOSTS;	/* best costs so far  */
+   unsigned  sr;			/* MV search range +/- 'sr' pixels */
+   int       x, y;			/* coordinates of motion vector */
+   int       y0, y1, x0, x1;		/* start/end coord. of search range */
+   unsigned  bitshift;			/* half_pixel coordinates multiplier */
+   word_t   *mcblock2 = Calloc (width * height, sizeof (word_t));
+
+   sr = wi->search_range;
+
+   y0 = max ((int) -sr, *my - (int) local_range);
+   y1 = min ((int) sr, *my + (int) local_range);
+   x0 = max ((int) -sr, *mx - (int) local_range);
+   x1 = min ((int) sr, *mx + (int) local_range);
+
+   *mx = *my = 0;
+
+   bitshift = (wi->half_pixel ? 2 : 1);	/* bit0 reserved for halfpixel pred. */
+
+   
+   for (y = y0; y < y1; y++)
+      for (x = x0; x < x1; x++)
+      {
+	 real_t costs;			/* costs arising if mv is chosen */
+	 
+	 /*
+	  *  Test each mv ('x', 'y') in the given search range:
+	  *  Get the new motion compensation image from 'reference' and compute
+	  *  the norm of the motion compensation prediction error
+	  *  'original' - 0.5 * ('firstmc' + 'reference')
+	  */
+	 if ((int) (xr * bitshift) + x < 0 ||	/* outside visible area */
+	     xr * bitshift + x > (original->width - width) * bitshift ||
+	     (int) (yr * bitshift) + y < 0 ||
+	     yr * bitshift + y > (original->height - height) * bitshift)
+	    continue;
+	 
+	 extract_mc_block (mcblock2, width, height,
+			   reference->pixels [GRAY], reference->width,
+			   wi->half_pixel, x0, y0, x, y);
+
+	 costs  = mcpe_norm (mt->original, x0, y0, width, height,
+			     mcblock1, mcblock2)
+		  + (mt->xbits [x + sr] + mt->ybits [y + sr]) * price;
+	 
+	 if (costs < mincosts)
+	 {
+	    mincosts = costs;
+	    *mx      = x;
+	    *my      = y;
+	 }
+      }
+
+   *bits = mt->xbits [*mx + sr] + mt->ybits [*my + sr];
+
+   Free (mcblock2);
+
+   return mincosts;
+}
diff --git a/converter/other/fiasco/codec/mwfa.h b/converter/other/fiasco/codec/mwfa.h
new file mode 100644
index 00000000..52f41866
--- /dev/null
+++ b/converter/other/fiasco/codec/mwfa.h
@@ -0,0 +1,44 @@
+/*
+ *  mwfa.h
+ *
+ *  Written by:		Michael Unger
+ *			Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _MWFA_H
+#define _MWFA_H
+
+#include "types.h"
+#include "image.h"
+#include "wfa.h"
+#include "cwfa.h"
+
+void
+fill_norms_table (unsigned x0, unsigned y0, unsigned level,
+		  const wfa_info_t *wi, motion_t *mt);
+void
+find_B_frame_mc (word_t *mcpe, real_t price, range_t *range,
+		 const wfa_info_t *wi, const motion_t *mt);
+void
+find_P_frame_mc (word_t *mcpe, real_t price, range_t *range,
+		 const wfa_info_t *wi, const motion_t *mt);
+void
+subtract_mc (image_t *image, const image_t *past, const image_t *future,
+	     const wfa_t *wfa);
+void
+free_motion (motion_t *mt);
+motion_t *
+alloc_motion (const wfa_info_t *wi);
+
+#endif /* not _MWFA_H */
+
diff --git a/converter/other/fiasco/codec/options.c b/converter/other/fiasco/codec/options.c
new file mode 100644
index 00000000..77dbaf00
--- /dev/null
+++ b/converter/other/fiasco/codec/options.c
@@ -0,0 +1,894 @@
+/*
+ *  options.c:		FIASCO options handling
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/10/28 17:39:31 $
+ *  $Author: hafner $
+ *  $Revision: 5.5 $
+ *  $State: Exp $
+ */
+
+#define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include "config.h"
+
+#include <string.h>
+#if STDC_HEADERS
+#	include <stdlib.h>
+#endif /* not STDC_HEADERS */
+
+#include <stdio.h>
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "wfa.h"
+#include "misc.h"
+#include "bit-io.h"
+#include "fiasco.h"
+#include "options.h"
+
+fiasco_c_options_t *
+fiasco_c_options_new (void)
+/*
+ *  FIASCO options constructor.
+ *  Allocate memory for the FIASCO coder options structure and
+ *  fill in default values.
+ *
+ *  Return value:
+ *	pointer to the new option structure
+ */
+{
+   c_options_t 	      *options = calloc (1, sizeof (c_options_t));
+   fiasco_c_options_t *public  = calloc (1, sizeof (fiasco_c_options_t));
+
+   if (!options || !public)
+   {
+      set_error (_("Out of memory."));
+      return NULL;
+   }
+   public->private 	      = options;
+   public->delete 	      = fiasco_c_options_delete;
+   public->set_tiling 	      = fiasco_c_options_set_tiling;
+   public->set_frame_pattern  = fiasco_c_options_set_frame_pattern;
+   public->set_basisfile      = fiasco_c_options_set_basisfile;
+   public->set_chroma_quality = fiasco_c_options_set_chroma_quality;
+   public->set_optimizations  = fiasco_c_options_set_optimizations;
+   public->set_video_param    = fiasco_c_options_set_video_param;
+   public->set_quantization   = fiasco_c_options_set_quantization;
+   public->set_progress_meter = fiasco_c_options_set_progress_meter;
+   public->set_smoothing      = fiasco_c_options_set_smoothing;
+   public->set_title   	      = fiasco_c_options_set_title;
+   public->set_comment        = fiasco_c_options_set_comment;
+   
+   strcpy (options->id, "COFIASCO");
+
+   /*
+    *  Set default value of fiasco options
+    */
+   options->basis_name 		  = strdup ("small.fco");
+   options->lc_min_level 	  = 4;
+   options->lc_max_level 	  = 12;
+   options->p_min_level 	  = 8;
+   options->p_max_level 	  = 10;
+   options->images_level 	  = 5;
+   options->max_states 		  = MAXSTATES;
+   options->chroma_max_states 	  = 40;
+   options->max_elements 	  = MAXEDGES;
+   options->tiling_exponent 	  = 4;
+   options->tiling_method 	  = FIASCO_TILING_VARIANCE_DSC;
+   options->id_domain_pool 	  = strdup ("rle");
+   options->id_d_domain_pool 	  = strdup ("rle");
+   options->id_rpf_model 	  = strdup ("adaptive");
+   options->id_d_rpf_model 	  = strdup ("adaptive");
+   options->rpf_mantissa 	  = 3;
+   options->rpf_range 		  = FIASCO_RPF_RANGE_1_50;
+   options->dc_rpf_mantissa 	  = 5;
+   options->dc_rpf_range 	  = FIASCO_RPF_RANGE_1_00;
+   options->d_rpf_mantissa 	  = 3;
+   options->d_rpf_range 	  = FIASCO_RPF_RANGE_1_50;
+   options->d_dc_rpf_mantissa 	  = 5;
+   options->d_dc_rpf_range 	  = FIASCO_RPF_RANGE_1_00;
+   options->chroma_decrease 	  = 2.0;
+   options->prediction 		  = NO;
+   options->delta_domains 	  = YES;
+   options->normal_domains 	  = YES;
+   options->search_range 	  = 16;
+   options->fps 		  = 25;
+   options->pattern 		  = strdup ("IPPPPPPPPP");
+   options->reference_filename 	  = NULL;
+   options->half_pixel_prediction = NO;
+   options->cross_B_search 	  = YES;
+   options->B_as_past_ref 	  = YES;
+   options->check_for_underflow   = NO;
+   options->check_for_overflow 	  = NO;
+   options->second_domain_block   = NO;
+   options->full_search 	  = NO;
+   options->progress_meter 	  = FIASCO_PROGRESS_NONE;
+   options->smoothing 	 	  = 70;
+   options->comment 		  = strdup ("");
+   options->title 		  = strdup ("");
+   
+   return public;
+}
+
+void
+fiasco_c_options_delete (fiasco_c_options_t *options)
+/*
+ *  FIASCO options destructor.
+ *  Free memory of FIASCO options struct.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	structure 'options' is discarded.
+ */
+{
+   c_options_t *this = cast_c_options (options);
+
+   if (!this)
+      return;
+   
+   Free (this->id_domain_pool);
+   Free (this->id_d_domain_pool);
+   Free (this->id_rpf_model);
+   Free (this->id_d_rpf_model);
+   Free (this->pattern);
+   Free (this->comment);
+   Free (this->title);
+   
+   Free (this);
+
+   return;
+}
+
+int
+fiasco_c_options_set_tiling (fiasco_c_options_t *options,
+			     fiasco_tiling_e method, unsigned exponent)
+/*
+ *  Set tiling `method' and `exponent'.
+ *  See type `fiasco_tiling_e' for a list of valid  tiling `methods'.
+ *  The image is subdivied into 2^`exponent' tiles
+ *
+ *  Return value:
+ *	1 on success
+ *	0 otherwise
+ */
+{
+   c_options_t *this = (c_options_t *) cast_c_options (options);
+
+   if (!this)
+   {
+      return 0;
+   }
+   switch (method)
+   {
+      case FIASCO_TILING_SPIRAL_ASC:
+      case FIASCO_TILING_SPIRAL_DSC:
+      case FIASCO_TILING_VARIANCE_ASC:
+      case FIASCO_TILING_VARIANCE_DSC:
+	 this->tiling_method = method;
+	 break;
+      default:
+	 set_error (_("Invalid tiling method `%d' specified "
+		      "(valid methods are 0, 1, 2, or 3)."), method);
+	 return 0;
+   }
+   this->tiling_exponent = exponent;
+   
+   return 1;
+}
+
+int
+fiasco_c_options_set_frame_pattern (fiasco_c_options_t *options,
+				    const char *pattern)
+/*
+ *  Set `pattern' of input frames.
+ *  `pattern' has to be a sequence of the following
+ *  characters (case insensitive):
+ *  'i' intra frame
+ *  'p' predicted frame
+ *  'b' bidirectional predicted frame
+ *  E.g. pattern = 'IBBPBBPBB'
+ *
+ *  When coding video frames the prediction type of input frame N is determined
+ *  by reading `pattern' [N] (`pattern' is periodically extended).
+ *  
+ *  Return value:
+ *	1 on success
+ *	0 otherwise
+ */
+{
+   c_options_t *this = (c_options_t *) cast_c_options (options);
+
+   if (!this)
+   {
+      return 0;
+   }
+   else if (!pattern)
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "pattern");
+      return 0;
+   }
+   else if (strlen (pattern) < 1)
+   {
+      set_error (_("Frame type pattern doesn't contain any character."));
+      return 0;
+   }
+   else
+   {
+      const char *str;
+      bool_t 	  parse_error = NO;
+      int	  c 	      = 0;
+      
+      for (str = pattern; *str && !parse_error; str++)
+	 switch (*str)
+	 {
+	    case 'i':
+	    case 'I':
+	    case 'b':
+	    case 'B':
+	    case 'p':
+	    case 'P':
+	       break;
+	    default:
+	       c = *str;
+	       parse_error = YES;
+	 }
+
+      if (parse_error)
+      {
+	 set_error (_("Frame type pattern contains invalid character `%c' "
+		      "(choose I, B or P)."), c);
+	 return 0;
+      }
+      else
+      {
+	 Free (this->pattern);
+	 this->pattern = strdup (pattern);
+
+	 return 1;
+      }
+   }
+}
+
+int
+fiasco_c_options_set_basisfile (fiasco_c_options_t *options,
+				const char *filename)
+/*
+ *  Set `filename' of FIASCO initial basis.
+ *  
+ *  Return value:
+ *	1 on success (if the file is readable)
+ *	0 otherwise
+ */
+{
+   c_options_t *this = (c_options_t *) cast_c_options (options);
+
+   if (!this)
+   {
+      return 0;
+   }
+   else if (!filename)
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "filename");
+      return 0;
+   }
+   else
+   {
+       /* Skip this because basis file may be linked with program, not
+          in a separate file.  See get_linked_basis().  NETPBM
+      FILE *file = open_file (filename, "FIASCO_DATA", READ_ACCESS);
+      if (file)
+      {
+	 fclose (file);
+	 return 1;
+      }
+      else
+      {
+	 set_error (_("Can't read basis file `%s'.\n%s."), filename,
+		    get_system_error ());
+	 return 0;
+      }
+      */ return 1;
+   }
+}
+
+int
+fiasco_c_options_set_chroma_quality (fiasco_c_options_t *options,
+				     float quality_factor,
+				     unsigned dictionary_size)
+/*
+ *  Set color compression parameters.
+ *  When coding chroma channels (Cb and Cr)
+ *  - approximation quality is given by `quality_factor' * `Y quality' and
+ *  - `dictionary_size' gives the number of dictionary elements.
+ *  
+ *  If 'quality' <= 0 then the luminancy coding quality is also during
+ *  chroma channel coding.
+ *  
+ *  Return value:
+ *	1 on success
+ *	0 otherwise
+ */
+{
+   c_options_t *this = (c_options_t *) cast_c_options (options);
+
+   if (!this)
+   {
+      return 0;
+   }
+   else if (!dictionary_size)
+   {
+      set_error (_("Size of chroma compression dictionary has to be "
+		   "a positive number."));
+      return 0;
+   }
+   else if (quality_factor <= 0)
+   {
+      set_error (_("Quality of chroma channel compression has to be "
+		   "positive value."));
+      return 0;
+   }
+   else
+   {
+      this->chroma_decrease   = quality_factor;
+      this->chroma_max_states = dictionary_size;
+
+      return 1;
+   }
+}
+
+int
+fiasco_c_options_set_optimizations (fiasco_c_options_t *options,
+				    unsigned min_block_level,
+				    unsigned max_block_level,
+				    unsigned max_elements,
+				    unsigned dictionary_size,
+				    unsigned optimization_level)
+/*
+ *  Set various optimization parameters.
+ *  - During compression only image blocks of size
+ *    {`min_block_level', ... ,`max_block_level'} are considered.
+ *    The smaller this set of blocks is the faster the coder runs
+ *    and the worse the image quality will be.  
+ *  - An individual approximation may use at most `max_elements'
+ *    elements of the dictionary which itself contains at most
+ *    `dictionary_size' elements. The smaller these values are
+ *    the faster the coder runs and the worse the image quality will be. 
+ *  - `optimization_level' enables some additional low level optimizations.
+ *    0: standard approximation method
+ *    1: significantly increases the approximation quality,
+ *       running time is twice as high as with the standard method
+ *    2: hardly increases the approximation quality of method 1, 
+ *       running time is twice as high as with method 1
+ *       (this method just remains for completeness)
+ *
+ *  Return value:
+ *	1 on success
+ *	0 otherwise
+ */
+{
+   c_options_t *this = (c_options_t *) cast_c_options (options);
+
+   if (!this)
+   {
+      return 0;
+   }
+   else if (!dictionary_size)
+   {
+      set_error (_("Size of dictionary has to be a positive number."));
+      return 0;
+   }
+   else if (!max_elements)
+   {
+      set_error (_("At least one dictionary element has to be used "
+		   "in an approximation."));
+      return 0;
+   }
+   else if (max_block_level < 4)
+   {
+      set_error (_("Maximum image block size has to be at least level 4."));
+      return 0;
+   }
+   else if (min_block_level < 4)
+   {
+      set_error (_("Minimum image block size has to be at least level 4."));
+      return 0;
+   }
+   else if (max_block_level < min_block_level)
+   {
+      set_error (_("Maximum block size has to be larger or "
+		   "equal minimum block size."));
+      return 0;
+   }
+   else
+   {
+      this->lc_min_level 	= min_block_level;
+      this->lc_max_level 	= max_block_level;
+      this->max_states 	 	= dictionary_size;
+      this->max_elements 	= max_elements;
+      this->second_domain_block = optimization_level > 0 ? YES : NO;
+      this->check_for_overflow  = optimization_level > 1 ? YES : NO;
+      this->check_for_underflow = optimization_level > 1 ? YES : NO;
+      this->full_search 	= optimization_level > 1 ? YES : NO;
+
+      return 1;
+   }
+}
+
+int
+fiasco_c_options_set_prediction (fiasco_c_options_t *options,
+				 int intra_prediction,
+				 unsigned min_block_level,
+				 unsigned max_block_level)
+/*
+ *  Set minimum and maximum size of image block prediction to
+ *  `min_block_level' and `max_block_level'.
+ *  (For either motion compensated prediction of inter frames
+ *   or DC based prediction of intra frames)
+ *  Prediction of intra frames is only used if `intra_prediction' != 0.
+ *
+ *  Return value:
+ *	1 on success
+ *	0 otherwise
+ */
+{
+   c_options_t *this = (c_options_t *) cast_c_options (options);
+
+   if (!this)
+   {
+      return 0;
+   }
+   else if (max_block_level < 6)
+   {
+      set_error (_("Maximum prediction block size has to be "
+		   "at least level 6"));
+      return 0;
+   }
+   else if (min_block_level < 6)
+   {
+      set_error (_("Minimum prediction block size has to be "
+		   "at least level 6"));
+      return 0;
+   }
+   else if (max_block_level < min_block_level)
+   {
+      set_error (_("Maximum prediction block size has to be larger or "
+		   "equal minimum block size."));
+      return 0;
+   }
+   else
+   {
+      this->p_min_level = min_block_level;
+      this->p_max_level = max_block_level;
+      this->prediction  = intra_prediction;
+      
+      return 1;
+   }
+}
+
+int
+fiasco_c_options_set_video_param (fiasco_c_options_t *options,
+				  unsigned frames_per_second,
+				  int half_pixel_prediction,
+				  int cross_B_search,
+				  int B_as_past_ref)
+/*
+ *  Set various parameters used for video compensation.
+ *  'frames_per_second' defines the frame rate which should be
+ *  used when the video is decoded. This value has no effect during coding,
+ *  it is just passed to the FIASCO output file.
+ *  If 'half_pixel_prediction' is not 0 then half pixel precise
+ *  motion compensated prediction is used.
+ *  If 'cross_B_search' is not 0 then the fast Cross-B-Search algorithm is
+ *  used to determine the motion vectors of interpolated prediction. Otherwise
+ *  exhaustive search (in the given search range) is used.
+ *  If 'B_as_past_ref' is not 0 then B frames are allowed to be used
+ *  for B frame predicion.
+ *  
+ *  Return value:
+ *	1 on success
+ *	0 otherwise
+ */
+{
+   c_options_t *this = (c_options_t *) cast_c_options (options);
+
+   if (!this)
+   {
+      return 0;
+   }
+   else
+   {
+      this->fps 	  	  = frames_per_second;
+      this->half_pixel_prediction = half_pixel_prediction;
+      this->cross_B_search 	  = cross_B_search;
+      this->B_as_past_ref 	  = B_as_past_ref;
+
+      return 1;
+   }
+}
+
+int
+fiasco_c_options_set_quantization (fiasco_c_options_t *options,
+				   unsigned mantissa,
+				   fiasco_rpf_range_e range,
+				   unsigned dc_mantissa,
+				   fiasco_rpf_range_e dc_range)
+/*
+ *  Set accuracy of coefficients quantization.
+ *  DC coefficients (of the constant dictionary vector f(x,y) = 1)
+ *  are quantized to values of the interval [-`dc_range', `dc_range'] using
+ *  #`dc_mantissa' bits. All other quantized coefficients are quantized in
+ *  an analogous way using the parameters `range' and `mantissa'.
+ *  
+ *  Return value:
+ *	1 on success
+ *	0 otherwise
+ */
+{
+   c_options_t *this = (c_options_t *) cast_c_options (options);
+
+   if (!this)
+   {
+      return 0;
+   }
+   else if (mantissa < 2 || mantissa > 8 || dc_mantissa < 2 || dc_mantissa > 8)
+   {
+      set_error (_("Number of RPF mantissa bits `%d', `%d' have to be in "
+		   "the interval [2,8]."), mantissa, dc_mantissa);
+      return 0;
+   }
+   else
+   {
+      if ((range == FIASCO_RPF_RANGE_0_75
+	  || range == FIASCO_RPF_RANGE_1_00
+	  || range == FIASCO_RPF_RANGE_1_50
+	   || range == FIASCO_RPF_RANGE_2_00)
+	  &&
+	  (dc_range == FIASCO_RPF_RANGE_0_75
+	   || dc_range == FIASCO_RPF_RANGE_1_00
+	   || dc_range == FIASCO_RPF_RANGE_1_50
+	   || dc_range == FIASCO_RPF_RANGE_2_00))
+      {
+	 this->rpf_range       = range;
+	 this->dc_rpf_range    = dc_range;
+	 this->rpf_mantissa    = mantissa;
+	 this->dc_rpf_mantissa = dc_mantissa;
+
+	 return 1;
+      }
+      else
+      {
+	 set_error (_("Invalid RPF ranges `%d', `%d' specified."),
+		    range, dc_range);
+	 return 0;
+      }
+   }
+}
+
+int
+fiasco_c_options_set_progress_meter (fiasco_c_options_t *options,
+				     fiasco_progress_e type)
+/*
+ *  Set type of progress meter.
+ *
+ *  Return value:
+ *	1 on success
+ *	0 otherwise
+ */
+{
+   c_options_t *this = (c_options_t *) cast_c_options (options);
+
+   if (!this)
+   {
+      return 0;
+   }
+   switch (type)
+   {
+      case FIASCO_PROGRESS_BAR:
+      case FIASCO_PROGRESS_PERCENT:
+      case FIASCO_PROGRESS_NONE:
+	 this->progress_meter = type;
+	 break;
+      default:
+	 set_error (_("Invalid progress meter `%d' specified "
+		      "(valid values are 0, 1, or 2)."), type);
+	 return 0;
+   }
+   return 1;
+}
+
+int
+fiasco_c_options_set_smoothing (fiasco_c_options_t *options, int smoothing)
+/*
+ *  Define `smoothing'-percentage along partitioning borders.
+ *  
+ *  Return value:
+ *	1 on success
+ *	0 otherwise
+ */
+{
+   c_options_t *this = (c_options_t *) cast_c_options (options);
+
+   if (!this)
+   {
+      return 0;
+   }
+   else if (smoothing < -1 || smoothing > 100)
+   {
+      set_error (_("Smoothing percentage must be in the range [-1, 100]."));
+      return 0;
+   }
+   else
+   {
+      this->smoothing = smoothing;
+      return 1;
+   }
+}
+
+int
+fiasco_c_options_set_comment (fiasco_c_options_t *options, const char *comment)
+/*
+ *  Define `comment' of FIASCO stream.
+ *  
+ *  Return value:
+ *	1 on success
+ *	0 otherwise
+ */
+{
+   c_options_t *this = (c_options_t *) cast_c_options (options);
+
+   if (!this)
+   {
+      return 0;
+   }
+   else if (!comment)
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "title");
+      return 0;
+   }
+   else
+   {
+      this->comment = strdup (comment);
+      return 1;
+   }
+}
+
+int
+fiasco_c_options_set_title (fiasco_c_options_t *options, const char *title)
+/*
+ *  Define `title' of FIASCO stream.
+ *  
+ *  Return value:
+ *	1 on success
+ *	0 otherwise
+ */
+{
+   c_options_t *this = (c_options_t *) cast_c_options (options);
+
+   if (!this)
+   {
+      return 0;
+   }
+   else if (!title)
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "title");
+      return 0;
+   }
+   else
+   {
+      this->title = strdup (title);
+      return 1;
+   }
+}
+
+c_options_t *
+cast_c_options (fiasco_c_options_t *options)
+/*
+ *  Cast generic pointer `options' to type c_options_t.
+ *  Check whether `options' is a valid object of type c_options_t.
+ *
+ *  Return value:
+ *	pointer to options struct on success
+ *      NULL otherwise
+ */
+{
+   c_options_t *this = (c_options_t *) options->private;
+   if (this)
+   {
+      if (!streq (this->id, "COFIASCO"))
+      {
+	 set_error (_("Parameter `options' doesn't match required type."));
+	 return NULL;
+      }
+   }
+   else
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "options");
+   }
+
+   return this;
+}
+
+/**************************************************************************
+***************************************************************************
+			       DECODER
+***************************************************************************
+**************************************************************************/
+
+fiasco_d_options_t *
+fiasco_d_options_new (void)
+/*
+ *  FIASCO options constructor.
+ *  Allocate memory for the FIASCO coder options structure and
+ *  fill in default values.
+ *
+ *  Return value:
+ *	pointer to the new option structure
+ */
+{
+   d_options_t 	      *options = calloc (1, sizeof (d_options_t));
+   fiasco_d_options_t *public  = calloc (1, sizeof (fiasco_d_options_t));
+
+   if (!options || !public)
+   {
+      set_error (_("Out of memory."));
+      return NULL;
+   }
+   public->private 	      = options;
+   public->delete 	      = fiasco_d_options_delete;
+   public->set_smoothing      = fiasco_d_options_set_smoothing;
+   public->set_magnification  = fiasco_d_options_set_magnification;
+   public->set_4_2_0_format   = fiasco_d_options_set_4_2_0_format;
+   
+   strcpy (options->id, "DOFIASCO");
+
+   /*
+    *  Set default value of fiasco decoder options
+    */
+   options->smoothing 	  = 70;
+   options->magnification = 0;
+   options->image_format  = FORMAT_4_4_4;
+   
+   return public;
+}
+
+void
+fiasco_d_options_delete (fiasco_d_options_t *options)
+/*
+ *  FIASCO options destructor.
+ *  Free memory of FIASCO options struct.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	structure 'options' is discarded.
+ */
+{
+   d_options_t *this = cast_d_options (options);
+
+   if (!this)
+      return;
+   
+   Free (this);
+
+   return;
+}
+
+int
+fiasco_d_options_set_smoothing (fiasco_d_options_t *options, int smoothing)
+/*
+ *  Define `smoothing'-percentage along partitioning borders.
+ *  
+ *  Return value:
+ *	1 on success
+ *	0 otherwise
+ */
+{
+   d_options_t *this = (d_options_t *) cast_d_options (options);
+
+   if (!this)
+   {
+      return 0;
+   }
+   else if (smoothing < -1 || smoothing > 100)
+   {
+      set_error (_("Smoothing percentage must be in the range [-1, 100]."));
+      return 0;
+   }
+   else
+   {
+      this->smoothing = smoothing;
+      return 1;
+   }
+}
+
+int
+fiasco_d_options_set_magnification (fiasco_d_options_t *options, int level)
+/*
+ *  Set magnification-'level' of decoded image.
+ *  0: width x height of original image
+ *  1: (2 * width) x (2 * height) of original image
+ *  -1: (width / 2 ) x (height / 2) of original image
+ *  etc.
+ *
+ *  Return value:
+ *	1 on success
+ *	0 otherwise
+ */
+{
+   d_options_t *this = (d_options_t *) cast_d_options (options);
+
+   if (!this)
+   {
+      return 0;
+   }
+   else
+   {
+      this->magnification = level;
+      return 1;
+   }
+}
+
+int
+fiasco_d_options_set_4_2_0_format (fiasco_d_options_t *options, int format)
+/*
+ *  Set image format to 4:2:0 or 4:4:4.
+ *  
+ *  Return value:
+ *	1 on success
+ *	0 otherwise
+ */
+{
+   d_options_t *this = (d_options_t *) cast_d_options (options);
+
+   if (!this)
+   {
+      return 0;
+   }
+   else
+   {
+      this->image_format = format ? FORMAT_4_2_0 : FORMAT_4_4_4;
+      return 1;
+   }
+}
+
+d_options_t *
+cast_d_options (fiasco_d_options_t *options)
+/*
+ *  Cast generic pointer `options' to type d_options_t.
+ *  Check whether `options' is a valid object of type d_options_t.
+ *
+ *  Return value:
+ *	pointer to options struct on success
+ *      NULL otherwise
+ */
+{
+   d_options_t *this = (d_options_t *) options->private;
+   
+   if (this)
+   {
+      if (!streq (this->id, "DOFIASCO"))
+      {
+	 set_error (_("Parameter `options' doesn't match required type."));
+	 return NULL;
+      }
+   }
+   else
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "options");
+   }
+
+   return this;
+}
+
+
diff --git a/converter/other/fiasco/codec/options.h b/converter/other/fiasco/codec/options.h
new file mode 100644
index 00000000..3af6be01
--- /dev/null
+++ b/converter/other/fiasco/codec/options.h
@@ -0,0 +1,80 @@
+/*
+ *  options.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/10/28 17:39:31 $
+ *  $Author: hafner $
+ *  $Revision: 5.4 $
+ *  $State: Exp $
+ */
+
+#ifndef _OPTIONS_H
+#define _OPTIONS_H
+
+typedef struct c_options
+{
+   char      	       id [9];
+   char  	      *basis_name;
+   unsigned  	       lc_min_level;
+   unsigned  	       lc_max_level;
+   unsigned  	       p_min_level;
+   unsigned  	       p_max_level;
+   unsigned  	       images_level;
+   unsigned  	       max_states;
+   unsigned  	       chroma_max_states;
+   unsigned  	       max_elements;
+   unsigned  	       tiling_exponent;
+   fiasco_tiling_e     tiling_method;
+   char        	      *id_domain_pool;
+   char        	      *id_d_domain_pool;
+   char        	      *id_rpf_model;
+   char        	      *id_d_rpf_model;
+   unsigned  	       rpf_mantissa;
+   real_t    	       rpf_range;
+   unsigned  	       dc_rpf_mantissa;
+   fiasco_rpf_range_e  dc_rpf_range;
+   unsigned  	       d_rpf_mantissa;
+   fiasco_rpf_range_e  d_rpf_range;
+   unsigned  	       d_dc_rpf_mantissa;
+   fiasco_rpf_range_e  d_dc_rpf_range;
+   real_t    	       chroma_decrease;
+   bool_t    	       prediction;
+   bool_t    	       delta_domains;
+   bool_t    	       normal_domains;
+   unsigned  	       search_range;
+   unsigned  	       fps;
+   char        	      *pattern;
+   char        	      *reference_filename;
+   bool_t    	       half_pixel_prediction;
+   bool_t    	       cross_B_search;
+   bool_t    	       B_as_past_ref;
+   bool_t    	       check_for_underflow;
+   bool_t    	       check_for_overflow;
+   bool_t    	       second_domain_block;
+   bool_t    	       full_search;
+   fiasco_progress_e   progress_meter;
+   char 	      *title;
+   char 	      *comment;
+   unsigned    	       smoothing;
+} c_options_t;
+
+typedef struct d_options
+{
+   char     id [9];
+   unsigned smoothing;
+   unsigned magnification;
+   format_e image_format;
+} d_options_t;
+
+c_options_t *
+cast_c_options (fiasco_c_options_t *options);
+d_options_t *
+cast_d_options (fiasco_d_options_t *options);
+
+#endif /* not _OPTIONS_H */
diff --git a/converter/other/fiasco/codec/prediction.c b/converter/other/fiasco/codec/prediction.c
new file mode 100644
index 00000000..351ba9df
--- /dev/null
+++ b/converter/other/fiasco/codec/prediction.c
@@ -0,0 +1,629 @@
+/*
+ *  prediction.c:	Range image prediction with MC or ND	
+ *
+ *  Written by:		Ullrich Hafner
+ *			Michael Unger
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#if HAVE_STRING_H
+#	include <string.h>
+#else /* not HAVE_STRING_H */
+#	include <strings.h>
+#endif /* not HAVE_STRING_H */
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "cwfa.h"
+#include "ip.h"
+#include "control.h"
+#include "misc.h"
+#include "subdivide.h"
+#include "bintree.h"
+#include "domain-pool.h"
+#include "approx.h"
+#include "wfalib.h"
+#include "mwfa.h"
+#include "prediction.h"
+
+#include "decoder.h"
+
+
+/*****************************************************************************
+
+			     local variables
+  
+*****************************************************************************/
+
+typedef struct state_data
+{
+   real_t final_distribution;
+   byte_t level_of_state;
+   byte_t domain_type;
+
+   real_t *images_of_state;
+   real_t *inner_products;
+   real_t *ip_states_state [MAXLEVEL];
+
+   word_t tree [MAXLABELS];
+   mv_t	  mv_tree [MAXLABELS];
+   word_t y_state [MAXLABELS];
+   byte_t y_column [MAXLABELS];
+   byte_t prediction [MAXLABELS];
+
+   u_word_t x [MAXLABELS];
+   u_word_t y [MAXLABELS];
+
+   real_t weight [MAXLABELS][MAXEDGES + 1];
+   word_t int_weight [MAXLABELS][MAXEDGES + 1];
+   word_t into [MAXLABELS][MAXEDGES + 1];
+} state_data_t;
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static real_t
+nd_prediction (real_t max_costs, real_t price, unsigned band, int y_state,
+	       range_t *range, wfa_t *wfa, coding_t *c);
+static real_t
+mc_prediction (real_t max_costs, real_t price, unsigned band, int y_state,
+	       range_t *range, wfa_t *wfa, coding_t *c);
+static state_data_t *
+store_state_data (unsigned from, unsigned to, unsigned max_level,
+		  wfa_t *wfa, coding_t *c);
+static void
+restore_state_data (unsigned from, unsigned to, unsigned max_level,
+		    state_data_t *data, wfa_t *wfa, coding_t *c);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+ 
+real_t
+predict_range (real_t max_costs, real_t price, range_t *range, wfa_t *wfa,
+	       coding_t *c, unsigned band, int y_state, unsigned states,
+	       const tree_t *tree_model, const tree_t *p_tree_model,
+	       const void *domain_model, const void *d_domain_model,
+	       const void *coeff_model, const void *d_coeff_model)
+{
+   unsigned	 state;		     	/* counter */
+   void		*rec_domain_model;   	/* domain model after recursion */
+   void		*rec_d_domain_model; 	/* p domain model after recursion */
+   void		*rec_coeff_model;    	/* coeff model after recursion */
+   void		*rec_d_coeff_model;  	/* d coeff model after recursion */
+   tree_t	 rec_tree_model;	/* tree_model after '' */
+   tree_t	 rec_p_tree_model;    	/* p_tree_model after '' */
+   unsigned	 rec_states;	     	/* wfa->states after '' */
+   real_t	*rec_pixels;	     	/* c->pixels after '' */
+   state_data_t	*rec_state_data;     	/* state_data struct after '' */
+   real_t	 costs;		     	/* current approximation costs */
+   unsigned	 level;		     	/* counter */
+   state_data_t	*sd;		     	/* pointer to state_data field */
+
+   /*
+    *  Store WFA data from state 'lc_states' to 'wfa->states' - 1 and
+    *  current state of probability models.
+    */
+   rec_domain_model   = c->domain_pool->model;
+   rec_d_domain_model = c->d_domain_pool->model;
+   rec_coeff_model    = c->coeff->model;
+   rec_d_coeff_model  = c->d_coeff->model;
+   rec_tree_model     = c->tree;
+   rec_p_tree_model   = c->p_tree;
+   rec_states         = wfa->states;	
+   rec_pixels         = c->pixels;
+   rec_state_data     = store_state_data (states, rec_states - 1,
+					  c->options.lc_max_level, wfa, c);
+   
+   /*
+    *  Restore probability models to the state before the recursive subdivision
+    *  has been started.
+    */
+   wfa->states             = states;
+   c->tree                 = *tree_model;
+   c->p_tree               = *p_tree_model;
+   c->domain_pool->model   = c->domain_pool->model_duplicate (domain_model);
+   c->d_domain_pool->model = c->d_domain_pool->model_duplicate (d_domain_model);
+   c->coeff->model   	   = c->coeff->model_duplicate (c->coeff, coeff_model);
+   c->d_coeff->model   	   = c->d_coeff->model_duplicate (c->d_coeff,
+							  d_coeff_model);
+   
+   if (c->mt->frame_type == I_FRAME)
+      costs = nd_prediction (max_costs, price, band, y_state, range, wfa, c); 
+   else
+      costs = mc_prediction (max_costs, price, band, y_state, range, wfa, c);
+   
+   c->pixels = rec_pixels;
+   
+   if (costs < MAXCOSTS)
+   {
+      /*
+       *  Free the memory used by the state_data struct
+       */
+      for (state = states; state < rec_states; state++)
+      {
+	 sd = &rec_state_data [state - states];
+	 for (level = c->options.images_level + 1;
+	      level <= c->options.lc_max_level; level++)
+	    if (sd->ip_states_state [level] != NULL)
+	       Free (sd->ip_states_state [level]);
+	 if (sd->images_of_state != NULL)
+	    Free (sd->images_of_state);
+	 if (sd->inner_products != NULL)
+	    Free (sd->inner_products);
+      }
+      if (states < rec_states)
+	 Free (rec_state_data);
+      c->domain_pool->model_free (rec_domain_model);
+      c->d_domain_pool->model_free (rec_d_domain_model);
+      c->coeff->model_free (rec_coeff_model);
+      c->d_coeff->model_free (rec_d_coeff_model);
+
+      costs = (range->tree_bits + range->matrix_bits + range->weights_bits
+	       + range->mv_tree_bits + range->mv_coord_bits
+	       + range->nd_tree_bits + range->nd_weights_bits) * price
+	      + range->err;
+   }
+   else
+   {
+      /*
+       *  Restore WFA to state before function was called
+       */
+      c->domain_pool->model_free (c->domain_pool->model);
+      c->d_domain_pool->model_free (c->d_domain_pool->model);
+      c->coeff->model_free (c->coeff->model);
+      c->d_coeff->model_free (c->d_coeff->model);
+      
+      c->domain_pool->model   = rec_domain_model;
+      c->d_domain_pool->model = rec_d_domain_model;
+      c->coeff->model         = rec_coeff_model;
+      c->d_coeff->model       = rec_d_coeff_model;
+      c->tree                 = rec_tree_model;
+      c->p_tree               = rec_p_tree_model;
+      
+      range->prediction = NO;
+      
+      if (wfa->states != states)
+	 remove_states (states, wfa);
+      restore_state_data (states, rec_states - 1, c->options.lc_max_level,
+			  rec_state_data, wfa, c);
+      costs = MAXCOSTS;
+   }
+ 
+   return costs;
+} 
+
+void
+clear_norms_table (unsigned level, const wfa_info_t *wi, motion_t *mt)
+/*
+ *  Clear norms arrays.
+ *
+ *  No return value.
+ */
+{
+   unsigned  range_size = wi->half_pixel
+			  ? square (wi->search_range)
+			  : square (2 * wi->search_range);
+
+   if (level > wi->p_min_level)
+   {
+      memset (mt->mc_forward_norms [level], 0, range_size * sizeof(real_t));
+      memset (mt->mc_backward_norms [level], 0, range_size * sizeof(real_t));
+   }
+}
+
+void
+update_norms_table (unsigned level, const wfa_info_t *wi, motion_t *mt)
+/*
+ *  Norms table of levels larger than the bottom level are computed
+ *  by summing up previously calculated displacement costs of lower levels.
+ *
+ *  No return value.
+ */
+{
+   unsigned  range_size = wi->half_pixel
+			  ? square (wi->search_range)
+			  : square (2 * wi->search_range);
+   
+   if (level > wi->p_min_level)
+   {
+      unsigned index;			/* index of motion vector */
+      
+      for (index = 0; index < range_size; index++)
+	 mt->mc_forward_norms [level][index]
+	    += mt->mc_forward_norms [level - 1][index];
+      if (mt->frame_type == B_FRAME)
+	 for (index = 0; index < range_size; index++)
+	    mt->mc_backward_norms [level][index]
+	       += mt->mc_backward_norms [level - 1][index];
+   }
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static real_t
+mc_prediction (real_t max_costs, real_t price, unsigned band, int y_state,
+	       range_t *range, wfa_t *wfa, coding_t *c)
+{
+   real_t    costs;		   	/* current approximation costs */
+   range_t   prange = *range;
+   unsigned  width  = width_of_level (range->level);
+   unsigned  height = height_of_level (range->level);
+   word_t   *mcpe   = Calloc (width * height, sizeof (word_t));
+
+   /*
+    *  If we are at the bottom level of the mc tree:
+    *  Fill in the norms table
+    */
+   if (prange.level == wfa->wfainfo->p_min_level) 
+      fill_norms_table (prange.x, prange.y, prange.level, wfa->wfainfo, c->mt);
+   /*
+    *  Predict 'range' with motion compensation according to frame type.
+    *  MCPE is returned in 'c->mcpe'
+    */
+   if (c->mt->frame_type == P_FRAME)
+      find_P_frame_mc (mcpe, price, &prange, wfa->wfainfo, c->mt);
+   else
+      find_B_frame_mc (mcpe, price, &prange, wfa->wfainfo, c->mt);
+   
+   costs = (prange.mv_tree_bits + prange.mv_coord_bits) * price;
+   
+   if (costs < max_costs)		/* motion vector not too expensive */
+   {
+      unsigned  last_state;		/* last WFA state before recursion */
+      real_t   *ipi [MAXSTATES];	/* inner products pointers */
+      unsigned  state;
+      real_t  	mvt, mvc;
+      
+      c->pixels = Calloc (width * height, sizeof (real_t));
+      cut_to_bintree (c->pixels, mcpe, width, height, 0, 0, width, height);
+   
+      /*
+       *  Approximate MCPE recursively.
+       */
+      last_state = wfa->states - 1;
+      for (state = 0; state <= last_state; state++)
+	 if (need_image (state, wfa))
+	 {
+	    ipi [state] = c->ip_images_state[state];
+	    c->ip_images_state[state]
+	       = Calloc (size_of_tree (c->products_level), sizeof (real_t));
+	 }
+
+      mvc = prange.mv_coord_bits;
+      mvt = prange.mv_tree_bits;
+      
+      prange.image           = 0;
+      prange.address         = 0;
+      prange.tree_bits       = 0;
+      prange.matrix_bits     = 0;
+      prange.weights_bits    = 0;
+      prange.mv_coord_bits   = 0;
+      prange.mv_tree_bits    = 0;
+      prange.nd_weights_bits = 0;
+      prange.nd_tree_bits    = 0;
+
+      compute_ip_images_state (prange.image, prange.address, prange.level,
+			       1, 0, wfa, c);
+      costs += subdivide (max_costs - costs, band, y_state, &prange,
+			  wfa, c, NO, YES);
+
+      if (costs < max_costs)		/* use motion compensation */
+      {
+	 unsigned img, adr;		/* temp. values */
+	 
+	 img                  = range->image;
+	 adr                  = range->address;
+	 *range               = prange;
+	 range->image         = img;
+	 range->address       = adr;
+	 range->mv_coord_bits = mvc;
+	 range->mv_tree_bits  = mvt;
+	 range->prediction    = YES;
+
+	 for (state = last_state + 1; state < wfa->states; state++)
+	    if (need_image (state, wfa))
+	       memset (c->ip_images_state [state], 0,
+		       size_of_tree (c->products_level) * sizeof (real_t));
+
+	 costs = (range->tree_bits + range->matrix_bits + range->weights_bits
+		  + range->mv_tree_bits + range->mv_coord_bits
+		  + range->nd_tree_bits + range->nd_weights_bits) * price
+		 + range->err;
+      }
+      else
+	 costs = MAXCOSTS;
+
+      for (state = 0; state <= last_state; state++)
+	 if (need_image (state, wfa))
+	 {
+	    Free (c->ip_images_state[state]);
+	    c->ip_images_state[state] = ipi [state];
+	 }
+      Free (c->pixels);
+   }
+   else
+      costs = MAXCOSTS;
+   
+   Free (mcpe);
+
+   return costs;
+}
+
+static real_t
+nd_prediction (real_t max_costs, real_t price, unsigned band, int y_state,
+	       range_t *range, wfa_t *wfa, coding_t *c)
+{
+   real_t  costs;			/* current approximation costs */
+   range_t lrange = *range;
+   
+   /*
+    *  Predict 'range' with DC component approximation
+    */
+   {
+      real_t x = get_ip_image_state (range->image, range->address,
+				     range->level, 0, c);
+      real_t y = get_ip_state_state (0, 0, range->level, c);
+      real_t w = btor (rtob (x / y, c->coeff->dc_rpf), c->coeff->dc_rpf);
+      word_t s [2] = {0, -1};
+
+      lrange.into [0] 	     = 0;
+      lrange.into [1] 	     = NO_EDGE;
+      lrange.weight [0]      = w;
+      lrange.mv_coord_bits   = 0;
+      lrange.mv_tree_bits    = 0;
+      lrange.nd_tree_bits    = tree_bits (LEAF, lrange.level, &c->p_tree);
+      lrange.nd_weights_bits = 0;
+      lrange.tree_bits       = 0;
+      lrange.matrix_bits     = 0;
+      lrange.weights_bits    = c->coeff->bits (&w, s, range->level, c->coeff);
+   }
+   costs = price * (lrange.weights_bits + lrange.nd_tree_bits);
+   
+   /*
+    *  Recursive aproximation of difference image
+    */
+   if (costs < max_costs)		
+   {
+      unsigned  state;
+      range_t  	rrange;			/* range: recursive subdivision */
+      unsigned  last_state;		/* last WFA state before recursion */
+      real_t   *ipi [MAXSTATES];	/* inner products pointers */
+      unsigned 	width  = width_of_level (range->level);
+      unsigned  height = height_of_level (range->level);
+      real_t   *pixels;
+
+      /*
+       *  Generate difference image original - approximation
+       */
+      {
+	 unsigned  n;
+	 real_t *src, *dst;		/* pointers to image data */
+	 real_t w = - lrange.weight [0] * c->images_of_state [0][0];
+		     
+	 src = c->pixels + range->address * size_of_level (range->level); 
+	 dst = c->pixels = pixels = Calloc (width * height, sizeof (real_t));
+
+	 for (n = width * height; n; n--)
+	    *dst++ = *src++ + w;
+      }
+      
+      /*
+       *  Approximate difference recursively.
+       */
+      rrange                 = *range;
+      rrange.tree_bits       = 0;
+      rrange.matrix_bits     = 0;
+      rrange.weights_bits    = 0;
+      rrange.mv_coord_bits   = 0;
+      rrange.mv_tree_bits    = 0;
+      rrange.nd_tree_bits    = 0;
+      rrange.nd_weights_bits = 0;
+      rrange.image           = 0;
+      rrange.address         = 0;
+
+      last_state = wfa->states - 1;
+      for (state = 0; state <= last_state; state++)
+	 if (need_image (state, wfa))
+	 {
+	    ipi [state] = c->ip_images_state[state];
+	    c->ip_images_state[state]
+	       = Calloc (size_of_tree (c->products_level), sizeof (real_t));
+	 }
+      
+      compute_ip_images_state (rrange.image, rrange.address, rrange.level,
+			       1, 0, wfa, c);
+      
+      costs += subdivide (max_costs - costs, band, y_state, &rrange, wfa, c,
+			  NO, YES);
+      
+      Free (pixels);
+
+      if (costs < max_costs && ischild (rrange.tree)) /* use prediction */
+      {
+	 unsigned img, adr;
+	 unsigned edge;
+
+	 img                     = range->image;
+	 adr                     = range->address;
+	 *range                  = rrange;
+	 range->image            = img;
+	 range->address          = adr;
+	 range->nd_tree_bits    += lrange.nd_tree_bits;
+	 range->nd_weights_bits += lrange.weights_bits;
+	 
+	 for (edge = 0; isedge (lrange.into [edge]); edge++)
+	 {
+	    range->into [edge]   = lrange.into [edge];
+	    range->weight [edge] = lrange.weight [edge];
+	 }
+	 range->into [edge] = NO_EDGE;
+	 range->prediction  = edge;
+
+	 for (state = last_state + 1; state < wfa->states; state++)
+	    if (need_image (state, wfa))
+	       memset (c->ip_images_state [state], 0,
+		       size_of_tree (c->products_level) * sizeof (real_t));
+      }
+      else
+	 costs = MAXCOSTS;
+      
+      for (state = 0; state <= last_state; state++)
+	 if (need_image (state, wfa))
+	 {
+	    Free (c->ip_images_state [state]);
+	    c->ip_images_state [state] = ipi [state];
+	 }
+   }
+   else
+      costs = MAXCOSTS;
+
+   return costs;
+}
+
+static state_data_t *
+store_state_data (unsigned from, unsigned to, unsigned max_level,
+		  wfa_t *wfa, coding_t *c)
+/*
+ *  Save and remove all states starting from state 'from'.
+ *
+ *  Return value:
+ *	pointer to array of state_data structs
+ */
+{
+   state_data_t *data;			/* array of savestates */
+   state_data_t *sd;			/* pointer to current savestates */
+   unsigned	 state, label, level;
+
+   if (to < from)
+      return NULL;			/* nothing to do */
+   
+   data = Calloc (to - from + 1, sizeof (state_data_t));
+   
+   for (state = from; state <= to; state++)
+   {
+      sd = &data [state - from];
+
+      sd->final_distribution = wfa->final_distribution [state];
+      sd->level_of_state     = wfa->level_of_state [state];
+      sd->domain_type        = wfa->domain_type [state];
+      sd->images_of_state    = c->images_of_state [state];
+      sd->inner_products     = c->ip_images_state [state];
+      
+      wfa->domain_type [state]   = 0;
+      c->images_of_state [state] = NULL;
+      c->ip_images_state [state] = NULL;
+				   
+      for (label = 0; label < MAXLABELS; label++) 
+      {
+	 sd->tree [label]     	= wfa->tree [state][label];
+	 sd->y_state [label]  	= wfa->y_state [state][label];
+	 sd->y_column [label] 	= wfa->y_column [state][label];
+	 sd->mv_tree [label]  	= wfa->mv_tree [state][label];
+	 sd->x [label]        	= wfa->x [state][label];
+	 sd->y [label]        	= wfa->y [state][label];
+	 sd->prediction [label] = wfa->prediction [state][label];
+
+	 memcpy (sd->weight [label], wfa->weight [state][label], 
+		 sizeof (real_t) * (MAXEDGES + 1));
+	 memcpy (sd->int_weight [label], wfa->int_weight [state][label], 
+		 sizeof (word_t) * (MAXEDGES + 1));
+	 memcpy (sd->into [label], wfa->into [state][label], 
+		 sizeof (word_t) * (MAXEDGES + 1));
+
+	 wfa->into [state][label][0] = NO_EDGE;
+	 wfa->tree [state][label]    = RANGE;
+	 wfa->y_state [state][label] = RANGE;
+      }
+      for (level = c->options.images_level + 1; level <= max_level;
+	   level++)
+      {
+	 sd->ip_states_state [level]       = c->ip_states_state [state][level];
+	 c->ip_states_state [state][level] = NULL;
+      }
+   }
+
+   return data;
+}
+
+static void
+restore_state_data (unsigned from, unsigned to, unsigned max_level,
+		    state_data_t *data, wfa_t *wfa, coding_t *c)
+/*
+ *  Restore all state data starting from state 'from'.
+ *  
+ *  No return value.
+ */
+{
+   state_data_t *sd;			/* pointer to state_data item */
+   unsigned	 state, label, level;
+
+   if (to < from)
+      return;				/* nothing to do */
+   
+   for (state = from; state <= to; state++)
+   {
+      sd = &data [state - from];
+      
+      wfa->final_distribution [state] = sd->final_distribution;
+      wfa->level_of_state [state]     = sd->level_of_state;
+      wfa->domain_type [state]        = sd->domain_type;
+      
+      if (c->images_of_state [state] != NULL)
+	 Free (c->images_of_state [state]);
+      c->images_of_state [state] = sd->images_of_state;
+      if (c->ip_images_state [state] != NULL)
+	 Free (c->ip_images_state [state]);
+      c->ip_images_state [state] = sd->inner_products;
+
+      for (label = 0; label < MAXLABELS; label++)
+      {
+	 wfa->tree [state][label]     	= sd->tree [label];
+	 wfa->y_state [state][label]  	= sd->y_state [label];
+	 wfa->y_column [state][label] 	= sd->y_column [label];
+	 wfa->mv_tree [state][label]  	= sd->mv_tree [label];
+	 wfa->x [state][label]        	= sd->x [label];
+	 wfa->y [state][label]        	= sd->y [label];
+	 wfa->prediction [state][label] = sd->prediction [label];
+	 
+	 memcpy (wfa->weight [state][label], sd->weight [label], 
+		 sizeof(real_t) * (MAXEDGES + 1));
+	 memcpy (wfa->int_weight [state][label], sd->int_weight [label], 
+		 sizeof(word_t) * (MAXEDGES + 1));
+	 memcpy (wfa->into [state][label], sd->into [label],  
+		 sizeof(word_t) * (MAXEDGES + 1));
+      }	 
+      for (level = c->options.images_level + 1; level <= max_level;
+	   level++)
+      {
+	 if (c->ip_states_state [state][level] != NULL)
+	    Free (c->ip_states_state [state][level]);
+	 c->ip_states_state [state][level] = sd->ip_states_state [level];
+      }
+   }
+
+   Free (data);
+   wfa->states = to + 1;
+}
diff --git a/converter/other/fiasco/codec/prediction.h b/converter/other/fiasco/codec/prediction.h
new file mode 100644
index 00000000..1068501a
--- /dev/null
+++ b/converter/other/fiasco/codec/prediction.h
@@ -0,0 +1,36 @@
+/*
+ *  prediction.h
+ *
+ *  Written by:		Ullrich Hafner
+ *			Michael Unger
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _PREDICTION_H
+#define _PREDICTION_H
+
+#include "types.h"
+#include "cwfa.h"
+
+real_t
+predict_range (real_t max_costs, real_t price, range_t *range, wfa_t *wfa,
+	       coding_t *c, unsigned band, int y_state, unsigned states,
+	       const tree_t *tree_model, const tree_t *p_tree_model,
+	       const void *domain_model, const void *d_domain_model,
+	       const void *coeff_model, const void *d_coeff_model);
+void
+update_norms_table (unsigned level, const wfa_info_t *wi, motion_t *mt);
+void
+clear_norms_table (unsigned level, const wfa_info_t *wi, motion_t *mt);
+
+#endif /* not _PREDICTION_H */
+
diff --git a/converter/other/fiasco/codec/subdivide.c b/converter/other/fiasco/codec/subdivide.c
new file mode 100644
index 00000000..b7982716
--- /dev/null
+++ b/converter/other/fiasco/codec/subdivide.c
@@ -0,0 +1,650 @@
+/*
+ *  subdivide.c:	Recursive subdivision of range images
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/07/15 17:59:31 $
+ *  $Author: hafner $
+ *  $Revision: 5.4 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#if HAVE_STRING_H
+#	include <string.h>
+#else /* not HAVE_STRING_H */
+#	include <strings.h>
+#endif /* not HAVE_STRING_H */
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "image.h"
+#include "cwfa.h"
+#include "approx.h"
+#include "ip.h"
+#include "bintree.h"
+#include "control.h"
+#include "prediction.h"
+#include "domain-pool.h"
+#include "mwfa.h"
+#include "misc.h"
+#include "subdivide.h"
+#include "list.h"
+#include "coeff.h"
+#include "wfalib.h"
+
+/*****************************************************************************
+
+				prototypes
+
+*****************************************************************************/
+
+static void
+init_new_state (bool_t auxiliary_state, bool_t delta, range_t *range,
+		const range_t *child, const int *y_state,
+		wfa_t *wfa, coding_t *c);
+static void
+init_range (range_t *range, const image_t *image, unsigned band,
+	    const wfa_t *wfa, coding_t *c);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+real_t 
+subdivide (real_t max_costs, unsigned band, int y_state, range_t *range,
+	   wfa_t *wfa, coding_t *c, bool_t prediction, bool_t delta)
+/*
+ *  Subdivide the current 'range' recursively and decide whether
+ *  a linear combination, a recursive subdivision, or a prediction is
+ *  the best choice of approximation.
+ *  'band' is the current color band, 'y_state' is the corresponding
+ *  state of the Y color component (color image compression only).
+ *  If 'prediction' is TRUE then also test motion compensation or
+ *  nondeterministic approximation.
+ *  If 'delta' is TRUE then current range is already predicted.
+ *  
+ *  Return value:
+ *	costs of the best approximation or MAXCOSTS if costs exceed 'max_costs'
+ *
+ *  Side effects:
+ *	'range'	factors and costs of linear combination are modified
+ *      'wfa'	new transitions and prediction coefficients are added
+ *	'c'	pixels and inner products are updated
+ */
+{
+   real_t    subdivide_costs;        /* Costs arising from approx. the current
+				       range with two childs */
+   real_t    lincomb_costs;          /* Costs arising from approx. the current
+				       range with a linear combination */
+   int	     new_y_state [MAXLABELS];	/* Corresponding state of Y */
+   real_t    price;			/* Approximation costs multiplier */
+   bool_t    try_mc;			/* YES: try MC prediction */
+   bool_t    try_nd;			/* YES: try ND prediction */
+   unsigned  states;			/* Number of states before the
+					   recursive subdivision starts */
+   void     *domain_model;		/* copy of domain pool model */      
+   void     *d_domain_model;		/* copy of delta domain pool model */
+   void     *lc_domain_model;		/* copy of domain pool model */
+   void     *lc_d_domain_model;		/* copy of delta domain pool model */
+   void	    *coeff_model;	        /* copy of coefficients model */
+   void	    *d_coeff_model;		/* copy of delta coefficients model */
+   void	    *lc_coeff_model;	        /* copy of coefficients model */
+   void	    *lc_d_coeff_model;		/* copy of delta coefficients model */
+   tree_t    tree_model;		/* copy of tree model */
+   tree_t    p_tree_model;		/* copy of pred. tree model */
+   range_t   lrange;			/* range of lin. comb. approx. */
+   range_t   rrange;			/* range of recursive approx. */
+   range_t   child [MAXLABELS];		/* new childs of the current range */
+   static unsigned percent = 0;		/* status of progress meter */
+
+   if (wfa->wfainfo->level == range->level)
+      percent = 0;
+   
+   range->into [0] = NO_EDGE;		/* default approximation: empty */
+   range->tree     = RANGE;
+
+   if (range->level < 3)		/* Don't process small ranges */
+      return MAXCOSTS;	
+
+   /*
+    *  If image permutation (tiling) is performed and the tiling level
+    *  is reached then get coordinates of the new block.
+    */
+   if (c->tiling->exponent
+       && range->level == wfa->wfainfo->level - c->tiling->exponent)
+   {
+      unsigned width, height;		/* size of range (dummies)*/
+      
+      if (c->tiling->vorder [range->global_address] < 0)
+	 return 0;			/* nothing to do */
+      else
+	 locate_subimage (wfa->wfainfo->level, range->level,
+			  c->tiling->vorder [range->global_address],
+			  &range->x, &range->y, &width, &height);
+   }
+
+   if (range->x >= c->mt->original->width ||
+       range->y >= c->mt->original->height)
+      return 0;				/* range is not visible */
+
+   /*
+    *  Check whether prediction is allowed or not
+    *  mc == motion compensation, nd == nondeterminism
+    */
+   try_mc = (prediction && c->mt->frame_type != I_FRAME			
+	     && range->level >= wfa->wfainfo->p_min_level
+	     && range->level <= wfa->wfainfo->p_max_level
+	     && (range->x + width_of_level (range->level)
+		 <= c->mt->original->width)
+	     && (range->y + height_of_level (range->level)
+		 <= c->mt->original->height));
+
+   try_nd = (prediction && c->mt->frame_type == I_FRAME
+	     && range->level >= wfa->wfainfo->p_min_level
+	     && range->level <= wfa->wfainfo->p_max_level);
+
+   if (try_mc)
+      clear_norms_table (range->level, wfa->wfainfo, c->mt);
+
+   
+   /*
+    *  Check if current range must be initialized. I.e. range pixels must
+    *  be copied from entire image to bintree pixel buffer. Moreover,
+    *  all inner products tables must be initialized.
+    */
+   if (range->level == c->options.lc_max_level)	
+      init_range (range, c->mt->original, band, wfa, c);
+   
+   price = c->price;
+   if (band != Y)			
+      price *= c->options.chroma_decrease; /* less quality for chroma bands */
+
+   /*
+    *  Compute childs of corresponding state in Y band
+    */
+   if (band != Y)			/* Cb and Cr bands only */
+   {
+      unsigned label;
+
+      for (label = 0; label < MAXLABELS; label++)
+	 if (ischild (y_state))
+	    new_y_state [label] = wfa->tree [y_state][label];
+	 else
+	    new_y_state [label] = RANGE;
+   }
+   else
+      new_y_state [0] = new_y_state [1] = RANGE;
+   
+   /*
+    *  Store contents of all models that may get modified during recursion
+    */
+   domain_model   = c->domain_pool->model_duplicate (c->domain_pool->model);
+   d_domain_model = c->d_domain_pool->model_duplicate (c->d_domain_pool->model);
+   coeff_model    = c->coeff->model_duplicate (c->coeff, c->coeff->model);
+   d_coeff_model  = c->d_coeff->model_duplicate (c->d_coeff, c->d_coeff->model);
+   tree_model     = c->tree;
+   p_tree_model   = c->p_tree;
+   states         = wfa->states;	
+   
+   /*
+    *  First alternative of range approximation:
+    *  Compute costs of linear combination.
+    */
+   if (range->level <= c->options.lc_max_level) /* range is small enough */
+   {
+      lrange                 = *range;
+      lrange.tree            = RANGE;
+      lrange.tree_bits       = tree_bits (LEAF, lrange.level, &c->tree);
+      lrange.matrix_bits     = 0;
+      lrange.weights_bits    = 0;
+      lrange.mv_tree_bits    = try_mc ? 1 : 0; /* mc allowed but not used */
+      lrange.mv_coord_bits   = 0;
+      lrange.nd_tree_bits    = 0;	
+      lrange.nd_weights_bits = 0;	
+      lrange.prediction	     = NO;
+      
+      lincomb_costs
+	 = approximate_range (max_costs, price, c->options.max_elements,
+			      y_state, &lrange,
+			      (delta ? c->d_domain_pool : c->domain_pool),
+			      (delta ? c->d_coeff : c->coeff), wfa, c);
+   }
+   else
+      lincomb_costs = MAXCOSTS;		
+
+   /*
+    *  Store contents of models that have been modified
+    *  by approximate_range () above ...
+    */
+   lc_domain_model   = c->domain_pool->model;
+   lc_d_domain_model = c->d_domain_pool->model;
+   lc_coeff_model    = c->coeff->model;
+   lc_d_coeff_model  = c->d_coeff->model;
+   /*
+    *  ... and restore them with values before lc
+    */
+   c->domain_pool->model   = c->domain_pool->model_duplicate (domain_model);
+   c->d_domain_pool->model = c->d_domain_pool->model_duplicate (d_domain_model);
+   c->coeff->model         = c->coeff->model_duplicate (c->coeff, coeff_model);
+   c->d_coeff->model       = c->d_coeff->model_duplicate (c->d_coeff,
+							  d_coeff_model);
+   
+   /*
+    *  Second alternative of range approximation:
+    *  Compute costs of recursive subdivision.
+    */
+   if (range->level > c->options.lc_min_level) /* range is large enough */
+   {
+      unsigned label;
+      
+      memset (&child [0], 0, 2 * sizeof (range_t)); /* initialize childs */
+
+      /*
+       *  Initialize a new range for recursive approximation
+       */
+      rrange                 = *range;
+      rrange.tree_bits       = tree_bits (CHILD, rrange.level, &c->tree);
+      rrange.matrix_bits     = 0;
+      rrange.weights_bits    = 0;
+      rrange.err             = 0;
+      rrange.mv_tree_bits    = try_mc ? 1 : 0;	/* mc allowed but not used */
+      rrange.mv_coord_bits   = 0;
+      rrange.nd_tree_bits    = try_nd ?
+			       tree_bits (CHILD, lrange.level, &c->p_tree): 0;
+      rrange.nd_weights_bits = 0;	
+      rrange.prediction	     = NO;
+
+      /*
+       *  Initialize the cost function and subdivide the current range.
+       *  Every child is approximated by a recursive call of subdivide()
+       */
+      subdivide_costs = (rrange.tree_bits + rrange.weights_bits
+			 + rrange.matrix_bits + rrange.mv_tree_bits
+			 + rrange.mv_coord_bits + rrange.nd_tree_bits
+			 + rrange.nd_weights_bits) * price;
+      
+      for (label = 0; label < MAXLABELS; label++) 
+      {
+	 real_t remaining_costs;	/* upper limit for next recursion */
+	 
+	 child[label].image          = rrange.image * MAXLABELS + label + 1;
+	 child[label].address        = rrange.address * MAXLABELS + label;
+	 child[label].global_address = rrange.global_address * MAXLABELS
+				       + label;
+	 child[label].level          = rrange.level - 1;
+	 child[label].x	= rrange.level & 1
+			  ? rrange.x
+			  : (rrange.x
+			     + label * width_of_level (rrange.level - 1));
+	 child[label].y = rrange.level & 1
+			  ? (rrange.y
+			     + label * height_of_level (rrange.level - 1))
+			  : rrange.y;
+	 
+	 /* 
+	  *  If neccessary compute the inner products of the new states
+	  *  (generated during the recursive approximation of child [0])
+	  */
+	 if (label && rrange.level <= c->options.lc_max_level)
+	    compute_ip_images_state (child[label].image, child[label].address,
+				     child[label].level, 1, states, wfa, c);
+	 /*
+	  *  Call subdivide() for both childs. 
+	  *  Abort the recursion if 'subdivide_costs' exceed 'lincomb_costs'
+	  *  or 'max_costs'.
+	  */
+	 remaining_costs = min (lincomb_costs, max_costs) - subdivide_costs;
+
+	 if (remaining_costs > 0)	/* still a way for improvement */
+	 {
+	    subdivide_costs += subdivide (remaining_costs, band,
+					  new_y_state [label], &child [label],
+					  wfa, c, prediction, delta);
+	 }
+	 else if (try_mc && child[label].level >= wfa->wfainfo->p_min_level)
+	 {
+	    fill_norms_table (child[label].x, child[label].y,
+			      child[label].level, wfa->wfainfo, c->mt);
+	 }
+	 
+	 if (try_mc)
+	    update_norms_table (rrange.level, wfa->wfainfo, c->mt);
+	 
+	 /*
+	  *  Update of progress meter
+	  */
+	 if (c->options.progress_meter != FIASCO_PROGRESS_NONE)
+	 {
+	    if (c->options.progress_meter == FIASCO_PROGRESS_PERCENT)
+	    {
+	       unsigned	new_percent; 	/* new status of progress meter */
+	 
+	       new_percent = (child[label].global_address + 1) * 100.0
+			     / (1 << (wfa->wfainfo->level - child[label].level));
+	       if (new_percent > percent)
+	       {
+		  percent = new_percent;
+		  info ("%3d%%  \r", percent);
+	       }
+	    }
+	    else if (c->options.progress_meter == FIASCO_PROGRESS_BAR)
+	    {
+	       unsigned	new_percent;	/* new status of progress meter */
+	 
+	       new_percent = (child[label].global_address + 1) * 50.0
+			     / (1 << (wfa->wfainfo->level
+				      - child[label].level));
+	       for (; new_percent > percent; percent++)
+	       {
+		  info ("#");
+	       }
+	    }
+	 }
+   
+	 /*
+	  *  If costs of subdivision exceed costs of linear combination 
+	  *  then abort recursion.
+	  */
+	 if (subdivide_costs >= min (lincomb_costs, max_costs)) 
+	 {
+	    subdivide_costs = MAXCOSTS;
+	    break; 
+	 }
+	 rrange.err             += child [label].err;
+	 rrange.tree_bits       += child [label].tree_bits;
+	 rrange.matrix_bits     += child [label].matrix_bits;
+	 rrange.weights_bits    += child [label].weights_bits;
+	 rrange.mv_tree_bits    += child [label].mv_tree_bits;
+	 rrange.mv_coord_bits   += child [label].mv_coord_bits;
+	 rrange.nd_weights_bits += child [label].nd_weights_bits;
+	 rrange.nd_tree_bits    += child [label].nd_tree_bits;
+
+	 tree_update (ischild (child [label].tree) ? CHILD : LEAF,
+		      child [label].level, &c->tree);
+	 tree_update (child [label].prediction ? LEAF : CHILD,
+		      child [label].level, &c->p_tree);
+      }
+   }
+   else
+      subdivide_costs = MAXCOSTS;
+
+   /*
+    *  Third alternative of range approximation:
+    *  Predict range via motion compensation or nondeterminism and 
+    *  approximate delta image. 
+    */
+   if (try_mc || try_nd)		/* try prediction */
+   {
+      real_t prediction_costs;	/* Costs arising from approx. the current
+				   range with prediction */
+
+      prediction_costs
+	 = predict_range (min (min (lincomb_costs, subdivide_costs),
+			       max_costs),
+			  price, range, wfa, c, band, y_state, states,
+			  &tree_model, &p_tree_model, domain_model,
+			  d_domain_model, coeff_model, d_coeff_model);
+      if (prediction_costs < MAXCOSTS)	/* prediction has smallest costs */
+      {
+	 c->domain_pool->model_free (domain_model);
+	 c->d_domain_pool->model_free (d_domain_model);
+	 c->domain_pool->model_free (lc_domain_model);
+	 c->d_domain_pool->model_free (lc_d_domain_model);
+	 c->coeff->model_free (coeff_model);
+	 c->d_coeff->model_free (d_coeff_model);
+	 c->coeff->model_free (lc_coeff_model);
+	 c->d_coeff->model_free (lc_d_coeff_model);
+	 
+	 return prediction_costs;
+      }
+   }
+
+   if (lincomb_costs >= MAXCOSTS && subdivide_costs >= MAXCOSTS)
+   {
+      /*
+       *  Return MAXCOSTS if neither a linear combination nor a recursive
+       *  subdivision yield costs less than 'max_costs'
+       */
+      c->domain_pool->model_free (c->domain_pool->model);
+      c->d_domain_pool->model_free (c->d_domain_pool->model);
+      c->domain_pool->model_free (lc_domain_model);
+      c->d_domain_pool->model_free (lc_d_domain_model);
+
+      c->coeff->model_free (c->coeff->model);
+      c->d_coeff->model_free (c->d_coeff->model);
+      c->coeff->model_free (lc_coeff_model);
+      c->d_coeff->model_free (lc_d_coeff_model);
+	 
+      c->domain_pool->model   = domain_model;
+      c->d_domain_pool->model = d_domain_model;
+      c->coeff->model	      = coeff_model;
+      c->d_coeff->model	      = d_coeff_model;
+      c->tree                 = tree_model;
+      c->p_tree               = p_tree_model;
+      
+      if (wfa->states != states)
+	 remove_states (states, wfa);
+
+      return MAXCOSTS;
+   }
+   else if (lincomb_costs < subdivide_costs) 
+   {
+      /*
+       *  Use the linear combination: The factors of the linear combination
+       *  are stored already in 'range', so revert the probability models
+       *  only. 
+       */
+      c->domain_pool->model_free (c->domain_pool->model);
+      c->d_domain_pool->model_free (c->d_domain_pool->model);
+      c->domain_pool->model_free (domain_model);
+      c->d_domain_pool->model_free (d_domain_model);
+
+      c->coeff->model_free (c->coeff->model);
+      c->d_coeff->model_free (c->d_coeff->model);
+      c->coeff->model_free (coeff_model);
+      c->d_coeff->model_free (d_coeff_model);
+      
+      c->domain_pool->model   = lc_domain_model;
+      c->d_domain_pool->model = lc_d_domain_model;
+      c->coeff->model	      = lc_coeff_model;
+      c->d_coeff->model	      = lc_d_coeff_model;
+      c->tree                 = tree_model;
+      c->p_tree               = p_tree_model;
+
+      *range = lrange;
+      
+      if (wfa->states != states)
+	 remove_states (states, wfa);
+
+      return lincomb_costs;
+   }
+   else
+   {
+      /*
+       *  Use the recursive subdivision: Generate a new state with transitions
+       *  given in child[].
+       *  Don't use state in linear combinations in any of the following cases:
+       *  - if color component is Cb or Cr
+       *  - if level of state > tiling level 
+       *  - if state is (partially) outside image geometry 
+       */
+      if (band > Y
+	  || (c->tiling->exponent
+	      && rrange.level > wfa->wfainfo->level - c->tiling->exponent)
+	  || (range->x + width_of_level (range->level)
+	      > c->mt->original->width)
+	  || (range->y + height_of_level (range->level)
+	      > c->mt->original->height))
+	 init_new_state (YES, delta, &rrange, child, new_y_state, wfa, c);
+      else
+	 init_new_state (NO, delta, &rrange, child, new_y_state, wfa, c);
+
+      *range = rrange;
+
+      c->domain_pool->model_free (domain_model);
+      c->d_domain_pool->model_free (d_domain_model);
+      c->domain_pool->model_free (lc_domain_model);
+      c->d_domain_pool->model_free (lc_d_domain_model);
+      c->coeff->model_free (coeff_model);
+      c->d_coeff->model_free (d_coeff_model);
+      c->coeff->model_free (lc_coeff_model);
+      c->d_coeff->model_free (lc_d_coeff_model);
+
+      return subdivide_costs;
+   }
+}
+
+void
+cut_to_bintree (real_t *dst, const word_t *src,
+		unsigned src_width, unsigned src_height,
+		unsigned x0, unsigned y0, unsigned width, unsigned height)
+/*
+ *  Cut region ('x0', 'y0', 'width', 'height') of the pixel array 'src'.
+ *  Size of image is given by 'src_width' x 'src_height'.
+ *  'dst' pixels are converted to bintree order and real format.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'dst []' is filled with corresponding region.
+ */
+{
+   const unsigned mask01      = 0x555555; /* binary ...010101010101 */
+   const unsigned mask10      = 0xaaaaaa; /* binary ...101010101010 */
+   const unsigned mask01plus1 = mask01 + 1; /* binary ...010101010110 */
+   const unsigned mask10plus1 = mask10 + 1; /* binary ...101010101011 */
+   unsigned  	  x, y;			/* pixel coordinates */
+   unsigned  	  xmask, ymask;		/* address conversion */
+
+   if (width != height && width != (height >> 1))
+      error ("Bintree cutting requires special type of images.");
+
+   ymask = 0;
+   for (y = y0; y < y0 + height; y++, ymask = (ymask + mask10plus1) & mask01)
+   {
+      xmask = 0;
+      for (x = x0; x < x0 + width; x++, xmask = (xmask + mask01plus1) & mask10)
+      {
+	 if (y >= src_height || x >= src_width)
+	    dst [xmask | ymask] = 0;
+	 else
+	    dst [xmask | ymask] = src [y * src_width + x] / 16;
+      }
+   }
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static void
+init_new_state (bool_t auxiliary_state, bool_t delta, range_t *range,
+		const range_t *child, const int *y_state,
+		wfa_t *wfa, coding_t *c)
+/*
+ *  Initializes a new state with all parameters needed for the encoding step.
+ *  If flag 'auxiliary_state' is set then don't insert state into domain pools.
+ *  If flag 'delta' is set then state represents a delta image (prediction via
+ *  nondeterminism or motion compensation).
+ *  'range' the current range image,
+ *   'child []' the left and right childs of 'range'.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	New state is appended to 'wfa' (and also its inner products and images
+ *      are computed and stored in 'c')
+ */
+{
+   unsigned label;
+   bool_t   state_is_domain = NO;
+
+   if (!auxiliary_state)
+   {
+      if (!delta || c->options.delta_domains)
+	 state_is_domain = c->domain_pool->append (wfa->states, range->level,
+						   wfa, c->domain_pool->model);
+      if (delta || c->options.normal_domains)
+	 state_is_domain = c->d_domain_pool->append (wfa->states, range->level,
+						     wfa,
+						     c->d_domain_pool->model)
+			   || state_is_domain;
+   }
+   else
+      state_is_domain = NO;
+   
+   range->into [0] = NO_EDGE;
+   range->tree     = wfa->states;
+   
+   for (label = 0; label < MAXLABELS; label++) 
+   {
+      wfa->tree [wfa->states][label]       = child [label].tree;
+      wfa->y_state [wfa->states][label]    = y_state [label];
+      wfa->mv_tree [wfa->states][label]    = child [label].mv;
+      wfa->x [wfa->states][label]          = child [label].x;
+      wfa->y [wfa->states][label]          = child [label].y;
+      wfa->prediction [wfa->states][label] = child [label].prediction;
+
+      append_transitions (wfa->states, label, child [label].weight,
+			  child [label].into, wfa);
+   }
+   wfa->delta_state [wfa->states] = delta;
+
+   if (range->err < 0)
+      warning ("Negative image norm: %f, %f", child [0].err, child [1].err);
+
+/*    state_is_domain = YES; */
+   
+   append_state (!state_is_domain,
+		 compute_final_distribution (wfa->states, wfa),
+		 range->level, wfa, c);
+}
+
+static void
+init_range (range_t *range, const image_t *image, unsigned band,
+	    const wfa_t *wfa, coding_t *c)
+/*
+ *  Read a new 'range' of the image 'image_name' (current color component
+ *  is 'band') and compute the new inner product arrays.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'c->pixels' are filled with pixel values of image block 
+ *	'c->ip_images_state' are computed with respect to new image block 
+ *	'range->address' and 'range->image' are initialized with zero
+ */
+{
+   unsigned state;
+   
+   /*
+    *  Clear already computed products
+    */
+   for (state = 0; state < wfa->states; state++)
+      if (need_image (state, wfa))
+	 memset (c->ip_images_state[state], 0,
+		 size_of_tree (c->products_level) * sizeof(real_t));
+
+   cut_to_bintree (c->pixels, image->pixels [band],
+		   image->width, image->height,
+		   range->x, range->y, width_of_level (range->level),
+		   height_of_level (range->level));
+   
+   range->address = range->image = 0;
+   compute_ip_images_state (0, 0, range->level, 1, 0, wfa, c);
+}
+
+
diff --git a/converter/other/fiasco/codec/subdivide.h b/converter/other/fiasco/codec/subdivide.h
new file mode 100644
index 00000000..b6840e58
--- /dev/null
+++ b/converter/other/fiasco/codec/subdivide.h
@@ -0,0 +1,33 @@
+/*
+ *  subdivide.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _SUBDIVIDE_H
+#define _SUBDIVIDE_H
+
+#include "types.h"
+#include "cwfa.h"
+
+real_t 
+subdivide (real_t max_costs, unsigned band, int y_state, range_t *range,
+	   wfa_t *wfa, coding_t *c, bool_t prediction, bool_t delta);
+void
+cut_to_bintree (real_t *dst, const word_t *src,
+		unsigned src_width, unsigned src_height,
+		unsigned x0, unsigned y0, unsigned width, unsigned height);
+
+#endif /* not _SUBDIVIDE_H */
+
+
diff --git a/converter/other/fiasco/codec/tiling.c b/converter/other/fiasco/codec/tiling.c
new file mode 100644
index 00000000..e820f7fb
--- /dev/null
+++ b/converter/other/fiasco/codec/tiling.c
@@ -0,0 +1,239 @@
+/*
+ *  tiling.c:		Subimage permutation
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#if STDC_HEADERS
+#	include <stdlib.h>
+#endif /* not STDC_HEADERS */
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "image.h"
+#include "misc.h"
+#include "wfalib.h"
+#include "tiling.h"
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static int
+cmpdecvar (const void *value1, const void *value2);
+static int
+cmpincvar (const void *value1, const void *value2);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+typedef struct var_list
+{
+   int	  address;			/* bintree address */
+   real_t variance;			/* variance of tile */
+} var_list_t;
+
+tiling_t *
+alloc_tiling (fiasco_tiling_e method, unsigned tiling_exponent,
+	      unsigned image_level)
+/*
+ *  Image tiling constructor.
+ *  Allocate memory for the tiling_t structure.
+ *  `method' defines the tiling method (spiral or variance,
+ *  ascending or descending).
+ *  In case of invalid parameters, a structure with tiling.exponent == 0 is
+ *  returned. 
+ *
+ *  Return value
+ *	pointer to the new tiling structure on success
+ */
+{
+   tiling_t *tiling = Calloc (1, sizeof (tiling_t));
+
+   if ((int) image_level - (int) tiling_exponent < 6)
+   {
+      tiling_exponent = 6;
+      warning (_("Image tiles must be at least 8x8 pixels large.\n"
+		 "Setting tiling size to 8x8 pixels."));
+   }
+   
+   switch (method)
+   {
+      case FIASCO_TILING_SPIRAL_ASC:
+      case FIASCO_TILING_SPIRAL_DSC:
+      case FIASCO_TILING_VARIANCE_ASC:
+      case FIASCO_TILING_VARIANCE_DSC:
+	 tiling_exponent = tiling_exponent;
+	 break;
+      default:
+	 warning (_("Invalid tiling method specified. Disabling tiling."));
+	 tiling_exponent = 0;
+	 break;
+   }
+
+   return tiling;
+}
+
+void
+free_tiling (tiling_t *tiling)
+/*
+ *  Tiling struct destructor:
+ *  Free memory of 'tiling' struct.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	structure 'tiling' is discarded.
+ */
+{
+   if (tiling->vorder)
+      Free (tiling->vorder);
+   Free (tiling);
+}
+
+void
+perform_tiling (const image_t *image, tiling_t *tiling)
+/*
+ *  Compute image tiling permutation.
+ *  The image is split into 2**'tiling->exponent' tiles.
+ *  Depending on 'tiling->method', the following algorithms are used:
+ *  "VARIANCE_ASC" :  Tiles are sorted by variance.
+ *                    The first tile has the lowest variance
+ *  "VARIANCE_DSC" :  Tiles are sorted by variance.
+ *                    The first tile has the largest variance
+ *  "SPIRAL_ASC" :    Tiles are sorted like a spiral starting
+ *                    in the middle of the image.
+ *  "SPIRAL_DSC" :    Tiles are sorted like a spiral starting
+ *                    in the upper left corner.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	The tiling permutation is stored in 'tiling->vorder'.
+ */
+{
+   if (tiling->exponent)
+   {
+      unsigned 	tiles = 1 << tiling->exponent; /* number of image tiles */
+      bool_t   *tile_valid;		/* tile i is in valid range ? */
+      
+      tiling->vorder = Calloc (tiles, sizeof (int));
+      tile_valid     = Calloc (tiles, sizeof (bool_t));
+
+      if (tiling->method == FIASCO_TILING_VARIANCE_ASC
+	  || tiling->method == FIASCO_TILING_VARIANCE_DSC)
+      {
+	 unsigned    address;		/* bintree address of tile */
+	 unsigned    number;		/* number of image tiles */
+	 unsigned    lx       = log2 (image->width - 1) + 1; /* x level */
+	 unsigned    ly       = log2 (image->height - 1) + 1; /* y level */
+	 unsigned    level    = max (lx, ly) * 2 - ((ly == lx + 1) ? 1 : 0);
+	 var_list_t *var_list = Calloc (tiles, sizeof (var_list_t));
+	 
+	 /*
+	  *  Compute variances of image tiles
+	  */
+	 for (number = 0, address = 0; address < tiles; address++)
+	 {
+	    unsigned width, height;	/* size of image tile */
+	    unsigned x0, y0;		/* NW corner of image tile */
+      
+	    locate_subimage (level, level - tiling->exponent, address,
+			     &x0, &y0, &width, &height);
+	    if (x0 < image->width && y0 < image->height) /* valid range */
+	    {
+	       if (x0 + width > image->width)	/* outside image area */
+		  width = image->width - x0;
+	       if (y0 + height > image->height) /* outside image area */
+		  height = image->height - y0;
+
+	       var_list [number].variance
+		  = variance (image->pixels [GRAY], x0, y0,
+			      width, height, image->width);
+	       var_list [number].address  = address;
+	       number++;
+	       tile_valid [address] = YES;
+	    }
+	    else
+	       tile_valid [address] = NO;
+	 }
+
+	 /*
+	  *  Sort image tiles according to sign of 'tiling->exp'
+	  */
+	 if (tiling->method == FIASCO_TILING_VARIANCE_DSC)
+	    qsort (var_list, number, sizeof (var_list_t), cmpdecvar);
+	 else
+	    qsort (var_list, number, sizeof (var_list_t), cmpincvar);
+
+	 for (number = 0, address = 0; address < tiles; address++)
+	    if (tile_valid [address])
+	    {
+	       tiling->vorder [address] = var_list [number].address;
+	       number++;
+	       debug_message ("tile number %d has original address %d",
+			      number, tiling->vorder [address]);
+	    }
+	    else
+	       tiling->vorder [address] = -1;
+
+	 Free (var_list);
+      }
+      else if (tiling->method == FIASCO_TILING_SPIRAL_DSC
+	       || tiling->method == FIASCO_TILING_SPIRAL_ASC)
+      {
+	 compute_spiral (tiling->vorder, image->width, image->height,
+			 tiling->exponent,
+			 tiling->method == FIASCO_TILING_SPIRAL_ASC);
+      }
+      else
+      {
+	 warning ("Unsupported image tiling method.\n"
+		  "Skipping image tiling step.");
+	 tiling->exponent = 0;
+      }
+   }
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static int
+cmpincvar (const void *value1, const void *value2)
+/*
+ *  Sorts by increasing variances (quicksort sorting function).
+ */
+{
+  return ((var_list_t *) value1)->variance - ((var_list_t *) value2)->variance;
+}
+
+static int
+cmpdecvar (const void *value1, const void *value2)
+/*
+ *  Sorts by decreasing variances (quicksort sorting function).
+ */
+{
+  return ((var_list_t *) value2)->variance - ((var_list_t *) value1)->variance;
+}
diff --git a/converter/other/fiasco/codec/tiling.h b/converter/other/fiasco/codec/tiling.h
new file mode 100644
index 00000000..2eb04fe0
--- /dev/null
+++ b/converter/other/fiasco/codec/tiling.h
@@ -0,0 +1,40 @@
+/*
+ *  tiling.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _TILING_H
+#define _TILING_H
+
+#include "image.h"
+#include "fiasco.h"
+
+typedef struct tiling
+{
+   unsigned    	    exponent;		/* Image is split in 2^exp tiles */
+   fiasco_tiling_e  method;		/* Method of Image tiling */
+   int	      	   *vorder;		/* Block permutation (size = 2^exp)
+					   -1 indicates empty block */
+} tiling_t;
+
+void
+perform_tiling (const image_t *image, tiling_t *tiling);
+tiling_t *
+alloc_tiling (fiasco_tiling_e method, unsigned tiling_exponent,
+	      unsigned image_level);
+void
+free_tiling (tiling_t *tiling);
+
+#endif /* not _TILING_H */
+
diff --git a/converter/other/fiasco/codec/wfa.h b/converter/other/fiasco/codec/wfa.h
new file mode 100644
index 00000000..8b9793f2
--- /dev/null
+++ b/converter/other/fiasco/codec/wfa.h
@@ -0,0 +1,141 @@
+/*
+ *  wfa.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/07/18 15:44:57 $
+ *  $Author: hafner $
+ *  $Revision: 5.3 $
+ *  $State: Exp $
+ */
+
+#ifndef _WFA_H
+#define _WFA_H
+
+#define MAXEDGES  5
+#define MAXSTATES 6000
+#define MAXLABELS 2			/* only bintree supported anymore */
+#define MAXLEVEL  22 
+
+#define FIASCO_BINFILE_RELEASE   2
+#define FIASCO_MAGIC	         "FIASCO" /* FIASCO magic number */
+#define FIASCO_BASIS_MAGIC       "Fiasco" /* FIASCO initial basis */
+
+#define NO_EDGE		-1
+#define RANGE		-1
+#define NO_RANGE	 0
+
+#define CHILD		 1
+#define LEAF		 0
+
+#define MAX_PROB	 9
+#define MIN_PROB	 1
+
+/*
+ *  WFA state types:
+ *	0:		 state is not allowed to be used in an
+ *			 approximation and it's image is not needed
+ *			 for ip computations.
+ *	AUXILIARY_MASK:  state is required for computation of ip's but is not
+ *			 allowed to be used in an approximation.
+ *	USE_DOMAIN_MASK: state is allowed to be used in an approximation.
+ */
+enum state_types {AUXILIARY_MASK = 1 << 0, USE_DOMAIN_MASK = 1 << 1};
+
+#define isedge(x)	   ((x) != NO_EDGE)
+#define isdomain(x)	   ((x) != NO_EDGE)
+#define isrange(x)	   ((x) == RANGE)
+#define ischild(x)	   ((x) != RANGE)
+#define isauxiliary(d,wfa) ((wfa)->domain_type[d] & AUXILIARY_MASK)
+#define usedomain(d, wfa)  ((wfa)->domain_type[d] & USE_DOMAIN_MASK)
+#define need_image(d,wfa)  (isauxiliary ((d), (wfa)) || usedomain ((d), (wfa)))
+
+typedef enum mc_type {NONE, FORWARD, BACKWARD, INTERPOLATED} mc_type_e;
+typedef enum frame_type {I_FRAME, P_FRAME, B_FRAME} frame_type_e;
+typedef enum header {HEADER_END, HEADER_TITLE, HEADER_COMMENT} header_type_e;
+
+typedef struct mv
+/*
+ *  Motion vector components
+ */
+{
+   mc_type_e type;			/* motion compensation type */
+   int       fx, fy;			/* forward vector coordinates */
+   int       bx, by;			/* backward vector coordinates */
+} mv_t;
+
+typedef struct range_info
+{
+   unsigned x, y;			/* coordinates of upper left corner */
+   unsigned level;			/* bintree level of range */
+} range_info_t;
+
+#include "image.h"
+#include "rpf.h"
+#include "bit-io.h"
+
+typedef struct wfa_info
+{
+   char	    *wfa_name;			/* filename of the WFA */
+   char	    *basis_name;		/* filename of the initial basis */
+   char     *title;			/* title of FIASCO stream */
+   char     *comment;			/* comment for FIASCO stream */
+   
+   unsigned  max_states;		/* max. cardinality of domain pool */
+   unsigned  chroma_max_states;		/* max. cardinality of domain pool for
+					   chroma band coding */
+   bool_t    color;			/* color image */
+   unsigned  width;			/* image width */
+   unsigned  height;			/* image height */
+   unsigned  level;			/* image level */
+   rpf_t    *rpf;			/* Standard reduced precision format */
+   rpf_t    *dc_rpf;			/* DC reduced precision format */
+   rpf_t    *d_rpf;			/* Delta reduced precision format */
+   rpf_t    *d_dc_rpf;			/* Delta DC reduced precision format */
+   unsigned  frames;			/* number of frames in the video */
+   unsigned  fps;			/* number of frames per second */
+   unsigned  p_min_level;		/* min. level of prediction */
+   unsigned  p_max_level;		/* max. level of prediction */
+   unsigned  search_range;		/* motion vector interval */
+   bool_t    half_pixel;		/* usage of half pixel precision */
+   bool_t    cross_B_search;		/* usage of Cross-B-Search */
+   bool_t    B_as_past_ref;		/* usage of B frames as ref's */
+   unsigned  smoothing;			/* smoothing of image along borders */
+   unsigned  release;			/* FIASCO file format release */
+} wfa_info_t;
+
+typedef struct wfa
+/*
+ *  Used to store all informations and data structures of a WFA
+ */
+{
+   wfa_info_t	*wfainfo;		/* misc. information about the WFA */
+   frame_type_e frame_type;		/* intra, predicted, bi-directional */
+   unsigned	states;			/* number of states */
+   unsigned	basis_states;		/* number of states in the basis */
+   unsigned	root_state;		/* root of the tree */
+   real_t	*final_distribution;    /* one pixel images */
+   byte_t	*level_of_state;	/* level of the image part which is
+					   represented by the current state */
+   byte_t	*domain_type;		/* Bit_0==1: auxilliary state
+					   Bit_1==1: used for Y compr */
+   mv_t		(*mv_tree)[MAXLABELS];	/* motion vectors */
+   word_t	(*tree)[MAXLABELS];	/* bintree partitioning */
+   u_word_t	(*x)[MAXLABELS];	/* range coordinate */
+   u_word_t	(*y)[MAXLABELS];	/* range coordinate */
+   word_t	(*into)[MAXLABELS][MAXEDGES + 1];   /* domain references */
+   real_t	(*weight)[MAXLABELS][MAXEDGES + 1]; /* lin.comb. coefficients */
+   word_t	(*int_weight)[MAXLABELS][MAXEDGES + 1]; /* bin. representation */
+   word_t	(*y_state)[MAXLABELS];	/* bintree of Y component */
+   byte_t	(*y_column)[MAXLABELS];	/* array for Y component references */
+   byte_t	(*prediction)[MAXLABELS]; /* DC prediction */
+   bool_t	(*delta_state);		/* delta state */
+} wfa_t;
+
+#endif /* not _WFA_H */
+
diff --git a/converter/other/fiasco/codec/wfalib.c b/converter/other/fiasco/codec/wfalib.c
new file mode 100644
index 00000000..a3acb975
--- /dev/null
+++ b/converter/other/fiasco/codec/wfalib.c
@@ -0,0 +1,774 @@
+/*
+ *  wfalib.c:		Library functions both for encoding and decoding
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/07/18 15:57:28 $
+ *  $Author: hafner $
+ *  $Revision: 5.5 $
+ *  $State: Exp $
+ */
+
+#define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include "config.h"
+
+#if STDC_HEADERS
+#	include <stdlib.h>
+#endif /* not STDC_HEADERS */
+
+#include <string.h>
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "wfa.h"
+#include "misc.h"
+#include "wfalib.h"
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static unsigned
+xy_to_address (unsigned x, unsigned y, unsigned level, unsigned n);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+wfa_t *
+alloc_wfa (bool_t coding)
+/*
+ *  WFA constructor:
+ *  Initialize the WFA structure 'wfa' and allocate memory.
+ *  Flag 'coding' indicates whether WFA is used for coding or decoding.
+ *
+ *  Return value:
+ *	pointer to the new WFA structure
+ */
+{
+   wfa_t *wfa = Calloc (1, sizeof (wfa_t));
+		 
+   /*
+    *  Allocate memory
+    */
+   wfa->final_distribution = Calloc (MAXSTATES, sizeof (real_t));
+   wfa->level_of_state     = Calloc (MAXSTATES, sizeof (byte_t));
+   wfa->domain_type        = Calloc (MAXSTATES, sizeof (byte_t));
+   wfa->delta_state        = Calloc (MAXSTATES, sizeof (bool_t));
+   wfa->tree               = Calloc (MAXSTATES * MAXLABELS, sizeof (word_t));
+   wfa->x                  = Calloc (MAXSTATES * MAXLABELS, sizeof (word_t));
+   wfa->y                  = Calloc (MAXSTATES * MAXLABELS, sizeof (word_t));
+   wfa->mv_tree            = Calloc (MAXSTATES * MAXLABELS, sizeof (mv_t));
+   wfa->y_state            = Calloc (MAXSTATES * MAXLABELS, sizeof (word_t));
+   wfa->into               = Calloc (MAXSTATES * MAXLABELS * (MAXEDGES + 1),
+				     sizeof (word_t));
+   wfa->weight             = Calloc (MAXSTATES * MAXLABELS * (MAXEDGES + 1),
+				     sizeof (real_t));
+   wfa->int_weight         = Calloc (MAXSTATES * MAXLABELS * (MAXEDGES + 1),
+				     sizeof (word_t));
+   wfa->wfainfo            = Calloc (1, sizeof (wfa_info_t));;
+   wfa->prediction         = Calloc (MAXSTATES * MAXLABELS, sizeof (byte_t));
+
+   wfa->wfainfo->wfa_name   = NULL;
+   wfa->wfainfo->basis_name = NULL;
+   wfa->wfainfo->title 	    = strdup ("");
+   wfa->wfainfo->comment    = strdup ("");
+
+   /*
+    *  Initialize structure
+    */
+   {
+      unsigned  state, label;
+
+      wfa->states       = 0;
+      wfa->basis_states = 0;
+      wfa->root_state   = 0;
+      for (state = 0; state < MAXSTATES; state++) 
+      {
+	 wfa->final_distribution [state] = 0;
+	 wfa->domain_type [state]        = 0;
+	 for (label = 0; label < MAXLABELS; label++)
+	 {
+	    wfa->into [state][label][0] = NO_EDGE;
+	    wfa->tree [state][label]    = RANGE;
+	    wfa->y_state [state][label] = RANGE;
+	 }
+      }
+   }
+
+   if (coding)				/* initialize additional variables */
+      wfa->y_column = Calloc (MAXSTATES * MAXLABELS, sizeof (byte_t));
+   else
+      wfa->y_column = NULL;
+   
+   return wfa;
+}
+
+void
+free_wfa (wfa_t *wfa)
+/*
+ *  WFA destructor:
+ *  Free memory of given 'wfa'.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'wfa' struct is discarded.
+ */
+{
+   if (wfa->wfainfo->wfa_name)
+      Free (wfa->wfainfo->wfa_name);
+   if (wfa->wfainfo->basis_name)
+      Free (wfa->wfainfo->basis_name);
+   if (wfa->wfainfo->title)
+      Free (wfa->wfainfo->title);
+   if (wfa->wfainfo->comment)
+      Free (wfa->wfainfo->comment);
+
+   Free (wfa->final_distribution);
+   Free (wfa->level_of_state);
+   Free (wfa->domain_type);
+   Free (wfa->tree);
+   Free (wfa->x);
+   Free (wfa->y);
+   Free (wfa->mv_tree);
+   Free (wfa->y_state);
+   Free (wfa->into);
+   Free (wfa->weight);
+   Free (wfa->int_weight);
+   Free (wfa->wfainfo);
+   Free (wfa->prediction);
+   Free (wfa->delta_state);
+   if (wfa->y_column)
+      Free (wfa->y_column);
+   Free (wfa);
+}
+
+real_t 
+compute_final_distribution (unsigned state, const wfa_t *wfa)
+/*
+ *  Compute the final distribution of the given 'state'.
+ *  Uses the fact that the generated 'wfa' is average preserving.
+ *
+ *  Return value:
+ *	final distribution
+ */
+{
+   unsigned label;
+   real_t   final = 0;
+
+   for (label = 0; label < MAXLABELS; label++)
+   {
+      unsigned edge;
+      int      domain;
+      
+      if (ischild (domain = wfa->tree [state][label]))
+	 final += wfa->final_distribution [domain];
+      for (edge = 0; isedge (domain = wfa->into [state][label][edge]); edge++)
+	 final += wfa->weight [state][label][edge]
+		  * wfa->final_distribution [domain];
+   }
+   
+   return final / MAXLABELS;
+}
+
+word_t *
+compute_hits (unsigned from, unsigned to, unsigned n, const wfa_t *wfa)
+/*
+ *  Selects the 'n' most popular domain images of the given 'wfa'.
+ *  Consider only linear combinations of state images
+ *  {i | 'from' <= i <= 'to'}. I.e. domains are in {i | from <= i < 'to'}
+ *  Always ensure that state 0 is among selected states even though from
+ *  may be > 0.
+ *  
+ *  Return value:
+ *	pointer to array of the most popular state images
+ *	sorted by increasing state numbers and terminated by -1
+ */
+{
+   word_t   *domains;
+   unsigned  state, label, edge;
+   int       domain;
+   pair_t   *hits = Calloc (to, sizeof (pair_t));
+
+   for (domain = 0; domain < (int) to; domain++)
+   {
+      hits [domain].value = domain;
+      hits [domain].key   = 0;
+   }
+   
+   for (state = from; state <= to; state++)
+      for (label = 0; label < MAXLABELS; label++)
+	 for (edge = 0; isedge (domain = wfa->into [state][label][edge]);
+	      edge++)
+	    hits [domain].key++;
+
+   qsort (hits + 1, to - 1, sizeof (pair_t), sort_desc_pair);
+
+   n       = min (to, n);
+   domains = Calloc (n + 1, sizeof (word_t));
+
+   for (domain = 0; domain < (int) n && (!domain || hits [domain].key);
+	domain++)
+      domains [domain] = hits [domain].value;
+   if (n != domain)
+      debug_message ("Only %d domains have been used in the luminance.",
+		     domain);
+   n = domain;
+   qsort (domains, n, sizeof (word_t), sort_asc_word);
+   domains [n] = -1;
+   
+   Free (hits);
+   
+   return domains;
+}
+
+void
+append_edge (unsigned from, unsigned into, real_t weight,
+	     unsigned label, wfa_t *wfa)
+/*
+ *  Append an edge from state 'from' to state 'into' with
+ *  the given 'label' and 'weight' to the 'wfa'.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'wfa' structure is changed.
+ */
+{
+   unsigned new;			/* position of the new edge */
+   unsigned edge;
+
+   /*
+    *  First look where to insert the new edge:
+    *  edges are sorted by increasing 'into' values
+    */
+   for (new = 0; (isedge (wfa->into [from][label][new])
+		  && wfa->into [from][label][new] < (int) into); new++)
+      ;
+   /*
+    *  Move the edges 'n' to position 'n+1', for n = max, ..., 'new'
+    */
+   for (edge = new; isedge (wfa->into [from][label][edge]); edge++)
+      ;
+   for (edge++; edge != new; edge--)
+   {
+      wfa->into [from][label][edge]    = wfa->into [from][label][edge - 1];
+      wfa->weight [from][label][edge]  = wfa->weight [from][label][edge - 1];
+      wfa->int_weight [from][label][edge]
+	 = wfa->int_weight [from][label][edge - 1];
+   }
+   /*
+    *  Insert the new edge
+    */
+   wfa->into [from][label][edge]       = into;
+   wfa->weight [from][label][edge]     = weight;
+   wfa->int_weight [from][label][edge] = weight * 512 + 0.5;
+}
+
+void 
+remove_states (unsigned from, wfa_t *wfa)
+/* 
+ *  Remove 'wfa' states 'wfa->basis_states',...,'wfa->states' - 1.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'wfa' structure is cleared for the given states.
+ */
+{
+   unsigned state;
+
+   for (state = from; state < wfa->states; state++)
+   {
+      unsigned label;
+      
+      for (label = 0; label < MAXLABELS; label++) 
+      {
+	 wfa->into [state][label][0]      = NO_EDGE;
+	 wfa->tree [state][label]         = RANGE;
+	 wfa->prediction [state][label]   = FALSE;
+	 wfa->y_state [state][label]      = RANGE;
+	 wfa->mv_tree [state][label].type = NONE;
+	 wfa->mv_tree [state][label].fx   = 0;
+	 wfa->mv_tree [state][label].fy   = 0;
+	 wfa->mv_tree [state][label].bx   = 0;
+	 wfa->mv_tree [state][label].by   = 0;
+      }
+      wfa->domain_type [state] = 0;
+      wfa->delta_state [state] = FALSE;
+   }
+
+   wfa->states = from;
+}
+
+void
+copy_wfa (wfa_t *dst, const wfa_t *src)
+/*
+ *  Copy WFA struct 'src' to WFA struct 'dst'.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'dst' is filled with same data as 'src'
+ *
+ *  NOTE: size of WFA 'dst' must be at least size of WFA 'src'
+ */
+{
+   unsigned state;
+
+   memset (dst->final_distribution, 0, MAXSTATES * sizeof (real_t));
+   memset (dst->level_of_state, 0, MAXSTATES * sizeof (byte_t));
+   memset (dst->domain_type, 0, MAXSTATES * sizeof (byte_t));
+   memset (dst->mv_tree, 0, MAXSTATES * MAXLABELS * sizeof (mv_t));
+   memset (dst->tree, 0, MAXSTATES * MAXLABELS * sizeof (word_t));
+   memset (dst->x, 0, MAXSTATES * MAXLABELS * sizeof (word_t));
+   memset (dst->y, 0, MAXSTATES * MAXLABELS * sizeof (word_t));
+   memset (dst->y_state, 0, MAXSTATES * MAXLABELS * sizeof (word_t));
+   memset (dst->into, NO_EDGE,
+	   MAXSTATES * MAXLABELS * (MAXEDGES + 1) * sizeof (word_t));
+   memset (dst->weight, 0,
+	   MAXSTATES * MAXLABELS * (MAXEDGES + 1) * sizeof (real_t));
+   memset (dst->int_weight, 0,
+	   MAXSTATES * MAXLABELS * (MAXEDGES + 1) * sizeof (word_t));
+   memset (dst->prediction, 0, MAXSTATES * MAXLABELS * sizeof (byte_t));
+   memset (dst->delta_state, 0, MAXSTATES * sizeof (bool_t));
+   if (dst->y_column)
+      memset (dst->y_column, 0, MAXSTATES * MAXLABELS * sizeof (byte_t));
+
+   for (state = 0; state < MAXSTATES; state++) /* clear WFA struct */
+   {
+      unsigned label;
+      
+      for (label = 0; label < MAXLABELS; label++)
+      {
+	 dst->into [state][label][0]      = NO_EDGE;
+	 dst->tree [state][label]         = RANGE;
+	 dst->mv_tree [state][label].type = NONE;
+	 dst->y_state[state][label]       = RANGE;
+      }
+      dst->delta_state [state] = NO;
+      dst->domain_type [state] = 0;
+   }
+   
+   dst->frame_type   = src->frame_type;
+   dst->states 	     = src->states;
+   dst->basis_states = src->basis_states;
+   dst->root_state   = src->root_state;
+
+   memcpy (dst->wfainfo, src->wfainfo, sizeof (wfa_info_t));
+
+   if (dst->states == 0)		/* nothing to do */
+      return;
+
+   memcpy (dst->final_distribution, src->final_distribution,
+	   src->states * sizeof (real_t));
+   memcpy (dst->level_of_state, src->level_of_state,
+	   src->states * sizeof (byte_t));
+   memcpy (dst->domain_type, src->domain_type,
+	   src->states * sizeof (byte_t));
+   memcpy (dst->delta_state, src->delta_state,
+	   src->states * sizeof (bool_t));
+   memcpy (dst->mv_tree, src->mv_tree,
+	   src->states * MAXLABELS * sizeof (mv_t));
+   memcpy (dst->tree, src->tree,
+	   src->states * MAXLABELS * sizeof (word_t));
+   memcpy (dst->x, src->x,
+	   src->states * MAXLABELS * sizeof (word_t));
+   memcpy (dst->y, src->y,
+	   src->states * MAXLABELS * sizeof (word_t));
+   memcpy (dst->y_state, src->y_state,
+	   src->states * MAXLABELS * sizeof (word_t));
+   memcpy (dst->into, src->into,
+	   src->states * MAXLABELS * (MAXEDGES + 1) * sizeof (word_t));
+   memcpy (dst->weight, src->weight,
+	   src->states * MAXLABELS * (MAXEDGES + 1) * sizeof (real_t));
+   memcpy (dst->int_weight, src->int_weight,
+	   src->states * MAXLABELS * (MAXEDGES + 1) * sizeof (word_t));
+   memcpy (dst->prediction, src->prediction,
+	   src->states * MAXLABELS * sizeof (byte_t));
+   if (dst->y_column)
+      memcpy (dst->y_column, src->y_column,
+	      src->states * MAXLABELS * sizeof (byte_t));
+}
+
+void
+locate_subimage (unsigned orig_level, unsigned level, unsigned bintree,
+		 unsigned *x, unsigned *y, unsigned *width, unsigned *height)
+/*
+ *  Compute pixel coordinates of the subimage which 'bintree' address is given.
+ *  The level of the original image is 'orig_level' and the level of the
+ *  subimage is 'level'.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'*x', '*y'		coordinates of the upper left corner
+ *      '*width', '*height'	size of image
+ */
+{
+   /*
+    *  Compute coordinates of the subimage
+    */
+   *x = *y = 0;				/* start at NW corner */
+   *width  = width_of_level (level);
+   *height = height_of_level (level);
+
+   if (level > orig_level)
+   {
+      error ("size of tile must be less or equal than image size.");
+      return;
+   }
+   else if (bintree >= (unsigned) (1 << (orig_level - level)))
+   {
+      error ("address out of bounds.");
+      return;
+   }
+   else if (level < orig_level)
+   {
+      unsigned mask;			/* mask for bintree -> xy conversion */
+      bool_t   hor;			/* 1 next subdivision is horizontal
+					   0 next subdivision is vertical */
+      unsigned l = orig_level - 1;	/* current level */
+      
+      hor = orig_level % 2;		/* start with vertival subdivision
+					   for square image and vice versa */
+   
+      for (mask = 1 << (orig_level - level - 1); mask; mask >>= 1, hor = !hor)
+      {
+	 if (bintree & mask)		/* change coordinates */
+	 {
+	    if (hor)			/* horizontal subdivision */
+	       *y += height_of_level (l);
+	    else			/* vertical subdivision */
+	       *x += width_of_level (l);
+	 }
+	 l--;
+      }
+   }
+}
+
+void
+compute_spiral (int *vorder, unsigned image_width, unsigned image_height,
+		unsigned tiling_exp, bool_t inc_spiral)
+/*
+ *  Compute image tiling with spiral order.
+ *  'inc_spiral' specifies whether the spiral starts in the middle
+ *  of the image (TRUE) or at the border (FALSE).
+ *  'image_width'x'image_height' define the size of the image.
+ *  The image is split into 'tiling->exp' tiles.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	vorder[] is filled with tiling permutation
+ */
+{
+   unsigned x, y;			/* current position */
+   unsigned xmin, xmax, ymin, ymax;	/* boundaries for current line */
+   unsigned width, height;		/* offset for each tile */
+   unsigned lx, ly, level;		/* level x and y */
+   unsigned tiles;			/* total number of tiles */
+   unsigned address;			/* bintree address */
+   
+   lx     = log2 (image_width - 1) + 1;
+   ly     = log2 (image_height - 1) + 1;
+   level  = max (lx, ly) * 2 - ((ly == lx + 1) ? 1 : 0);
+   tiles  = 1 << tiling_exp;		/* Number of image tiles */
+   width  = width_of_level (level - tiling_exp);
+   height = height_of_level (level - tiling_exp);
+   for (address = 0; address < tiles; address++)
+   {
+      unsigned x0, y0, width, height;
+      
+      locate_subimage (level, level - tiling_exp, address,
+		       &x0, &y0, &width, &height);
+      vorder [address] = (x0 < image_width && y0 < image_height) ? 0 : -1;
+   }
+
+   xmin    = 0;
+   xmax    = width_of_level (level);
+   ymin    = 0;
+   ymax    = height_of_level (level);
+   address = 0;
+
+   /*
+    *  1234
+    *  CDE5  Traverse image in spiral order 
+    *  BGF6  starting at the top left corner
+    *  A987
+    */
+   while (TRUE)
+   {
+      for (x = xmin, y = ymin; x < xmax; x += width) /* W>E */
+      {
+	 while (vorder [address] == -1)
+	    address++;
+	 if (x < image_width && y < image_height) /* valid range */
+	    vorder [address++] = xy_to_address (x, y, level, tiling_exp);
+	 while (address < tiles && vorder [address] == -1)
+	    address++;
+      }
+      ymin += height;
+
+      if (address >= tiles)
+	 break;
+      
+      for (x = xmax - width, y = ymin; y < ymax; y += height) /* N>S  */
+      {
+	 while (vorder [address] == -1)
+	    address++;
+	 if (x <= image_width && y <= image_height) /* valid range */
+	    vorder [address++] = xy_to_address (x, y, level, tiling_exp);
+	 while (address < tiles && vorder [address] == -1)
+	    address++;
+      }
+      xmax -= width;
+
+      if (address >= tiles)
+	 break;
+
+      for (x = xmax - width, y = ymax - width; x >= xmin; x -= width) /* E<W */
+      {
+	 while (vorder [address] == -1)
+	    address++;
+	 if (x <= image_width && y <= image_height) /* valid range */
+	    vorder [address++] = xy_to_address (x, y, level, tiling_exp);
+	 while (address < tiles && vorder [address] == -1)
+	    address++;
+      }
+      ymax -= height;
+
+      if (address >= tiles)
+	 break;
+
+      for (x = xmin, y = ymax - height; y >= ymin; y -= height)	/* S>N */
+      {
+	 while (vorder [address] == -1)
+	    address++;
+	 if (x <= image_width && y <= image_height) /* valid range */
+	    vorder [address++] = xy_to_address (x, y, level, tiling_exp);
+	 while (address < tiles && vorder [address] == -1)
+	    address++;
+      }
+      xmin += width;
+	 
+      if (address >= tiles)
+	 break;
+   }
+
+   if (inc_spiral)
+   {
+      int i = 0, j = tiles - 1;
+
+      while (i < j)
+      {
+	 int tmp;
+	    
+	 while (vorder [i] == -1)
+	    i++;
+	 while (vorder [j] == -1)
+	    j--;
+	    
+	 tmp 	       = vorder [i];
+	 vorder [i] = vorder [j];
+	 vorder [j] = tmp;
+	 i++;
+	 j--;
+      }
+   }
+   /*
+    *  Print tiling info
+    */
+   {
+      unsigned number;
+      
+      for (number = 0, address = 0; address < tiles; address++)
+	 if (vorder [address] != -1)
+	    debug_message ("number %d: address %d",
+			   number++, vorder [address]);
+   }
+}
+
+bool_t
+find_range (unsigned x, unsigned y, unsigned band,
+	    const wfa_t *wfa, unsigned *range_state, unsigned *range_label)
+/*
+ *  Find a range ('*range_state', '*range_label') that contains
+ *  pixel ('x', 'y') in the iven color 'band'.
+ *
+ *  Return value:
+ *	TRUE on success, or FALSE if there is no such range
+ *
+ *  Side effects:
+ *	'*range_state' and '*range_label' are modified on success.
+ */
+{
+   unsigned state, label;
+   unsigned first_state, last_state;
+   bool_t   success = NO;
+   
+   first_state = wfa->basis_states;
+   last_state  = wfa->states;
+   if (wfa->wfainfo->color)
+      switch (band)
+      {
+	 case Y:
+	    first_state = wfa->basis_states;
+	    last_state  = wfa->tree [wfa->tree [wfa->root_state][0]][0];
+	    break;
+	 case Cb:
+	    first_state = wfa->tree [wfa->tree [wfa->root_state][0]][0] + 1;
+	    last_state  = wfa->tree [wfa->tree [wfa->root_state][0]][1];
+	    break;
+	 case Cr:
+	    first_state = wfa->tree [wfa->tree [wfa->root_state][0]][1] + 1;
+	    last_state  = wfa->states;
+	    break;
+	 default:
+	    error ("unknown color component.");
+      }
+
+   for (state = first_state; state < last_state; state++)
+      for (label = 0; label < MAXLABELS; label++)
+	 if (isrange (wfa->tree [state][label]))
+	    if (x >= wfa->x [state][label] && y >= wfa->y [state][label]
+		&& x < (unsigned) (wfa->x [state][label]
+			+ width_of_level (wfa->level_of_state [state] - 1))
+		&& y < (unsigned) (wfa->y [state][label]
+			+ height_of_level (wfa->level_of_state [state] - 1))) 
+	    {
+	       success      = YES;
+	       *range_state = state;
+	       *range_label = label;
+
+	       return success;
+	    }
+
+   return success;
+}
+
+void
+sort_ranges (unsigned state, unsigned *domain,
+	     range_sort_t *rs, const wfa_t *wfa)
+/*
+ *  Generate list of ranges in coder order.
+ *  'state' is the current state of the call tree while 'domain' is the
+ *  index of the last added WFA state.
+ *
+ *  Side effects:
+ *	'domain' is incremented after recursion returns
+ *	'rs'	 is filled accordingly
+ *
+ *  No return value.
+ */
+{
+   unsigned label;
+   
+   for (label = 0; label < MAXLABELS; label++)
+   {
+      if (isrange (wfa->tree [state][label]))
+	 rs->range_subdivided [rs->range_no] = NO;
+      else
+      {
+	 sort_ranges (wfa->tree [state][label], domain, rs, wfa);
+	 rs->range_subdivided [rs->range_no] = YES;
+      }
+
+      rs->range_state [rs->range_no]      = state;
+      rs->range_label [rs->range_no]      = label;
+      rs->range_max_domain [rs->range_no] = *domain;
+      while (!usedomain (rs->range_max_domain [rs->range_no], wfa))
+	 rs->range_max_domain [rs->range_no]--;
+
+      if (label == 1 || !rs->range_subdivided [rs->range_no])
+	 rs->range_no++;
+   }
+   
+   (*domain)++;
+}
+
+bool_t
+locate_delta_images (wfa_t *wfa)
+/*
+ *  Locate all WFA states that are part of a delta approximation.
+ *  I.e., these states are assigned to ranges that have been predicted
+ *  via MC or ND.
+ *
+ *  Return value:
+ *	TRUE	at least one state is part of a delta approximation
+ *	FALSE	no delta approximations in this WFA
+ *
+ *  Side effects:
+ *	'wfa->delta [state][label]' is set accordingly.
+ */
+{
+   unsigned state, label;
+   bool_t   delta = NO;
+
+   for (state = wfa->root_state; state >= wfa->basis_states; state--)
+      wfa->delta_state [state] = NO;
+
+   for (state = wfa->root_state; state >= wfa->basis_states; state--)
+      for (label = 0; label < MAXLABELS; label++)
+	 if (ischild (wfa->tree [state][label]))
+	    if (wfa->mv_tree [state][label].type != NONE
+		|| isedge (wfa->into [state][label][0])
+		|| wfa->delta_state [state])
+	    {
+	       delta = YES;
+	       wfa->delta_state [wfa->tree [state][label]] = YES;
+	    }
+
+   return delta;
+}
+
+/*****************************************************************************
+
+				private code
+  
+******************************************************************************/
+
+static unsigned
+xy_to_address (unsigned x, unsigned y, unsigned level, unsigned n)
+/*
+ *  Compute bintree address of subimage at coordinates ('x', 'y').
+ *  Size of original image is determined by 'level'.
+ *  'n' specifies number of iterations.
+ *
+ *  Return value:
+ *	address of subimage
+ */ 
+{ 
+   unsigned address = 0;
+
+   while (n--)
+   {
+      address <<= 1;
+      if (--level % 2) 
+      {
+	 if (x & width_of_level (level))
+	    address++;
+      }
+      else
+      {
+	 if (y & height_of_level (level))
+	    address++;
+      }
+   }
+   
+   return address;
+}
diff --git a/converter/other/fiasco/codec/wfalib.h b/converter/other/fiasco/codec/wfalib.h
new file mode 100644
index 00000000..4622fcd2
--- /dev/null
+++ b/converter/other/fiasco/codec/wfalib.h
@@ -0,0 +1,66 @@
+/*
+ *  wfalib.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _WFALIB_H
+#define _WFALIB_H
+
+#include "types.h"
+#include "wfa.h"
+#include "list.h"
+
+typedef struct range_sort
+{
+   u_word_t *range_state;
+   byte_t   *range_label;
+   u_word_t *range_max_domain;
+   bool_t   *range_subdivided;
+   unsigned  range_no;
+} range_sort_t;
+
+bool_t
+locate_delta_images (wfa_t *wfa);
+void
+sort_ranges (unsigned state, unsigned *domain,
+	     range_sort_t *rs, const wfa_t *wfa);
+bool_t
+find_range (unsigned x, unsigned y, unsigned band,
+	    const wfa_t *wfa, unsigned *range_state, unsigned *range_label);
+void
+compute_spiral (int *vorder, unsigned image_width, unsigned image_height,
+		unsigned tiling_exp, bool_t inc_spiral);
+void
+locate_subimage (unsigned orig_level, unsigned level, unsigned bintree,
+		 unsigned *x, unsigned *y, unsigned *width, unsigned *height);
+void
+copy_wfa (wfa_t *dst, const wfa_t *src);
+void 
+remove_states (unsigned from, wfa_t *wfa);
+void
+append_edge (unsigned from, unsigned into, real_t weight,
+	     unsigned label, wfa_t *wfa);
+word_t *
+compute_hits (unsigned from, unsigned to, unsigned n, const wfa_t *wfa);
+real_t 
+compute_final_distribution (unsigned state, const wfa_t *wfa);
+wfa_t *
+alloc_wfa (bool_t coding);
+void
+free_wfa (wfa_t *wfa);
+bool_t
+locate_delta_images (wfa_t *wfa);
+
+#endif /* not _WFALIB_H */
+
diff --git a/converter/other/fiasco/config.h b/converter/other/fiasco/config.h
new file mode 100644
index 00000000..d6b15a84
--- /dev/null
+++ b/converter/other/fiasco/config.h
@@ -0,0 +1,96 @@
+/* config.h.  Generated automatically by configure.  */
+/* But then manually maintained as part of Netpbm! */
+
+/* config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define to empty if the keyword does not work.  */
+/* #undef const */
+
+/* Define if you don't have vprintf but do have _doprnt.  */
+/* #undef HAVE_DOPRNT */
+
+/* Define if you have the vprintf function.  */
+#define HAVE_VPRINTF 1
+
+/* Define to `unsigned' if <sys/types.h> doesn't define.  */
+/* #undef size_t */
+
+/* Define if you have the ANSI C header files.  */
+#define STDC_HEADERS 1
+
+/* Define if you can safely include both <sys/time.h> and <time.h>.  */
+#define TIME_WITH_SYS_TIME 1
+
+/* Define if your processor stores words with the most significant
+   byte first (like Motorola and SPARC, unlike Intel and VAX).  */
+/* #undef WORDS_BIGENDIAN */
+
+/* Define if the X Window System is missing or not being used.  */
+#define X_DISPLAY_MISSING 1
+
+/* Define if shifting of signed integers works */
+#define HAVE_SIGNED_SHIFT 1
+
+/* Define if you don't have the CLOCKS_PER_SEC define in <time.h>. */
+/* #undef CLOCKS_PER_SEC */
+
+/* The number of bytes in a char.  */
+#define SIZEOF_CHAR 1
+
+/* The number of bytes in a int.  */
+#define SIZEOF_INT 4
+
+/* The number of bytes in a short.  */
+#define SIZEOF_SHORT 2
+
+/* Define if you have the log2 function.  */
+/* #undef HAVE_LOG2 */
+/* For Netpbm, we won't use log2() because it might not exist.  In
+   Netpbm, misc.c contains Log2(), so we just define log2 as Log2 here.
+   But first, we include <math.h> because if we don't it may get included
+   after config.h, and then there could be a redefinition issue with log2.
+*/
+#include <math.h>
+#undef log2
+#define log2 Log2
+
+/* Define if you have the memmove function.  */
+#define HAVE_MEMMOVE 1
+
+/* Define if you have the strcasecmp function.  */
+#define HAVE_STRCASECMP 1
+
+/* Define if you have the strdup function.  */
+#define HAVE_STRDUP 1
+
+/* Define if you have the <X11/extensions/XShm.h> header file.  */
+/* #undef HAVE_X11_EXTENSIONS_XSHM_H */
+
+/* Define if you have the <assert.h> header file.  */
+#define HAVE_ASSERT_H 1
+
+/* Define if you have the <features.h> header file.  */
+#define HAVE_FEATURES_H 1
+
+/* Define if you have the <setjmp.h> header file.  */
+#define HAVE_SETJMP_H 1
+
+/* Define if you have the <string.h> header file.  */
+#define HAVE_STRING_H 1
+
+/* Define if you have the <sys/time.h> header file.  */
+#define HAVE_SYS_TIME_H 1
+
+/* Define if you have the <unistd.h> header file.  */
+#define HAVE_UNISTD_H 1
+
+/* Define if you have the m library (-lm).  */
+#define HAVE_LIBM 1
+
+/* Name of package */
+#define PACKAGE "fiasco"
+
+/* Version number of package */
+#define VERSION "1.0"
+
+#define FIASCO_SHARE "/etc/"
diff --git a/converter/other/fiasco/display.c b/converter/other/fiasco/display.c
new file mode 100644
index 00000000..9e531149
--- /dev/null
+++ b/converter/other/fiasco/display.c
@@ -0,0 +1,422 @@
+/*
+ *  display.c:		X11 display of frames
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ *		
+ *  Based on mpeg2decode, (C) 1994, MPEG Software Simulation Group
+ *  and      mpeg2play,   (C) 1994 Stefan Eckart
+ *                                 <stefan@lis.e-technik.tu-muenchen.de>
+ *  and      tmndec       (C) 1995, 1996  Telenor R&D, Norway
+ */
+
+/*
+ *  $Date: 2000/07/03 19:35:59 $
+ *  $Author: hafner $
+ *  $Revision: 5.2 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#ifndef X_DISPLAY_MISSING
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+
+#if STDC_HEADERS
+#	include <stdlib.h>
+#	include <string.h>
+#else /* not STDC_HEADERS */
+#	if HAVE_STRING_H
+#		include <string.h>
+#	else /* not HAVE_STRING_H */
+#		include <strings.h>
+#	endif /* not HAVE_STRING_H */
+#endif /* not STDC_HEADERS */
+
+#include "types.h"
+#include "macros.h"
+#include "display.h"
+#include "binerror.h"
+
+/*****************************************************************************
+
+	       shared memory functions (if USE_SHM is defined)
+  
+*****************************************************************************/
+
+#ifdef USE_SHM
+
+#ifdef HAVE_FEATURES_H
+#include <features.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+
+int
+XShmQueryExtension (Display *dpy);
+int
+XShmGetEventBase (Display *dpy);
+
+static int
+HandleXError (Display *dpy, XErrorEvent *event);
+static void
+InstallXErrorHandler (x11_info_t *xinfo);
+static void
+DeInstallXErrorHandler (x11_info_t *xinfo);
+
+static int		shmem_flag;
+static XShmSegmentInfo	shminfo1, shminfo2;
+static int		gXErrorFlag;
+static int		CompletionType = -1;
+
+static int
+HandleXError (Display *dpy, XErrorEvent *event)
+{
+   gXErrorFlag = 1;
+   
+   return 0;
+}
+
+static void
+InstallXErrorHandler (x11_info_t *xinfo)
+{
+   XSetErrorHandler (HandleXError);
+   XFlush (xinfo->display);
+}
+
+static void
+DeInstallXErrorHandler (x11_info_t *xinfo)
+{
+   XSetErrorHandler (NULL);
+   XFlush (xinfo->display);
+}
+
+#endif /* USE_SHM */
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+void
+display_image (unsigned x0, unsigned y0, x11_info_t *xinfo)
+/*
+ *  Display 'image' at pos ('x0', 'y0') in the current window
+ *  (given by 'xinfo->window'). 
+ *
+ *  No return value.
+ */
+{
+   int byte_order_check = 1;
+   
+   /*
+    *  Always work in native bit and byte order. This tells Xlib to
+    *  reverse bit and byte order if necessary when crossing a
+    *  network. Frankly, this part of XImages is somewhat
+    *  underdocumented, so this may not be exactly correct.
+    */
+   if (*(char *) & byte_order_check == 1)
+   {
+      xinfo->ximage->byte_order       = LSBFirst;
+      xinfo->ximage->bitmap_bit_order = LSBFirst;
+   }
+   else
+   {
+      xinfo->ximage->byte_order       = MSBFirst;
+      xinfo->ximage->bitmap_bit_order = MSBFirst;
+   }    
+
+   /* Display dithered image */
+#ifdef USE_SHM
+   if (shmem_flag)
+   {
+      XEvent xev;
+      
+      XShmPutImage (xinfo->display, xinfo->window, xinfo->gc, xinfo->ximage,
+		    0, 0, x0, y0, xinfo->ximage->width, xinfo->ximage->height,
+		    True);
+      XFlush (xinfo->display);
+      
+      while (!XCheckTypedEvent (xinfo->display, CompletionType, &xev))
+	 ;
+   }
+   else 
+#endif /* USE_SHM */
+   {
+      xinfo->ximage->data = (char *) xinfo->pixels; 
+      XPutImage (xinfo->display, xinfo->window, xinfo->gc, xinfo->ximage, 0, 0,
+		 x0, y0, xinfo->ximage->width, xinfo->ximage->height);
+   }
+}
+
+void
+close_window (x11_info_t *xinfo)
+{
+#ifdef USE_SHM
+   if (shmem_flag && xinfo->ximage)
+   {
+      XShmDetach (xinfo->display, &shminfo1);
+      XDestroyImage (xinfo->ximage);
+      xinfo->ximage = NULL;
+      shmdt (shminfo1.shmaddr);
+   }
+   else
+#endif /* USE_SHM */
+   if (xinfo->ximage)
+   {
+      XDestroyImage (xinfo->ximage);
+      xinfo->ximage = NULL;
+   }
+   if (xinfo->display)
+   {
+      XCloseDisplay (xinfo->display);
+      xinfo->display = NULL;
+   }
+}
+
+x11_info_t *
+open_window (const char *titlename, const char *iconname,
+	     unsigned width, unsigned height)
+/*
+ *  Open a X11 window of size 'width'x'height'.
+ *  If 'color' is false then allocate a colormap with grayscales.
+ *  Window and icon titles are given by 'titlename' and 'iconname',
+ *  respectively.
+ *
+ *  Return value:
+ *	X11 info struct containing display, gc, window ID and colormap.
+ */
+{
+   XVisualInfo		visual_template; /* template for XGetVisualInfo() */
+   XVisualInfo		visual_info;	/* return value of XGetVisualInfo() */
+   int			visual_n;	/* # of matches of XGetVisualInfo */
+   XEvent		xev;		
+   XSizeHints		hint;		
+   XSetWindowAttributes xswa;		
+   unsigned int		fg, bg;		/* foreground and background color */
+   unsigned int		mask;		/* event mask */
+   x11_info_t		*xinfo = calloc (1, sizeof (x11_info_t));
+   long                 visual_mask;
+   
+   if (!xinfo)
+      error ("Out of memory");
+   /*
+    *  Initialization of display
+    */
+   xinfo->display = XOpenDisplay (NULL);
+   if (xinfo->display == NULL)
+      error ("Can't open display.\n"
+	     "Make sure that your environment variable DISPLAY "
+	     "is set correctly.");
+
+   xinfo->screen = DefaultScreen (xinfo->display);
+   xinfo->gc     = DefaultGC (xinfo->display, xinfo->screen);
+
+   {
+      unsigned   depth [] 	= {32, 24, 16};
+      int        class [] 	= {TrueColor, PseudoColor};
+      const char *class_text [] = {"TrueColor", "PseudoColor"};
+      Status     found 		= 0;
+      unsigned   d, c;
+
+      for (d = 0; !found && d < sizeof (depth) / sizeof (unsigned); d++)
+	 for (c = 0; !found && c < sizeof (class) / sizeof (int); c++)
+	 {
+	    found = XMatchVisualInfo (xinfo->display, xinfo->screen,
+				      depth [d], class [c], &visual_info);
+	    if (found)
+	       fprintf (stderr, "%s : %d bit colordepth.\n",
+			class_text [c], depth [d]);
+	 }
+      if (!found && fiasco_get_verbosity ())
+	 error ("Can't find a 16/24/32 bit TrueColor/DirectColor display");
+   }
+   
+   /* Width and height of the display window */
+   hint.x = hint.y = 0;
+   hint.min_width  = hint.max_width  = hint.width  = width;
+   hint.min_height = hint.max_height = hint.height = height;
+   hint.flags = PSize | PMinSize | PMaxSize;
+
+   /* Get some colors */
+   bg = WhitePixel (xinfo->display, xinfo->screen);
+   fg = BlackPixel (xinfo->display, xinfo->screen);
+
+   /* Make the window */
+   mask = CWBackPixel | CWBorderPixel;
+   if (visual_info.depth >= 16)
+   {
+      mask |= CWColormap;
+      xswa.colormap = XCreateColormap (xinfo->display,
+				       DefaultRootWindow (xinfo->display),
+				       visual_info.visual, AllocNone);
+   }
+   xswa.background_pixel = bg;
+   xswa.border_pixel     = fg;
+   xinfo->window = XCreateWindow (xinfo->display,
+				  DefaultRootWindow (xinfo->display), 0, 0,
+				  width, height, 1, visual_info.depth,
+				  InputOutput, visual_info.visual,
+				  mask, &xswa);
+
+   XSelectInput (xinfo->display, xinfo->window, StructureNotifyMask);
+
+   /* Tell other applications about this window */
+   XSetStandardProperties (xinfo->display, xinfo->window, titlename, iconname,
+			   None, NULL, 0, &hint);
+
+   /* Map window. */
+   XMapWindow (xinfo->display, xinfo->window);
+
+   /* Wait for map. */
+   do
+   {
+      XNextEvent (xinfo->display, &xev);
+   }
+   while (xev.type != MapNotify || xev.xmap.event != xinfo->window);
+
+   /* Allocate colors */
+
+   return xinfo;
+}
+
+void
+alloc_ximage (x11_info_t *xinfo, unsigned width, unsigned height)
+/*
+ *  Allocate ximage of size 'width'x'height'.
+ *  If USE_SHM is defined then use shared memory extensions.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'ximage->ximage' and 'ximage->pixels' are set to useful values.
+ */
+{
+   char dummy;
+   
+#ifdef USE_SHM
+   if (XShmQueryExtension(xinfo->display))
+   {
+      if (fiasco_get_verbosity ())
+	 fprintf (stderr, "Trying shared memory.\n");
+      shmem_flag = 1;
+   }
+   else
+   {
+      shmem_flag = 0;
+      if (fiasco_get_verbosity ())
+	 fprintf (stderr,
+		  "Shared memory not supported\nReverting to normal Xlib.\n");
+   }
+
+   if (shmem_flag)
+      CompletionType = XShmGetEventBase (xinfo->display) + ShmCompletion;
+
+   InstallXErrorHandler (xinfo);
+
+   if (shmem_flag)
+   {
+      xinfo->ximage = XShmCreateImage (xinfo->display,
+				       DefaultVisual (xinfo->display,
+						      xinfo->screen),
+				       DefaultDepth (xinfo->display,
+						     xinfo->screen), ZPixmap,
+				       NULL, &shminfo1, width, height);
+
+      /* If no go, then revert to normal Xlib calls. */
+
+      if (xinfo->ximage == NULL)
+      {
+	 if (fiasco_get_verbosity ())
+	    fprintf (stderr,
+		     "Shared memory error, disabling (Ximage error).\n");
+	 goto shmemerror;
+      }
+
+      /* Success here, continue. */
+
+      shminfo1.shmid = shmget (IPC_PRIVATE, xinfo->ximage->bytes_per_line
+			       * xinfo->ximage->height, IPC_CREAT | 0777);
+
+      if (shminfo1.shmid < 0)
+      {
+	 XDestroyImage (xinfo->ximage);
+	 if (fiasco_get_verbosity ())
+	    fprintf (stderr,
+		     "Shared memory error, disabling (seg id error).\n");
+	 goto shmemerror;
+      }
+
+      shminfo1.shmaddr = (char *) shmat (shminfo1.shmid, 0, 0);
+      shminfo2.shmaddr = (char *) shmat (shminfo2.shmid, 0, 0);
+
+      if (shminfo1.shmaddr == ((char *) -1))
+      {
+	 XDestroyImage (xinfo->ximage);
+	 if (shminfo1.shmaddr != ((char *) -1))
+	    shmdt (shminfo1.shmaddr);
+	 if (fiasco_get_verbosity ())
+	    fprintf (stderr,
+		     "Shared memory error, disabling (address error).\n");
+	 goto shmemerror;
+      }
+
+      xinfo->ximage->data = shminfo1.shmaddr;
+      xinfo->pixels       = (byte_t *) xinfo->ximage->data;
+      shminfo1.readOnly   = False;
+
+      XShmAttach (xinfo->display, &shminfo1);
+      XSync (xinfo->display, False);
+
+      if (gXErrorFlag)
+      {
+	 /* Ultimate failure here. */
+	 XDestroyImage (xinfo->ximage);
+	 shmdt (shminfo1.shmaddr);
+	 if (fiasco_get_verbosity ())
+	    fprintf (stderr, "Shared memory error, disabling.\n");
+	 gXErrorFlag = 0;
+	 goto shmemerror;
+      }
+      else
+	 shmctl (shminfo1.shmid, IPC_RMID, 0);
+      if (fiasco_get_verbosity ())
+	 fprintf (stderr, "Sharing memory.\n");
+   }
+   else
+   {
+     shmemerror:
+      shmem_flag = 0;
+#endif /* USE_SHM */
+
+      xinfo->ximage = XCreateImage (xinfo->display,
+				    DefaultVisual (xinfo->display,
+						   xinfo->screen),
+				    DefaultDepth (xinfo->display,
+						  xinfo->screen),
+				    ZPixmap, 0, &dummy, width, height, 8, 0);
+      xinfo->pixels = calloc (width * height,
+			      xinfo->ximage->depth <= 8
+			      ? sizeof (byte_t)
+			      : (xinfo->ximage->depth <= 16
+				 ? sizeof (u_word_t) : sizeof (unsigned int)));
+      if (!xinfo->pixels)
+	 error ("Out of memory.");
+    
+#ifdef USE_SHM
+   }
+
+   DeInstallXErrorHandler (xinfo);
+#endif /* USE_SHM */
+}
+
+#endif /* not X_DISPLAY_MISSING */
diff --git a/converter/other/fiasco/display.h b/converter/other/fiasco/display.h
new file mode 100644
index 00000000..5f30b117
--- /dev/null
+++ b/converter/other/fiasco/display.h
@@ -0,0 +1,49 @@
+/*
+ *  display.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:51:17 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _DISPLAY_H
+#define _DISPLAY_H
+
+#ifndef X_DISPLAY_MISSING
+
+#include <X11/Xlib.h>
+
+#include "types.h"
+#include "image.h"
+
+typedef struct x11_info
+{
+   Display *display;
+   int	    screen;			/* default screen number */
+   Window   window;			
+   XImage  *ximage;
+   GC	    gc;
+   byte_t  *pixels;
+} x11_info_t;
+
+void
+display_image (unsigned x0, unsigned y0, x11_info_t *xinfo);
+void
+close_window (x11_info_t *xinfo);
+x11_info_t *
+open_window (const char *titlename, const char *iconname,
+	     unsigned width, unsigned height);
+void
+alloc_ximage (x11_info_t *xinfo, unsigned width, unsigned height);
+
+#endif /* X_DISPLAY_MISSING */
+
+#endif /* not _DISPLAY_H */
diff --git a/converter/other/fiasco/doc/README.LIB b/converter/other/fiasco/doc/README.LIB
new file mode 100644
index 00000000..4bf8c382
--- /dev/null
+++ b/converter/other/fiasco/doc/README.LIB
@@ -0,0 +1,51 @@
+---------------------------------------------------------------------------
+	 FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+		       Copyright (C) 1994-2000
+     Ullrich Hafner <hafner@bigfoot.de>, http://ulli.linuxave.net
+			     Version 1.0
+---------------------------------------------------------------------------
+
+FIASCO is an image and video compression system based on fractal
+coding which outperforms the well known JPEG and MPEG
+standards. FIASCO has been developed during my Ph.D. thesis "Low
+Bit-Rate Image and Video Coding with Weighted Finite Automata", Mensch
+& Buch Verlag, ISBN 3-89820-002-7.
+
+Some information about the FIASCO compression library:
+The library consists of the five "classes"
+
+	- fiasco_coder: used to encode a still image or a sequence of
+	  frames to a FIASCO stream, see fiasco_coder(3) or the file
+	  bin/cwfa.c for details.
+
+	- fiasco_decoder: used to decode the individual frames step by
+	  step, see fiasco_decoder(3) or the file bin/dwfa.c for
+	  details. 
+
+	- fiasco_image: internal representation of an decoded FIASCO
+	  image, see fiasco_image(3) or the file bin/dwfa.c for
+	  details. 
+
+	- fiasco_renderer: used to render the generated image object
+	  to one of the supported X11 output formats, see
+	  fiasco_render(3) or the files bin/dwfa.c or bin/pnmpsnr.c for
+	  details.
+
+	- fiasco_options: used to control various decoder and encoder
+	  options, see fiasco_options(3) or the files bin/cwfa.c,
+	  bin/dwfa.c or bin/pnmpsnr.c for details.
+
+
+Since the coder doesn't store any internal information, the only
+method of this class is the function fiasco_coder ().
+
+For all other classes, a new object is created with the
+fiasco_[object]_new () function, e.g., fiasco_decoder_new () creates a
+new fiasco_decoder_t object. Each object has to be deleted manually by
+calling the destructor fiasco_[object]_delete () (or by calling the
+method object->delete (object)). If you prefer C++ calls: every
+function of the type fiasco_[object]_[method] can be called via
+[object]->[method] ([object], args), too.
+
+Note: PLEASE use only functions, which are noted in the fiasco.h file
+(i.e., all functions and types with the prefix fiasco_)!
diff --git a/converter/other/fiasco/doc/fiasco_c_options.3 b/converter/other/fiasco/doc/fiasco_c_options.3
new file mode 100644
index 00000000..58b2da44
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_c_options.3
@@ -0,0 +1 @@
+.so man3/fiasco_c_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_c_options_delete.3 b/converter/other/fiasco/doc/fiasco_c_options_delete.3
new file mode 100644
index 00000000..58b2da44
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_c_options_delete.3
@@ -0,0 +1 @@
+.so man3/fiasco_c_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_c_options_new.3 b/converter/other/fiasco/doc/fiasco_c_options_new.3
new file mode 100644
index 00000000..52efb86c
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_c_options_new.3
@@ -0,0 +1,432 @@
+.\" $Id: fiasco_c_options_new.3,v 1.1 2000/10/28 17:35:06 hafner Exp $
+.TH fiasco 3 "April, 2000" "FIASCO" "Fractal Image And Sequence COdec"
+
+.SH NAME
+.B fiasco_c_options_new, fiasco_c_options_delete,
+.B fiasco_c_options_set_progress_meter, fiasco_c_options_set_basisfile,
+.B fiasco_c_options_set_smoothing, fiasco_c_options_set_tiling, 
+.B fiasco_c_options_set_chroma_quality, fiasco_c_options_set_optimizations,
+.B fiasco_c_options_set_prediction, fiasco_c_options_set_video_param,
+.B fiasco_c_options_set_quantization, fiasco_c_options_set_frame_pattern
+.B fiasco_c_options_set_title, fiasco_c_options_set_comment
+\- define additional options of FIASCO coder and decoder 
+
+.SH SYNOPSIS
+.B #include <fiasco.h>
+.sp
+.BI "fiasco_c_options_t *"
+.fi
+.BI "fiasco_c_options_new"
+.fi
+.BI "   (void);"
+.sp
+.BI "void"
+.fi
+.BI "fiasco_c_options_delete"
+.fi
+.BI "   (fiasco_c_options_t * "options );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_c_options_set_basisfile"
+.fi
+.BI "   (fiasco_c_options_t * "options ,
+.fi
+.BI "    const char * "filename );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_c_options_set_chroma_quality"
+.fi
+.BI "   (fiasco_c_options_t * "options ,
+.fi
+.BI "    float "quality_factor ,
+.fi
+.BI "    unsigned "dictionary_size );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_c_options_set_comment"
+.fi
+.BI "   (fiasco_c_options_t * "options ,
+.fi
+.BI "    const char * "comment );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_c_options_set_frame_pattern"
+.fi
+.BI "   (fiasco_c_options_t * "options ,
+.fi
+.BI "    const char * "pattern );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_c_options_set_optimizations"
+.fi
+.BI "   (fiasco_c_options_t * "options ,
+.fi
+.BI "    unsigned "min_block_level ,
+.fi
+.BI "    unsigned "max_block_level ,
+.fi
+.BI "    unsigned "max_elements ,
+.fi
+.BI "    unsigned "dictionary_size ,
+.fi
+.BI "    unsigned "optimization_level );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_c_options_set_quantization"
+.fi
+.BI "   (fiasco_c_options_t * "options ,
+.fi
+.BI "    unsigned "mantissa ,
+.fi
+.BI "    fiasco_rpf_range_e "range ,
+.fi
+.BI "    unsigned "dc_mantissa ,
+.fi
+.BI "    fiasco_rpf_range_e "dc_range );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_c_options_set_prediction"
+.fi
+.BI "   (fiasco_c_options_t * "options ,
+.fi
+.BI "    int "intra_prediction ,
+.fi
+.BI "    unsigned "min_block_level ,
+.fi
+.BI "    unsigned "max_block_level );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_c_options_set_progress_meter"
+.fi
+.BI "   (fiasco_c_options_t * "options ,
+.fi
+.BI "    fiasco_progress_e "type );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_c_options_set_smoothing"
+.fi
+.BI "   (fiasco_c_options_t * "options ,
+.fi
+.BI "    unsigned "smoothing );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_c_options_set_tiling"
+.fi
+.BI "   (fiasco_c_options_t * "options ,
+.fi
+.BI "    fiasco_tiling_e "method ,
+.fi
+.BI "    unsigned "exponent );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_c_options_set_title"
+.fi
+.BI "   (fiasco_c_options_t * "options ,
+.fi
+.BI "    const char * "title );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_c_options_set_video_param"
+.fi
+.BI "   (fiasco_c_options_t * "options ,
+.fi
+.BI "    unsigned "frames_per_second ,
+.fi
+.BI "    int "half_pixel_prediction ,
+.fi
+.BI "    int "cross_B_search ,
+.fi
+.BI "    int "B_as_past_ref );
+.fi
+
+.SH DESCRIPTION
+The \fBfiasco_c_options_new()\fP function allocates and initializes a
+FIASCO options object which is used to control additional compression
+ parameters.
+
+Conversely, the function \fBfiasco_c_options_delete()\fP discards the
+given FIASCO coder options object.
+
+Several member functions are available to modify the default behavior
+of the FIASCO coder.
+
+\fBfiasco_c_options_set_smoothing()\fP sets the
+\fIsmoothing\fP-percentage along partitioning borders when the image
+is regenerated; default is 70. This value is stored in the FIASCO file
+and is used as default smoothing percentage in the decoder.
+
+\fBfiasco_c_options_set_frame_pattern()\fP sets the type of inter frame
+compression which should be applied to individual frames of a video
+stream; default is "IPPPPPPPPP". 
+
+\fBfiasco_c_options_set_tiling()\fP sets \fImethod\fP and \fIexponent\fP
+of the image tiling algorithm which runs as initial step of the
+encoder; by default the image is subdivided into 16 tiles which
+are sorted by decreasing variance. 
+
+\fBfiasco_c_options_set_basisfile()\fP sets the \fIfilename\fP of
+the FIASCO initial basis (codebook of dictionary vectors); default is
+"small.fco". 
+
+\fBfiasco_c_options_set_chroma_quality()\fP sets the quality used when
+coding the chroma channels of a color image to the term "\fIquality\fP
+of luminance / \fIquality_factor\fP"; default is 2. Moreover, the size
+of the codebook is limited by \fIdictionary_size\fP; default is 40
+elements. 
+
+\fBfiasco_c_options_set_comment()\fP sets a \fIcomment\fP string to be
+stored in the FIASCO file; default is the empty string. 
+
+\fBfiasco_c_options_set_title()\fP sets a \fItitle\fP string to be
+stored in the FIASCO file; default is the empty string. 
+
+\fBfiasco_c_options_set_optimizations()\fP toggles various coding
+optimizations. E.g., the size of the dictionary (default is 10000),
+the subset of dictionary elements to use for an individual
+approximation (default is 5), the size of the image blocks to consider
+(4x4, ..., 64x64), and some additional low level
+optimizations (default level is 1). 
+
+\fBfiasco_c_options_set_prediction()\fP enables an additional intra
+block prediction by using a DC component approximation. By giving
+levels \fImin_block_level\fP and \fImax_block_level\fP the prediction
+can be limited to a small range of blocks only. By default, this
+method is disabled. 
+
+\fBfiasco_c_options_set_video_param()\fP defines the framerate (default
+is 25) and toggles whether to use half pixel precise motion
+compensated prediction (disabled by default), whether to determine
+motion vectors of interpolated prediction with the Cross-B-Search
+algorithm (disabled by default), and whether to allow B frames to be
+used for B frame predicion (disabled by default).
+
+\fBfiasco_c_options_set_quantization()\fP defines the quantization
+parameters of the approximation coefficients. By default the range of
+DC coefficients is [-1,+1] using a mantissa of 5 bits (and one sign
+bit). By default, all other coefficients are quantized with 3 mantissa
+bits in the interval [-1.5,+1.5].
+
+\fBfiasco_c_options_set_progress_meter()\fP sets the type of progress
+meter to be used during coding. By default, an RPM style progress bar
+using 50 hash marks (####) is used.
+
+.SH ARGUMENTS
+.TP
+options
+This object encapsulates various coding parameters.  
+
+.TP
+smoothing
+This percentage (range is 0 - i.e., no smoothing - to 100) defines how
+much the regenerated image is smoothed along the partitioning borders.
+
+.TP
+method
+Defines the algorithm which should be used to sort the image tiles
+which are generated in the initial coding step. If \fImethod\fP is
+\fBFIASCO_VARIANCE_ASC\fP then the tiles are sorted by variance - the
+first tile has the lowest variance. Conversely, when using
+\fBFIASCO_VARIANCE_DSC\fP the first tile has the largest variance. If
+\fImethod\fP is \fBFIASCO_SPIRAL_ASC\fP then the tiles are sorted like
+a spiral starting in the middle of the image. Conversely, when using
+\fBFIASCO_SPIRAL_DSC\fP the tiles are sorted like a spiral starting in
+the upper left corner.
+
+.TP
+exponent
+This value sets the number of image tiles - which are generated in the
+initial step of the encoder - to 2^\fIexponent\fP.
+
+.TP
+title
+This value is the title string of the FIASCO file. It is displayed, e.g.,
+in the window title of the decoder.
+
+.TP
+comment
+This value defines an optional comment to be stored in the FIASCO file.
+
+.TP
+pattern
+This string defines the sequence of frame types. Character \fIn\fP of
+the string defines the type of frame \fIn\fP (\fIpattern\fP is
+periodically extended). Three different frame types are available
+(case insensitive): choose 'i' for intra-frames (no inter frame
+prediction is used), 'p' for predicted frames (a frame of the
+past is used for prediction), or 'b' for bi-directional predicted
+frames (both a frame of the past and the future is used for
+prediction).
+
+.TP
+filename
+The initial basis (codebook) of the coder is loaded from this
+(ASCII) file. Files that already come with FIASCO are "small.fco" (3 elements),
+"medium.fco" (132 elements), and "large.fco" (219 elements). 
+
+.TP
+quality_factor
+When coding chroma channels (Cb and Cr band) the approximation quality
+is determined by the term `quality of Y component' / \fIquality_factor\fP.
+
+.TP
+dictionary_size
+FIASCO uses a dictionary (codebook) of variable size to approximate
+individual image blocks. The size of the codebook can be limited by
+\fIdictionary_size\fP to reduce the coding time, however, at the cost
+of decreasing quality. 
+
+.TP
+min_block_level
+During coding only those image blocks are considered for approximation
+(or prediction) which binary tree level is larger than
+\fImin_block_level\fP (minimum value is 3). (Since FIASCO internally
+works with binary trees, the size of an image block is determined by
+the \fIlevel\fP of the corresponding binary tree). Refer to following
+table to convert these values:
+
+.ce
+level | width | height
+.fi
+------+-------+--------
+.fi
+  0   |    1  |    1  
+.fi
+  1   |    1  |    2  
+.fi
+  2   |    2  |    2  
+.fi
+  3   |    2  |    4  
+.fi
+  4   |    4  |    4  
+.fi
+  5   |    4  |    8  
+.fi
+  6   |    8  |    8  
+.fi
+  7   |    8  |   16
+.fi
+------+-------+--------
+.fi
+The larger this value is the faster the coder runs but the worse the
+image quality will be.
+
+.TP
+max_block_level
+During coding only those image blocks are considered for approximation
+(or prediction) which binary tree level is smaller than
+\fImax_block_level\fP. The smaller this value is the faster the coder
+runs but the worse the image quality will be.
+
+.TP
+max_elements
+This value defines how many dictionary elements can be
+used to approximate an individual image block. The smaller this positive
+value (range is 1 to 5) is the faster the coder runs but the worse the
+image quality will be. 
+
+.TP
+optimization_level
+Additional low level optimizations are available by setting
+\fIoptimization_level\fP to one of the following values:
+.fi
+0 standard approximation method
+.fi
+1 slightly increases the approximation quality, running time is
+twice as high as with the standard method 
+.fi
+2 hardly increases the approximation quality of method 1, running time
+is twice as high as with method 1 (this method just remains for
+completeness) 
+.fi
+
+.TP
+intra_prediction
+If \fIintra_prediction\fP is set to a non-zero value then an
+additional block prediction of intra-frames is enabled. For some
+images, the image quality is slightly improved, however, at the cost of
+a significantly increased running time of the coder. 
+
+.TP
+frames_per_second
+This value defines the frame rate, i.e., how many frames per second
+should be displayed. This value has no effect during coding, it is just
+passed to the FIASCO output file where it is read and used by the
+decoder.
+
+.TP
+half_pixel_prediction
+A non-zero value enables half pixel precise motion compensated
+prediction. 
+
+.TP
+cross_B_search
+A non-zero value enables the fast Cross-B-Search algorithm to determine
+the motion vectors of an interpolated prediction. Otherwise,
+exhaustive search (in the given search range) is used. 
+
+.TP
+B_as_past_ref
+A non-zero value allows not only I- and P-frames but also B-frames to be
+used for a forward or bi-directional predicion.
+
+.TP
+mantissa, range
+Approximation coefficients are quantized to a small number of
+values (in fixed point format) in the interval [-\fIrange\fP,
++\fIrange\fP]. The number of \fImantissa\fP bits defines the accuracy of
+quantization.
+
+.TP
+dc_mantissa, dc_range
+Approximation coefficients of the DC component are quantized in a
+different way: the number of mantissa bits is given by
+\fIdc_mantissa\fP whereas the quantization interval is given by
+[-\fIdc_range\fP, +\fBdc_range\fP].
+
+.TP
+type
+This value sets the \fItype\fP of progress meter which should be used
+during coding. The following types are available:
+.fi
+\fBFIASCO_PROGRESS_NONE\fP:  no output at all
+.fi
+\fBFIASCO_PROGRESS_BAR\fP: print hash marks (###)
+\fBFIASCO_PROGRESS_PERCENT\fP: percentage meter (50%)
+
+.SH RETURN VALUES
+The function \fBfiasco_c_options_new()\fP returns a pointer to the
+newly allocated coder option object. If an error has been catched, a
+NULL pointer is returned.
+
+All set functions return 1 on success and 0 if an error has been
+catched.  
+
+In case of an error, use the function fiasco_get_error_message(3) to
+get a string with the last error message of FIASCO.
+
+.SH "SEE ALSO"
+.br
+.BR fiasco_decoder "(3), " fiasco_coder (3)
+
+Ullrich Hafner, Juergen Albert, Stefan Frank, and Michael Unger.
+\fBWeighted Finite Automata for Video Compression\fP, IEEE Journal on
+Selected Areas In Communications, January 1998
+.br
+Ullrich Hafner. \fBLow Bit-Rate Image and Video Coding with Weighted
+Finite Automata\fP, Ph.D. thesis, Mensch & Buch Verlag, ISBN
+3-89820-002-7, October 1999.
+
+.SH AUTHOR
+Ullrich Hafner <hafner@bigfoot.de>
diff --git a/converter/other/fiasco/doc/fiasco_c_options_set_basisfile.3 b/converter/other/fiasco/doc/fiasco_c_options_set_basisfile.3
new file mode 100644
index 00000000..58b2da44
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_c_options_set_basisfile.3
@@ -0,0 +1 @@
+.so man3/fiasco_c_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_c_options_set_chroma_quality.3 b/converter/other/fiasco/doc/fiasco_c_options_set_chroma_quality.3
new file mode 100644
index 00000000..58b2da44
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_c_options_set_chroma_quality.3
@@ -0,0 +1 @@
+.so man3/fiasco_c_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_c_options_set_comment.3 b/converter/other/fiasco/doc/fiasco_c_options_set_comment.3
new file mode 100644
index 00000000..58b2da44
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_c_options_set_comment.3
@@ -0,0 +1 @@
+.so man3/fiasco_c_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_c_options_set_frame_pattern.3 b/converter/other/fiasco/doc/fiasco_c_options_set_frame_pattern.3
new file mode 100644
index 00000000..58b2da44
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_c_options_set_frame_pattern.3
@@ -0,0 +1 @@
+.so man3/fiasco_c_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_c_options_set_optimizations.3 b/converter/other/fiasco/doc/fiasco_c_options_set_optimizations.3
new file mode 100644
index 00000000..58b2da44
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_c_options_set_optimizations.3
@@ -0,0 +1 @@
+.so man3/fiasco_c_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_c_options_set_prediction.3 b/converter/other/fiasco/doc/fiasco_c_options_set_prediction.3
new file mode 100644
index 00000000..58b2da44
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_c_options_set_prediction.3
@@ -0,0 +1 @@
+.so man3/fiasco_c_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_c_options_set_progress_meter.3 b/converter/other/fiasco/doc/fiasco_c_options_set_progress_meter.3
new file mode 100644
index 00000000..58b2da44
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_c_options_set_progress_meter.3
@@ -0,0 +1 @@
+.so man3/fiasco_c_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_c_options_set_quantization.3 b/converter/other/fiasco/doc/fiasco_c_options_set_quantization.3
new file mode 100644
index 00000000..58b2da44
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_c_options_set_quantization.3
@@ -0,0 +1 @@
+.so man3/fiasco_c_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_c_options_set_smoothing.3 b/converter/other/fiasco/doc/fiasco_c_options_set_smoothing.3
new file mode 100644
index 00000000..58b2da44
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_c_options_set_smoothing.3
@@ -0,0 +1 @@
+.so man3/fiasco_c_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_c_options_set_tiling.3 b/converter/other/fiasco/doc/fiasco_c_options_set_tiling.3
new file mode 100644
index 00000000..58b2da44
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_c_options_set_tiling.3
@@ -0,0 +1 @@
+.so man3/fiasco_c_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_c_options_set_title.3 b/converter/other/fiasco/doc/fiasco_c_options_set_title.3
new file mode 100644
index 00000000..58b2da44
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_c_options_set_title.3
@@ -0,0 +1 @@
+.so man3/fiasco_c_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_c_options_set_video_param.3 b/converter/other/fiasco/doc/fiasco_c_options_set_video_param.3
new file mode 100644
index 00000000..58b2da44
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_c_options_set_video_param.3
@@ -0,0 +1 @@
+.so man3/fiasco_c_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_coder.3 b/converter/other/fiasco/doc/fiasco_coder.3
new file mode 100644
index 00000000..3d1c6b87
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_coder.3
@@ -0,0 +1,106 @@
+.\" $Id: fiasco_coder.3,v 1.2 2000/10/28 17:39:32 hafner Exp $
+.TH fiasco 3 "April, 2000" "FIASCO" "Fractal Image And Sequence COdec"
+
+.SH NAME
+.B  fiasco_coder
+\- compress image files to a FIASCO file
+
+.SH SYNOPSIS
+.B #include <fiasco.h>
+.sp
+.BI "int "
+.fi
+.BI "fiasco_coder (char const * const * "image_names ,
+.fi
+.BI "              const char * "fiasco_name ,
+.fi
+.BI "              float "quality ,
+.fi
+.BI "              const fiasco_c_options_t * "options );
+.fi
+
+.SH DESCRIPTION
+The \fBfiasco-coder()\fP function compresses the image file(s) given
+by the list of \fIimage_names\fP and creates the new FIASCO output file
+\fIfiasco_name\fP. Besides the approximation \fIquality\fP, several
+compression parameters can be adjusted by the class \fBoptions\fP (see
+fiasco_c_options_new(3)).
+
+.SH ARGUMENTS
+
+.TP
+image_names
+NULL terminated array of image filenames to process. If the first
+array element is "-" or a NULL pointer then FIASCO reads the image
+from standard input. Each array element either has to be an image
+filename or a template of the form:
+
+.ce
+prefix[start-end{+,-}step]suffix
+
+Templates are useful when compressing video streams: e.g., if the template
+"img0[12-01-2].pgm" is given as array element, then FIASCO compresses the
+images img012.pgm, img010.pgm, ..., img002.pgm (in this order).
+
+If a filename is a relative path then the images are searched for in
+the current directory and in the (colon-separated) list of directories
+given by the environment variable \fBFIASCO_IMAGES\fP.
+
+.TP
+fiasco_name
+Name of the FIASCO output file. If the name is "-" or NULL then the
+file is produced on standard output.
+
+If \fIfiasco_name\fP is a relative path and the environment variable
+\fBFIASCO_DATA\fP is a (colon-separated) list of directories, then the
+output file is written to the first (writable) directory of this
+list. Otherwise, the current directory is used to store the output
+file.
+
+.TP
+quality
+Defines the quality of compression. Quality has to be a positive
+value, its typical range is 1.0 (worst) to 100.0 (best). Larger values
+are also allowed - at the cost of exploding encoding times.
+
+.TP
+options
+This "class" encapsulates the various coding and decoding
+parameters. Use the functions fiasco_c_options_new(3) and
+fiasco_c_options_delete(3) to create and delete an object of this
+class. Several member functions (see fiasco_c_options(3)) are
+available to change the default values.
+
+.SH RETURN VALUE
+The function \fBfiasco_coder()\fP returns 1 if the FIASCO file has
+been successfully written. If an error has been catched during
+compression, 0 is returned - use the function
+fiasco_get_error_message(3) to get the last error message of FIASCO.
+
+.SH ENVIRONMENT
+.PD 0
+.TP
+.B FIASCO_IMAGES
+Search path for image files. Default is "./".
+.TP
+.B FIASCO_DATA
+Search and save path for FIASCO files. Default is "./".
+.PD 
+
+.SH "SEE ALSO"
+.br
+.BR fiasco_c_options_new "(3), " fiasco_c_options_delete (3), 
+.br
+.BR fiasco_c_options "(3), " fiasco_get_error_message (3)
+.br
+
+Ullrich Hafner, Juergen Albert, Stefan Frank, and Michael Unger.
+\fBWeighted Finite Automata for Video Compression\fP, IEEE Journal on
+Selected Areas In Communications, January 1998
+.br
+Ullrich Hafner. \fBLow Bit-Rate Image and Video Coding with Weighted
+Finite Automata\fP, Ph.D. thesis, Mensch & Buch Verlag, ISBN
+3-89820-002-7, October 1999.
+
+.SH AUTHOR
+Ullrich Hafner <hafner@bigfoot.de>
diff --git a/converter/other/fiasco/doc/fiasco_d_options.3 b/converter/other/fiasco/doc/fiasco_d_options.3
new file mode 100644
index 00000000..21f1db63
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_d_options.3
@@ -0,0 +1 @@
+.so man3/fiasco_d_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_d_options_delete.3 b/converter/other/fiasco/doc/fiasco_d_options_delete.3
new file mode 100644
index 00000000..21f1db63
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_d_options_delete.3
@@ -0,0 +1 @@
+.so man3/fiasco_d_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_d_options_new.3 b/converter/other/fiasco/doc/fiasco_d_options_new.3
new file mode 100644
index 00000000..4294330a
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_d_options_new.3
@@ -0,0 +1,122 @@
+.\" $Id: fiasco_d_options_new.3,v 1.1 2000/10/28 17:35:12 hafner Exp $
+.TH fiasco 3 "April, 2000" "FIASCO" "Fractal Image And Sequence COdec"
+
+.SH NAME
+.B  fiasco_d_options_new, fiasco_d_options_set_magnification,
+.B fiasco_d_options_delete, fiasco_d_options_set_smoothing
+.B fiasco_d_options_set_4_2_0_format
+\- define additional options of FIASCO decoder 
+
+.SH SYNOPSIS
+.B #include <fiasco.h>
+.sp
+.BI "fiasco_d_options_t *"
+.fi
+.BI "fiasco_d_options_new"
+.fi
+.BI "   (void);"
+.sp
+.BI "void"
+.fi
+.BI "fiasco_d_options_delete"
+.fi
+.BI "   (fiasco_d_options_t * "options );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_d_options_set_4_2_0_format"
+.fi
+.BI "   (fiasco_d_options_t * "options ,
+.fi
+.BI "    int "format );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_d_options_set_magnification"
+.fi
+.BI "   (fiasco_d_options_t * "options ,
+.fi
+.BI "    int "level );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_d_options_set_smoothing"
+.fi
+.BI "   (fiasco_d_options_t * "options ,
+.fi
+.BI "    unsigned "smoothing );
+.fi
+
+.SH DESCRIPTION
+The \fBfiasco_d_options_new()\fP function allocates and initializes a
+FIASCO options object which is used to control additional
+decompression parameters.
+
+Conversely, the function \fBfiasco_d_options_delete()\fP discards the
+given FIASCO decoder options object.
+
+Several member functions are available to modify the default behavior
+of the FIASCO decoder.
+
+\fBfiasco_d_options_set_smoothing()\fP sets the
+\fIsmoothing\fP-percentage along partitioning borders when the images
+are regenerated; default is 70.
+
+\fBfiasco_d_options_set_magnification()\fP sets the \fImagnification\fP
+of the regenerated image; default is 0, i.e., the image geometry is
+not changed.
+
+\fBfiasco_d_options_set_4_2_0_format()\fP defines whether the decoder
+should use the default 4:4:4 format or the 4:2:0 format. The latter
+one significantly reduces the decoding time at the cost of some
+additional blocking artefacts.
+
+.SH ARGUMENTS
+.TP
+options
+This object encapsulates various decoding parameters.  
+
+.TP
+smoothing
+This percentage (range is 0 - i.e., no smoothing - to 100) defines how
+much the regenerated image is smoothed along the partitioning borders.
+
+.TP
+level
+This value gives the magnification of the decoded image with respect
+to the original size. Positive values increase and negative values
+decrease the width and height of the image by a factor of
+2^abs(\fIlevel\fP).
+
+.TP
+format
+If \fIformat\fP is 0 then the 4:4:4 color image format is used, i.e.,
+the chroma channel are of the same size as the luminance. Otherwise,
+the 4:2:0 format is used. Then, width and height of each chroma
+channel is only one half of the width and height of the luminance.
+
+.SH RETURN VALUES
+The function \fBfiasco_d_options_new()\fP returns a pointer to the
+newly allocated decoder option object. If an error has been catched, a
+NULL pointer is returned.
+
+All set functions return 1 on success and 0 if an error has been
+catched.  
+
+In case of an error, use the function fiasco_get_error_message(3) to
+get a string with the last error message of FIASCO.
+
+.SH "SEE ALSO"
+.br
+.BR fiasco_decoder "(3), " fiasco_coder (3)
+
+Ullrich Hafner, Juergen Albert, Stefan Frank, and Michael Unger.
+\fBWeighted Finite Automata for Video Compression\fP, IEEE Journal on
+Selected Areas In Communications, January 1998
+.br
+Ullrich Hafner. \fBLow Bit-Rate Image and Video Coding with Weighted
+Finite Automata\fP, Ph.D. thesis, Mensch & Buch Verlag, ISBN
+3-89820-002-7, October 1999.
+
+.SH AUTHOR
+Ullrich Hafner <hafner@bigfoot.de>
diff --git a/converter/other/fiasco/doc/fiasco_d_options_set_4_2_0_format.3 b/converter/other/fiasco/doc/fiasco_d_options_set_4_2_0_format.3
new file mode 100644
index 00000000..21f1db63
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_d_options_set_4_2_0_format.3
@@ -0,0 +1 @@
+.so man3/fiasco_d_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_d_options_set_magnification.3 b/converter/other/fiasco/doc/fiasco_d_options_set_magnification.3
new file mode 100644
index 00000000..21f1db63
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_d_options_set_magnification.3
@@ -0,0 +1 @@
+.so man3/fiasco_d_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_d_options_set_smoothing.3 b/converter/other/fiasco/doc/fiasco_d_options_set_smoothing.3
new file mode 100644
index 00000000..21f1db63
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_d_options_set_smoothing.3
@@ -0,0 +1 @@
+.so man3/fiasco_d_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_decoder.3 b/converter/other/fiasco/doc/fiasco_decoder.3
new file mode 100644
index 00000000..33c0d21b
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_decoder.3
@@ -0,0 +1 @@
+.so man3/fiasco_decoder_new.3
diff --git a/converter/other/fiasco/doc/fiasco_decoder_delete.3 b/converter/other/fiasco/doc/fiasco_decoder_delete.3
new file mode 100644
index 00000000..33c0d21b
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_decoder_delete.3
@@ -0,0 +1 @@
+.so man3/fiasco_decoder_new.3
diff --git a/converter/other/fiasco/doc/fiasco_decoder_get_comment.3 b/converter/other/fiasco/doc/fiasco_decoder_get_comment.3
new file mode 100644
index 00000000..33c0d21b
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_decoder_get_comment.3
@@ -0,0 +1 @@
+.so man3/fiasco_decoder_new.3
diff --git a/converter/other/fiasco/doc/fiasco_decoder_get_frame.3 b/converter/other/fiasco/doc/fiasco_decoder_get_frame.3
new file mode 100644
index 00000000..33c0d21b
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_decoder_get_frame.3
@@ -0,0 +1 @@
+.so man3/fiasco_decoder_new.3
diff --git a/converter/other/fiasco/doc/fiasco_decoder_get_framerate.3 b/converter/other/fiasco/doc/fiasco_decoder_get_framerate.3
new file mode 100644
index 00000000..33c0d21b
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_decoder_get_framerate.3
@@ -0,0 +1 @@
+.so man3/fiasco_decoder_new.3
diff --git a/converter/other/fiasco/doc/fiasco_decoder_get_height.3 b/converter/other/fiasco/doc/fiasco_decoder_get_height.3
new file mode 100644
index 00000000..33c0d21b
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_decoder_get_height.3
@@ -0,0 +1 @@
+.so man3/fiasco_decoder_new.3
diff --git a/converter/other/fiasco/doc/fiasco_decoder_get_length.3 b/converter/other/fiasco/doc/fiasco_decoder_get_length.3
new file mode 100644
index 00000000..33c0d21b
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_decoder_get_length.3
@@ -0,0 +1 @@
+.so man3/fiasco_decoder_new.3
diff --git a/converter/other/fiasco/doc/fiasco_decoder_get_title.3 b/converter/other/fiasco/doc/fiasco_decoder_get_title.3
new file mode 100644
index 00000000..33c0d21b
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_decoder_get_title.3
@@ -0,0 +1 @@
+.so man3/fiasco_decoder_new.3
diff --git a/converter/other/fiasco/doc/fiasco_decoder_get_width.3 b/converter/other/fiasco/doc/fiasco_decoder_get_width.3
new file mode 100644
index 00000000..33c0d21b
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_decoder_get_width.3
@@ -0,0 +1 @@
+.so man3/fiasco_decoder_new.3
diff --git a/converter/other/fiasco/doc/fiasco_decoder_is_color.3 b/converter/other/fiasco/doc/fiasco_decoder_is_color.3
new file mode 100644
index 00000000..33c0d21b
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_decoder_is_color.3
@@ -0,0 +1 @@
+.so man3/fiasco_decoder_new.3
diff --git a/converter/other/fiasco/doc/fiasco_decoder_new.3 b/converter/other/fiasco/doc/fiasco_decoder_new.3
new file mode 100644
index 00000000..05e981a9
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_decoder_new.3
@@ -0,0 +1,194 @@
+.\" $Id: fiasco_decoder_new.3,v 1.5 2000/10/28 17:39:32 hafner Exp $
+.TH fiasco 3 "April, 2000" "FIASCO" "Fractal Image And Sequence COdec"
+
+.SH NAME
+.B  fiasco_decoder_new, fiasco_decoder_delete,
+.B fiasco_decoder_write_frame, fiasco_decoder_get_frame,
+.B fiasco_decoder_get_length, fiasco_decoder_get_rate,
+.B fiasco_decoder_get_width, fiasco_decoder_get_height
+.B fiasco_decoder_get_title, fiasco_decoder_get_comment
+.B fiasco_decoder_is_color
+\- decompress a FIASCO file
+
+.SH SYNOPSIS
+.B #include <fiasco.h>
+.sp
+.BI "fiasco_decoder_t *"
+.fi
+.BI "fiasco_decoder_new (const char * "fiasco_name ,
+.fi
+.BI "                    const fiasco_d_options_t * "options );
+.sp
+.BI "void"
+.fi
+.BI "fiasco_decoder_delete (fiasco_decoder_t * "decoder );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_decoder_write_frame (fiasco_decoder_t * "decoder ,
+.fi
+.BI "                            const char * "image_name );
+.sp
+.BI "fiasco_image_t *"
+.fi
+.BI "fiasco_decoder_get_frame (fiasco_decoder_t * "decoder );
+.sp
+.BI "unsigned"
+.fi
+.BI "fiasco_decoder_get_length (fiasco_decoder_t * "decoder );
+.sp
+.BI "unsigned"
+.fi
+.BI "fiasco_decoder_get_rate (fiasco_decoder_t * "decoder );
+.sp
+.BI "unsigned"
+.fi
+.BI "fiasco_decoder_get_width (fiasco_decoder_t * "decoder );
+.sp
+.BI "unsigned"
+.fi
+.BI "fiasco_decoder_get_height (fiasco_decoder_t * "decoder );
+.sp
+.BI "const char *"
+.fi
+.BI "fiasco_decoder_get_title (fiasco_decoder_t * "decoder );
+.sp
+.BI "const char *"
+.fi
+.BI "fiasco_decoder_get_comment (fiasco_decoder_t * "decoder );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_decoder_is_color (fiasco_decoder_t * "decoder );
+.fi
+
+.SH DESCRIPTION
+The \fBfiasco_decoder_new()\fP function initializes the decompression
+of FIASCO file \fIfiasco_name\fP. Several decompression parameters
+can be adjusted by the class \fIoptions\fP (see
+fiasco_d_options_new(3)).
+
+The individual frames of a FIASCO video can be decompressed by calling
+successively either function \fBfiasco_decoder_write_frame()\fP or
+\fBfiasco_decoder_get_frame()\fP.
+
+The function \fBfiasco_decoder_write_frame()\fP decompresses the
+current frame and writes it in raw pgm(5) or ppm(5) format to the file
+\fIimage_name\fP. If \fIimage_name\fP=- or a NULL pointer then the
+image file is produced on the standard output. If \fIimage_name\fP is a
+relative path and the environment variable \fBFIASCO_IMAGES\fP is a
+(colon-separated) list of directories, then the output file is
+written to the first (writable) directory of this list. Otherwise, the
+current directory is used to store the file.
+
+The function \fBfiasco_decoder_get_frame()\fP decompresses the
+current frame and returns the computed image object. Use the function
+fiasco_renderer_new(3) to create a renderer object that converts the
+FIASCO image to the desired format. 
+
+After all frames have been decompressed, the function
+\fBfiasco_decoder_delete()\fP should be called to close the input file
+and to free temporarily allocated memory.
+
+Number of available frames, frame rate and frames geometry, type of the
+FIASCO file are accessible through member functions
+\fBfiasco_decoder_get_length()\fP,
+\fBfiasco_decoder_get_rate()\fP,  
+\fBfiasco_decoder_get_width()\fP,
+\fBfiasco_decoder_get_height()\fP,
+and \fBfiasco_decoder_is_color()\fP. Use \fBfiasco_decoder_get_title()\fP,
+\fBfiasco_decoder_get_comment()\fP to read title and comment strings of the
+FIASCO file. 
+
+.SH ARGUMENTS
+
+.TP
+fiasco_name
+Filename of the FIASCO input file. If \fIfiasco_name\fP is a NULL pointer
+or "-" then the decoder reads from standard input. If the file is not
+found in the current directory and the environment variable
+\fBFIASCO_DATA\fP is a (colon-separated) list of directories, then the
+input file is searched for in these directories, too.
+
+.TP
+options
+This "class" encapsulates the various coding and decoding
+parameters. Use the functions fiasco_d_options_new(3) and
+fiasco_d_options_delete(3) to create and delete an object of this
+class. Several member functions (see fiasco_d_options(3)) are
+available to change the default values.
+
+.TP
+decoder
+The decoder "class" encapsulates the FIASCO decoder. It is used to
+store the internal state of the decoder.
+
+.SH RETURN VALUES
+The function \fBfiasco_decoder_new()\fP returns a pointer to the newly
+allocated decoder object. If an error has been catched, a NULL pointer
+is returned.
+
+The function \fBfiasco_decoder_write_frame()\fP returns 1 if the file
+has been successfully written. Otherwise, the function returns 0.
+
+The function \fBfiasco_decoder_get_frame()\fP returns a pointer to the
+newly allocated FIASCO image object. If an error has been catched, a NULL
+pointer is returned.
+
+The function \fBfiasco_decoder_get_length()\fP returns the number of
+frames of the FIASCO file. If an error has been catched, 0 is
+returned. 
+
+The function \fBfiasco_decoder_get_rate()\fP returns the
+framerate (number of frames per second) of the FIASCO file. If an
+error has been catched, 0 is returned.
+
+The function \fBfiasco_decoder_get_width()\fP returns the width of the
+decoded frames of the FIASCO file. If an error has been catched, 0 is
+returned.
+
+The function \fBfiasco_decoder_get_height()\fP returns the height of the
+decoded frames of the FIASCO file. If an error has been catched, 0 is
+returned.
+
+The function \fBfiasco_decoder_get_title()\fP returns an optional
+title of the FIASCO file. If an error has been catched, 0 is returned.
+
+The function \fBfiasco_decoder_get_comment()\fP returns an optional
+comment of the FIASCO file. If an error has been catched, 0 is returned.
+
+The function \fBfiasco_decoder_is_color()\fP returns 0 if the decoded
+frames are grayscale images, otherwise a non-zero value is
+returned.
+
+In case of an error in one of the above functions, use the function
+fiasco_get_error_message(3) to get a string describing the last error
+message of FIASCO.
+
+.SH ENVIRONMENT
+.PD 0
+.TP
+.B FIASCO_IMAGES
+Search path for image files. Default is "./".
+.TP
+.B FIASCO_DATA
+Search and save path for FIASCO files. Default is "./".
+.PD 
+
+.SH "SEE ALSO"
+.br
+.BR fiasco_d_options_new "(3), " fiasco_d_options_delete (3), 
+.br
+.BR fiasco_d_options "(3), " fiasco_get_error_message (3)
+.br
+
+Ullrich Hafner, Juergen Albert, Stefan Frank, and Michael Unger.
+\fBWeighted Finite Automata for Video Compression\fP, IEEE Journal on
+Selected Areas In Communications, January 1998
+.br
+Ullrich Hafner. \fBLow Bit-Rate Image and Video Coding with Weighted
+Finite Automata\fP, Ph.D. thesis, Mensch & Buch Verlag, ISBN
+3-89820-002-7, October 1999.
+
+.SH AUTHOR
+Ullrich Hafner <hafner@bigfoot.de>
diff --git a/converter/other/fiasco/doc/fiasco_decoder_write_frame.3 b/converter/other/fiasco/doc/fiasco_decoder_write_frame.3
new file mode 100644
index 00000000..33c0d21b
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_decoder_write_frame.3
@@ -0,0 +1 @@
+.so man3/fiasco_decoder_new.3
diff --git a/converter/other/fiasco/doc/fiasco_get_error_message.3 b/converter/other/fiasco/doc/fiasco_get_error_message.3
new file mode 100644
index 00000000..09d593fb
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_get_error_message.3
@@ -0,0 +1,41 @@
+.\" $Id: fiasco_get_error_message.3,v 1.1 2000/06/14 19:07:02 hafner Exp $
+.TH fiasco 3 "April, 2000" "FIASCO" "Fractal Image And Sequence COdec"
+
+.SH NAME
+.B  fiasco_get_error_message
+\- return string describing last error catched in FIASCO library
+
+.SH SYNOPSIS
+.B #include <fiasco.h>
+.sp
+.BI "const char * "
+.fi
+.BI "fiasco_get_error_message (void);"
+.fi
+
+.SH DESCRIPTION
+The \fBfiasco_get_error_message()\fP function returns a string
+describing the last error that has been catched in the FIASCO library.
+
+.SH RETURN VALUE
+The function \fBfiasco_get_error_message()\fP returns the appropriate
+description string, or an empty string if no error has been catched so
+far.
+
+.SH "SEE ALSO"
+.br
+.BR fiasco_options "(3), " fiasco_coder (3), 
+.br
+.BR fiasco_decoder "(3), " fiasco_renderer (3)
+.br
+
+Ullrich Hafner, Juergen Albert, Stefan Frank, and Michael Unger.
+\fBWeighted Finite Automata for Video Compression\fP, IEEE Journal on
+Selected Areas In Communications, January 1998
+.br
+Ullrich Hafner. \fBLow Bit-Rate Image and Video Coding with Weighted
+Finite Automata\fP, Ph.D. thesis, Mensch & Buch Verlag, ISBN
+3-89820-002-7, October 1999.
+
+.SH AUTHOR
+Ullrich Hafner <hafner@bigfoot.de>
diff --git a/converter/other/fiasco/doc/fiasco_get_verbosity.3 b/converter/other/fiasco/doc/fiasco_get_verbosity.3
new file mode 100644
index 00000000..884cd19e
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_get_verbosity.3
@@ -0,0 +1 @@
+.so man3/fiasco_set_verbosity.3
diff --git a/converter/other/fiasco/doc/fiasco_image.3 b/converter/other/fiasco/doc/fiasco_image.3
new file mode 100644
index 00000000..f8df38d5
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_image.3
@@ -0,0 +1 @@
+.so man3/fiasco_image_new.3
diff --git a/converter/other/fiasco/doc/fiasco_image_delete.3 b/converter/other/fiasco/doc/fiasco_image_delete.3
new file mode 100644
index 00000000..f8df38d5
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_image_delete.3
@@ -0,0 +1 @@
+.so man3/fiasco_image_new.3
diff --git a/converter/other/fiasco/doc/fiasco_image_get_height.3 b/converter/other/fiasco/doc/fiasco_image_get_height.3
new file mode 100644
index 00000000..f8df38d5
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_image_get_height.3
@@ -0,0 +1 @@
+.so man3/fiasco_image_new.3
diff --git a/converter/other/fiasco/doc/fiasco_image_get_width.3 b/converter/other/fiasco/doc/fiasco_image_get_width.3
new file mode 100644
index 00000000..f8df38d5
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_image_get_width.3
@@ -0,0 +1 @@
+.so man3/fiasco_image_new.3
diff --git a/converter/other/fiasco/doc/fiasco_image_is_color.3 b/converter/other/fiasco/doc/fiasco_image_is_color.3
new file mode 100644
index 00000000..f8df38d5
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_image_is_color.3
@@ -0,0 +1 @@
+.so man3/fiasco_image_new.3
diff --git a/converter/other/fiasco/doc/fiasco_image_new.3 b/converter/other/fiasco/doc/fiasco_image_new.3
new file mode 100644
index 00000000..10625b63
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_image_new.3
@@ -0,0 +1,95 @@
+.\" $Id: fiasco_image_new.3,v 1.2 2000/06/14 19:26:06 hafner Exp $
+.TH fiasco 3 "April, 2000" "FIASCO" "Fractal Image And Sequence COdec"
+
+.SH NAME
+.B  fiasco_image_new, fiasco_image_delete, fiasco_image_get_width,
+.B  fiasco_image_get_height,  fiasco_image_is_color
+\- handle FIASCO image objects
+
+.SH SYNOPSIS
+.B #include <fiasco.h>
+.sp
+.BI "fiasco_image_t *"
+.fi
+.BI "fiasco_image_new (const char * "filename );
+.sp
+.BI "void"
+.fi
+.BI "fiasco_image_delete (fiasco_image_t * "image );
+.sp
+.BI "unsigned"
+.fi
+.BI "fiasco_image_get_width (const fiasco_image_t * "image );
+.sp
+.BI "unsigned"
+.fi
+.BI "fiasco_image_get_height (const fiasco_image_t * "image );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_image_is_color (const fiasco_image_t * "image );
+.fi
+
+.SH DESCRIPTION
+The \fBfiasco_image_new()\fP function reads the given image file and
+allocates and initializes a FIASCO image object. Use the function
+fiasco_renderer_new(3) to create a renderer object that converts the
+FIASCO image to the desired image format.
+
+The function \fBfiasco_image_delete()\fP deletes the image object and
+frees the image buffer. 
+
+Image geometry and type are accessible through member functions
+\fBfiasco_image_get_width()\fP,
+\fBfiasco_image_get_height()\fP,
+and \fBfiasco_image_is_color()\fP. 
+
+.SH ARGUMENTS
+
+.TP
+image
+The image "class" encapsulates the FIASCO image object. It is used to
+store the pixel values of the decoded or read image.
+
+.TP
+filename
+Image filename to process. If \fIfilename\fP is "-" or a NULL pointer
+then the image is read from standard input. If a filename is a
+relative path then the images are searched for in the current
+directory and in the (colon-separated) list of directories given by
+the environment variable \fBFIASCO_IMAGES\fP.
+
+.SH RETURN VALUE
+The function \fBfiasco_image_new()\fP returns a pointer to the newly
+allocated image object. If an error has been catched, a NULL pointer
+is returned.
+
+The function \fBfiasco_image_get_width()\fP returns the width of the
+image. If an error has been catched, 0 is returned.
+
+The function \fBfiasco_image_get_height()\fP returns the height of the
+image. If an error has been catched, 0 is returned.
+
+The function \fBfiasco_image_is_color()\fP returns 0 if the image
+object is a grayscale image, otherwise a non-zero value is returned.
+
+In case of an error in one of the above functions, use the function
+fiasco_get_error_message(3) to get a string with the last error
+message of FIASCO.
+
+.SH "SEE ALSO"
+.br
+.BR fiasco_decoder_get_frame "(3), " fiasco_get_error_message (3)
+.BR fiasco_renderer_new (3)
+.br
+
+Ullrich Hafner, Juergen Albert, Stefan Frank, and Michael Unger.
+\fBWeighted Finite Automata for Video Compression\fP, IEEE Journal on
+Selected Areas In Communications, January 1998
+.br
+Ullrich Hafner. \fBLow Bit-Rate Image and Video Coding with Weighted
+Finite Automata\fP, Ph.D. thesis, Mensch & Buch Verlag, ISBN
+3-89820-002-7, October 1999.
+
+.SH AUTHOR
+Ullrich Hafner <hafner@bigfoot.de>
diff --git a/converter/other/fiasco/doc/fiasco_options.3 b/converter/other/fiasco/doc/fiasco_options.3
new file mode 100644
index 00000000..493fcce9
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_options.3
@@ -0,0 +1 @@
+.so man3/fiasco_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_options_delete.3 b/converter/other/fiasco/doc/fiasco_options_delete.3
new file mode 100644
index 00000000..493fcce9
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_options_delete.3
@@ -0,0 +1 @@
+.so man3/fiasco_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_options_new.3 b/converter/other/fiasco/doc/fiasco_options_new.3
new file mode 100644
index 00000000..26e070ca
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_options_new.3
@@ -0,0 +1,441 @@
+.\" $Id: fiasco_options_new.3,v 1.2 2000/06/25 16:38:06 hafner Exp $
+.TH fiasco 3 "April, 2000" "FIASCO" "Fractal Image And Sequence COdec"
+
+.SH NAME
+.B  fiasco_options_new, fiasco_options_set_magnification,
+.B fiasco_options_delete, fiasco_options_set_progress_meter,
+.B fiasco_options_set_smoothing, fiasco_options_set_tiling, 
+.B fiasco_options_set_4_2_0_format, fiasco_options_set_basisfile,
+.B fiasco_options_set_chroma_quality, fiasco_options_set_optimizations,
+.B fiasco_options_set_prediction, fiasco_options_set_video_param,
+.B fiasco_options_set_quantization, fiasco_options_set_frame_pattern
+\- define additional options of FIASCO coder and decoder 
+
+.SH SYNOPSIS
+.B #include <fiasco.h>
+.sp
+.BI "fiasco_options_t *"
+.fi
+.BI "fiasco_options_new"
+.fi
+.BI "   (void);"
+.sp
+.BI "void"
+.fi
+.BI "fiasco_options_delete"
+.fi
+.BI "   (fiasco_options_t * "options );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_options_set_4_2_0_format"
+.fi
+.BI "   (fiasco_options_t * "options ,
+.fi
+.BI "    int "format );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_options_set_basisfile"
+.fi
+.BI "   (fiasco_options_t * "options ,
+.fi
+.BI "    const char * "filename );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_options_set_chroma_quality"
+.fi
+.BI "   (fiasco_options_t * "options ,
+.fi
+.BI "    float "quality_factor ,
+.fi
+.BI "    unsigned "dictionary_size );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_options_set_frame_pattern"
+.fi
+.BI "   (fiasco_options_t * "options ,
+.fi
+.BI "    const char * "pattern );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_options_set_magnification"
+.fi
+.BI "   (fiasco_options_t * "options ,
+.fi
+.BI "    int "level );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_options_set_optimizations"
+.fi
+.BI "   (fiasco_options_t * "options ,
+.fi
+.BI "    unsigned "min_block_level ,
+.fi
+.BI "    unsigned "max_block_level ,
+.fi
+.BI "    unsigned "max_elements ,
+.fi
+.BI "    unsigned "dictionary_size ,
+.fi
+.BI "    unsigned "optimization_level );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_options_set_quantization"
+.fi
+.BI "   (fiasco_options_t * "options ,
+.fi
+.BI "    unsigned "mantissa ,
+.fi
+.BI "    fiasco_rpf_range_e "range ,
+.fi
+.BI "    unsigned "dc_mantissa ,
+.fi
+.BI "    fiasco_rpf_range_e "dc_range );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_options_set_prediction"
+.fi
+.BI "   (fiasco_options_t * "options ,
+.fi
+.BI "    int "intra_prediction ,
+.fi
+.BI "    unsigned "min_block_level ,
+.fi
+.BI "    unsigned "max_block_level );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_options_set_progress_meter"
+.fi
+.BI "   (fiasco_options_t * "options ,
+.fi
+.BI "    fiasco_progress_e "type );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_options_set_smoothing"
+.fi
+.BI "   (fiasco_options_t * "options ,
+.fi
+.BI "    unsigned "smoothing );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_options_set_tiling"
+.fi
+.BI "   (fiasco_options_t * "options ,
+.fi
+.BI "    fiasco_tiling_e "method ,
+.fi
+.BI "    unsigned "exponent );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_options_set_video_param"
+.fi
+.BI "   (fiasco_options_t * "options ,
+.fi
+.BI "    unsigned "frames_per_second ,
+.fi
+.BI "    int "half_pixel_prediction ,
+.fi
+.BI "    int "cross_B_search ,
+.fi
+.BI "    int "B_as_past_ref );
+.fi
+
+.SH DESCRIPTION
+The \fBfiasco_options_new()\fP function allocates and initializes a
+FIASCO options object which is used to control additional compression and
+decompression parameters.
+
+Conversely, the function \fBfiasco_options_delete()\fP discards the
+given FIASCO options object.
+
+Several member functions are available to modify the default behavior
+of the FIASCO coder and decoder. 
+
+\fBfiasco_options_set_smoothing()\fP sets the
+\fIsmoothing\fP-percentage along partitioning borders when the image
+is regenerated; default is 70. This option is used both by the decoder
+and encoder. You should use the \fIsmoothing\fP value specified in the
+FIASCO file when you are decoding video frames.
+
+\fBfiasco_options_set_magnification()\fP sets the \fImagnification\fP
+of the regenerated image; default is 0, i.e., the image geometry is
+not changed. This option is used by the decoder only.
+
+\fBfiasco_options_set_4_2_0_format()\fP defines whether the decoder
+should use the default 4:4:4 format or the 4:2:0 format. The latter
+one significantly reduces the decoding time at the cost of some
+additional blocking artefacts. This option is used by the decoder only.
+
+\fBfiasco_options_set_frame_pattern()\fP sets the type of inter frame
+compression which should be applied to individual frames of a video
+stream; default is "IPPPPPPPPP". 
+
+\fBfiasco_options_set_tiling()\fP sets \fImethod\fP and \fIexponent\fP
+of the image tiling algorithm which runs as initial step of the
+encoder; by default the image is subdivided into 16 tiles which
+are sorted by decreasing variance. 
+
+\fBfiasco_options_set_basisfile()\fP sets the \fIfilename\fP of
+the FIASCO initial basis (codebook of dictionary vectors); default is
+"small.fco". 
+
+\fBfiasco_options_set_chroma_quality()\fP sets the quality used when
+coding the chroma channels of a color image to the term "\fIquality\fP
+of luminance / \fIquality_factor\fP"; default is 2. Moreover, the size
+of the codebook is limited by \fIdictionary_size\fP; default is 40
+elements. 
+
+\fBfiasco_options_set_optimizations()\fP toggles various coding
+optimizations. E.g., the size of the dictionary (default is 10000),
+the subset of dictionary elements to use for an individual
+approximation (default is 5), the size of the image blocks to consider
+(4x4, ..., 64x64), and some additional low level
+optimizations (default level is 1). 
+
+\fBfiasco_options_set_prediction()\fP enables an additional intra
+block prediction by using a DC component approximation. By giving
+levels \fImin_block_level\fP and \fImax_block_level\fP the prediction
+can be limited to a small range of blocks only. By default, this
+method is disabled. 
+
+\fBfiasco_options_set_video_param()\fP defines the framerate (default
+is 25) and toggles whether to use half pixel precise motion
+compensated prediction (disabled by default), whether to determine
+motion vectors of interpolated prediction with the Cross-B-Search
+algorithm (disabled by default), and whether to allow B frames to be
+used for B frame predicion (disabled by default).
+
+\fBfiasco_options_set_quantization()\fP defines the quantization
+parameters of the approximation coefficients. By default the range of
+DC coefficients is [-1,+1] using a mantissa of 5 bits (and one sign
+bit). By default, all other coefficients are quantized with 3 mantissa
+bits in the interval [-1.5,+1.5].
+
+\fBfiasco_options_set_progress_meter()\fP sets the type of progress
+meter to be used during coding. By default, an RPM style progress bar
+using 50 hash marks (####) is used.
+
+.SH ARGUMENTS
+.TP
+options
+This object encapsulates the various coding and decoding parameters.  
+
+.TP
+smoothing
+This percentage (range is 0 - i.e., no smoothing - to 100) defines how
+much the regenerated image is smoothed along the partitioning borders.
+
+.TP
+level
+This value gives the magnification of the decoded image with respect
+to the original size. Positive values increase and negative values
+decrease the width and height of the image by a factor of
+2^abs(\fIlevel\fP).
+
+.TP
+format
+If \fIformat\fP is 0 then the 4:4:4 color image format is used, i.e.,
+the chroma channel are of the same size as the luminance. Otherwise,
+the 4:2:0 format is used. Then, width and height of each chroma
+channel is only one half of the width and height of the luminance.
+
+.TP
+method
+Defines the algorithm which should be used to sort the image tiles
+which are generated in the initial coding step. If \fImethod\fP is
+\fBFIASCO_VARIANCE_ASC\fP then the tiles are sorted by variance - the
+first tile has the lowest variance. Conversely, when using
+\fBFIASCO_VARIANCE_DSC\fP the first tile has the largest variance. If
+\fImethod\fP is \fBFIASCO_SPIRAL_ASC\fP then the tiles are sorted like
+a spiral starting in the middle of the image. Conversely, when using
+\fBFIASCO_SPIRAL_DSC\fP the tiles are sorted like a spiral starting in
+the upper left corner.
+
+.TP
+exponent
+This value sets the number of image tiles - which are generated in the
+initial step of the encoder - to 2^\fIexponent\fP.
+
+.TP
+pattern
+This string defines the sequence of frame types. Character \fIn\fP of
+the string defines the type of frame \fIn\fP (\fIpattern\fP is
+periodically extended). Three different frame types are available
+(case insensitive): choose 'i' for intra-frames (no inter frame
+prediction is used), 'p' for predicted frames (a frame of the
+past is used for prediction), or 'b' for bi-directional predicted
+frames (both a frame of the past and the future is used for
+prediction).
+
+.TP
+filename
+The initial basis (codebook) of the coder is loaded from this
+(ASCII) file. Files that already come with FIASCO are "small.fco" (3 elements),
+"medium.fco" (132 elements), and "large.fco" (219 elements). 
+
+.TP
+quality_factor
+When coding chroma channels (Cb and Cr band) the approximation quality
+is determined by the term `quality of Y component' / \fIquality_factor\fP.
+
+.TP
+dictionary_size
+FIASCO uses a dictionary (codebook) of variable size to approximate
+individual image blocks. The size of the codebook can be limited by
+\fIdictionary_size\fP to reduce the coding time, however, at the cost
+of decreasing quality. 
+
+.TP
+min_block_level
+During coding only those image blocks are considered for approximation
+(or prediction) which binary tree level is larger than
+\fImin_block_level\fP (minimum value is 3). (Since FIASCO internally
+works with binary trees, the size of an image block is determined by
+the \fIlevel\fP of the corresponding binary tree). Refer to following
+table to convert these values:
+
+.ce
+level | width | height
+.fi
+------+-------+--------
+.fi
+  0   |    1  |    1  
+.fi
+  1   |    1  |    2  
+.fi
+  2   |    2  |    2  
+.fi
+  3   |    2  |    4  
+.fi
+  4   |    4  |    4  
+.fi
+  5   |    4  |    8  
+.fi
+  6   |    8  |    8  
+.fi
+  7   |    8  |   16
+.fi
+------+-------+--------
+.fi
+The larger this value is the faster the coder runs but the worse the
+image quality will be.
+
+.TP
+max_block_level
+During coding only those image blocks are considered for approximation
+(or prediction) which binary tree level is smaller than
+\fImax_block_level\fP. The smaller this value is the faster the coder
+runs but the worse the image quality will be.
+
+.TP
+max_elements
+This value defines how many dictionary elements can be
+used to approximate an individual image block. The smaller this positive
+value (range is 1 to 5) is the faster the coder runs but the worse the
+image quality will be. 
+
+.TP
+optimization_level
+Additional low level optimizations are available by setting
+\fIoptimization_level\fP to one of the following values:
+.fi
+0 standard approximation method
+.fi
+1 slightly increases the approximation quality, running time is
+twice as high as with the standard method 
+.fi
+2 hardly increases the approximation quality of method 1, running time
+is twice as high as with method 1 (this method just remains for
+completeness) 
+.fi
+
+.TP
+intra_prediction
+If \fIintra_prediction\fP is set to a non-zero value then an
+additional block prediction of intra-frames is enabled. For some
+images, the image quality is slightly improved, however, at the cost of
+a significantly increased running time of the coder. 
+
+.TP
+frames_per_second
+This value defines the frame rate, i.e., how many frames per second
+should be displayed. This value has no effect during coding, it is just
+passed to the FIASCO output file where it is read and used by the
+decoder.
+
+.TP
+half_pixel_prediction
+A non-zero value enables half pixel precise motion compensated
+prediction. 
+
+.TP
+cross_B_search
+A non-zero value enables the fast Cross-B-Search algorithm to determine
+the motion vectors of an interpolated prediction. Otherwise,
+exhaustive search (in the given search range) is used. 
+
+.TP
+B_as_past_ref
+A non-zero value allows not only I- and P-frames but also B-frames to be
+used for a forward or bi-directional predicion.
+
+.TP
+mantissa, range
+Approximation coefficients are quantized to a small number of
+values (in fixed point format) in the interval [-\fIrange\fP,
++\fIrange\fP]. The number of \fImantissa\fP bits defines the accuracy of
+quantization.
+
+.TP
+dc_mantissa, dc_range
+Approximation coefficients of the DC component are quantized in a
+different way: the number of mantissa bits is given by
+\fIdc_mantissa\fP whereas the quantization interval is given by
+[-\fIdc_range\fP, +\fBdc_range\fP].
+
+.TP
+type
+This value sets the \fItype\fP of progress meter which should be used
+during coding. The following types are available:
+.fi
+\fBFIASCO_PROGRESS_NONE\fP:  no output at all
+.fi
+\fBFIASCO_PROGRESS_BAR\fP: print hash marks (###)
+\fBFIASCO_PROGRESS_PERCENT\fP: percentage meter (50%)
+
+.SH RETURN VALUES
+The function \fBfiasco_decoder_new()\fP returns a pointer to the newly
+allocated option object. If an error has been catched, a NULL pointer
+is returned.
+
+All set functions return 1 on success and 0 if an error has been
+catched.  
+
+In case of an error, use the function fiasco_get_error_message(3) to
+get a string with the last error message of FIASCO.
+
+.SH "SEE ALSO"
+.br
+.BR fiasco_decoder "(3), " fiasco_coder (3)
+
+Ullrich Hafner, Juergen Albert, Stefan Frank, and Michael Unger.
+\fBWeighted Finite Automata for Video Compression\fP, IEEE Journal on
+Selected Areas In Communications, January 1998
+.br
+Ullrich Hafner. \fBLow Bit-Rate Image and Video Coding with Weighted
+Finite Automata\fP, Ph.D. thesis, Mensch & Buch Verlag, ISBN
+3-89820-002-7, October 1999.
+
+.SH AUTHOR
+Ullrich Hafner <hafner@bigfoot.de>
diff --git a/converter/other/fiasco/doc/fiasco_options_set_4_2_0_format.3 b/converter/other/fiasco/doc/fiasco_options_set_4_2_0_format.3
new file mode 100644
index 00000000..493fcce9
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_options_set_4_2_0_format.3
@@ -0,0 +1 @@
+.so man3/fiasco_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_options_set_basisfile.3 b/converter/other/fiasco/doc/fiasco_options_set_basisfile.3
new file mode 100644
index 00000000..493fcce9
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_options_set_basisfile.3
@@ -0,0 +1 @@
+.so man3/fiasco_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_options_set_chroma_quality.3 b/converter/other/fiasco/doc/fiasco_options_set_chroma_quality.3
new file mode 100644
index 00000000..493fcce9
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_options_set_chroma_quality.3
@@ -0,0 +1 @@
+.so man3/fiasco_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_options_set_frame_pattern.3 b/converter/other/fiasco/doc/fiasco_options_set_frame_pattern.3
new file mode 100644
index 00000000..493fcce9
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_options_set_frame_pattern.3
@@ -0,0 +1 @@
+.so man3/fiasco_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_options_set_magnification.3 b/converter/other/fiasco/doc/fiasco_options_set_magnification.3
new file mode 100644
index 00000000..493fcce9
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_options_set_magnification.3
@@ -0,0 +1 @@
+.so man3/fiasco_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_options_set_optimizations.3 b/converter/other/fiasco/doc/fiasco_options_set_optimizations.3
new file mode 100644
index 00000000..493fcce9
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_options_set_optimizations.3
@@ -0,0 +1 @@
+.so man3/fiasco_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_options_set_prediction.3 b/converter/other/fiasco/doc/fiasco_options_set_prediction.3
new file mode 100644
index 00000000..493fcce9
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_options_set_prediction.3
@@ -0,0 +1 @@
+.so man3/fiasco_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_options_set_progress_meter.3 b/converter/other/fiasco/doc/fiasco_options_set_progress_meter.3
new file mode 100644
index 00000000..493fcce9
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_options_set_progress_meter.3
@@ -0,0 +1 @@
+.so man3/fiasco_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_options_set_quantization.3 b/converter/other/fiasco/doc/fiasco_options_set_quantization.3
new file mode 100644
index 00000000..493fcce9
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_options_set_quantization.3
@@ -0,0 +1 @@
+.so man3/fiasco_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_options_set_smoothing.3 b/converter/other/fiasco/doc/fiasco_options_set_smoothing.3
new file mode 100644
index 00000000..493fcce9
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_options_set_smoothing.3
@@ -0,0 +1 @@
+.so man3/fiasco_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_options_set_tiling.3 b/converter/other/fiasco/doc/fiasco_options_set_tiling.3
new file mode 100644
index 00000000..493fcce9
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_options_set_tiling.3
@@ -0,0 +1 @@
+.so man3/fiasco_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_options_set_video_param.3 b/converter/other/fiasco/doc/fiasco_options_set_video_param.3
new file mode 100644
index 00000000..493fcce9
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_options_set_video_param.3
@@ -0,0 +1 @@
+.so man3/fiasco_options_new.3
diff --git a/converter/other/fiasco/doc/fiasco_renderer.3 b/converter/other/fiasco/doc/fiasco_renderer.3
new file mode 100644
index 00000000..0aec996e
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_renderer.3
@@ -0,0 +1 @@
+.so man3/fiasco_renderer_new.3
diff --git a/converter/other/fiasco/doc/fiasco_renderer_delete.3 b/converter/other/fiasco/doc/fiasco_renderer_delete.3
new file mode 100644
index 00000000..0aec996e
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_renderer_delete.3
@@ -0,0 +1 @@
+.so man3/fiasco_renderer_new.3
diff --git a/converter/other/fiasco/doc/fiasco_renderer_new.3 b/converter/other/fiasco/doc/fiasco_renderer_new.3
new file mode 100644
index 00000000..b24d8462
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_renderer_new.3
@@ -0,0 +1,125 @@
+.\" $Id: fiasco_renderer_new.3,v 1.2 2000/06/14 18:58:35 hafner Exp $
+.TH fiasco 3 "April, 2000" "FIASCO" "Fractal Image And Sequence COdec"
+
+.SH NAME
+.B  fiasco_renderer_new, fiasco_renderer_delete
+\- convert a FIASCO image object to an X11 XImage
+
+.SH SYNOPSIS
+.B #include <fiasco.h>
+.sp
+.BI "fiasco_renderer_t *"
+.fi
+.BI "fiasco_renderer_new (unsigned long "red_mask ,
+.fi
+.BI "                     unsigned long "green_mask ,
+.fi
+.BI "                     unsigned long "blue_mask ,
+.fi
+.BI "                     unsigned "bpp ,
+.fi
+.BI "                     int "double_resolution );
+.sp
+.BI "void"
+.fi
+.BI "fiasco_renderer_delete (fiasco_renderer_t * "renderer );
+.sp
+.BI "int"
+.fi
+.BI "fiasco_renderer_render (const fiasco_renderer_t * "renderer ,
+.fi
+.BI "                        unsigned char * "data );
+.fi
+.BI "                        const fiasco_image_t * "fiasco_image );
+.fi
+
+.SH DESCRIPTION
+The \fBfiasco_renderer_new()\fP function allocates and initializes a
+renderer object which has to be used to convert an internal FIASCO
+image object to one of the supported X11 formats. Currently, the FIASCO
+image can be rendered to an X11 XImage of either 16, 24, or 32 bits
+per pixel. Additional formats will be supported upon request.  
+
+Function \fBfiasco_renderer_render()\fP is used to convert the given
+FIASCO image object to the specified format. 
+
+After all frames are rendered, the function
+\fBfiasco_renderer_delete()\fP should be called to free temporarily
+allocated memory and to discard the renderer object.
+
+Note that the FIASCO renderer class is not restricted to X11 images: a
+FIASCO image object can be converted to an image data array of the
+form RGBRGB... by setting \fIred_mask\fP=0xff0000,
+\fIgreen_mask\fP=0xff00, \fIblue_mask\fP=0xff, and \fIbpp\fP=24.
+
+.SH ARGUMENTS
+
+.TP
+bpp
+Determines the number of bits of a single pixel of the X11 XImage
+structure (see XCreateImage(3)). If the XImage is already allocated
+then the value XImage->bits_per_pixel should be used. Currently, 16,
+24, and 32 bits per pixel are supported.
+
+.TP
+red_mask
+Determines which bits of a pixel should be used for the red
+component. If the XImage is already allocated then the value
+XImage->red_mask should be used. E.g., if \fIbpp=16\fP and
+\fIred_mask=0xf800\fP then each pixel is stored with two bytes. The
+red component uses bits 11-15, the remaining green and blue components
+use bits 0-10.
+
+.TP
+green_mask
+Determines which bits of a pixel should be used for the green
+component. If the XImage is already allocated then the value
+XImage->green_mask should be used.
+
+.TP
+blue_mask
+Determines which bits of a pixel should be used for the blue
+component. If the XImage is already allocated then the value
+XImage->blue_mask should be used.
+
+.TP
+data
+A pointer to the image data. If the XImage is already allocated then
+the value XImage->data should be used. This array has to be large
+enough to hold the decoded image at the given size (geometry and bits
+per pixel).
+
+.TP
+fiasco_image
+This object represents the decoded image which has been
+created by the FIASCO functions fiasco_decoder_get_frame(3) or
+fiasco_image_new(3).
+
+.SH RETURN VALUE
+The function \fBfiasco_renderer_new()\fP returns a pointer to the newly
+allocated renderer object. If an error has been catched, a NULL pointer
+is returned.
+
+The function \fBfiasco_renderer_render()\fP returns 1 if the image
+has been successfully converted. Otherwise, the function returns 0.
+
+In case of an error in one of the above functions, use the function
+fiasco_get_error_message(3) to get a string with the last error
+message of FIASCO.
+
+.SH "SEE ALSO"
+.br
+.BR fiasco_decoder_get_frame "(3), " fiasco_get_error_message (3)
+.BR fiasco_image_new (3)
+.br
+
+Ullrich Hafner, Juergen Albert, Stefan Frank, and Michael Unger.
+\fBWeighted Finite Automata for Video Compression\fP, IEEE Journal on
+Selected Areas In Communications, January 1998
+.br
+Ullrich Hafner. \fBLow Bit-Rate Image and Video Coding with Weighted
+Finite Automata\fP, Ph.D. thesis, Mensch & Buch Verlag, ISBN
+3-89820-002-7, October 1999.
+
+.SH AUTHOR
+Ullrich Hafner <hafner@bigfoot.de>
diff --git a/converter/other/fiasco/doc/fiasco_renderer_render.3 b/converter/other/fiasco/doc/fiasco_renderer_render.3
new file mode 100644
index 00000000..0aec996e
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_renderer_render.3
@@ -0,0 +1 @@
+.so man3/fiasco_renderer_new.3
diff --git a/converter/other/fiasco/doc/fiasco_set_verbosity.3 b/converter/other/fiasco/doc/fiasco_set_verbosity.3
new file mode 100644
index 00000000..746854b1
--- /dev/null
+++ b/converter/other/fiasco/doc/fiasco_set_verbosity.3
@@ -0,0 +1,46 @@
+.\" $Id: fiasco_set_verbosity.3,v 1.1 2000/06/06 20:55:05 hafner Exp $
+.TH fiasco 3 "April, 2000" "FIASCO" "Fractal Image And Sequence COdec"
+
+.SH NAME
+.B  fiasco_get_verbosity, fiasco_set_verbosity
+\- get or set verbosity of FIASCO library
+
+.SH SYNOPSIS
+.B #include <fiasco.h>
+.sp
+.BI "fiasco_verbosity_e"
+.fi
+.BI "fiasco_get_verbosity (void);"
+.sp
+.BI "void"
+.fi
+.BI "fiasco_set_verbosity (fiasco_verbosity_e "level );
+.fi
+
+.SH DESCRIPTION
+The \fBfiasco_get_verbosity()\fP function returns the current
+verbosity level of the FIASCO library. Conversely, the function
+\fBfiasco_set_verbosity()\fP sets the verbosity of the FIASCO library
+to the given \fIlevel\fP.
+
+.SH RETURN VALUE
+The function \fBfiasco_get_verbosity()\fP returns the current
+verbosity level. Level either is \fBFIASCO_NO_VERBOSITY\fP (no output at
+all), \fBFIASCO_SOME_VERBOSITY\fP (show progress meter) or
+\fBFIASCO_ULTIMATE_VERBOSITY\fP (show debugging output).
+
+.SH "SEE ALSO"
+.br
+.BR fiasco_coder (3), fiasco_decoder (3)
+.br
+
+Ullrich Hafner, Juergen Albert, Stefan Frank, and Michael Unger.
+\fBWeighted Finite Automata for Video Compression\fP, IEEE Journal on
+Selected Areas In Communications, January 1998
+.br
+Ullrich Hafner. \fBLow Bit-Rate Image and Video Coding with Weighted
+Finite Automata\fP, Ph.D. thesis, Mensch & Buch Verlag, ISBN
+3-89820-002-7, October 1999.
+
+.SH AUTHOR
+Ullrich Hafner <hafner@bigfoot.de>
diff --git a/converter/other/fiasco/fiasco.h b/converter/other/fiasco/fiasco.h
new file mode 100644
index 00000000..235b1279
--- /dev/null
+++ b/converter/other/fiasco/fiasco.h
@@ -0,0 +1,425 @@
+/*
+ *  fiasco.h		
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ */
+
+/*
+ *  $Date: 2000/10/28 17:39:28 $
+ *  $Author: hafner $
+ *  $Revision: 5.6 $
+ *  $State: Exp $
+ */
+
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+# define __BEGIN_DECLS extern "C" {
+# define __END_DECLS }
+#else
+# define __BEGIN_DECLS /* empty */
+# define __END_DECLS /* empty */
+#endif
+
+#ifndef _FIASCO_H
+#define _FIASCO_H 1
+
+__BEGIN_DECLS
+
+/****************************************************************************
+			  FIASCO data types
+****************************************************************************/
+
+/*
+ *  Verbosity level:
+ *  FIASCO_NO_VERBOSITY:       No output at all.
+ *  FIASCO_SOME_VERBOSITY:     Show progress meter during coding
+ *  FIASCO_ULTIMATE_VERBOSITY: Show debugging output
+ */
+typedef enum {FIASCO_NO_VERBOSITY,
+	      FIASCO_SOME_VERBOSITY,
+	      FIASCO_ULTIMATE_VERBOSITY} fiasco_verbosity_e;
+  
+/*
+ *  Image tiling methods:
+ *  VARIANCE_ASC:  Tiles are sorted by variance.
+ *                 The first tile has the lowest variance.
+ *  VARIANCE_DSC:  Tiles are sorted by variance.
+ *                 The first tile has the largest variance.
+ *  SPIRAL_ASC:    Tiles are sorted like a spiral starting
+ *                 in the middle of the image.
+ *  SPIRAL_DSC:    Tiles are sorted like a spiral starting
+ *                 in the upper left corner.
+ */
+typedef enum {FIASCO_TILING_SPIRAL_ASC,
+	      FIASCO_TILING_SPIRAL_DSC,
+	      FIASCO_TILING_VARIANCE_ASC,
+	      FIASCO_TILING_VARIANCE_DSC} fiasco_tiling_e;
+
+/*
+ *  Range of reduced precision format:
+ *  FIASCO_RPF_RANGE_0_75: use interval [-0.75,0.75]
+ *  FIASCO_RPF_RANGE_1_00: use interval [-1.00,1.00]
+ *  FIASCO_RPF_RANGE_1_50: use interval [-1.50,0.75]
+ *  FIASCO_RPF_RANGE_2_00: use interval [-2.00,2.00]
+ */
+typedef enum {FIASCO_RPF_RANGE_0_75,
+	      FIASCO_RPF_RANGE_1_00,
+	      FIASCO_RPF_RANGE_1_50,
+	      FIASCO_RPF_RANGE_2_00} fiasco_rpf_range_e;
+
+/*
+ *  Type of progress meter to be used during coding
+ *  FIASCO_PROGRESS_NONE:    no output at all
+ *  FIASCO_PROGRESS_BAR:     RPM style progress bar using 50 hash marks ###### 
+ *  FIASCO_PROGRESS_PERCENT: percentage meter 50% 
+ */
+typedef enum {FIASCO_PROGRESS_NONE,
+	      FIASCO_PROGRESS_BAR,
+	      FIASCO_PROGRESS_PERCENT} fiasco_progress_e;
+
+/*
+ * Class to encapsulate FIASCO images.
+ */
+typedef struct fiasco_image
+{
+   void		(*delete)	(struct fiasco_image *image);
+   unsigned	(*get_width)	(struct fiasco_image *image);
+   unsigned	(*get_height)	(struct fiasco_image *image);
+   int		(*is_color)	(struct fiasco_image *image);
+   void *private;
+} fiasco_image_t;
+
+/*
+ * Class to store internal state of decoder.
+ */
+typedef struct fiasco_decoder
+{
+   int			(*delete)	 (struct fiasco_decoder *decoder);
+   int			(*write_frame)   (struct fiasco_decoder *decoder,
+					  const char *filename);
+   fiasco_image_t *	(*get_frame)     (struct fiasco_decoder *decoder);
+   unsigned		(*get_length)    (struct fiasco_decoder *decoder);
+   unsigned		(*get_rate)	 (struct fiasco_decoder *decoder);
+   unsigned		(*get_width)	 (struct fiasco_decoder *decoder);
+   unsigned		(*get_height)	 (struct fiasco_decoder *decoder);
+   const char *		(*get_title)	 (struct fiasco_decoder *decoder);
+   const char *		(*get_comment)	 (struct fiasco_decoder *decoder);
+   int			(*is_color)	 (struct fiasco_decoder *decoder);
+   void *private;
+} fiasco_decoder_t;
+
+/*
+ * Class to encapsulate advanced coder options.
+ */
+typedef struct fiasco_c_options
+{
+   void (*delete)            (struct fiasco_c_options *options);
+   int (*set_tiling)         (struct fiasco_c_options *options,
+			      fiasco_tiling_e method,
+			      unsigned exponent);
+   int (*set_frame_pattern)  (struct fiasco_c_options *options,
+			      const char *pattern);
+   int (*set_basisfile)      (struct fiasco_c_options *options,
+			      const char *filename);
+   int (*set_chroma_quality) (struct fiasco_c_options *options,
+			      float quality_factor,
+			      unsigned dictionary_size);
+   int (*set_optimizations)  (struct fiasco_c_options *options,
+			      unsigned min_block_level,
+			      unsigned max_block_level,
+			      unsigned max_elements,
+			      unsigned dictionary_size,
+			      unsigned optimization_level);
+   int (*set_prediction)     (struct fiasco_c_options *options,
+			      int intra_prediction,
+			      unsigned min_block_level,
+			      unsigned max_block_level);
+   int (*set_video_param)    (struct fiasco_c_options *options,
+			      unsigned frames_per_second,
+			      int half_pixel_prediction,
+			      int cross_B_search,
+			      int B_as_past_ref);
+   int (*set_quantization)   (struct fiasco_c_options *options,
+			      unsigned mantissa,
+			      fiasco_rpf_range_e range,
+			      unsigned dc_mantissa,
+			      fiasco_rpf_range_e dc_range);
+   int (*set_progress_meter) (struct fiasco_c_options *options,
+			      fiasco_progress_e type);
+   int (*set_smoothing)      (struct fiasco_c_options *options,
+			      int smoothing);
+   int (*set_comment)        (struct fiasco_c_options *options,
+			      const char *comment);
+   int (*set_title)          (struct fiasco_c_options *options,
+			      const char *title);
+   void *private;
+} fiasco_c_options_t;
+
+/*
+ * Class to encapsulate advanced decoder options.
+ */
+typedef struct fiasco_d_options
+{
+   void (*delete)            (struct fiasco_d_options *options);
+   int (*set_smoothing)      (struct fiasco_d_options *options,
+			      int smoothing);
+   int (*set_magnification)  (struct fiasco_d_options *options,
+			      int level);
+   int (*set_4_2_0_format)   (struct fiasco_d_options *options,
+			      int format);
+   void *private;
+} fiasco_d_options_t;
+
+/*
+ * Class to convert internal FIASCO image structure to a XImage structure.
+ * Method `renderer()' is used to convert internal image to XImage. 
+ * Method `delete()' is used to delete and free internal image. 
+ */
+typedef struct fiasco_renderer
+{
+   int  (*render) (const struct fiasco_renderer *this,
+		   unsigned char *data,
+		   const fiasco_image_t *fiasco_image);
+   void (*delete) (struct fiasco_renderer *this);
+   void *private;
+} fiasco_renderer_t;
+
+/****************************************************************************
+		       miscellaneous functions
+****************************************************************************/
+  
+/* Get last error message of FIASCO library */
+const char *fiasco_get_error_message (void);
+
+/* Set verbosity of FIASCO library */
+void fiasco_set_verbosity (fiasco_verbosity_e level);
+
+/* Get verbosity of FIASCO library */
+fiasco_verbosity_e fiasco_get_verbosity (void);
+
+/****************************************************************************
+			  decoder functions
+****************************************************************************/
+
+/* Decode FIASCO image or sequence */
+fiasco_decoder_t *fiasco_decoder_new (const char *filename,
+				      const fiasco_d_options_t *options);
+
+/* Flush and discard FIASCO decoder */
+int fiasco_decoder_delete (fiasco_decoder_t *decoder);
+
+/* Decode next FIASCO frame and write to PNM image 'filename' */
+int fiasco_decoder_write_frame (fiasco_decoder_t *decoder,
+				const char *filename);
+
+/* Decode next FIASCO frame to FIASCO image structure */
+fiasco_image_t *fiasco_decoder_get_frame (fiasco_decoder_t *decoder);
+
+/* Get width of FIASCO image or sequence */
+unsigned fiasco_decoder_get_width (fiasco_decoder_t *decoder);
+
+/* Get height of FIASCO image or sequence */
+unsigned fiasco_decoder_get_height (fiasco_decoder_t *decoder);
+
+/* Get width of FIASCO image or sequence */
+int fiasco_decoder_is_color (fiasco_decoder_t *decoder);
+
+/* Get frame rate of FIASCO sequence */
+unsigned fiasco_decoder_get_rate (fiasco_decoder_t *decoder);
+
+/* Get number of frames of FIASCO file */
+unsigned fiasco_decoder_get_length (fiasco_decoder_t *decoder);
+
+/* Get title of FIASCO file */
+const char *
+fiasco_decoder_get_title (fiasco_decoder_t *decoder);
+
+/* Get comment of FIASCO file */
+const char *
+fiasco_decoder_get_comment (fiasco_decoder_t *decoder);
+
+/****************************************************************************
+			  image functions
+****************************************************************************/
+
+/* Read FIASCO image (raw ppm or pgm format) */
+fiasco_image_t * fiasco_image_new (const char *filename);
+
+/* Discard FIASCO image */
+void fiasco_image_delete (fiasco_image_t *image); 
+
+/* Get width of FIASCO image or sequence */
+unsigned fiasco_image_get_width (fiasco_image_t *image);
+
+/* Get height of FIASCO image or sequence */
+unsigned fiasco_image_get_height (fiasco_image_t *image);
+
+/* Get width of FIASCO image or sequence */
+int fiasco_image_is_color (fiasco_image_t *image);
+
+/****************************************************************************
+			  renderer functions
+****************************************************************************/
+
+/* Constructor of FIASCO image structure to a XImage renderer */
+fiasco_renderer_t *
+fiasco_renderer_new (unsigned long red_mask, unsigned long green_mask,
+		     unsigned long blue_mask, unsigned bpp,
+		     int double_resolution);
+
+/* Destructor of FIASCO image structure to a XImage renderer */
+void
+fiasco_renderer_delete (fiasco_renderer_t *renderer);
+
+/* FIASCO image structure to a XImage renderer */
+int
+fiasco_renderer_render (const fiasco_renderer_t *renderer,
+			unsigned char *ximage,
+			const fiasco_image_t *fiasco_image);
+
+/****************************************************************************
+			   coder functions
+****************************************************************************/
+
+/* Encode image or sequence by FIASCO */
+int fiasco_coder (char const * const *inputname,
+		  const char *outputname,
+		  float quality,
+		  const fiasco_c_options_t *options);
+
+/****************************************************************************
+		 coder options functions
+****************************************************************************/
+
+/* FIASCO additional options constructor */
+fiasco_c_options_t *fiasco_c_options_new (void);
+
+/* FIASCO additional options destructor */
+void fiasco_c_options_delete (fiasco_c_options_t *options);
+
+/* Define `smoothing'-percentage along partitioning borders.*/
+int fiasco_c_options_set_smoothing (fiasco_c_options_t *options,
+				    int smoothing);
+
+/* Set type of frame prediction for sequence of frames */
+int fiasco_c_options_set_frame_pattern (fiasco_c_options_t *options,
+					const char *pattern);
+
+/* Set method and number of tiles for image tiling */
+int fiasco_c_options_set_tiling (fiasco_c_options_t *options,
+				 fiasco_tiling_e method,
+				 unsigned exponent);
+
+/* Set FIASCO initial basis file */
+int fiasco_c_options_set_basisfile (fiasco_c_options_t *options,
+				    const char *filename);
+
+/* Set quality and dictionary size of chroma compression */
+int fiasco_c_options_set_chroma_quality (fiasco_c_options_t *options,
+					 float quality_factor,
+					 unsigned dictionary_size);
+
+/*
+ *  Since FIASCO internally works with binary trees, all functions
+ *  (which are handling image geometry) rather expect the `level' of
+ *  the corresponding binary tree than the traditional `width' and
+ *  `height' arguments.  Refer to following table to convert these
+ *  values:
+ *  
+ *  level | width | height
+ *  ------+-------+--------
+ *    0   |    1  |    1  
+ *    1   |    1  |    2  
+ *    2   |    2  |    2  
+ *    3   |    2  |    4  
+ *    4   |    4  |    4  
+ *    5   |    4  |    8  
+ *    6   |    8  |    8  
+ *    7   |    8  |   16
+ *
+ */
+   
+/* Set various optimization parameters. */
+int fiasco_c_options_set_optimizations (fiasco_c_options_t *options,
+					unsigned min_block_level,
+					unsigned max_block_level,
+					unsigned max_elements,
+					unsigned dictionary_size,
+					unsigned optimization_level);
+
+/* Set minimum and maximum size of image block prediction */
+int fiasco_c_options_set_prediction (fiasco_c_options_t *options,
+				     int intra_prediction,
+				     unsigned min_block_level,
+				     unsigned max_block_level);
+
+/*  Set various parameters used for video compensation */
+int fiasco_c_options_set_video_param (fiasco_c_options_t *options,
+				      unsigned frames_per_second,
+				      int half_pixel_prediction,
+				      int cross_B_search,
+				      int B_as_past_ref);
+
+/* Set accuracy of coefficients quantization */
+int fiasco_c_options_set_quantization (fiasco_c_options_t *options,
+				       unsigned mantissa,
+				       fiasco_rpf_range_e range,
+				       unsigned dc_mantissa,
+				       fiasco_rpf_range_e dc_range);
+
+/* Set type of progress meter */
+int fiasco_c_options_set_progress_meter (fiasco_c_options_t *options,
+					 fiasco_progress_e type);
+
+/*  Set comment of FIASCO stream */
+int fiasco_c_options_set_comment (fiasco_c_options_t *options,
+				  const char *comment);
+
+/*  Set title of FIASCO stream */
+int fiasco_c_options_set_title (fiasco_c_options_t *options,
+				const char *title);
+
+/****************************************************************************
+		 decoder options functions
+****************************************************************************/
+
+/* FIASCO additional options constructor */
+fiasco_d_options_t *fiasco_d_options_new (void);
+
+/* FIASCO additional options destructor */
+void fiasco_d_options_delete (fiasco_d_options_t *options);
+
+
+/* Define `smoothing'-percentage along partitioning borders.*/
+int fiasco_d_options_set_smoothing (fiasco_d_options_t *options,
+				  int smoothing);
+
+/* Set magnification-'level' of decoded image */
+int fiasco_d_options_set_magnification (fiasco_d_options_t *options,
+				      int level);
+
+/* Set image format to 4:2:0 or 4:4:4 */
+int fiasco_d_options_set_4_2_0_format (fiasco_d_options_t *options,
+				     int format);
+
+__END_DECLS
+
+#endif /* not _FIASCO_H */
diff --git a/converter/other/fiasco/fiascotopnm.c b/converter/other/fiasco/fiascotopnm.c
new file mode 100644
index 00000000..6d8b6f7f
--- /dev/null
+++ b/converter/other/fiasco/fiascotopnm.c
@@ -0,0 +1,477 @@
+/*
+ *  dwfa.c:     Decoding of WFA-files
+ *
+ *  Written by:     Ullrich Hafner
+ *          Michael Unger
+ *      
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+ 
+/*
+ *  $Date: 2000/10/28 17:39:29 $
+ *  $Author: hafner $
+ *  $Revision: 5.7 $
+ *  $State: Exp $
+ */
+
+#define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include "config.h"
+#include "pnm.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "types.h"
+#include "macros.h"
+
+#include <getopt.h>
+
+#include "binerror.h"
+#include "misc.h"
+#include "params.h"
+#include "fiasco.h"
+
+#ifndef X_DISPLAY_MISSING
+
+#   include "display.h"
+#   include "buttons.h"
+
+static x11_info_t *xinfo = NULL;
+
+#endif /* not X_DISPLAY_MISSING */
+
+/*****************************************************************************
+
+                prototypes
+  
+*****************************************************************************/
+
+static int 
+checkargs (int argc, char **argv, bool_t *double_resolution, bool_t *panel,
+           int *fps, char **image_name, fiasco_d_options_t **options);
+static void
+video_decoder (const char *wfa_name, const char *image_name, bool_t panel,
+               bool_t double_resolution, int fps, fiasco_d_options_t *options);
+static void
+get_output_template (const char *image_name, const char *wfa_name,
+                     bool_t color, char **basename, char **suffix);
+
+#ifndef X_DISPLAY_MISSING
+
+static void
+show_stored_frames (unsigned char * const *frame_buffer, int last_frame,
+                    x11_info_t *xinfo, binfo_t *binfo, size_t size,
+                    unsigned frame_time);
+
+#endif /* not X_DISPLAY_MISSING */
+
+/*****************************************************************************
+
+                public code
+  
+*****************************************************************************/
+
+int 
+main (int argc, char **argv)
+{
+    char               *image_name        = NULL; /* output filename */
+    bool_t              double_resolution = NO; /* double resolution of image */
+    bool_t              panel             = NO; /* control panel */
+    int             fps               = -1; /* frame display rate */
+    fiasco_d_options_t *options           = NULL; /* additional coder options */
+    int                 last_arg;    /* last processed cmdline parameter */
+
+    init_error_handling (argv[0]);
+
+    last_arg = checkargs (argc, argv, &double_resolution, &panel, &fps,
+                          &image_name, &options);
+   
+    if (last_arg >= argc)
+        video_decoder ("-", image_name, panel, double_resolution, fps, options);
+    else
+        while (last_arg++ < argc)
+            video_decoder (argv [last_arg - 1], image_name, panel,
+                           double_resolution, fps, options);
+
+    return 0;
+}
+
+/*****************************************************************************
+
+                private code
+  
+*****************************************************************************/
+
+static param_t params [] =
+{
+#ifdef X_DISPLAY_MISSING
+    {"output", "FILE", 'o', PSTR, {0}, "-",
+     "Write raw PNM frame(s) to `%s'."},
+#else  /* not X_DISPLAY_MISSING */
+    {"output", "FILE", 'o', POSTR, {0}, NULL,
+     "Write raw PNM frame(s) to INPUT.ppm/pgm [or `%s']."},
+#endif /* not X_DISPLAY_MISSING */
+    {"double", NULL, 'd', PFLAG, {0}, "FALSE",
+     "Interpolate images to double size before display."},
+    {"fast", NULL, 'r', PFLAG, {0}, "FALSE",
+     "Use 4:2:0 format for fast, low quality output."},
+    {"panel", NULL, 'p', PFLAG, {0}, "FALSE",
+     "Display control panel."},
+    {"magnify", "NUM", 'm', PINT, {0}, "0",
+     "Magnify/reduce image size by a factor of 4^`%s'."},
+    {"framerate", "NUM", 'F', PINT, {0}, "-1",
+     "Set display rate to `%s' frames per second."},
+    {"smoothing", "NUM", 's', PINT, {0}, "-1",
+     "Smooth image(s) by factor `%s' (0-100)"},
+    {NULL, NULL, 0, 0, {0}, NULL, NULL }
+};
+
+static int 
+checkargs (int argc, char **argv, bool_t *double_resolution, bool_t *panel,
+           int *fps, char **image_name, fiasco_d_options_t **options)
+/*
+ *  Check validness of command line parameters and of the parameter files.
+ *
+ *  Return value.
+ *  index in argv of the first argv-element that is not an option.
+ *
+ *  Side effects:
+ *  'double_resolution', 'panel', 'fps', 'image_name' and 'options'
+ *      are modified.
+ */
+{
+    int optind;              /* last processed commandline param */
+
+    optind = parseargs (params, argc, argv,
+#ifdef X_DISPLAY_MISSING
+                        "Decode FIASCO-FILEs and write frame(s) to disk.",
+#else  /* not X_DISPLAY_MISSING */
+                        "Decode and display FIASCO-FILEs using X11.",
+#endif /* not X_DISPLAY_MISSING */
+                        "With no FIASCO-FILE, or if FIASCO-FILE is -, "
+                        "read standard input.\n"
+#ifndef X_DISPLAY_MISSING
+                        "With --output=[FILE] specified, "
+                        "write frames without displaying them.\n\n"
+#endif  /* not X_DISPLAY_MISSING */
+                        "Environment:\n"
+                        "FIASCO_DATA   Search path for automata files. "
+                        "Default: ./\n"
+                        "FIASCO_IMAGES Save path for image files. "
+                        "Default: ./", " [FIASCO-FILE]...",
+                        FIASCO_SHARE, "system.fiascorc", ".fiascorc");
+
+    *image_name        =   (char *)   parameter_value (params, "output");
+    *double_resolution = *((bool_t *) parameter_value (params, "double"));
+    *panel             = *((bool_t *) parameter_value (params, "panel"));
+    *fps           = *((int *)    parameter_value (params, "framerate"));
+
+    /*
+     *  Additional options ... (have to be set with the fiasco_set_... methods)
+     */
+    *options = fiasco_d_options_new ();
+
+    {
+        int n = *((int *) parameter_value (params, "smoothing"));
+      
+        if (!fiasco_d_options_set_smoothing (*options, max (-1, n)))
+            error (fiasco_get_error_message ());
+    }
+
+    {
+        int n = *((int *) parameter_value (params, "magnify"));
+      
+        if (!fiasco_d_options_set_magnification (*options, n))
+            error (fiasco_get_error_message ());
+    }
+   
+    {
+        bool_t n = *((bool_t *) parameter_value (params, "fast"));
+      
+        if (!fiasco_d_options_set_4_2_0_format (*options, n > 0 ? YES : NO))
+            error (fiasco_get_error_message ());
+    }
+
+    return optind;
+}
+
+static void
+video_decoder (const char *wfa_name, const char *image_name, bool_t panel,
+               bool_t double_resolution, int fps, fiasco_d_options_t *options)
+{
+#ifndef X_DISPLAY_MISSING
+    fiasco_renderer_t  *renderer     = NULL;
+    unsigned char     **frame_buffer = NULL;
+    binfo_t            *binfo        = NULL; /* buttons info */
+#endif /* not X_DISPLAY_MISSING */
+   
+    do
+    {
+        unsigned      width, height, frames, n;
+        fiasco_decoder_t *decoder_state;
+        char             *filename;
+        char             *basename;   /* basename of decoded frame */
+        char             *suffix;     /* suffix of decoded frame */
+        unsigned      frame_time;
+      
+        if (!(decoder_state = fiasco_decoder_new (wfa_name, options)))
+            error (fiasco_get_error_message ());
+   
+        if (fps <= 0)         /* then use value of FIASCO file */ 
+            fps = fiasco_decoder_get_rate (decoder_state);
+        frame_time = fps ? (1000 / fps) : (1000 / 25);
+
+        if (!(width = fiasco_decoder_get_width (decoder_state)))
+            error (fiasco_get_error_message ());
+      
+        if (!(height = fiasco_decoder_get_height (decoder_state)))
+            error (fiasco_get_error_message ());
+
+        if (!(frames = fiasco_decoder_get_length (decoder_state)))
+            error (fiasco_get_error_message ());
+      
+        get_output_template (image_name, wfa_name,
+                             fiasco_decoder_is_color (decoder_state),
+                             &basename, &suffix);
+
+        filename = calloc (strlen (basename) + strlen (suffix) + 2
+                           + 10 + (int) (log10 (frames) + 1), sizeof (char));
+        if (!filename)
+            error ("Out of memory.");
+
+        for (n = 0; n < frames; n++)
+        {
+            clock_t fps_timer;     /* frames per second timer struct */
+     
+            prg_timer (&fps_timer, START);
+     
+            if (image_name)        /* just write frame to disk */
+            {
+                if (frames == 1)        /* just one image */
+                {
+                    if (streq (image_name, "-"))
+                        strcpy (filename, "-");
+                    else
+                        sprintf (filename, "%s.%s", basename, suffix);
+                }
+                else
+                {
+                    fprintf (stderr, "Decoding frame %d to file `%s.%0*d.%s\n",
+                             n, basename, (int) (log10 (frames - 1) + 1),
+                             n, suffix);
+                    sprintf (filename, "%s.%0*d.%s", basename,
+                             (int) (log10 (frames - 1) + 1), n, suffix);
+                }
+
+                if (!fiasco_decoder_write_frame (decoder_state, filename))
+                    error (fiasco_get_error_message ());
+            }
+#ifndef X_DISPLAY_MISSING
+            else
+            {
+                fiasco_image_t *frame;
+        
+                if (!(frame = fiasco_decoder_get_frame (decoder_state)))
+                    error (fiasco_get_error_message ());
+        
+                if (frames == 1)
+                    panel = NO;
+
+                if (xinfo == NULL)      /* initialize X11 window */
+                {
+                    const char * const title = 
+                        fiasco_decoder_get_title (decoder_state);
+                    char        titlename [MAXSTRLEN];
+
+           
+                    sprintf (titlename, "dfiasco " VERSION ": %s",
+                             strlen (title) > 0 ? title : wfa_name);
+                    xinfo = 
+                        open_window (titlename, "dfiasco",
+                                     (width  << (double_resolution ? 1 : 0)),
+                                     (height << (double_resolution ? 1 : 0))
+                                     + (panel ? 30 : 0));
+                    alloc_ximage (xinfo, width  << (double_resolution ? 1 : 0),
+                                  height << (double_resolution ? 1 : 0));
+                    if (panel)       /* initialize button panel */
+                        binfo = init_buttons (xinfo, n, frames, 30, 10);
+                    renderer = 
+                        fiasco_renderer_new (xinfo->ximage->red_mask,
+                                             xinfo->ximage->green_mask,
+                                             xinfo->ximage->blue_mask,
+                                             xinfo->ximage->bits_per_pixel,
+                                             double_resolution);
+                    if (!renderer)
+                        error (fiasco_get_error_message ());
+                }
+                renderer->render (renderer, xinfo->pixels, frame);
+                frame->delete (frame);
+        
+                if (frame_buffer != NULL) /* store next frame */
+                {
+                    size_t size = (width  << (double_resolution ? 1 : 0))
+                        * (height << (double_resolution ? 1 : 0))
+                        * (xinfo->ximage->depth <= 8
+                           ? sizeof (byte_t)
+                           : (xinfo->ximage->depth <= 16
+                              ? sizeof (u_word_t)
+                              : sizeof (unsigned int)));
+
+                    frame_buffer [n] = malloc (size);
+                    if (!frame_buffer [n])
+                        error ("Out of memory.");
+                    memcpy (frame_buffer [n], xinfo->pixels, size);
+
+                    if (n == frames - 1)
+                    {
+                        show_stored_frames (frame_buffer, frames - 1,
+                                            xinfo, binfo, size, frame_time);
+                        break;
+                    }
+                }
+
+                display_image (0, 0, xinfo);
+                if (frames == 1)
+                    wait_for_input (xinfo);
+                else if (panel)
+                {
+                    check_events (xinfo, binfo, n, frames);
+                    if (binfo->pressed [QUIT_BUTTON]) 
+                        /* start from beginning */
+                        break;
+                    if (binfo->pressed [STOP_BUTTON]) 
+                        /* start from beginning */
+                        n = frames;
+           
+                    if (binfo->pressed [RECORD_BUTTON] && frame_buffer == NULL)
+                    {
+                        n = frames;
+                        frame_buffer = 
+                            calloc (frames, sizeof (unsigned char *));
+                        if (!frame_buffer)
+                            error ("Out of memory.");
+                    }
+                }
+                while (prg_timer (&fps_timer, STOP) < frame_time) /* wait */
+                    ;
+            }
+#endif /* not X_DISPLAY_MISSING */   
+        }
+        free (filename);
+   
+        fiasco_decoder_delete (decoder_state);
+    } while (panel
+
+#ifndef X_DISPLAY_MISSING
+             && !binfo->pressed [QUIT_BUTTON]
+#endif /* not X_DISPLAY_MISSING */
+        
+        );
+
+#ifndef X_DISPLAY_MISSING
+    if (renderer)
+        renderer->delete (renderer);
+
+    if (!image_name)
+    {
+        close_window (xinfo);
+        free (xinfo);
+        xinfo = NULL;
+        if (binfo)
+            free (binfo);
+    }
+#endif /* not X_DISPLAY_MISSING */
+}
+
+static void
+get_output_template (const char *image_name, const char *wfa_name,
+                     bool_t color, char **basename, char **suffix)
+/*
+ *  Generate image filename template for output of image sequences.
+ *  'wfa_name' is the filename of the WFA stream.
+ *  Images are either saved with filename 'basename'.'suffix' (still images)
+ *  or 'basename'.%03d.'suffix' (videos).
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *  '*basename' and '*suffix' is set.
+ */
+{
+    if (!wfa_name || streq (wfa_name, "-"))
+        wfa_name = "stdin";       
+    /*
+     *  Generate filename template
+     */
+    if (!image_name || streq (image_name, "") || streq (image_name, "-"))
+    {
+        *basename = strdup (wfa_name);
+        *suffix   = NULL;
+    }
+    else
+    {
+        *basename = strdup (image_name);
+        *suffix   = strrchr (*basename, '.');
+    }
+    
+    if (*suffix)         /* found name 'basename.suffix' */
+    {
+        **suffix = 0;         /* remove dot */
+        (*suffix)++;
+        if (**suffix == 0)
+            *suffix = strdup (color ? "ppm" : "pgm");
+    }
+    else             /* no suffix found, generate one */
+        *suffix = strdup (color ? "ppm" : "pgm");
+}
+
+#ifndef X_DISPLAY_MISSING
+
+static void
+show_stored_frames (unsigned char * const *frame_buffer, int last_frame,
+                    x11_info_t *xinfo, binfo_t *binfo, size_t size,
+                    unsigned frame_time)
+/*
+ *  After a WFA video stream has been saved, all frames have been
+ *  decoded and stored in memory. These frames are then displayed
+ *  in an endless loop.
+ *
+ *  This function never returns, the program is terminated if the
+ *  STOP button is pressed.
+ */
+{
+    int n = last_frame;          /* frame number */
+   
+    while (1)
+    {
+        clock_t fps_timer;        /* frames per second timer struct */
+      
+        prg_timer (&fps_timer, START);
+      
+        display_image (0, 0, xinfo);
+        check_events (xinfo, binfo, n, last_frame + 1);
+
+        if (binfo->pressed [STOP_BUTTON])
+            n = 0;
+        else if (binfo->pressed [QUIT_BUTTON])
+            break;
+        else if (binfo->pressed [PLAY_BUTTON])
+            n++;
+        else if (binfo->pressed [RECORD_BUTTON]) /* REWIND is mapped RECORD */
+            n--;
+        if (n < 0)
+            n = last_frame;
+        if (n > last_frame)
+            n = 0;
+
+        memcpy (xinfo->pixels, frame_buffer [n], size);
+        while (prg_timer (&fps_timer, STOP) < frame_time) /* wait */
+            ;
+    };
+}
+
+#endif /* not X_DISPLAY_MISSING */
diff --git a/converter/other/fiasco/getopt.c b/converter/other/fiasco/getopt.c
new file mode 100644
index 00000000..0b2d1b75
--- /dev/null
+++ b/converter/other/fiasco/getopt.c
@@ -0,0 +1,1002 @@
+/* Getopt for GNU.
+   NOTE: getopt is now part of the C library, so if you don't know what
+   "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+   before changing it!
+
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97
+   Free Software Foundation, Inc.
+
+   This file is part of the GNU C Library.  Its master source is NOT part of
+   the C library, however.  The master source lives in /gd/gnu/lib.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+   Ditto for AIX 3.2 and <stdlib.h>.  */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <string.h>
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef	__GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+   contain conflicting prototypes for getopt.  */
+#include <stdlib.h>
+#include <unistd.h>
+#endif	/* GNU C library.  */
+
+#ifdef VMS
+#include <unixlib.h>
+#if HAVE_STRING_H - 0
+#include <string.h>
+#endif
+#endif
+
+#if defined (WIN32) && !defined (__CYGWIN32__)
+/* It's not Unix, really.  See?  Capital letters.  */
+#include <windows.h>
+#define getpid() GetCurrentProcessId()
+#endif
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.
+   When compiling libc, the _ macro is predefined.  */
+#ifdef HAVE_LIBINTL_H
+# include <libintl.h>
+# define _(msgid)	gettext (msgid)
+#else
+# define _(msgid)	(msgid)
+#endif
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+   but it behaves differently for the user, since it allows the user
+   to intersperse the options with the other arguments.
+
+   As `getopt' works, it permutes the elements of ARGV so that,
+   when it is done, all the options precede everything else.  Thus
+   all application programs are extended to handle flexible argument order.
+
+   Setting the environment variable POSIXLY_CORRECT disables permutation.
+   Then the behavior is completely standard.
+
+   GNU application programs can use a third alternative mode in which
+   they can distinguish the relative order of options and other arguments.  */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+char *optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+/* 1003.2 says this must be 1 before any call.  */
+int optind = 1;
+
+/* Formerly, initialization of getopt depended on optind==0, which
+   causes problems with re-calling getopt as programs generally don't
+   know that. */
+
+int __getopt_initialized = 0;
+
+/* The next char to be scanned in the option-element
+   in which the last option character we returned was found.
+   This allows us to pick up the scan where we left off.
+
+   If this is zero, or a null string, it means resume the scan
+   by advancing to the next ARGV-element.  */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+   for unrecognized options.  */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+   This must be initialized on some systems to avoid linking in the
+   system's own getopt implementation.  */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+   If the caller did not specify anything,
+   the default is REQUIRE_ORDER if the environment variable
+   POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+   REQUIRE_ORDER means don't recognize them as options;
+   stop option processing when the first non-option is seen.
+   This is what Unix does.
+   This mode of operation is selected by either setting the environment
+   variable POSIXLY_CORRECT, or using `+' as the first character
+   of the list of option characters.
+
+   PERMUTE is the default.  We permute the contents of ARGV as we scan,
+   so that eventually all the non-options are at the end.  This allows options
+   to be given in any order, even with programs that were not written to
+   expect this.
+
+   RETURN_IN_ORDER is an option available to programs that were written
+   to expect options and other ARGV-elements in any order and that care about
+   the ordering of the two.  We describe each non-option ARGV-element
+   as if it were the argument of an option with character code 1.
+   Using `-' as the first character of the list of option characters
+   selects this mode of operation.
+
+   The special argument `--' forces an end of option-scanning regardless
+   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
+   `--' can cause `getopt' to return -1 with `optind' != ARGC.  */
+
+static enum
+{
+  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable.  */
+static char *posixly_correct;
+
+#ifdef	__GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+   because there are many ways it can cause trouble.
+   On some systems, it contains special magic macros that don't work
+   in GCC.  */
+#include <string.h>
+#define	my_index	strchr
+#else
+
+/* Avoid depending on library functions or files
+   whose names are inconsistent.  */
+
+char *getenv ();
+
+static char *
+my_index (str, chr)
+     const char *str;
+     int chr;
+{
+  while (*str)
+    {
+      if (*str == chr)
+	return (char *) str;
+      str++;
+    }
+  return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+   If not using GCC, it is ok not to declare it.  */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+   That was relevant to code that was here before.  */
+#if !defined (__STDC__) || !__STDC__
+/* gcc with -traditional declares the built-in strlen to return int,
+   and has done so at least since version 2.4.5. -- rms.  */
+extern int strlen (const char *);
+#endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+
+/* Handle permutation of arguments.  */
+
+/* Describe the part of ARGV that contains non-options that have
+   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
+   `last_nonopt' is the index after the last of them.  */
+
+static int first_nonopt;
+static int last_nonopt;
+
+#ifdef _LIBC
+/* Bash 2.0 gives us an environment variable containing flags
+   indicating ARGV elements that should not be considered arguments.  */
+
+static const char *nonoption_flags;
+static int nonoption_flags_len;
+
+static int original_argc;
+static char *const *original_argv;
+
+/* Make sure the environment variable bash 2.0 puts in the environment
+   is valid for the getopt call we must make sure that the ARGV passed
+   to getopt is that one passed to the process.  */
+static void store_args (int argc, char *const *argv) __attribute__ ((unused));
+static void
+store_args (int argc, char *const *argv)
+{
+  /* XXX This is no good solution.  We should rather copy the args so
+     that we can compare them later.  But we must not use malloc(3).  */
+  original_argc = argc;
+  original_argv = argv;
+}
+text_set_element (__libc_subinit, store_args);
+#endif
+
+/* Exchange two adjacent subsequences of ARGV.
+   One subsequence is elements [first_nonopt,last_nonopt)
+   which contains all the non-options that have been skipped so far.
+   The other is elements [last_nonopt,optind), which contains all
+   the options processed since those non-options were skipped.
+
+   `first_nonopt' and `last_nonopt' are relocated so that they describe
+   the new indices of the non-options in ARGV after they are moved.  */
+
+#if defined (__STDC__) && __STDC__
+static void exchange (char **);
+#endif
+
+static void
+exchange (argv)
+     char **argv;
+{
+  int bottom = first_nonopt;
+  int middle = last_nonopt;
+  int top = optind;
+  char *tem;
+
+  /* Exchange the shorter segment with the far end of the longer segment.
+     That puts the shorter segment into the right place.
+     It leaves the longer segment in the right place overall,
+     but it consists of two parts that need to be swapped next.  */
+
+  while (top > middle && middle > bottom)
+    {
+      if (top - middle > middle - bottom)
+	{
+	  /* Bottom segment is the short one.  */
+	  int len = middle - bottom;
+	  register int i;
+
+	  /* Swap it with the top part of the top segment.  */
+	  for (i = 0; i < len; i++)
+	    {
+	      tem = argv[bottom + i];
+	      argv[bottom + i] = argv[top - (middle - bottom) + i];
+	      argv[top - (middle - bottom) + i] = tem;
+	    }
+	  /* Exclude the moved bottom segment from further swapping.  */
+	  top -= len;
+	}
+      else
+	{
+	  /* Top segment is the short one.  */
+	  int len = top - middle;
+	  register int i;
+
+	  /* Swap it with the bottom part of the bottom segment.  */
+	  for (i = 0; i < len; i++)
+	    {
+	      tem = argv[bottom + i];
+	      argv[bottom + i] = argv[middle + i];
+	      argv[middle + i] = tem;
+	    }
+	  /* Exclude the moved top segment from further swapping.  */
+	  bottom += len;
+	}
+    }
+
+  /* Update records for the slots the non-options now occupy.  */
+
+  first_nonopt += (optind - last_nonopt);
+  last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made.  */
+
+#if defined (__STDC__) && __STDC__
+static const char *_getopt_initialize (int, char *const *, const char *);
+#endif
+static const char *
+_getopt_initialize (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  /* Start processing options with ARGV-element 1 (since ARGV-element 0
+     is the program name); the sequence of previously skipped
+     non-option ARGV-elements is empty.  */
+
+  first_nonopt = last_nonopt = optind = 1;
+
+  nextchar = NULL;
+
+  posixly_correct = getenv ("POSIXLY_CORRECT");
+
+  /* Determine how to handle the ordering of options and nonoptions.  */
+
+  if (optstring[0] == '-')
+    {
+      ordering = RETURN_IN_ORDER;
+      ++optstring;
+    }
+  else if (optstring[0] == '+')
+    {
+      ordering = REQUIRE_ORDER;
+      ++optstring;
+    }
+  else if (posixly_correct != NULL)
+    ordering = REQUIRE_ORDER;
+  else
+    ordering = PERMUTE;
+
+#ifdef _LIBC
+  if (posixly_correct == NULL
+      && argc == original_argc && argv == original_argv)
+    {
+      /* Bash 2.0 puts a special variable in the environment for each
+	 command it runs, specifying which ARGV elements are the results of
+	 file name wildcard expansion and therefore should not be
+	 considered as options.  */
+      char var[100];
+      sprintf (var, "_%d_GNU_nonoption_argv_flags_", getpid ());
+      nonoption_flags = getenv (var);
+      if (nonoption_flags == NULL)
+	nonoption_flags_len = 0;
+      else
+	nonoption_flags_len = strlen (nonoption_flags);
+    }
+  else
+    nonoption_flags_len = 0;
+#endif
+
+  return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+   given in OPTSTRING.
+
+   If an element of ARGV starts with '-', and is not exactly "-" or "--",
+   then it is an option element.  The characters of this element
+   (aside from the initial '-') are option characters.  If `getopt'
+   is called repeatedly, it returns successively each of the option characters
+   from each of the option elements.
+
+   If `getopt' finds another option character, it returns that character,
+   updating `optind' and `nextchar' so that the next call to `getopt' can
+   resume the scan with the following option character or ARGV-element.
+
+   If there are no more option characters, `getopt' returns -1.
+   Then `optind' is the index in ARGV of the first ARGV-element
+   that is not an option.  (The ARGV-elements have been permuted
+   so that those that are not options now come last.)
+
+   OPTSTRING is a string containing the legitimate option characters.
+   If an option character is seen that is not listed in OPTSTRING,
+   return '?' after printing an error message.  If you set `opterr' to
+   zero, the error message is suppressed but we still return '?'.
+
+   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+   so the following text in the same ARGV-element, or the text of the following
+   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+   wants an optional arg; if there is text in the current ARGV-element,
+   it is returned in `optarg', otherwise `optarg' is set to zero.
+
+   If OPTSTRING starts with `-' or `+', it requests different methods of
+   handling the non-option ARGV-elements.
+   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+   Long-named options begin with `--' instead of `-'.
+   Their names may be abbreviated as long as the abbreviation is unique
+   or is an exact match for some defined option.  If they have an
+   argument, it follows the option name in the same ARGV-element, separated
+   from the option name by a `=', or else the in next ARGV-element.
+   When `getopt' finds a long-named option, it returns 0 if that option's
+   `flag' field is nonzero, the value of the option's `val' field
+   if the `flag' field is zero.
+
+   The elements of ARGV aren't really const, because we permute them.
+   But we pretend they're const in the prototype to be compatible
+   with other systems.
+
+   LONGOPTS is a vector of `struct option' terminated by an
+   element containing a name which is zero.
+
+   LONGIND returns the index in LONGOPT of the long-named option found.
+   It is only valid when a long-named option has been found by the most
+   recent call.
+
+   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+   long-named options.  */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+     const struct option *longopts;
+     int *longind;
+     int long_only;
+{
+  optarg = NULL;
+
+  if (!__getopt_initialized || optind == 0)
+    {
+      optstring = _getopt_initialize (argc, argv, optstring);
+      optind = 1;		/* Don't scan ARGV[0], the program name.  */
+      __getopt_initialized = 1;
+    }
+
+  /* Test whether ARGV[optind] points to a non-option argument.
+     Either it does not have option syntax, or there is an environment flag
+     from the shell indicating it is not an option.  The later information
+     is only used when the used in the GNU libc.  */
+#ifdef _LIBC
+#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0'	      \
+		     || (optind < nonoption_flags_len			      \
+			 && nonoption_flags[optind] == '1'))
+#else
+#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#endif
+
+  if (nextchar == NULL || *nextchar == '\0')
+    {
+      /* Advance to the next ARGV-element.  */
+
+      /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+	 moved back by the user (who may also have changed the arguments).  */
+      if (last_nonopt > optind)
+	last_nonopt = optind;
+      if (first_nonopt > optind)
+	first_nonopt = optind;
+
+      if (ordering == PERMUTE)
+	{
+	  /* If we have just processed some options following some non-options,
+	     exchange them so that the options come first.  */
+
+	  if (first_nonopt != last_nonopt && last_nonopt != optind)
+	    exchange ((char **) argv);
+	  else if (last_nonopt != optind)
+	    first_nonopt = optind;
+
+	  /* Skip any additional non-options
+	     and extend the range of non-options previously skipped.  */
+
+	  while (optind < argc && NONOPTION_P)
+	    optind++;
+	  last_nonopt = optind;
+	}
+
+      /* The special ARGV-element `--' means premature end of options.
+	 Skip it like a null option,
+	 then exchange with previous non-options as if it were an option,
+	 then skip everything else like a non-option.  */
+
+      if (optind != argc && !strcmp (argv[optind], "--"))
+	{
+	  optind++;
+
+	  if (first_nonopt != last_nonopt && last_nonopt != optind)
+	    exchange ((char **) argv);
+	  else if (first_nonopt == last_nonopt)
+	    first_nonopt = optind;
+	  last_nonopt = argc;
+
+	  optind = argc;
+	}
+
+      /* If we have done all the ARGV-elements, stop the scan
+	 and back over any non-options that we skipped and permuted.  */
+
+      if (optind == argc)
+	{
+	  /* Set the next-arg-index to point at the non-options
+	     that we previously skipped, so the caller will digest them.  */
+	  if (first_nonopt != last_nonopt)
+	    optind = first_nonopt;
+	  return -1;
+	}
+
+      /* If we have come to a non-option and did not permute it,
+	 either stop the scan or describe it to the caller and pass it by.  */
+
+      if (NONOPTION_P)
+	{
+	  if (ordering == REQUIRE_ORDER)
+	    return -1;
+	  optarg = argv[optind++];
+	  return 1;
+	}
+
+      /* We have found another option-ARGV-element.
+	 Skip the initial punctuation.  */
+
+      nextchar = (argv[optind] + 1
+		  + (longopts != NULL && argv[optind][1] == '-'));
+    }
+
+  /* Decode the current option-ARGV-element.  */
+
+  /* Check whether the ARGV-element is a long option.
+
+     If long_only and the ARGV-element has the form "-f", where f is
+     a valid short option, don't consider it an abbreviated form of
+     a long option that starts with f.  Otherwise there would be no
+     way to give the -f short option.
+
+     On the other hand, if there's a long option "fubar" and
+     the ARGV-element is "-fu", do consider that an abbreviation of
+     the long option, just like "--fu", and not "-f" with arg "u".
+
+     This distinction seems to be the most useful approach.  */
+
+  if (longopts != NULL
+      && (argv[optind][1] == '-'
+	  || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+    {
+      char *nameend;
+      const struct option *p;
+      const struct option *pfound = NULL;
+      int exact = 0;
+      int ambig = 0;
+      int indfound = -1;
+      int option_index;
+
+      for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+	/* Do nothing.  */ ;
+
+      /* Test all long options for either exact match
+	 or abbreviated matches.  */
+      for (p = longopts, option_index = 0; p->name; p++, option_index++)
+	if (!strncmp (p->name, nextchar, nameend - nextchar))
+	  {
+	    if ((unsigned int) (nameend - nextchar)
+		== (unsigned int) strlen (p->name))
+	      {
+		/* Exact match found.  */
+		pfound = p;
+		indfound = option_index;
+		exact = 1;
+		break;
+	      }
+	    else if (pfound == NULL)
+	      {
+		/* First nonexact match found.  */
+		pfound = p;
+		indfound = option_index;
+	      }
+	    else
+	      /* Second or later nonexact match found.  */
+	      ambig = 1;
+	  }
+
+      if (ambig && !exact)
+	{
+	  if (opterr)
+	    fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+		     argv[0], argv[optind]);
+	  nextchar += strlen (nextchar);
+	  optind++;
+	  optopt = 0;
+	  return '?';
+	}
+
+      if (pfound != NULL)
+	{
+	  option_index = indfound;
+	  optind++;
+	  if (*nameend)
+	    {
+	      /* Don't test has_arg with >, because some C compilers don't
+		 allow it to be used on enums.  */
+	      if (pfound->has_arg)
+		optarg = nameend + 1;
+	      else
+		{
+            if (opterr) {
+		   if (argv[optind - 1][1] == '-')
+		    /* --option */
+		    fprintf (stderr,
+		     _("%s: option `--%s' doesn't allow an argument\n"),
+		     argv[0], pfound->name);
+		   else
+		    /* +option or -option */
+		    fprintf (stderr,
+		     _("%s: option `%c%s' doesn't allow an argument\n"),
+		     argv[0], argv[optind - 1][0], pfound->name);
+            }
+
+		  nextchar += strlen (nextchar);
+
+		  optopt = pfound->val;
+		  return '?';
+		}
+	    }
+	  else if (pfound->has_arg == 1)
+	    {
+	      if (optind < argc)
+		optarg = argv[optind++];
+	      else
+		{
+		  if (opterr)
+		    fprintf (stderr,
+			   _("%s: option `%s' requires an argument\n"),
+			   argv[0], argv[optind - 1]);
+		  nextchar += strlen (nextchar);
+		  optopt = pfound->val;
+		  return optstring[0] == ':' ? ':' : '?';
+		}
+	    }
+	  nextchar += strlen (nextchar);
+	  if (longind != NULL)
+	    *longind = option_index;
+	  if (pfound->flag)
+	    {
+	      *(pfound->flag) = pfound->val;
+	      return 0;
+	    }
+	  return pfound->val;
+	}
+
+      /* Can't find it as a long option.  If this is not getopt_long_only,
+	 or the option starts with '--' or is not a valid short
+	 option, then it's an error.
+	 Otherwise interpret it as a short option.  */
+      if (!long_only || argv[optind][1] == '-'
+	  || my_index (optstring, *nextchar) == NULL)
+	{
+	  if (opterr)
+	    {
+	      if (argv[optind][1] == '-')
+		/* --option */
+		fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+			 argv[0], nextchar);
+	      else
+		/* +option or -option */
+		fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+			 argv[0], argv[optind][0], nextchar);
+	    }
+	  nextchar = (char *) "";
+	  optind++;
+	  optopt = 0;
+	  return '?';
+	}
+    }
+
+  /* Look at and handle the next short option-character.  */
+
+  {
+    char c = *nextchar++;
+    char *temp = my_index (optstring, c);
+
+    /* Increment `optind' when we start to process its last character.  */
+    if (*nextchar == '\0')
+      ++optind;
+
+    if (temp == NULL || c == ':')
+      {
+	if (opterr)
+	  {
+	    if (posixly_correct)
+	      /* 1003.2 specifies the format of this message.  */
+	      fprintf (stderr, _("%s: illegal option -- %c\n"),
+		       argv[0], c);
+	    else
+	      fprintf (stderr, _("%s: invalid option -- %c\n"),
+		       argv[0], c);
+	  }
+	optopt = c;
+	return '?';
+      }
+    /* Convenience. Treat POSIX -W foo same as long option --foo */
+    if (temp[0] == 'W' && temp[1] == ';')
+      {
+	char *nameend;
+	const struct option *p;
+	const struct option *pfound = NULL;
+	int exact = 0;
+	int ambig = 0;
+	int indfound = 0;
+	int option_index;
+
+	/* This is an option that requires an argument.  */
+	if (*nextchar != '\0')
+	  {
+	    optarg = nextchar;
+	    /* If we end this ARGV-element by taking the rest as an arg,
+	       we must advance to the next element now.  */
+	    optind++;
+	  }
+	else if (optind == argc)
+	  {
+	    if (opterr)
+	      {
+		/* 1003.2 specifies the format of this message.  */
+		fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+			 argv[0], c);
+	      }
+	    optopt = c;
+	    if (optstring[0] == ':')
+	      c = ':';
+	    else
+	      c = '?';
+	    return c;
+	  }
+	else
+	  /* We already incremented `optind' once;
+	     increment it again when taking next ARGV-elt as argument.  */
+	  optarg = argv[optind++];
+
+	/* optarg is now the argument, see if it's in the
+	   table of longopts.  */
+
+	for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
+	  /* Do nothing.  */ ;
+
+	/* Test all long options for either exact match
+	   or abbreviated matches.  */
+	for (p = longopts, option_index = 0; p->name; p++, option_index++)
+	  if (!strncmp (p->name, nextchar, nameend - nextchar))
+	    {
+	      if ((unsigned int) (nameend - nextchar) == strlen (p->name))
+		{
+		  /* Exact match found.  */
+		  pfound = p;
+		  indfound = option_index;
+		  exact = 1;
+		  break;
+		}
+	      else if (pfound == NULL)
+		{
+		  /* First nonexact match found.  */
+		  pfound = p;
+		  indfound = option_index;
+		}
+	      else
+		/* Second or later nonexact match found.  */
+		ambig = 1;
+	    }
+	if (ambig && !exact)
+	  {
+	    if (opterr)
+	      fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
+		       argv[0], argv[optind]);
+	    nextchar += strlen (nextchar);
+	    optind++;
+	    return '?';
+	  }
+	if (pfound != NULL)
+	  {
+	    option_index = indfound;
+	    if (*nameend)
+	      {
+		/* Don't test has_arg with >, because some C compilers don't
+		   allow it to be used on enums.  */
+		if (pfound->has_arg)
+		  optarg = nameend + 1;
+		else
+		  {
+		    if (opterr)
+		      fprintf (stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+			       argv[0], pfound->name);
+
+		    nextchar += strlen (nextchar);
+		    return '?';
+		  }
+	      }
+	    else if (pfound->has_arg == 1)
+	      {
+		if (optind < argc)
+		  optarg = argv[optind++];
+		else
+		  {
+		    if (opterr)
+		      fprintf (stderr,
+			       _("%s: option `%s' requires an argument\n"),
+			       argv[0], argv[optind - 1]);
+		    nextchar += strlen (nextchar);
+		    return optstring[0] == ':' ? ':' : '?';
+		  }
+	      }
+	    nextchar += strlen (nextchar);
+	    if (longind != NULL)
+	      *longind = option_index;
+	    if (pfound->flag)
+	      {
+		*(pfound->flag) = pfound->val;
+		return 0;
+	      }
+	    return pfound->val;
+	  }
+	  nextchar = NULL;
+	  return 'W';	/* Let the application handle it.   */
+      }
+    if (temp[1] == ':')
+      {
+	if (temp[2] == ':')
+	  {
+	    /* This is an option that accepts an argument optionally.  */
+	    if (*nextchar != '\0')
+	      {
+		optarg = nextchar;
+		optind++;
+	      }
+	    else
+	      optarg = NULL;
+	    nextchar = NULL;
+	  }
+	else
+	  {
+	    /* This is an option that requires an argument.  */
+	    if (*nextchar != '\0')
+	      {
+		optarg = nextchar;
+		/* If we end this ARGV-element by taking the rest as an arg,
+		   we must advance to the next element now.  */
+		optind++;
+	      }
+	    else if (optind == argc)
+	      {
+		if (opterr)
+		  {
+		    /* 1003.2 specifies the format of this message.  */
+		    fprintf (stderr,
+			   _("%s: option requires an argument -- %c\n"),
+			   argv[0], c);
+		  }
+		optopt = c;
+		if (optstring[0] == ':')
+		  c = ':';
+		else
+		  c = '?';
+	      }
+	    else
+	      /* We already incremented `optind' once;
+		 increment it again when taking next ARGV-elt as argument.  */
+	      optarg = argv[optind++];
+	    nextchar = NULL;
+	  }
+      }
+    return c;
+  }
+}
+
+int
+getopt (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  return _getopt_internal (argc, argv, optstring,
+			   (const struct option *) 0,
+			   (int *) 0,
+			   0);
+}
+
+#endif	/* Not ELIDE_CODE.  */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+   the above definition of `getopt'.  */
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+
+      c = getopt (argc, argv, "abc:d:0123456789");
+      if (c == -1)
+	break;
+
+      switch (c)
+	{
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+	  if (digit_optind != 0 && digit_optind != this_option_optind)
+	    printf ("digits occur in two different argv-elements.\n");
+	  digit_optind = this_option_optind;
+	  printf ("option %c\n", c);
+	  break;
+
+	case 'a':
+	  printf ("option a\n");
+	  break;
+
+	case 'b':
+	  printf ("option b\n");
+	  break;
+
+	case 'c':
+	  printf ("option c with value `%s'\n", optarg);
+	  break;
+
+	case '?':
+	  break;
+
+	default:
+	  printf ("?? getopt returned character code 0%o ??\n", c);
+	}
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+	printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
diff --git a/converter/other/fiasco/getopt.h b/converter/other/fiasco/getopt.h
new file mode 100644
index 00000000..9acca708
--- /dev/null
+++ b/converter/other/fiasco/getopt.h
@@ -0,0 +1,129 @@
+/* Declarations for getopt.
+   Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+
+   This file is part of the GNU C Library.  Its master source is NOT part of
+   the C library, however.  The master source lives in /gd/gnu/lib.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+   for unrecognized options.  */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized.  */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+   of `struct option' terminated by an element containing a name which is
+   zero.
+
+   The field `has_arg' is:
+   no_argument		(or 0) if the option does not take an argument,
+   required_argument	(or 1) if the option requires an argument,
+   optional_argument 	(or 2) if the option takes an optional argument.
+
+   If the field `flag' is not NULL, it points to a variable that is set
+   to the value given in the field `val' when the option is found, but
+   left unchanged if the option is not found.
+
+   To have a long-named option do something other than set an `int' to
+   a compiled-in constant, such as set a value from `optarg', set the
+   option's `flag' field to zero and its `val' field to a nonzero
+   value (the equivalent single-letter option character, if there is
+   one).  For long options that have a zero `flag' field, `getopt'
+   returns the contents of the `val' field.  */
+
+struct option
+{
+  const char *name;
+  /* has_arg can't be an enum because some compilers complain about
+     type mismatches in all the code that assumes it is an int.  */
+  int has_arg;
+  int *flag;
+  int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'.  */
+
+#define	no_argument		0
+#define required_argument	1
+#define optional_argument	2
+
+#if defined (__STDC__) && __STDC__
+#ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+   differences in the consts, in stdlib.h.  To avoid compilation
+   errors, only prototype getopt for the GNU C library.  */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+		        const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+			     const char *shortopts,
+		             const struct option *longopts, int *longind);
+
+/* Internal only.  Users should not call this directly.  */
+extern int _getopt_internal (int argc, char *const *argv,
+			     const char *shortopts,
+		             const struct option *longopts, int *longind,
+			     int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* __STDC__ */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/converter/other/fiasco/getopt1.c b/converter/other/fiasco/getopt1.c
new file mode 100644
index 00000000..8347bb13
--- /dev/null
+++ b/converter/other/fiasco/getopt1.c
@@ -0,0 +1,189 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+   Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+
+   This file is part of the GNU C Library.  Its master source is NOT part of
+   the C library, however.  The master source lives in /gd/gnu/lib.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "getopt.h"
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#endif
+
+#ifndef	NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+   If an option that starts with '-' (not '--') doesn't match a long option,
+   but does match a short option, it is parsed as a short option
+   instead.  */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif	/* Not ELIDE_CODE.  */
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+      int option_index = 0;
+      static struct option long_options[] =
+      {
+	{"add", 1, 0, 0},
+	{"append", 0, 0, 0},
+	{"delete", 1, 0, 0},
+	{"verbose", 0, 0, 0},
+	{"create", 0, 0, 0},
+	{"file", 1, 0, 0},
+	{0, 0, 0, 0}
+      };
+
+      c = getopt_long (argc, argv, "abc:d:0123456789",
+		       long_options, &option_index);
+      if (c == -1)
+	break;
+
+      switch (c)
+	{
+	case 0:
+	  printf ("option %s", long_options[option_index].name);
+	  if (optarg)
+	    printf (" with arg %s", optarg);
+	  printf ("\n");
+	  break;
+
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+	  if (digit_optind != 0 && digit_optind != this_option_optind)
+	    printf ("digits occur in two different argv-elements.\n");
+	  digit_optind = this_option_optind;
+	  printf ("option %c\n", c);
+	  break;
+
+	case 'a':
+	  printf ("option a\n");
+	  break;
+
+	case 'b':
+	  printf ("option b\n");
+	  break;
+
+	case 'c':
+	  printf ("option c with value `%s'\n", optarg);
+	  break;
+
+	case 'd':
+	  printf ("option d with value `%s'\n", optarg);
+	  break;
+
+	case '?':
+	  break;
+
+	default:
+	  printf ("?? getopt returned character code 0%o ??\n", c);
+	}
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+	printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
diff --git a/converter/other/fiasco/input/Makefile b/converter/other/fiasco/input/Makefile
new file mode 100644
index 00000000..c01af772
--- /dev/null
+++ b/converter/other/fiasco/input/Makefile
@@ -0,0 +1,26 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../../..
+  BUILDDIR = $(SRCDIR)
+endif
+FIASCOSUBDIR = converter/other/fiasco
+SUBDIR = $(FIASCOSUBDIR)/input
+BUILDDIR = ../../../..
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+OBJECTS =  basis.o matrices.o mc.o nd.o read.o tree.o weights.o
+
+MERGE_OBJECTS = $(OBJECTS)
+
+INCLUDES = -I$(SRCDIR)/$(FIASCOSUBDIR) -I$(SRCDIR)/$(FIASCOSUBDIR)/lib \
+	   -I$(SRCDIR)/$(FIASCOSUBDIR)/codec
+
+all: libfiasco_input.a
+
+include $(SRCDIR)/Makefile.common
+
+libfiasco_input.a: $(OBJECTS)
+	$(AR) -rc $@ $(OBJECTS)
+	$(RANLIB) $@
+
diff --git a/converter/other/fiasco/input/basis.c b/converter/other/fiasco/input/basis.c
new file mode 100644
index 00000000..cef075e6
--- /dev/null
+++ b/converter/other/fiasco/input/basis.c
@@ -0,0 +1,141 @@
+/*
+ *  basis.c:		WFA initial basis files	
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/25 16:38:06 $
+ *  $Author: hafner $
+ *  $Revision: 5.3 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "wfa.h"
+#include "wfalib.h"
+
+#include "basis.h"
+
+typedef struct basis_values
+{
+   unsigned  states;
+   real_t   *final;
+   bool_t   *use_domain;
+   real_t (*transitions)[4];
+} basis_values_t;
+
+typedef struct
+{
+    const char *filename;
+    void (*function)(basis_values_t *bv);
+} basis_file_t;
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static void
+small_init (basis_values_t *bv);
+
+static basis_file_t const basis_files[] = { 
+    {"small.fco", small_init},
+    {"small.wfa", small_init},
+    {NULL, NULL} 
+};
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+bool_t
+get_linked_basis (const char *basis_name, wfa_t *wfa)
+/*
+ *  Check wether given WFA initial basis 'basis_name' is already linked
+ *  with the excecutable. If the basis is available then fill the 'wfa' struct
+ *  according to the stored data, otherwise print a warning message.
+ *
+ *  Return value:
+ *	true on success, false if basis is not available yet.
+ *
+ *  Side effects:
+ *	'wfa' struct is filled on success.
+ */
+{
+   bool_t	  success = NO;		/* indicates if basis is found */
+   unsigned	  n;			/* counter */
+   basis_values_t bv;			/* basis values */
+   
+   for (n = 0; basis_files [n].filename != NULL; n++)
+      if (streq (basis_files [n].filename, basis_name))	/* basis is stored */
+      {
+	 unsigned state, edge;		
+	 
+	 (*basis_files [n].function) (&bv); /* initialize local variables */
+	 /*
+	  *  Generate WFA
+	  */
+	 wfa->basis_states = wfa->states = bv.states + 1;
+	 wfa->domain_type[0]             = USE_DOMAIN_MASK; 
+	 wfa->final_distribution[0]      = 128;
+	 append_edge (0, 0, 1.0, 0, wfa);
+	 append_edge (0, 0, 1.0, 1, wfa);
+	 for (state = 1; state < wfa->basis_states; state++)
+	 {
+	    wfa->final_distribution [state] = bv.final [state - 1];
+	    wfa->domain_type [state]        = bv.use_domain [state - 1]
+					      ? USE_DOMAIN_MASK
+					      : AUXILIARY_MASK;
+	 }
+	 for (edge = 0; isedge (bv.transitions [edge][0]); edge++)
+	    append_edge (bv.transitions [edge][0], bv.transitions [edge][1],
+			 bv.transitions [edge][2], bv.transitions [edge][3],
+			 wfa);
+	 
+	 success = YES;
+	 break;
+      }
+
+   if (!success)
+      warning ("WFA initial basis '%s' isn't linked with the excecutable yet."
+	       "\nLoading basis from disk instead.", basis_name);
+
+   return success;
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+/*****************************************************************************
+				basis "small.wfa"
+*****************************************************************************/
+
+static unsigned	states_small           = 2;
+static bool_t	use_domain_small[]     = {YES, YES};
+static real_t 	final_small[]          = {64, 64};
+static real_t 	transitions_small[][4] = {{1, 2, 0.5, 0}, {1, 2, 0.5, 1},
+				      	 {1, 0, 0.5, 1}, {2, 1, 1.0, 0},
+				      	 {2, 1, 1.0, 1}, {-1, 0, 0, 0}};
+static void
+small_init (basis_values_t *bv)
+{
+   bv->states      = states_small;
+   bv->final       = final_small;
+   bv->use_domain  = use_domain_small;
+   bv->transitions = transitions_small;
+}
diff --git a/converter/other/fiasco/input/basis.h b/converter/other/fiasco/input/basis.h
new file mode 100644
index 00000000..fa26bca2
--- /dev/null
+++ b/converter/other/fiasco/input/basis.h
@@ -0,0 +1,26 @@
+/*
+ *  basis.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:13 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _BASIS_H
+#define _BASIS_H
+
+#include "wfa.h"
+
+bool_t
+get_linked_basis (const char *basis_name, wfa_t *wfa);
+
+#endif /* not _BASIS_H */
+
diff --git a/converter/other/fiasco/input/matrices.c b/converter/other/fiasco/input/matrices.c
new file mode 100644
index 00000000..47cde1aa
--- /dev/null
+++ b/converter/other/fiasco/input/matrices.c
@@ -0,0 +1,644 @@
+/*
+ *  matrices.c:		Input of transition matrices
+ *
+ *  Written by:		Ullrich Hafner
+ *  
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:13 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "bit-io.h"
+#include "arith.h"
+#include "misc.h"
+#include "wfalib.h"
+
+#include "matrices.h"
+
+#if STDC_HEADERS
+#	include <stdlib.h>
+#endif /* not STDC_HEADERS */
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static unsigned
+delta_decoding (wfa_t *wfa, unsigned last_domain, bitfile_t *input);
+static unsigned
+column_0_decoding (wfa_t *wfa, unsigned last_row, bitfile_t *input);
+static unsigned
+chroma_decoding (wfa_t *wfa, bitfile_t *input);
+static void
+compute_y_state (int state, int y_state, wfa_t *wfa);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+unsigned
+read_matrices (wfa_t *wfa, bitfile_t *input)
+/* 
+ *  Read transitions of WFA given from the stream 'input'.
+ *
+ *  Return value:
+ *	number of edges
+ *
+ *  Side effects:
+ *	'wfa->into' is filled with decoded values 
+ */
+{
+   unsigned total;			/* total number of edges in the WFA */
+   unsigned root_state = wfa->wfainfo->color
+			 ? wfa->tree [wfa->tree [wfa->root_state][0]][0]
+			 : wfa->root_state;
+
+   total  = column_0_decoding (wfa, root_state, input);
+   total += delta_decoding (wfa, root_state, input);
+   if (wfa->wfainfo->color)
+      total += chroma_decoding (wfa, input);
+       
+   return total;
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static unsigned
+delta_decoding (wfa_t *wfa, unsigned last_domain, bitfile_t *input)
+/*
+ *  Read transition matrices which are encoded with delta coding
+ *  from stream 'input'.
+ *  'last_domain' is the maximum state number used as domain image.
+ *
+ *  Return value:
+ *	number of non-zero matrix elements (WFA edges)
+ *
+ *  Side effects:
+ *	'wfa->into' is filled with decoded values 
+ */
+{
+   range_sort_t	 rs;			/* ranges are sorted as in the coder */
+   unsigned	 max_domain;		/* dummy used for recursion */
+   unsigned	 range;
+   unsigned	 count [MAXEDGES + 1];
+   unsigned 	 state, label;
+   unsigned	*n_edges;		/* number of elements per row */
+   unsigned	 total = 0;		/* total number of decoded edges */
+
+   /*
+    *  Generate a list of range blocks.
+    *  The order is the same as in the coder.
+    */
+   rs.range_state      = Calloc ((last_domain + 1) * MAXLABELS,
+				 sizeof (u_word_t));
+   rs.range_label      = Calloc ((last_domain + 1) * MAXLABELS,
+				 sizeof (byte_t));
+   rs.range_max_domain = Calloc ((last_domain + 1) * MAXLABELS,
+				 sizeof (u_word_t));
+   rs.range_subdivided = Calloc ((last_domain + 1) * MAXLABELS,
+				 sizeof (bool_t));
+   rs.range_no	       = 0;
+   max_domain 	       = wfa->basis_states - 1;
+   sort_ranges (last_domain, &max_domain, &rs, wfa);
+
+   /*
+    *  Get row statistics
+    */
+   {
+      arith_t  *decoder;
+      model_t  *elements;
+      unsigned 	max_edges = read_rice_code (3, input);
+      
+      /*
+       *  Get the probability array of the number of edges distribution
+       *  and allocate the corresponding model.
+       */
+      {
+	 unsigned edge;
+	 
+	 for (edge = 0; edge <= max_edges; edge++)
+	    count [edge] = read_rice_code ((int) log2 (last_domain) - 2,
+					   input);
+	 elements = alloc_model (max_edges + 1, 0, 0, count);
+      }
+      
+      /*
+       *  Get number of elements per matrix row
+       */
+      {
+	 unsigned row;
+      
+	 n_edges = Calloc (wfa->states, sizeof (unsigned));
+	 decoder = alloc_decoder (input);
+	 for (row = range = 0; range < rs.range_no; range++)
+	    if (!rs.range_subdivided [range])
+	    {
+	       state = rs.range_state [range];
+	       label = rs.range_label [range];
+	       
+	       n_edges [row++]
+		  = decode_symbol (decoder, elements)
+		  - (isedge (wfa->into [state][label][0]) ? 1 : 0);
+	    }
+	 
+	 free_decoder (decoder);
+	 free_model (elements);
+      }
+   }
+   
+   /*
+    *  Get matrix elements
+    */
+   {
+      unsigned row;
+      u_word_t *mapping1           = Calloc (wfa->states, sizeof (word_t));
+      u_word_t *mapping_coder1     = Calloc (wfa->states, sizeof (word_t));
+      u_word_t *mapping2           = Calloc (wfa->states, sizeof (word_t));
+      u_word_t *mapping_coder2     = Calloc (wfa->states, sizeof (word_t));
+      bool_t	use_normal_domains = get_bit (input);
+      bool_t	use_delta_domains  = get_bit (input);
+	  
+      /*
+       *  Generate array of states which are admitted domains.
+       *  When coding intra frames 'mapping1' == 'mapping2' otherwise
+       *  'mapping1' is a list of 'normal' domains which are admitted for 
+       *             coding intra blocks
+       *  'mapping2' is a list of 'delta' domains which are admitted for
+       *             coding the motion compensated prediction error 
+       */
+      {
+	 unsigned n1, n2, state;
+	    
+	 for (n1 = n2 = state = 0; state < wfa->states; state++)
+	 {
+	    mapping1 [n1] = state;
+	    mapping_coder1 [state] = n1;
+	    if (usedomain (state, wfa)
+		&& (state < wfa->basis_states
+		    || use_delta_domains || !wfa->delta_state [state]))
+	       n1++;
+	    
+	    mapping2 [n2] = state;
+	    mapping_coder2 [state] = n2;
+	    if (usedomain (state, wfa)
+		&& (state < wfa->basis_states || use_normal_domains
+		    || wfa->delta_state [state]))
+	       n2++;
+	 }
+      }
+	 
+      for (row = 0, range = 0; range < rs.range_no; range++)
+	 if (!rs.range_subdivided [range])
+	 {
+	    u_word_t *mapping;
+	    u_word_t *mapping_coder;
+	    unsigned  max_value;
+	    unsigned  edge;
+	    unsigned  state = rs.range_state [range];
+	    unsigned  label = rs.range_label [range];
+	    unsigned  last  = 1;
+
+	    if (wfa->delta_state [state] ||
+		wfa->mv_tree [state][label].type != NONE)
+	    {
+	       mapping 	     = mapping2;
+	       mapping_coder = mapping_coder2;
+	    }
+	    else
+	    {
+	       mapping 	     = mapping1;
+	       mapping_coder = mapping_coder1;
+	    }
+	    max_value = mapping_coder [rs.range_max_domain [range]];
+	    for (edge = n_edges [row]; edge; edge--)
+	    {
+	       unsigned domain;
+
+	       if (max_value - last)
+		  domain = read_bin_code (max_value - last, input) + last;
+	       else
+		  domain = max_value;
+	       append_edge (state, mapping [domain], -1, label, wfa);
+	       last = domain + 1;
+	       total++;
+	    }
+	    row++;
+	 }
+      Free (mapping1);
+      Free (mapping_coder1);
+      Free (mapping2);
+      Free (mapping_coder2);
+   }
+      
+   Free (n_edges);
+   Free (rs.range_state);
+   Free (rs.range_label);
+   Free (rs.range_max_domain);
+   Free (rs.range_subdivided);
+
+   return total;
+}
+
+static unsigned
+column_0_decoding (wfa_t *wfa, unsigned last_row, bitfile_t *input)
+/*
+ *  Read column 0 of the transition matrices of the 'wfa' which are coded
+ *  with quasi arithmetic coding from stream 'input'.
+ *  All rows from 'wfa->basis_states' up to 'last_row' are decoded.
+ * 
+ *  Return value:
+ *	number of non-zero matrix elements (WFA edges)
+ *
+ *  Side effects:
+ *	'wfa->into' is filled with decoded values 
+ */
+{
+   unsigned  row;			/* current matrix row */
+   unsigned  total = 0;			/* total number of edges in col 0 */
+   unsigned *prob_ptr;			/* pointer to current probability */
+   unsigned *last;			/* pointer to minimum probability */
+   unsigned *first;			/* pointer to maximum probability */
+   unsigned *new_prob_ptr;		/* ptr to probability of last domain */
+   unsigned *prob;			/* probability array */
+   u_word_t  high;			/* Start of the current code range */
+   u_word_t  low;			/* End of the current code range */
+   u_word_t  code;			/* The present input code value */
+   word_t   *is_leaf;			/* pointer to the tree structure */
+
+   /*
+    *  Compute the asymmetric probability array
+    *  prob[] = { 1/2, 1/2, 1/4, 1/4, 1/4, 1/4,
+    *             1/8, ... , 1/16, ..., 1/(MAXPROB+1)}
+    */
+   {
+      unsigned n;
+      unsigned index;			/* probability index */
+      unsigned exp;			/* current exponent */
+      
+      prob = Calloc (1 << (MAX_PROB + 1), sizeof (unsigned));
+   
+      for (index = 0, n = MIN_PROB; n <= MAX_PROB; n++)
+	 for (exp = 0; exp < 1U << n; exp++, index++)
+	    prob [index] = n;
+   }
+
+   first = prob_ptr = new_prob_ptr = prob;
+   last  = first + 1020;
+   
+   is_leaf = wfa->tree [wfa->basis_states]; /* use pointer arithmetics ... */
+
+   high = HIGH;				/* 1.0 */
+   low  = LOW;				/* 0.0 */
+   code = get_bits (input, 16);		
+
+   /*
+    *  Decode column 0 with a quasi arithmetic coder (QAC).
+    *  Advantage of this QAC with respect to a binary AC:
+    *  Instead of using time consuming multiplications and divisions
+    *  to compute the probability of the most probable symbol (MPS) and
+    *  the range of the interval, a table look up procedure linked
+    *  with a shift operation is used for both computations.
+    *
+    *  Loops and array accesses have been removed
+    *  to make real time decoding possible.
+    */
+   for (row = wfa->basis_states; row <= last_row; row++)
+   {
+      unsigned count;			/* value in the current interval */
+      
+      /*
+       *  Read label 0 element
+       */
+      if (isrange (*is_leaf++))		/* valid matrix index */
+      {
+	 count = high - ((high - low) >> *prob_ptr);
+	 if (code < count)
+	 {
+	    if (prob_ptr < last)	/* model update */
+	       prob_ptr++;
+	    /*
+	     *  Decode the MPS '0'
+	     */
+	    high = count - 1;
+
+	    RESCALE_INPUT_INTERVAL;
+	 }
+	 else
+	 {
+	    prob_ptr = ((prob_ptr - first) >> 1) + first; /* model update */
+	    /*
+	     *  Decode the LPS '1'
+	     */
+	    low = count;
+
+	    RESCALE_INPUT_INTERVAL;
+	    /*
+	     *  Restore the transition (weight = -1)
+	     */
+	    append_edge (row, 0, -1, 0, wfa);
+	    total++;
+	 }
+      }
+      /*
+       *  Read label 1 element
+       */
+      if (isrange (*is_leaf++)) /* valid matrix index */
+      {
+	 count = high - ((high - low) >> *prob_ptr);
+	 if (code < count)
+	 {
+	    if (prob_ptr < last)
+	       prob_ptr++;		/* model update */
+	    /*
+	     *  Decode the MPS '0'
+	     */
+	    high = count - 1;
+
+	    RESCALE_INPUT_INTERVAL;
+	 }
+	 else
+	 {
+	    prob_ptr = ((prob_ptr - first) >> 1) + first; /* model update */
+	    /*
+	     *  Decode the LPS '1'
+	     */
+	    low = count;
+
+	    RESCALE_INPUT_INTERVAL;
+	    /*
+	     *  Restore the transition (weight = -1)
+	     */
+	    append_edge (row, 0, -1, 1, wfa);
+	    total++;
+	 }
+      }
+   }
+
+   INPUT_BYTE_ALIGN (input);
+
+   Free (prob);
+   
+   return total;
+}
+
+static unsigned
+chroma_decoding (wfa_t *wfa, bitfile_t *input)
+/*
+ *  Read transition matrices of 'wfa' states which are part of the
+ *  chroma channels Cb and Cr from stream 'input'.
+ *
+ *  Return value:
+ *	number of non-zero matrix elements (WFA edges)
+ *
+ *  Side effects:
+ *	'wfa->into' is filled with decoded values 
+ */
+{
+   unsigned  domain;			/* current domain, counter */
+   unsigned  total = 0;			/* total number of chroma edges */
+   unsigned *prob_ptr;			/* pointer to current probability */
+   unsigned *last;			/* pointer to minimum probability */
+   unsigned *first;			/* pointer to maximum probability */
+   unsigned *new_prob_ptr;		/* ptr to probability of last domain */
+   unsigned *prob;			/* probability array */
+   u_word_t  high;			/* Start of the current code range */
+   u_word_t  low;			/* End of the current code range */
+   u_word_t  code;			/* The present input code value */
+   word_t   *y_domains;			/* domain images corresponding to Y */
+   int	     save_index;		/* YES: store current probabilty */
+
+   /*
+    *  Compute the asymmetric probability array
+    *  prob[] = { 1/2, 1/2, 1/4, 1/4, 1/4, 1/4,
+    *                     1/8, ... , 1/16, ..., 1/(MAXPROB+1)}
+    */
+   {
+      unsigned n;
+      unsigned index;			/* probability index */
+      unsigned exp;			/* current exponent */
+      
+      prob = Calloc (1 << (MAX_PROB + 1), sizeof (unsigned));
+   
+      for (index = 0, n = MIN_PROB; n <= MAX_PROB; n++)
+	 for (exp = 0; exp < 1U << n; exp++, index++)
+	    prob [index] = n;
+   }
+
+   high = HIGH;				/* 1.0 */
+   low  = LOW;				/* 0.0 */
+   code = get_bits (input, 16);
+
+   /*
+    *  Compute list of admitted domains
+    */
+   y_domains = compute_hits (wfa->basis_states,
+			     wfa->tree [wfa->tree [wfa->root_state][0]][0],
+			     wfa->wfainfo->chroma_max_states, wfa);
+   
+   first = prob_ptr = new_prob_ptr = prob;
+   last  = first + 1020;
+
+   /*
+    *  First of all, read all matrix columns given in the list 'y_domains'
+    *  which note all admitted domains.
+    *  These matrix elements are stored with QAC (see column_0_decoding ()).
+    */
+   for (domain = 0; y_domains [domain] != -1; domain++)
+   {
+      unsigned 	row	= wfa->tree [wfa->tree [wfa->root_state][0]][0] + 1;
+      word_t   *is_leaf = wfa->tree [row];
+
+      prob_ptr   = new_prob_ptr;
+      save_index = YES;
+
+      for (; row < wfa->states; row++)
+      {
+	 unsigned count;		/* value in the current interval */
+	 /*
+	  *  Read label 0 element
+	  */
+	 if (isrange (*is_leaf++)) 	/* valid matrix index */
+	 {
+	    count = high - ((high - low) >> *prob_ptr);
+	    if (code < count)
+	    {
+	       if (prob_ptr < last)
+		  prob_ptr++;
+	       /*
+		*  Decode the MPS '0'
+		*/
+	       high = count - 1;
+
+	       RESCALE_INPUT_INTERVAL;
+	    }
+	    else
+	    {
+	       prob_ptr = ((prob_ptr - first) >> 1) + first;
+	       /*
+		*  Decode the LPS '1'
+		*/
+	       low = count;
+
+	       RESCALE_INPUT_INTERVAL;
+	       /*
+		*  Restore the transition (weight = -1)
+		*/
+	       append_edge (row, y_domains [domain], -1, 0, wfa);
+	       total++;
+	    }
+	 }
+	 /*
+	  *  Read label 1 element
+	  */
+	 if (isrange (*is_leaf++)) /* valid matrix index */
+	 {
+	    count = high - ((high - low) >> *prob_ptr);
+	    if (code < count)
+	    {
+	       if (prob_ptr < last)
+		  prob_ptr++;
+	       /*
+		*  Decode the MPS '0'
+		*/
+	       high = count - 1;
+
+	       RESCALE_INPUT_INTERVAL;
+	    }
+	    else
+	    {
+	       prob_ptr = ((prob_ptr - first) >> 1) + first;
+	       /*
+		*  Decode the LPS '1'
+		*/
+	       low = count;
+
+	       RESCALE_INPUT_INTERVAL;
+	       /*
+		*  Restore the transition (weight = -1)
+		*/
+	       append_edge (row, y_domains [domain], -1, 1, wfa);
+	       total++;
+	    }
+	 }
+	 if (save_index)
+	 {
+	    save_index 	 = NO;
+	    new_prob_ptr = prob_ptr;
+	 }
+      }
+   }
+
+   Free (y_domains);
+
+   compute_y_state (wfa->tree [wfa->tree [wfa->root_state][0]][1],
+		    wfa->tree [wfa->tree [wfa->root_state][0]][0], wfa);
+   compute_y_state (wfa->tree [wfa->tree [wfa->root_state][1]][0],
+		    wfa->tree [wfa->tree [wfa->root_state][0]][0], wfa);
+   
+   first = prob_ptr = new_prob_ptr = prob;
+
+   /*
+    *  Decode the additional column which indicates whether there
+    *  are transitions to a state with same spatial coordinates
+    *  in the Y component.
+    *
+    *  Again, quasi arithmetic decoding is used for this task.
+    */
+   {
+      unsigned 	row;
+      
+      for (row = wfa->tree [wfa->tree [wfa->root_state][0]][0] + 1;
+	   row < wfa->states; row++)
+      {
+	 int label;			/* current label */
+
+	 for (label = 0; label < MAXLABELS; label++)
+	 {
+	    u_word_t count = high - ((high - low) >> *prob_ptr);
+
+	    if (code < count)
+	    {
+	       if (prob_ptr < last)
+		  prob_ptr++;
+	       /*
+		*  Decode the MPS '0'
+		*/
+	       high = count - 1;
+
+	       RESCALE_INPUT_INTERVAL;
+	    }
+	    else
+	    {
+	       prob_ptr = ((prob_ptr - first) >> 1) + first;
+	       /*
+		*  Decode the LPS '1'
+		*/
+	       low = count;
+
+	       RESCALE_INPUT_INTERVAL;
+	       /*
+		*  Restore the transition (weight = -1)
+		*/
+	       append_edge (row, wfa->y_state [row][label], -1, label, wfa);
+	       total++;
+	    }
+	 }
+      }
+   }
+
+   INPUT_BYTE_ALIGN (input);
+
+   Free (prob);
+
+   return total;
+}
+
+static void
+compute_y_state (int state, int y_state, wfa_t *wfa)
+/*
+ *  Compute the 'wfa->y_state' array which denotes those states of
+ *  the Y band that have the same spatial coordinates as the corresponding
+ *  states of the Cb and Cr bands.
+ *  The current root of the Y tree is given by 'y_state'.
+ *  The current root of the tree of the chroma channel is given by 'state'.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'wfa->y_state' is filled with the generated tree structure.
+ */
+{
+   unsigned label;
+   
+   for (label = 0; label < MAXLABELS; label++)
+      if (isrange (y_state))
+	 wfa->y_state [state][label] = RANGE;
+      else
+      {
+	 wfa->y_state [state][label] = wfa->tree [y_state][label];
+	 if (!isrange (wfa->tree [state][label]))
+	    compute_y_state (wfa->tree [state][label],
+			     wfa->y_state [state][label], wfa);
+      }
+      
+}
diff --git a/converter/other/fiasco/input/matrices.h b/converter/other/fiasco/input/matrices.h
new file mode 100644
index 00000000..ba8fd9bf
--- /dev/null
+++ b/converter/other/fiasco/input/matrices.h
@@ -0,0 +1,27 @@
+/*
+ *  matrices.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:13 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _MATRICES_H
+#define _MATRICES_H
+
+#include "wfa.h"
+#include "bit-io.h"
+
+unsigned
+read_matrices (wfa_t *wfa, bitfile_t *input);
+
+#endif /* not _MATRICES_H */
+
diff --git a/converter/other/fiasco/input/mc.c b/converter/other/fiasco/input/mc.c
new file mode 100644
index 00000000..070d839e
--- /dev/null
+++ b/converter/other/fiasco/input/mc.c
@@ -0,0 +1,334 @@
+/*
+ *  mc.c:	Input of motion compensation	
+ *
+ *  written by: Michael Unger
+ *		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:13 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include <stdlib.h>
+#include "config.h"
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "wfa.h"
+#include "bit-io.h"
+#include "misc.h"
+#include "mvcode.h"
+
+#include "mc.h"
+
+/*****************************************************************************
+
+			     local variables
+  
+*****************************************************************************/
+
+typedef struct huff_node 
+{
+   int		     code_index;	/* leaf if index >= 0 */
+   struct huff_node *left;		/* follow if '0' bit read */
+   struct huff_node *right;		/* follow if '1' bit read */
+   int		     index_set [34];
+} huff_node_t;
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static void
+decode_mc_tree (frame_type_e frame_type, unsigned max_state,
+		wfa_t *wfa, bitfile_t *input);
+static void
+decode_mc_coords (unsigned max_state, wfa_t *wfa, bitfile_t *input);
+static int
+get_mv (int f_code, huff_node_t *hn, bitfile_t *input);
+static huff_node_t *
+create_huff_tree (void);
+static void
+create_huff_node (huff_node_t *hn, int bits_processed);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+void
+read_mc (frame_type_e frame_type, wfa_t *wfa, bitfile_t *input)
+/*
+ *  Read motion compensation information of the 'input' stream.
+ *  Depending on 'frame_type' different decoding methods are used.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'wfa->mv_tree' is filled with the decoded values.
+ */
+{
+   unsigned max_state = wfa->wfainfo->color
+			? wfa->tree [wfa->tree [wfa->root_state][0]][0]
+			: wfa->states;
+
+   decode_mc_tree (frame_type, max_state, wfa, input);
+   decode_mc_coords (max_state, wfa, input);
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static void
+decode_mc_tree (frame_type_e frame_type, unsigned max_state,
+		wfa_t *wfa, bitfile_t *input)
+/*
+ *  Read tree of motion compensation decisions of the 'input' stream.
+ *  Depending on 'frame_type' different decoding methods are used.
+ *  'max_state' is the last state with motion compensation infos.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'wfa->mv_tree' is filled with decoded values.
+ */
+{
+   unsigned  state;			/* current state */
+   unsigned *queue;			/* states in breadth first order */
+   unsigned  last;			/* last node
+					   (update for each new node) */
+
+   /*
+    *  Traverse tree in breadth first order (starting at level
+    *  'wfa->wfainfo->p_max_level'). Use a queue to store the childs
+    *  of each node ('last' is the next free queue element).  
+    */
+   queue = Calloc (MAXSTATES, sizeof (unsigned));
+   for (last = 0, state = wfa->basis_states; state < max_state; state++)
+      if (wfa->level_of_state [state] - 1 == (int) wfa->wfainfo->p_max_level)
+	 queue [last++] = state;	/* init level 'p_max_level' */
+
+   if (frame_type == P_FRAME)
+   {
+      unsigned label;			/* current label */
+      unsigned current;			/* current node to process */
+      
+      for (current = 0; current < last; current++)
+	 for (label = 0; label < MAXLABELS; label++)
+	 {
+	    state = queue[current];
+	    if (wfa->x [state][label]	/* process visible states only */
+		+  width_of_level (wfa->level_of_state [state] - 1)
+		<= wfa->wfainfo->width
+		&&
+		wfa->y [state][label]
+		+  height_of_level (wfa->level_of_state [state] - 1)
+		<= wfa->wfainfo->height)
+	    {
+	       wfa->mv_tree [state][label].type
+		  = get_bit (input) ? NONE : FORWARD;
+	    }
+	    else
+	       wfa->mv_tree [state][label].type = NONE;
+	    if (wfa->mv_tree [state][label].type == NONE &&
+		!isrange (wfa->tree [state][label]) &&
+		wfa->level_of_state [state] - 1 >=
+		(int) wfa->wfainfo->p_min_level) 
+	       queue [last++] = wfa->tree [state][label]; /* append child  */
+	 }
+   }
+   else
+   {
+      unsigned label;			/* current label */
+      unsigned current;			/* current node to process */
+      
+      for (current = 0; current < last; current++)
+	 for (label = 0; label < MAXLABELS; label++)
+	 {
+	    state = queue[current];
+	    if (wfa->x [state][label]	/* process visible states only */
+		+ width_of_level (wfa->level_of_state [state] - 1)
+		> wfa->wfainfo->width
+		||
+		wfa->y [state][label]
+		+ height_of_level (wfa->level_of_state [state] - 1)
+		> wfa->wfainfo->height)
+	       wfa->mv_tree[state][label].type = NONE;
+	    else if (get_bit (input))	/* 1   */
+	       wfa->mv_tree[state][label].type = NONE;
+	    else if (get_bit (input))	/* 01  */
+	       wfa->mv_tree[state][label].type = INTERPOLATED;
+	    else if (get_bit (input))	/* 001 */ 
+	       wfa->mv_tree[state][label].type = BACKWARD;
+	    else			/* 000 */ 
+	       wfa->mv_tree[state][label].type = FORWARD;
+	    if (wfa->mv_tree[state][label].type == NONE &&
+		!isrange (wfa->tree[state][label]) &&
+		wfa->level_of_state[state] - 1
+		>= (int) wfa->wfainfo->p_min_level) 
+	       queue[last++] = wfa->tree[state][label]; /* append child  */
+	 }
+   }
+   
+   INPUT_BYTE_ALIGN (input);
+   Free (queue);
+}
+
+static void
+decode_mc_coords (unsigned max_state, wfa_t *wfa, bitfile_t *input)
+/*
+ *  Read motion vector coordinates of the 'input' stream. They are stored
+ *  with the static Huffman code of the MPEG and H.263 standards.
+ *  'max_state' is the last state with motion compensation infos.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'wfa->mv_tree' is filled with decoded values.
+ */
+{
+   unsigned	       label;		/* current label */
+   unsigned	       state;		/* current state */
+   mv_t		      *mv;		/* current motion vector */
+   static huff_node_t *huff_mv_root = NULL; /* root of huffman tree */
+ 
+   if (huff_mv_root == NULL)
+      huff_mv_root = create_huff_tree ();
+   
+   for (state = wfa->basis_states; state < max_state; state++)
+      for (label = 0; label < MAXLABELS; label++)
+      {
+	 mv = &wfa->mv_tree[state][label];
+	 switch (mv->type)
+	 {
+	    case NONE:
+	       break;
+	    case FORWARD:
+	       mv->fx = get_mv (1, huff_mv_root, input);
+	       mv->fy = get_mv (1, huff_mv_root, input);
+	       break;	    
+	    case BACKWARD:	    
+	       mv->bx = get_mv (1, huff_mv_root, input);
+	       mv->by = get_mv (1, huff_mv_root, input);
+	       break;	    
+	    case INTERPOLATED:   
+	       mv->fx = get_mv (1, huff_mv_root, input);
+	       mv->fy = get_mv (1, huff_mv_root, input);
+	       mv->bx = get_mv (1, huff_mv_root, input);
+	       mv->by = get_mv (1, huff_mv_root, input);
+	       break;
+	 }
+      }
+
+   INPUT_BYTE_ALIGN (input);
+}
+ 
+static int
+get_mv (int f_code, huff_node_t *hn, bitfile_t *input)
+/* 
+ *  Decode next motion vector component in bitstream 
+ *  by traversing the huffman tree.
+ */
+{
+   int vlc_code, vlc_code_magnitude, residual, diffvec;
+
+   while (hn->code_index < 0)
+   {
+      if (hn->code_index == -2)
+	 error ("wrong huffman code !");
+      if (get_bit (input))
+	 hn = hn->right;
+      else
+	 hn = hn->left;
+   }
+   vlc_code = hn->code_index - 16;
+   if (vlc_code == 0 || f_code == 1) 
+      return vlc_code;
+
+   vlc_code_magnitude = abs (vlc_code) - 1;
+   if (f_code <= 1)
+      residual = 0;
+   else
+      residual = get_bits (input, f_code - 1);
+   diffvec = (vlc_code_magnitude << (f_code - 1)) + residual + 1;
+   
+   return vlc_code > 0 ? diffvec : - diffvec;
+}
+
+static huff_node_t *
+create_huff_tree (void)
+/*
+ *  Construct huffman tree from code table
+ */
+{
+   unsigned	i;
+   huff_node_t *huff_root = Calloc (1, sizeof (huff_node_t));
+   
+   /*
+    *  The nodes' index set contains indices of all codewords that are
+    *  still decodable by traversing further down from the node.
+    *  (The root node has the full index set.)
+    */
+
+   for (i = 0; i < 33; i++)
+      huff_root->index_set [i] = i;
+   huff_root->index_set [i] = -1;	/* end marker */
+
+   create_huff_node (huff_root, 0);
+
+   return huff_root;
+}
+
+static void
+create_huff_node (huff_node_t *hn, int bits_processed)
+/*
+ *  Create one node in the huffman tree
+ */
+{
+   int lind = 0;			/* next index of left huff_node */
+   int rind = 0;			/* next index of right huff_node */
+   int code_len, i, ind;
+
+   hn->code_index = -1;
+   if (hn->index_set [0] < 0)		/* empty index set ? */
+   {
+      hn->code_index = -2;		/* error */
+      return;
+   }
+   hn->left  = Calloc (1, sizeof (huff_node_t));
+   hn->right = Calloc (1, sizeof (huff_node_t));
+
+   for (i = 0; (ind = hn->index_set[i]) >= 0; i++)
+   {
+      code_len = mv_code_table[ind][1];
+      if (code_len == bits_processed)	/* generate leaf */
+      {
+	 hn->code_index = ind;
+	 Free (hn->left); 
+	 Free (hn->right);
+	 return;
+      }
+      if (mv_code_table[ind][0] & (1 << (code_len - 1 - bits_processed)))
+	 hn->right->index_set[rind++] = ind;
+      else
+	 hn->left->index_set[lind++] = ind;
+   }
+   hn->right->index_set[rind] = -1;	/* set end markers */
+   hn->left->index_set[lind]  = -1;
+   create_huff_node (hn->left, bits_processed + 1);
+   create_huff_node (hn->right, bits_processed + 1);
+}
diff --git a/converter/other/fiasco/input/mc.h b/converter/other/fiasco/input/mc.h
new file mode 100644
index 00000000..1e14d287
--- /dev/null
+++ b/converter/other/fiasco/input/mc.h
@@ -0,0 +1,28 @@
+/*
+ *  mc.h
+ *
+ *  written by: Michael Unger
+ *		Ullrich Hafner
+ 
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:13 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _MC_H
+#define _MC_H
+
+#include "wfa.h"
+#include "bit-io.h"
+
+void
+read_mc (frame_type_e frame_type, wfa_t *wfa, bitfile_t *input);
+
+#endif /* not _MC_H */
+
diff --git a/converter/other/fiasco/input/nd.c b/converter/other/fiasco/input/nd.c
new file mode 100644
index 00000000..1a68bfbf
--- /dev/null
+++ b/converter/other/fiasco/input/nd.c
@@ -0,0 +1,237 @@
+/*
+ *  nd.c:		Input of prediction tree	
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:13 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "bit-io.h"
+#include "arith.h"
+#include "misc.h"
+#include "list.h"
+#include "wfalib.h"
+
+#include "nd.h"
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static void
+decode_nd_coefficients (unsigned total, wfa_t *wfa, bitfile_t *input);
+static unsigned
+decode_nd_tree (wfa_t *wfa, bitfile_t *input);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+void
+read_nd (wfa_t *wfa, bitfile_t *input)
+/*
+ *  Read transitions of the nondetermistic 'wfa' part from 'input' stream.
+ *  ND is used only at levels {'wfa->p_min_level', ... , 'wfa->p_max_level'}.
+ *
+ *  Side effects:
+ *	'wfa->into' and 'wfa->weights' are filled with the decoded values
+ */
+{
+   unsigned total = decode_nd_tree (wfa, input);
+   
+   if (total > 0)
+      decode_nd_coefficients (total, wfa, input);
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static unsigned
+decode_nd_tree (wfa_t *wfa, bitfile_t *input)
+/*
+ *  Read 'wfa' prediction tree of given 'input' stream.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'wfa->into' is filled with the decoded values
+ */
+{
+   lqueue_t *queue;			/* queue of states */
+   int       next, state;		/* state and its current child */
+   unsigned  total = 0;			/* total number of predicted states */
+   u_word_t  sum0, sum1;		/* Probability model */
+   u_word_t  code;			/* The present input code value */
+   u_word_t  low;			/* Start of the current code range */
+   u_word_t  high;			/* End of the current code range */
+
+   /*
+    *  Initialize arithmetic decoder
+    */
+   code = get_bits (input, 16);
+   low  = 0;
+   high = 0xffff;
+   sum0 = 1;
+   sum1 = 11;
+
+   queue = alloc_queue (sizeof (int));
+   state = wfa->root_state;
+   queue_append (queue, &state);
+
+   /*
+    *  Traverse the WFA tree in breadth first order (using a queue).
+    */
+   while (queue_remove (queue, &next))
+   {
+      unsigned label;
+
+      if (wfa->level_of_state [next] > wfa->wfainfo->p_max_level + 1) 
+      {
+	 /*
+	  *  Nondetermismn is not allowed at levels larger than
+	  *  'wfa->wfainfo->p_max_level'.
+	  */
+	 for (label = 0; label < MAXLABELS; label++)
+	    if (ischild (state = wfa->tree [next][label]))
+	       queue_append (queue, &state); /* continue with childs */
+      }
+      else if (wfa->level_of_state [next] > wfa->wfainfo->p_min_level)
+      {
+	 for (label = 0; label < MAXLABELS; label++)
+	    if (ischild (state = wfa->tree [next][label]))
+	    {
+	       unsigned count;		/* Current interval count */
+	       unsigned range;		/* Current interval range */
+	       
+	       count = (((code - low) + 1) * sum1 - 1) / ((high - low) + 1);
+	       if (count < sum0)
+	       {
+		  /*
+		   *  Decode a '0' symbol
+		   *  First, the range is expanded to account for the
+		   *  symbol removal.
+		   */
+		  range = (high - low) + 1;
+		  high = low + (u_word_t) ((range * sum0) / sum1 - 1 );
+		  RESCALE_INPUT_INTERVAL;
+		  /*
+		   *  Update the frequency counts
+		   */
+		  sum0++;
+		  sum1++;
+		  if (sum1 > 50) /* scale the symbol frequencies */
+		  {
+		     sum0 >>= 1;
+		     sum1 >>= 1;
+		     if (!sum0)
+			sum0 = 1;
+		     if (sum0 >= sum1)
+			sum1 = sum0 + 1;
+		  }
+		  if (wfa->level_of_state [state] > wfa->wfainfo->p_min_level)
+		     queue_append (queue, &state);
+	       }
+	       else
+	       {
+		  /*
+		   *  Decode a '1' symbol
+		   *  First, the range is expanded to account for the
+		   *  symbol removal.
+		   */
+		  range = (high - low) + 1;
+		  high = low + (u_word_t) ((range * sum1) / sum1 - 1);
+		  low  = low + (u_word_t) ((range * sum0) / sum1);
+		  RESCALE_INPUT_INTERVAL;
+		  /*
+		   *  Update the frequency counts
+		   */
+		  sum1++;
+		  if (sum1 > 50) /* scale the symbol frequencies */
+		  {
+		     sum0 >>= 1;
+		     sum1 >>= 1;
+		     if (!sum0)
+			sum0 = 1;
+		     if (sum0 >= sum1)
+			sum1 = sum0 + 1;
+		  }
+		  append_edge (next, 0, -1, label, wfa);
+		  total++;
+	       }
+	    }
+      }
+   }
+   free_queue (queue);
+
+   INPUT_BYTE_ALIGN (input);
+
+   return total;
+}
+
+static void
+decode_nd_coefficients (unsigned total, wfa_t *wfa, bitfile_t *input)
+/*
+ *  Read #'total' weights of nondeterministic part of 'wfa'  
+ *  of given 'input' stream.
+ *  'frame' gives the current frame number.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'wfa->weights' is filled with the decoded values.
+ */
+{
+   unsigned *coefficients;		/* array of factors to encode */
+   unsigned *ptr;			/* pointer to current factor */
+   
+   /*
+    *  Decode array of coefficients stored with arithmetic coding
+    */
+   {
+      const int	scaling  = 50;		/* scaling factor of prob. model */
+      unsigned  c_symbols = 1 << (wfa->wfainfo->dc_rpf->mantissa_bits + 1);
+      
+      ptr = coefficients = decode_array (input, NULL, &c_symbols, 1,
+					 total, scaling);
+   }
+   
+   /*
+    *  Fill 'wfa->weights' with decoded coefficients
+    */
+   {
+      unsigned state, label;
+      
+      for (state = wfa->basis_states; state < wfa->states; state++)
+	 for (label = 0; label < MAXLABELS; label++)
+	    if (ischild (wfa->tree [state][label])
+		&& isedge (wfa->into [state][label][0]))
+	    {
+	       wfa->weight [state][label][0] = btor (*ptr++,
+						     wfa->wfainfo->dc_rpf);
+	       wfa->int_weight [state][label][0]
+		  = wfa->weight [state][label][0] * 512 + 0.5;
+	    }
+   }
+   Free (coefficients);
+}
diff --git a/converter/other/fiasco/input/nd.h b/converter/other/fiasco/input/nd.h
new file mode 100644
index 00000000..2c2fff4b
--- /dev/null
+++ b/converter/other/fiasco/input/nd.h
@@ -0,0 +1,28 @@
+/*
+ *  nd.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:13 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _ND_H
+#define _ND_H
+
+#include "wfa.h"
+#include "rpf.h"
+#include "bit-io.h"
+
+void
+read_nd (wfa_t *wfa, bitfile_t *input);
+
+#endif /* not _ND_H */
+
diff --git a/converter/other/fiasco/input/read.c b/converter/other/fiasco/input/read.c
new file mode 100644
index 00000000..26bae7e4
--- /dev/null
+++ b/converter/other/fiasco/input/read.c
@@ -0,0 +1,499 @@
+/*
+ *  read.c:		Input of WFA files
+ *
+ *  Written by:		Ullrich Hafner
+ *  
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/07/18 15:44:58 $
+ *  $Author: hafner $
+ *  $Revision: 5.4 $
+ *  $State: Exp $
+ */
+
+#define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#include <string.h>
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "wfa.h"
+#include "misc.h"
+#include "rpf.h"
+#include "bit-io.h"
+#include "wfalib.h"
+
+#include "tree.h"
+#include "matrices.h"
+#include "weights.h"
+#include "nd.h"
+#include "mc.h"
+#include "basis.h"
+#include "read.h"
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static void
+read_tiling (tiling_t *tiling, unsigned image_width, unsigned image_height,
+	     unsigned image_level, bitfile_t *input);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+bitfile_t *
+open_wfa (const char *filename, wfa_info_t *wi)
+/*
+ *  Open WFA file 'filename' and read header information.
+ *
+ *  Return value:
+ *	Pointer to input stream (fileposition: first WFA frame)
+ *
+ *  Side effects:
+ *	The values of the header of 'filename' are copied to 'wfainfo'. 
+ *
+ */
+{
+   bitfile_t *input;			/* pointer to WFA bitfile */
+   
+   assert (filename && wi);
+   
+   wi->wfa_name = strdup (filename);
+
+   /*
+    *  Check whether 'filename' is a regular WFA file
+    */
+   {
+      unsigned 	n;
+      const char     *str;
+      
+      if (!(input = open_bitfile (filename, "FIASCO_DATA", READ_ACCESS)))
+          file_error (filename);
+   
+      for (str = FIASCO_MAGIC, n = strlen (FIASCO_MAGIC); n; n--)
+          if (get_bits (input, 8) != (unsigned) *str++)
+              error ("Input file %s is not a valid FIASCO file!", filename);
+      get_bits (input, 8);		/* fetch newline */
+   }
+   
+   /*
+    *  Read WFA header information
+    */
+   {
+      char	      basis_name [MAXSTRLEN]; /* temp. buffer */
+      const unsigned  rice_k = 8; 	/* parameter of Rice Code */
+      char     	     *str    = basis_name;
+      
+      while ((*str++ = get_bits (input, 8)) != 0
+	     && str < basis_name + MAXSTRLEN)
+	 ;
+      if (str == basis_name + MAXSTRLEN)
+	 error ("Input file %s is not a valid FIASCO file!", filename);
+      
+      {
+	 wi->release = read_rice_code (rice_k, input);
+
+	 if (wi->release > FIASCO_BINFILE_RELEASE)
+	    error ("Can't decode FIASCO files of file format release `%d'."
+		   "\nCurrent file format release is `%d'.", wi->release,
+		   FIASCO_BINFILE_RELEASE);
+      }
+
+      if (wi->release > 1)
+      {
+	 header_type_e type;
+	 
+	 while ((type = read_rice_code (rice_k, input)) != HEADER_END)
+	 {
+	    char     buffer [MAXSTRLEN];
+	    unsigned n = 0;
+	    
+	    switch (type)
+	    {
+	       case HEADER_TITLE:
+		  while ((buffer [n++] = get_bits (input, 8)))
+		     ;
+		  wi->title = strdup (buffer);
+		  break;
+	       case HEADER_COMMENT:
+		  while ((buffer [n++] = get_bits (input, 8)))
+		     ;
+		  wi->comment = strdup (buffer);
+		  break;
+	       default:			/* should not happen */
+		  break;
+	    }
+	 }
+      }
+
+      wi->basis_name = strdup (basis_name);
+      wi->max_states = read_rice_code (rice_k, input);
+      wi->color      = get_bit (input) ? YES : NO;
+      wi->width      = read_rice_code (rice_k, input);
+      wi->height     = read_rice_code (rice_k, input);
+
+      /*
+       *  Compute bintree level
+       */
+      {
+	 unsigned lx = log2 (wi->width - 1) + 1;
+	 unsigned ly = log2 (wi->height - 1) + 1;
+      
+	 wi->level = max (lx, ly) * 2 - ((ly == lx + 1) ? 1 : 0);
+      }
+      wi->chroma_max_states = wi->color ? read_rice_code (rice_k, input) : -1;
+      wi->p_min_level       = read_rice_code (rice_k, input);
+      wi->p_max_level       = read_rice_code (rice_k, input);
+      wi->frames            = read_rice_code (rice_k, input);
+      wi->smoothing	    = read_rice_code (rice_k, input);
+
+      /*
+       *  Read RPF models from disk
+       */
+      {
+	 unsigned 	    mantissa;
+	 fiasco_rpf_range_e range;
+
+	 mantissa = get_bits (input, 3) + 2;
+	 range    = get_bits (input, 2);
+	 wi->rpf  = alloc_rpf (mantissa, range);
+	 
+	 if (get_bit (input))		/* different DC model */
+	 {
+	    mantissa   = get_bits (input, 3) + 2;
+	    range      = get_bits (input, 2);
+	    wi->dc_rpf = alloc_rpf (mantissa, range);
+	 }
+	 else				/* use same model for DC coefficents */
+	    wi->dc_rpf = alloc_rpf (wi->rpf->mantissa_bits,
+				    wi->rpf->range_e);
+
+	 if (get_bit (input))		/* different delta model */
+	 {
+	    mantissa  = get_bits (input, 3) + 2;
+	    range     = get_bits (input, 2);
+	    wi->d_rpf = alloc_rpf (mantissa, range);
+	 }
+	 else
+	    wi->d_rpf = alloc_rpf (wi->rpf->mantissa_bits,
+				   wi->rpf->range_e);
+	 
+	 if (get_bit (input))		/* different DC delta model */
+	 {
+	    mantissa  	 = get_bits (input, 3) + 2;
+	    range     	 = get_bits (input, 2);
+	    wi->d_dc_rpf = alloc_rpf (mantissa, range);
+	 }
+	 else
+	    wi->d_dc_rpf = alloc_rpf (wi->dc_rpf->mantissa_bits,
+				      wi->dc_rpf->range_e);
+      }
+
+      if (wi->frames > 1)		/* motion compensation stuff */
+      {
+	 wi->fps           = read_rice_code (rice_k, input);
+	 wi->search_range  = read_rice_code (rice_k, input);
+	 wi->half_pixel    = get_bit (input) ? YES : NO;
+	 wi->B_as_past_ref = get_bit (input) ? YES : NO;
+      }
+   }
+   
+   INPUT_BYTE_ALIGN (input);
+
+   return input;
+}
+
+void
+read_basis (const char *filename, wfa_t *wfa)
+/*
+ *  Read WFA initial basis 'filename' and fill 'wfa' struct.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	wfa->into, wfa->weights, wfa->final_distribution, wfa->basis_states
+ *	wfa->domain_type wfa->wfainfo->basis_name, are filled with the
+ *	values of the WFA basis.
+ */
+{
+   FILE	*input;				/* ASCII WFA initial basis file */
+
+   assert (filename && wfa);
+
+   if (!wfa->wfainfo->basis_name ||
+       !streq (wfa->wfainfo->basis_name, filename))
+   {
+      if (wfa->wfainfo->basis_name)
+	 Free (wfa->wfainfo->basis_name);
+      wfa->wfainfo->basis_name = strdup (filename);
+   }
+   
+   if (get_linked_basis (filename, wfa))
+      return;				/* basis is linked with excecutable */
+   
+   /*
+    *  Check whether 'wfa_name' is a regular ASCII WFA initial basis file
+    */
+   {
+      char magic [MAXSTRLEN];		/* WFA magic number */
+
+      if (!(input = open_file (filename, "FIASCO_DATA", READ_ACCESS)))
+	 file_error(filename);
+      
+      if (fscanf (input, MAXSTRLEN_SCANF, magic) != 1)
+	 error ("Format error: ASCII FIASCO initial basis file %s", filename);
+      else if (strneq (FIASCO_BASIS_MAGIC, magic))
+	 error ("Input file %s is not an ASCII FIASCO initial basis!",
+		filename);
+   }
+   
+   /*
+    *  WFA ASCII format:
+    *
+    *  Note: State 0 is assumed to be the constant function f(x, y) = 128.
+    *        Don't define any transitions of state 0 in an initial basis. 
+    *
+    *  Header:
+    *   type		|description
+    *	----------------+-----------
+    *   string		|MAGIC Number "Wfa"
+    *	int		|Number of basis states 'N'
+    *	bool_t-array[N]	|use vector in linear combinations,
+    *			|0: don't use vector (auxilliary state)
+    *			|1: use vector in linear combinations
+    *	float-array[N]	|final distribution of every state
+    *
+    *  Transitions:
+    *
+    *      <state 1>			current state
+    *      <label> <into> <weight>	transition 1 of current state
+    *      <label> <into> <weight>	transition 2 of current state
+    *      ...
+    *      <-1>				last transition marker
+    *      <state 2>
+    *      ...
+    *      <-1>				last transition marker
+    *      <state N>
+    *      ...
+    *
+    *      <-1>				last transition marker
+    *      <-1>				last state marker
+    */
+   {
+      unsigned state;
+
+      if (fscanf (input ,"%u", &wfa->basis_states) != 1)
+	 error ("Format error: ASCII FIASCO initial basis file %s", filename);
+
+      /*
+       *  State 0 is assumed to be the constant function f(x, y) = 128.
+       */
+      wfa->domain_type [0]        = USE_DOMAIN_MASK; 
+      wfa->final_distribution [0] = 128;
+      wfa->states 		  = wfa->basis_states;
+      wfa->basis_states++;
+
+      append_edge (0, 0, 1.0, 0, wfa);
+      append_edge (0, 0, 1.0, 1, wfa);
+   
+      for (state = 1; state < wfa->basis_states; state++)
+	 wfa->domain_type [state]
+	    = read_int (input) ? USE_DOMAIN_MASK : AUXILIARY_MASK;
+
+      for (state = 1; state < wfa->basis_states; state++)
+	 wfa->final_distribution[state] = read_real (input);
+
+      /*
+       *  Read transitions
+       */
+      for (state = 1; state < wfa->basis_states; state++)
+      {
+	 unsigned domain;
+	 int      label;
+	 real_t   weight;
+
+	 if (read_int (input) != (int) state)
+	    error ("Format error: ASCII FIASCO initial basis file %s",
+		   filename);
+
+	 while((label = read_int (input)) != -1)
+	 {
+	    domain = read_int (input);
+	    weight = read_real (input);
+	    append_edge (state, domain, weight, label, wfa);
+	 }
+      }
+   }
+   
+   fclose (input);
+}
+
+unsigned
+read_next_wfa (wfa_t *wfa, bitfile_t *input)
+/*
+ *  Read next WFA frame of the WFA stream 'input'.
+ *  WFA header information has to be already present in the 'wfainfo' struct.
+ *  (i.e. open_wfa must be called first!)
+ *  
+ *  No return value.
+ *
+ *  Side effects:
+ *	wfa->into, wfa->weights, wfa->final_distribution, wfa->states
+ *	wfa->x, wfa->y, wfa->level_of_state, wfa->domain_type
+ *      mt->type, mt->number are filled with the values of the WFA file.
+ */
+{
+   tiling_t tiling;			/* tiling information */
+   unsigned frame_number;		/* current frame number */
+   
+   assert (wfa && input);
+   
+   /*
+    *  Frame header information
+    */
+   {
+      const unsigned rice_k = 8;	/* parameter of Rice Code */
+
+      wfa->states     = read_rice_code (rice_k, input);
+      wfa->frame_type = read_rice_code (rice_k, input);
+      frame_number    = read_rice_code (rice_k, input);
+   }
+
+   if (wfa->wfainfo->release > 1)	/* no alignment in version 1 */
+   {
+      INPUT_BYTE_ALIGN (input);
+   }
+   
+   /*
+    *  Read image tiling info 
+    */
+   if (get_bit (input))			/* tiling performed ? */
+      read_tiling (&tiling, wfa->wfainfo->width, wfa->wfainfo->height,
+		   wfa->wfainfo->level, input);
+   else
+      tiling.exponent = 0;
+   
+   INPUT_BYTE_ALIGN (input);
+
+   read_tree (wfa, &tiling, input);
+
+   /*
+    *  Compute domain pool.
+    *  Large images have not been used due to image tiling.
+    */
+   {
+      unsigned state;
+   
+      for (state = wfa->basis_states; state < wfa->states; state++)
+	 if ((!wfa->wfainfo->color
+	      || (int) state <= wfa->tree [wfa->tree [wfa->root_state][0]][0])
+	     &&
+	     (!tiling.exponent ||
+	      wfa->level_of_state [state] <= (wfa->wfainfo->level
+					      - tiling.exponent))
+	     && ((wfa->x [state][0]
+		 + width_of_level (wfa->level_of_state [state]))
+		 <= wfa->wfainfo->width)
+	     && ((wfa->y [state][0]
+		 + height_of_level (wfa->level_of_state [state]))
+		 <= wfa->wfainfo->height))
+	    wfa->domain_type [state] = USE_DOMAIN_MASK;
+	 else
+	    wfa->domain_type [state] = 0;
+   }
+   
+   if (tiling.exponent)
+      Free (tiling.vorder);
+
+   if (get_bit (input))			/* nondeterministic prediction used */
+      read_nd (wfa, input);
+
+   if (wfa->frame_type != I_FRAME)	/* motion compensation used */
+      read_mc (wfa->frame_type, wfa, input);
+
+   locate_delta_images (wfa);
+   
+   /*
+    *  Read linear combinations (coefficients and indices)
+    */
+   {
+      unsigned edges = read_matrices (wfa, input); 
+
+      if (edges)
+	 read_weights (edges, wfa, input);
+   }
+
+   /*
+    *  Compute final distribution of all states
+    */
+   {
+      unsigned state;
+   
+      for (state = wfa->basis_states; state <= wfa->states; state++)
+	 wfa->final_distribution[state]
+	    = compute_final_distribution (state, wfa);
+   }
+
+   return frame_number;
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static void
+read_tiling (tiling_t *tiling, unsigned image_width, unsigned image_height,
+	     unsigned image_level, bitfile_t *input)
+/*
+ *  Read image tiling information from the given file 'input'
+ *  and store parameters in struct 'tiling'.
+ *  
+ *  No return value.
+ */
+{
+   const unsigned rice_k = 8;		/* parameter of Rice Code */
+   
+   tiling->exponent = read_rice_code (rice_k, input);
+   
+   if (get_bit (input))			/* variance order */
+   {
+      unsigned tile;			/* current image tile */
+      unsigned x0, y0;			/* NW corner of image tile */
+      unsigned width, height;		/* size of image tile */
+
+      tiling->vorder = Calloc (1 << tiling->exponent, sizeof (int));
+      for (tile = 0; tile <  1U << tiling->exponent; tile++)
+      {
+	 locate_subimage (image_level, image_level - tiling->exponent, tile,
+			  &x0, &y0, &width, &height);
+	 if (x0 < image_width && y0 < image_height) 
+	    tiling->vorder [tile] = get_bits (input, tiling->exponent);
+	 else
+	    tiling->vorder [tile] = -1;
+      }
+   }
+   else					/* spiral order */
+   {
+      tiling->vorder = Calloc (1 << tiling->exponent, sizeof (int));
+      compute_spiral (tiling->vorder, image_width, image_height,
+		      tiling->exponent, get_bit (input) ? YES : NO);
+   }
+}
diff --git a/converter/other/fiasco/input/read.h b/converter/other/fiasco/input/read.h
new file mode 100644
index 00000000..d0d0ee13
--- /dev/null
+++ b/converter/other/fiasco/input/read.h
@@ -0,0 +1,31 @@
+/*
+ *  read.h
+ *
+ *  Written by:		Ullrich Hafner
+ *  
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:13 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _READ_H
+#define _READ_H
+
+#include "wfa.h"
+#include "bit-io.h"
+
+bitfile_t *
+open_wfa (const char *filename, wfa_info_t *wfainfo);
+void
+read_basis (const char *filename, wfa_t *wfa);
+unsigned
+read_next_wfa (wfa_t *wfa, bitfile_t *input);
+
+#endif /* not _READ_H */
+
diff --git a/converter/other/fiasco/input/tree.c b/converter/other/fiasco/input/tree.c
new file mode 100644
index 00000000..e3e7117e
--- /dev/null
+++ b/converter/other/fiasco/input/tree.c
@@ -0,0 +1,303 @@
+/*
+ *  tree.c:		Input of bintree partitioning
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:13 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "bit-io.h"
+#include "arith.h"
+#include "misc.h"
+#include "wfalib.h"
+#include "tiling.h"
+
+#include "tree.h"
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static unsigned
+restore_depth_first_order (unsigned src_state, unsigned level, unsigned x,
+			   unsigned y, unsigned *dst_state,
+			   word_t (*bfo_tree)[MAXLABELS],
+			   wfa_t *wfa, tiling_t *tiling);
+static void 
+decode_tree (bitfile_t *input, byte_t *data, unsigned n_data, unsigned scaling,
+	     u_word_t sum0, u_word_t sum1);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+void
+read_tree (wfa_t *wfa, tiling_t *tiling, bitfile_t *input)
+/*
+ *  Read bintree partitioning of WFA from the 'input' stream.
+ *  'tiling' provides the information about image tiling, if applied.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'wfa->tree', 'wfa->x', 'wfa->y', 'wfa->level_of_state'
+ *      are filled with decoded values.
+ */
+{
+   byte_t *bitstring;			/* the encoded data */
+   word_t (*bfo_tree)[MAXLABELS];	/* node numbers in BFO */
+      
+   /*
+    *  Read WFA tree stored in breadth first order
+    */
+   {
+      unsigned total = (wfa->states - wfa->basis_states) * MAXLABELS;
+      unsigned scale = total / 20;
+
+      bitstring = Calloc (total, sizeof (byte_t));
+      decode_tree (input, bitstring, total, scale, 1, 11);
+   }
+   
+   /*
+    *  Generate tree using a breadth first traversal
+    */
+   {
+      unsigned 	next;			/* next free node number of the tree */
+      unsigned 	state;
+      unsigned 	label;
+      byte_t   *buffer = bitstring;	/* pointer to decoded data */
+      
+      bfo_tree = Calloc (wfa->states * MAXLABELS, sizeof (word_t));
+      for (state = 0, next = 1; state < next; state++)
+	 for (label = 0; label < MAXLABELS; label++)
+	    bfo_tree [state][label] = *buffer++ ? next++ : RANGE;
+   }
+
+   /*
+    *  Traverse tree and restore depth first order
+    */
+   {
+      unsigned dst_state = wfa->basis_states;
+
+      wfa->root_state
+	 = restore_depth_first_order (0, (wfa->wfainfo->level
+					  + (wfa->wfainfo->color ? 2 : 0)),
+				      0, 0, &dst_state, bfo_tree, wfa, tiling);
+   }
+
+   Free (bitstring);
+   Free (bfo_tree);
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static unsigned
+restore_depth_first_order (unsigned src_state, unsigned level, unsigned x,
+			   unsigned y, unsigned *dst_state,
+			   word_t (*bfo_tree)[MAXLABELS],
+			   wfa_t *wfa, tiling_t *tiling)
+/*
+ *  Map state 'src_state' (breadth first order) 
+ *  to state '*dst_state' (depth first order)
+ *  Add a tree edge 'state' --> 'child' with label and weight 1.0
+ *  if required.
+ *  'x', 'y' give the coordinates of the current state in the 'color' image
+ *  of size 'image_level'. 'tiling' defines the image partitioning. 
+ *  
+ *  Return value:
+ *	new node number in depth first order
+ *
+ *  Side effects:
+ *	'wfa->tree', 'wfa->x', 'wfa->y', 'wfa->level_of_state'
+ *      are filled with decoded values.
+ */
+{
+   unsigned newx [MAXLABELS];		/* x coordinate of childs */
+   unsigned newy [MAXLABELS];		/* y coordinate of childs */
+   unsigned x0, y0;			/* NW corner of image tile */
+   unsigned width, height;		/* size of image tile */
+
+   /*
+    *  If tiling is performed then replace current coordinates
+    */
+   if (tiling->exponent && level == wfa->wfainfo->level - tiling->exponent)
+   {
+      unsigned tile;
+      
+      for (tile = 0; tile < 1U << tiling->exponent; tile++)
+      {
+	 locate_subimage (wfa->wfainfo->level, level, tile,
+			  &x0, &y0, &width, &height);
+	 if (x0 == x && y0 == y) /* matched ! */
+	 {
+	    locate_subimage (wfa->wfainfo->level, level, tiling->vorder[tile],
+			     &x, &y, &width, &height);
+	    break;
+	 }
+      }
+   }
+   /*
+    *  Coordinates of childs 0 and 1
+    */
+   if (wfa->wfainfo->color && level == wfa->wfainfo->level + 1)
+      newx[0] = newy[0] = newx[1] = newy[1] = 0;
+   else
+   {
+      newx[0] = x;
+      newy[0] = y;
+      newx[1] = level & 1 ? x : x + width_of_level (level - 1);
+      newy[1] = level & 1 ? y + height_of_level (level - 1) : y;
+   }
+   
+   /*
+    *  Remap node numbers
+    */
+   {
+      int      child [MAXLABELS];	/* childs of current node (state) */
+      int      domain;			/* current domain */
+      unsigned label;
+
+      for (label = 0; label < MAXLABELS; label++)
+	 if (!isrange (domain = bfo_tree [src_state][label]))
+	    child [label] = restore_depth_first_order (domain, level - 1,
+						       newx [label],
+						       newy [label], dst_state,
+						       bfo_tree, wfa, tiling);
+	 else
+	    child [label] = RANGE;
+
+      for (label = 0; label < MAXLABELS; label++)
+      {
+	 wfa->tree [*dst_state][label] = child [label];
+	 wfa->x [*dst_state][label]    = newx [label];
+	 wfa->y [*dst_state][label]    = newy [label];
+      }
+      wfa->level_of_state [*dst_state] = level;
+   }
+   
+   return (*dst_state)++;
+}	
+
+/****************************************************************************
+
+                 Binary adaptive arithmetic compression
+ 
+****************************************************************************/
+
+static void 
+decode_tree (bitfile_t *input, byte_t *data, unsigned n_data, unsigned scaling,
+	     u_word_t sum0, u_word_t sum1)
+/*
+ *  Decode bintree partitioning using adaptive binary arithmetic decoding.
+ *  'input'	input stream,
+ *  'data'	buffer for decoded szmbols,
+ *  'n_data'	number of symbols to decode,
+ *  'scaling'	rescale probability models if range > 'scaling'
+ *  'sum0'	initial totals of symbol '0'
+ *  'sum1'	initial totals of symbol '1'
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'data []' is filled with the decoded bitstring
+ */
+{
+   u_word_t code;			/* The present input code value */
+   u_word_t low;			/* Start of the current code range */
+   u_word_t high;			/* End of the current code range */
+   unsigned n;				/* Data counter */
+
+   assert (data);
+
+   code = get_bits (input, 16);
+   low  = 0;
+   high = 0xffff;
+
+   for (n = n_data; n; n--) 
+   {
+      unsigned count;			/* Current interval count */
+      unsigned range;			/* Current interval range */
+      
+      count = (((code - low) + 1) * sum1 - 1) / ((high - low) + 1);
+      if (count < sum0)
+      {
+	 /*
+	  *  Decode a '0' symbol
+	  *  First, the range is expanded to account for the symbol removal.
+	  */
+	 range = (high - low) + 1;
+	 high = low + (u_word_t) ((range * sum0) / sum1 - 1 );
+
+	 RESCALE_INPUT_INTERVAL;
+
+	 *data++ = 0;
+	 /*
+	  *  Update the frequency counts
+	  */
+	 sum0++;
+	 sum1++;
+	 if (sum1 > scaling) /* scale the symbol frequencies */
+	 {
+	    sum0 >>= 1;
+	    sum1 >>= 1;
+	    if (!sum0)
+	       sum0 = 1;
+	    if (sum0 >= sum1)
+	       sum1 = sum0 + 1;
+	 }
+
+      }
+      else
+      {
+	 /*
+	  *  Decode a '1' symbol
+	  *  First, the range is expanded to account for the symbol removal.
+	  */
+	 range = (high - low) + 1;
+	 high = low + (u_word_t) ((range * sum1) / sum1 - 1);
+	 low  = low + (u_word_t) ((range * sum0) / sum1);
+
+	 RESCALE_INPUT_INTERVAL;
+
+	 *data++ = 1;
+	 /*
+	  *  Update the frequency counts
+	  */
+	 sum1++;
+	 if (sum1 > scaling) /* scale the symbol frequencies */
+	 {
+	    sum0 >>= 1;
+	    sum1 >>= 1;
+	    if (!sum0)
+	       sum0 = 1;
+	    if (sum0 >= sum1)
+	       sum1 = sum0 + 1;
+	 }
+      }
+   }
+   INPUT_BYTE_ALIGN (input);
+}
+
+
diff --git a/converter/other/fiasco/input/tree.h b/converter/other/fiasco/input/tree.h
new file mode 100644
index 00000000..e4b5f2d8
--- /dev/null
+++ b/converter/other/fiasco/input/tree.h
@@ -0,0 +1,28 @@
+/*
+ *  tree.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:13 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _TREE_H
+#define _TREE_H
+
+#include "wfa.h"
+#include "bit-io.h"
+#include "tiling.h"
+
+void
+read_tree (wfa_t *wfa, tiling_t *tiling, bitfile_t *input);
+
+#endif /* not _TREE_H */
+
diff --git a/converter/other/fiasco/input/weights.c b/converter/other/fiasco/input/weights.c
new file mode 100644
index 00000000..55339980
--- /dev/null
+++ b/converter/other/fiasco/input/weights.c
@@ -0,0 +1,200 @@
+/*
+ *  weights.c:		Input of weights
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:13 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "bit-io.h"
+#include "arith.h"
+#include "rpf.h"
+#include "misc.h"
+
+#include "weights.h"
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+void
+read_weights (unsigned total, wfa_t *wfa, bitfile_t *input)
+/*
+ *  Read #'total' weights from input stream 'input' and
+ *  update transitions of the WFA states with corresponding weights.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'wfa->weights' are filled with the decoded values
+ */
+{
+   unsigned	    state;
+   unsigned	    label;
+   unsigned	    edge;		/* current edge */
+   unsigned	   *weights_array;	/* array of weights to encode */
+   unsigned	   *level_array;	/* array of corresponding levels */
+   unsigned	    offset1, offset2;	/* prob. model offsets. */
+   unsigned	    offset3, offset4;	/* prob. model offsets. */
+   bool_t	    delta_approx = NO; 	/* true if delta has been used */
+   
+   /*
+    *  Check whether delta approximation has been used
+    */
+   for (state = wfa->basis_states; state < wfa->states; state++)
+      if (wfa->delta_state [state])
+      {
+	 delta_approx = YES;
+	 break;
+      }
+  
+   /*
+    *  Generate array of corresponding levels (context of probability model)
+    */
+   {
+      int 	min_level, max_level; 	/* min and max range level */
+      int 	d_min_level, d_max_level; /* min and max range level (delta) */
+      unsigned *lptr;			/* pointer to current corresp. level */
+      int	domain;			/* current domain */
+      bool_t	dc, d_dc;		/* indicates whether DC is used */
+
+      /*
+       *  Compute minimum and maximum level of delta and normal approximations
+       */
+      min_level = d_min_level = MAXLEVEL;
+      max_level = d_max_level = 0;
+      dc 	= d_dc	   = NO;
+   
+      for (state = wfa->basis_states; state < wfa->states; state++)
+	 for (label = 0; label < MAXLABELS; label++)
+	    if (isrange (wfa->tree [state][label]))
+	    {
+	       if (delta_approx && wfa->delta_state [state])
+	       {
+		  d_min_level = min (d_min_level,
+				     wfa->level_of_state [state] - 1);
+		  d_max_level = max (d_max_level,
+				     wfa->level_of_state [state] - 1);
+		  if (wfa->into [state][label][0] == 0)
+		     d_dc = YES;
+	       }
+	       else
+	       {
+		  min_level = min (min_level, wfa->level_of_state [state] - 1);
+		  max_level = max (max_level, wfa->level_of_state [state] - 1);
+		  if (wfa->into [state][label][0] == 0)
+		     dc = YES;
+	       }
+	    }
+      if (min_level > max_level)		/* no lc found */
+	 max_level = min_level - 1;
+      if (d_min_level > d_max_level)
+	 d_max_level = d_min_level - 1;
+
+      offset1 = dc ? 1 : 0;
+      offset2 = offset1 + (d_dc ? 1 : 0);
+      offset3 = offset2 + (max_level - min_level + 1);
+      offset4 = offset3 + (d_max_level - d_min_level + 1);
+
+      lptr = level_array = Calloc (total, sizeof (int));
+      for (state = wfa->basis_states; state < wfa->states; state++)
+	 for (label = 0; label < MAXLABELS; label++)
+	    if (isrange (wfa->tree[state][label]))
+	       for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
+		    edge++)
+	       {
+		  if ((unsigned) (lptr - level_array) >= total)
+		     error ("Can't read more than %d weights.", total);
+		  if (domain)
+		  {
+		     if (delta_approx && wfa->delta_state [state])
+			*lptr++ = offset3 + wfa->level_of_state [state]
+				  - 1 - d_min_level;
+		     else
+			*lptr++ = offset2 + wfa->level_of_state [state]
+				  - 1 - min_level;
+		  }
+		  else
+		     *lptr++ = delta_approx && wfa->delta_state [state]
+			       ? offset1 : 0;
+	       }
+   }
+
+   /*
+    *  Decode the list of weights with an arithmetic decoder
+    */
+   {
+      unsigned	      i;
+      unsigned	     *c_symbols = Calloc (offset4, sizeof (unsigned));
+      const unsigned  scale 	= 500; 	/* scaling of probability model */
+
+      c_symbols [0] = 1 << (wfa->wfainfo->dc_rpf->mantissa_bits + 1);
+      if (offset1 != offset2)
+	 c_symbols [offset1] = 1 << (wfa->wfainfo->d_dc_rpf->mantissa_bits
+				     + 1);
+      for (i = offset2; i < offset3; i++)
+	 c_symbols [i] = 1 << (wfa->wfainfo->rpf->mantissa_bits + 1);
+      for (; i < offset4; i++)
+	 c_symbols [i] = 1 << (wfa->wfainfo->d_rpf->mantissa_bits + 1);
+      
+      weights_array = decode_array (input, level_array, c_symbols,
+				    offset4, total, scale);
+      Free (c_symbols);
+   }
+   Free (level_array);
+
+   /*
+    *  Update transitions with decoded weights
+    */
+   {
+      unsigned *wptr = weights_array;	/* pointer to current weight */
+      int	domain;			/* current domain */
+
+      for (state = wfa->basis_states; state < wfa->states; state++)
+	 for (label = 0; label < MAXLABELS; label++)
+	    if (isrange (wfa->tree[state][label]))
+	       for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
+		    edge++)
+	       {
+		  if (domain)		/* not DC component */
+		  {
+		     if (delta_approx && wfa->delta_state [state])
+			wfa->weight [state][label][edge]
+			   = btor (*wptr++, wfa->wfainfo->d_rpf);
+		     else
+			wfa->weight [state][label][edge]
+			   = btor (*wptr++, wfa->wfainfo->rpf);
+		  }
+		  else
+		  {
+		     if (delta_approx && wfa->delta_state [state])
+			wfa->weight [state][label][edge]
+			   = btor (*wptr++, wfa->wfainfo->d_dc_rpf);
+		     else
+			wfa->weight [state][label][edge]
+			   = btor (*wptr++, wfa->wfainfo->dc_rpf);
+		  }
+		  wfa->int_weight [state][label][edge]
+		     = wfa->weight [state][label][edge] * 512 + 0.5;
+	       }
+   }
+   
+   Free (weights_array);
+}
+ 
diff --git a/converter/other/fiasco/input/weights.h b/converter/other/fiasco/input/weights.h
new file mode 100644
index 00000000..1e2285a9
--- /dev/null
+++ b/converter/other/fiasco/input/weights.h
@@ -0,0 +1,27 @@
+/*
+ *  weights.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:13 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _WEIGHTS_H
+#define _WEIGHTS_H
+
+#include "wfa.h"
+#include "bit-io.h"
+
+void
+read_weights (unsigned total, wfa_t *wfa, bitfile_t *input);
+
+#endif /* not _WEIGHTS_H */
+
diff --git a/converter/other/fiasco/lib/Makefile b/converter/other/fiasco/lib/Makefile
new file mode 100644
index 00000000..99d7c1d7
--- /dev/null
+++ b/converter/other/fiasco/lib/Makefile
@@ -0,0 +1,33 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../../..
+  BUILDDIR = $(SRCDIR)
+endif
+FIASCOSUBDIR = converter/other/fiasco
+SUBDIR = $(FIASCOSUBDIR)/lib
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+OBJECTS = \
+  arith.o \
+  bit-io.o \
+  dither.o \
+  error.o \
+  image.o \
+  list.o \
+  misc.o \
+  rpf.o \
+  mvcode.o \
+
+MERGE_OBJECTS = $(OBJECTS)
+
+INCLUDES = -I$(SRCDIR)/$(FIASCOSUBDIR)
+
+all: libfiasco_lib.a
+
+include $(SRCDIR)/Makefile.common
+
+libfiasco_lib.a: $(OBJECTS)
+	$(AR) -rc $@ $(OBJECTS)
+	$(RANLIB) $@
+
diff --git a/converter/other/fiasco/lib/arith.c b/converter/other/fiasco/lib/arith.c
new file mode 100644
index 00000000..e3745bf7
--- /dev/null
+++ b/converter/other/fiasco/lib/arith.c
@@ -0,0 +1,708 @@
+/*
+ *  arith.c:		Adaptive arithmetic coding and decoding
+ *
+ *  Written by:		Ullrich Hafner
+ *  
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:49:37 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "bit-io.h"
+#include "misc.h"
+#include "arith.h"
+
+/******************************************************************************
+
+				public code
+  
+******************************************************************************/
+
+arith_t *
+alloc_encoder (bitfile_t *output)
+/*
+ *  Arithmetic coder constructor:
+ *  Initialize the arithmetic coder.
+ *  
+ *  Return value:
+ *	A pointer to the new coder structure
+ */
+{
+   arith_t *arith = Calloc (1, sizeof (arith_t));
+
+   assert (output);
+   
+   arith->low       = LOW;
+   arith->high      = HIGH;
+   arith->underflow = 0;
+   arith->file 	    = output;
+
+   return arith;
+}
+
+void
+free_encoder (arith_t *arith)
+/*
+ *  Arithmetic encoder destructor.
+ *  Flush the arithmetic coder. Append all remaining bits to the
+ *  output stream. Append zero bits to get the output file byte aligned.
+ *  
+ *  No return value.
+ */
+{
+   u_word_t   low;			/* start of the current code range  */
+   u_word_t   high;			/* end of the current code range    */
+   u_word_t   underflow;		/* number of underflow bits pending */
+   bitfile_t *output;
+   
+   assert (arith);
+
+   low       = arith->low;
+   high      = arith->high;
+   underflow = arith->underflow;
+   output    = arith->file;
+   
+   low = high;
+
+   RESCALE_OUTPUT_INTERVAL;
+
+   OUTPUT_BYTE_ALIGN (output);
+
+   Free (arith);
+}
+
+real_t
+encode_symbol (unsigned symbol, arith_t *arith, model_t *model)
+/*
+ *  Encode the given 'symbol' using the given probability 'model'.
+ *  The current state of the arithmetic coder is given by 'arith'.
+ *  Output bits are appended to the stream 'output'.
+ *
+ *  The model is updated after encoding the symbol (if neccessary the
+ *  symbol counts are rescaled).
+ *  
+ *  Return value:
+ *	information content of the encoded symbol.
+ *
+ *  Side effects:
+ *	'model' is updated (probability distribution)
+ *	'arith' is updated (coder state)
+ */
+{
+   u_word_t   low_count;		/* lower bound of 'symbol' interval */
+   u_word_t   high_count;		/* upper bound of 'symbol' interval */
+   u_word_t   scale;			/* range of all 'm' symbol intervals */
+   unsigned   range;			/* range of current interval */
+   unsigned   index;			/* index of probability model */
+   u_word_t   low;			/* start of the current code range  */
+   u_word_t   high;			/* end of the current code range    */
+   u_word_t   underflow;		/* number of underflow bits pending */
+   bitfile_t *output;			/* output file */
+   
+   assert (model && arith);
+
+   /*
+    * Get interval values
+    */
+   low       = arith->low;
+   high      = arith->high;
+   underflow = arith->underflow;
+   output    = arith->file;
+
+   assert (high > low);
+   
+   if (model->order > 0)		/* order-'n' model*/
+   {
+      unsigned power;			/* multiplicator */
+      unsigned i;
+
+      /*
+       *  Compute index of the probability model to use.
+       *  See init_model() for more details.
+       */
+      power = 1;			/* multiplicator */
+      index = 0;			/* address of prob. model */
+	 
+      for (i = 0; i < model->order; i++) /* genarate a M-nary number */
+      {
+	 index += model->context [i] * power;	
+	 power *= model->symbols;
+      }
+
+      index *= model->symbols + 1;	/* we need space for M + 1 elements */
+
+      for (i = 0; i < model->order - 1; i++)
+	 model->context [i] = model->context [i + 1];
+      model->context [i] = symbol;
+   }
+   else
+      index = 0;
+
+   scale      = model->totals [index + model->symbols];
+   low_count  = model->totals [index + symbol];
+   high_count = model->totals [index + symbol + 1];
+
+   /*
+    *  Compute the new interval depending on the input 'symbol'.
+    */
+   range = (high - low) + 1;
+   high  = low + (u_word_t) ((range * high_count) / scale - 1);
+   low   = low + (u_word_t) ((range * low_count) / scale);
+   
+   RESCALE_OUTPUT_INTERVAL;
+   
+   if (model->scale > 0)		/* adaptive model */
+   {
+      unsigned i;
+
+      /*
+       *  Update probability model
+       */
+      for (i = symbol + 1; i <= model->symbols; i++)
+	 model->totals [index + i]++;
+      if (model->totals [index + model->symbols] > model->scale) /* scaling */
+      {
+	 for (i = 1; i <= model->symbols; i++)
+	 {
+	    model->totals [index + i] >>= 1;
+	    if (model->totals [index + i] <= model->totals [index + i - 1])
+	       model->totals [index + i] = model->totals [index + i - 1] + 1;
+	 }
+      }
+   }
+
+   /*
+    *  Store interval values
+    */
+   arith->low  	    = low;
+   arith->high 	    = high;
+   arith->underflow = underflow;
+   
+   return - log2 ((high_count - low_count) / (real_t) scale);
+}
+
+void
+encode_array (bitfile_t *output, const unsigned *data, const unsigned *context,
+	      const unsigned *c_symbols, unsigned n_context, unsigned n_data,
+	      unsigned scaling)
+/*
+ *  Arithmetic coding of #'n_data' symbols given in the array 'data'.
+ *  If 'n_context' > 1 then a number (context [n]) is assigned to every
+ *  data element n, specifying which context (i.e. number of symbols given by
+ *  c_symbols [context [n]] and adaptive probability model) must be used.
+ *  Rescale probability models if range > 'scaling'.
+ *
+ *  No return value.
+ */
+{
+   u_word_t **totals;			/* probability model */
+
+   if (!n_context)
+      n_context = 1;			/* always use one context */
+
+   assert (output && c_symbols && data);
+   assert (n_context == 1 || context);
+   
+   /*
+    *  Allocate probability models, start with uniform distribution
+    */
+   totals = Calloc (n_context, sizeof (u_word_t *));
+   {
+      unsigned c;
+      
+      for (c = 0; c < n_context; c++)
+      {
+	 unsigned i;
+      
+	 totals [c]    = Calloc (c_symbols [c] + 1, sizeof (u_word_t));
+	 totals [c][0] = 0;
+      
+	 for (i = 0; i < c_symbols [c]; i++)
+	    totals [c][i + 1] = totals [c][i] + 1;
+      }
+   }
+
+   /*
+    *  Encode array elements
+    */
+   {
+      u_word_t low  	 = 0;		/* Start of the current code range */
+      u_word_t high 	 = 0xffff;	/* End of the current code range */
+      u_word_t underflow = 0;		/* Number of underflow bits pending */
+      unsigned n;
+      
+      for (n = 0; n < n_data; n++)
+      {
+	 u_word_t low_count;		/* lower bound of 'symbol' interval */
+	 u_word_t high_count;		/* upper bound of 'symbol' interval */
+	 u_word_t scale;		/* range of all 'm' symbol intervals */
+	 unsigned range;		/* current range */
+	 int	  d;			/* current data symbol */
+	 int	  c;			/* context of current data symbol */
+
+	 d = data [n];
+	 c = n_context > 1 ? context [n] : 0; 
+      
+	 scale	    = totals [c][c_symbols [c]];
+	 low_count  = totals [c][d];
+	 high_count = totals [c][d + 1];
+
+	 /*
+	  * Rescale high and low for the new symbol.
+	  */
+	 range = (high - low) + 1;
+	 high  = low + (u_word_t) ((range * high_count) / scale - 1);
+	 low   = low + (u_word_t) ((range * low_count) / scale);
+	 RESCALE_OUTPUT_INTERVAL;
+      
+	 /*
+	  *  Update probability models
+	  */
+	 {
+	    unsigned i;
+
+	    for (i = d + 1; i < c_symbols [c] + 1; i++)
+	       totals [c][i]++;
+	 
+	    if (totals [c][c_symbols [c]] > scaling) /* scaling */
+	       for (i = 1; i < c_symbols [c] + 1; i++)
+	       {
+		  totals [c][i] >>= 1;
+		  if (totals [c][i] <= totals [c][i - 1])
+		     totals [c][i] = totals [c][i - 1] + 1;
+	       }
+	 }
+      }
+      /*
+       *  Flush arithmetic encoder
+       */
+      low = high;
+      RESCALE_OUTPUT_INTERVAL;
+      OUTPUT_BYTE_ALIGN (output);
+   }
+   
+   /*
+    *  Cleanup ...
+    */
+   {
+      unsigned c;
+      for (c = 0; c < n_context; c++)
+	 Free (totals [c]);
+      Free (totals);
+   }
+}
+
+arith_t *
+alloc_decoder (bitfile_t *input)
+/*
+ *  Arithmetic decoder constructor:
+ *  Initialize the arithmetic decoder with the first
+ *  16 input bits from the stream 'input'.
+ *  
+ *  Return value:
+ *	A pointer to the new decoder structure
+ */
+
+{
+   arith_t *arith = Calloc (1, sizeof (arith_t));
+   
+   assert (input);
+   
+   arith->low  = LOW;
+   arith->high = HIGH;
+   arith->code = get_bits (input, 16);
+   arith->file = input;
+
+   return arith;
+}
+
+void
+free_decoder (arith_t *arith)
+/*
+ *  Arithmetic decoder destructor:
+ *  Flush the arithmetic decoder, i.e., read bits to get the input
+ *  file byte aligned. 
+ *  
+ *  No return value.
+ *
+ *  Side effects:
+ *	structure 'arith' is discarded.
+ */
+{
+   assert (arith);
+
+   INPUT_BYTE_ALIGN (arith->file);
+
+   Free (arith);
+}
+
+unsigned
+decode_symbol (arith_t *arith, model_t *model)
+/*
+ *  Decode the next symbol - the state of the arithmetic decoder
+ *  is given in 'arith'. Read refinement bits from the stream 'input'
+ *  and use the given probability 'model'. Update the probability model after
+ *  deconding the symbol (if neccessary also rescale the symbol counts).
+ *  
+ *  Return value:
+ *	decoded symbol
+ *
+ *  Side effects:
+ *	'model' is updated (probability distribution)
+ *	'arith' is updated (decoder state)
+ */
+{
+   unsigned   range;			/* range of current interval */
+   unsigned   count;			/* value in the current interval */
+   unsigned   index;			/* index of probability model */
+   unsigned   symbol;			/* decoded symbol */
+   u_word_t   scale;			/* range of all 'm' symbol intervals */
+   u_word_t   low;			/* start of the current code range  */
+   u_word_t   high;			/* end of the current code range    */
+   u_word_t   code;			/* the present input code value */
+   bitfile_t *input;			/* input file */
+
+   assert (arith && model);
+   
+   /*
+    * Get interval values
+    */
+   low   = arith->low;
+   high  = arith->high;
+   code  = arith->code;
+   input = arith->file;
+
+   assert (high > low);
+   
+   if (model->order > 0)		/* order-'n' model */
+   {
+      unsigned power;			/* multiplicator */
+      unsigned i;
+      
+      /*
+       *  Compute index of the probability model to use.
+       *  See init_model() for more details.
+       */
+      power = 1;			/* multiplicator */
+      index = 0;			/* address of prob. model */
+	 
+      for (i = 0; i < model->order; i++) /* genarate a m-nary number */
+      {
+	 index += model->context[i] * power;	
+	 power *= model->symbols;
+      }
+
+      index *= model->symbols + 1;	/* we need space for m + 1 elements */
+   }
+   else
+      index = 0;
+
+   scale = model->totals [index + model->symbols];
+   range = (high - low) + 1;
+   count = ((code - low + 1) * scale - 1) / range;
+
+   for (symbol = model->symbols; count < model->totals [index + symbol];
+	symbol--)
+      ;
+
+   if (model->order > 0)		/* order-'n' model */
+   {
+      unsigned i;
+      
+      for (i = 0; i < model->order - 1; i++)
+	 model->context [i] = model->context [i + 1];
+      model->context [i] = symbol;
+   }
+
+   /*
+    *  Compute interval boundaries
+    */
+   {
+      u_word_t low_count;		/* lower bound of 'symbol' interval */
+      u_word_t high_count;		/* upper bound of 'symbol' interval */
+      
+      low_count  = model->totals [index + symbol];
+      high_count = model->totals [index + symbol + 1];
+      high       = low + (u_word_t) ((range * high_count) / scale - 1 );
+      low        = low + (u_word_t) ((range * low_count) / scale );
+   }
+   
+   RESCALE_INPUT_INTERVAL;
+   
+   if (model->scale > 0)		/* adaptive model */
+   {
+      unsigned i;
+
+      /*
+       *  Update probability model
+       */
+      for (i = symbol + 1; i <= model->symbols; i++)
+	 model->totals [index + i]++;
+      if (model->totals [index + model->symbols] > model->scale) /* scaling */
+      {
+	 for (i = 1; i <= model->symbols; i++)
+	 {
+	    model->totals [index + i] >>= 1;
+	    if (model->totals [index + i] <= model->totals [index + i - 1])
+	       model->totals [index + i] = model->totals [index + i - 1] + 1;
+	 }
+      }
+   }
+   
+   /*
+    *  Store interval values
+    */
+   arith->low  = low;
+   arith->high = high;
+   arith->code = code;
+
+   return symbol;
+}
+
+unsigned *
+decode_array (bitfile_t *input, const unsigned *context,
+	      const unsigned *c_symbols, unsigned n_context,
+	      unsigned n_data, unsigned scaling)
+/*
+ *  Arithmetic decoding of #'n_data' symbols.
+ *  If 'n_context' > 1 then a number (context [n]) is assigned to every
+ *  data element n, specifying which context (i.e. number of symbols given by
+ *  c_symbols [context [n]] and adaptive probability model) must be used.
+ *  Rescale probability models if range > 'scaling'.
+ *
+ *  Return value:
+ *	pointer to array containing the decoded symbols
+ */
+{
+   unsigned  *data;			/* array to store decoded symbols */
+   u_word_t **totals;			/* probability model */
+   
+   if (n_context < 1)
+      n_context = 1;			/* always use one context */
+   assert (input && c_symbols);
+   assert (n_context == 1 || context);
+
+   data = Calloc (n_data, sizeof (unsigned));
+   
+   /*
+    *  Allocate probability models, start with uniform distribution
+    */
+   totals = Calloc (n_context, sizeof (u_word_t *));
+   {
+      unsigned c;
+      
+      for (c = 0; c < n_context; c++)
+      {
+	 unsigned i;
+      
+	 totals [c]    = Calloc (c_symbols [c] + 1, sizeof (u_word_t));
+	 totals [c][0] = 0;
+      
+	 for (i = 0; i < c_symbols [c]; i++)
+	    totals [c][i + 1] = totals [c][i] + 1;
+      }
+   }
+
+   /*
+    *  Fill array 'data' with decoded values
+    */
+   {
+      u_word_t code = get_bits (input, 16); /* The present input code value */
+      u_word_t low  = 0;		/* Start of the current code range */
+      u_word_t high = 0xffff;		/* End of the current code range */
+      unsigned n;
+      
+      for (n = 0; n < n_data; n++) 
+      {
+	 u_word_t scale;		/* range of all 'm' symbol intervals */
+	 u_word_t low_count;		/* lower bound of 'symbol' interval */
+	 u_word_t high_count;		/* upper bound of 'symbol' interval */
+	 unsigned count;		/* value in the current interval */
+	 unsigned range;		/* current interval range */
+	 unsigned d;			/* current data symbol */
+	 unsigned c;			/* context of current data symbol */
+
+	 c = n_context > 1 ? context [n] : 0; 
+
+	 assert (high > low);
+	 scale = totals [c][c_symbols [c]];
+	 range = (high - low) + 1;
+	 count = (((code - low) + 1 ) * scale - 1) / range;
+      
+	 for (d = c_symbols [c]; count < totals [c][d]; d--) /* next symbol */
+	    ;
+	 low_count  = totals [c][d];
+	 high_count = totals [c][d + 1];
+
+	 high = low + (u_word_t) ((range * high_count) / scale - 1 );
+	 low  = low + (u_word_t) ((range * low_count) / scale );
+	 RESCALE_INPUT_INTERVAL;
+
+	 /*
+	  *  Updata probability models
+	  */
+	 {
+	    unsigned i;
+
+	    for (i = d + 1; i < c_symbols [c] + 1; i++)
+	       totals [c][i]++;
+	 
+	    if (totals [c][c_symbols [c]] > scaling) /* scaling */
+	       for (i = 1; i < c_symbols [c] + 1; i++)
+	       {
+		  totals [c][i] >>= 1;
+		  if (totals [c][i] <= totals [c][i - 1])
+		     totals [c][i] = totals [c][i - 1] + 1;
+	       }
+	 }
+	 data [n] = d;
+      }
+      INPUT_BYTE_ALIGN (input);
+   }
+
+   /*
+    *  Cleanup ...
+    */
+   {
+      unsigned c;
+      
+      for (c = 0; c < n_context; c++)
+	 Free (totals [c]);
+      Free (totals);
+   }
+   
+   return data;
+}
+
+model_t *
+alloc_model (unsigned m, unsigned scale, unsigned n, unsigned *totals)
+/*
+ *  Model constructor:
+ *  allocate and initialize an order-'n' probability model.
+ *  The size of the source alphabet is 'm'. Rescale the symbol counts after
+ *  'scale' symbols are encoded/decoded. The initial probability of every
+ *  symbol is 1/m.
+ *  If 'scale' = 0 then use static modeling (p = 1/n).
+ *  If 'totals' is not NULL then use this array of 'm' values to set
+ *  the initial counts.
+ *
+ *  Return value:
+ *	a pointer to the new probability model structure.
+ *  
+ *  Note: We recommend a small size of the alphabet because no escape codes
+ *  are used to encode/decode previously unseen symbols.
+ *  
+ */
+{
+   model_t  *model;			/* new probability model */
+   unsigned  num;			/* number of contexts to allocate */
+   bool_t    cont;			/* continue flag */
+   bool_t    dec;			/* next order flag */
+   unsigned  i;
+
+   /*
+    *  Allocate memory for the structure
+    */
+   model          = Calloc (1, sizeof (model_t));
+   model->symbols = m;
+   model->scale   = scale;
+   model->order   = n;
+   model->context = n > 0 ? Calloc (n, sizeof (unsigned)) : NULL;
+   /*
+    *  Allocate memory for the probabilty model.
+    *  Each of the m^n different contexts requires its own probability model.
+    */
+   for (num = 1, i = 0; i < model->order; i++)
+      num *= model->symbols;
+
+   model->totals = Calloc (num * (model->symbols + 1), sizeof (unsigned));
+
+   for (i = 0; i < model->order; i++)
+      model->context[i] = 0;		/* start with context 0,0, .. ,0 */
+   cont = YES;
+   while (cont)				/* repeat while context != M ... M */
+   {
+      int	power;			/* multiplicator */
+      int	index;			/* index of probability model */
+      /*
+       *  There are m^n different contexts:
+       *  Let "context_1 context_2 ... context_n symbol" be the current input
+       *  stream then the index of the probability model is given by:
+       *  index = context_1 * M^0 + context_2 * M^1 + ... + context_n * M^(n-1)
+       */
+      power = 1;			/* multiplicator */
+      index = 0;			/* address of prob. model */
+	 
+      for (i = 0; i < model->order; i++)	/* genarate a m-nary number */
+      {
+	 index += model->context[i] * power;	
+	 power *= model->symbols;
+      }
+
+      index *= model->symbols + 1;	/* size of each model is m + 1 */
+
+      model->totals [index + 0] = 0;	/* always zero */
+	 
+      for (i = 1; i <= model->symbols; i++) /* prob of each symbol is 1/m or
+					       as given in totals */
+	 model->totals[index + i] = model->totals [index + i - 1]
+				    + (totals ? totals [i - 1] : 1);
+
+      if (model->order == 0)		/* order-0 model */
+	 cont = NO;
+      else				/* try next context */
+	 for (i = model->order - 1, dec = YES; dec; i--)
+	 {
+	    dec = NO;
+	    model->context[i]++;
+	    if (model->context[i] >= model->symbols) 
+	    {
+	       /* change previous context */
+	       model->context[i] = 0;
+	       if (i > 0)		/* there's still a context remaining */
+		  dec = YES;
+	       else
+		  cont = NO;		/* all context models initilized */
+	    }
+	 }
+   }
+   for (i = 0; i < model->order; i++)
+      model->context[i] = 0;		/* start with context 0,0, .. ,0 */
+
+   return model;
+}
+
+void
+free_model (model_t *model)
+/*
+ *  Model destructor:
+ *  Free memory allocated by the arithmetic 'model'.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	struct 'model' is discarded
+ */
+{
+   if (model != NULL)
+   {
+      if (model->context != NULL)
+	 Free (model->context);
+      Free (model->totals);
+      Free (model);
+   }
+   else
+      warning ("Can't free model <NULL>.");
+}
diff --git a/converter/other/fiasco/lib/arith.h b/converter/other/fiasco/lib/arith.h
new file mode 100644
index 00000000..744eb9d7
--- /dev/null
+++ b/converter/other/fiasco/lib/arith.h
@@ -0,0 +1,122 @@
+/*
+ *  arith.h
+ *
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:49:37 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _ARITH_H
+#define _ARITH_H
+
+#include "types.h"
+#include "bit-io.h"
+
+typedef struct model
+{
+   unsigned  symbols;			/* number of symbols in the alphabet */
+   unsigned  scale;			/* if totals > scale rescale totals */
+   unsigned  order;			/* order of the probability model */
+   unsigned *context;			/* context of the model */
+   unsigned *totals;			/* the totals */
+} model_t;
+
+typedef struct arith
+{
+   u_word_t   low;			/* start of the current code range */
+   u_word_t   high;			/* end of the current code range */
+   u_word_t   underflow;		/* number of underflow bits pending */
+   u_word_t   code;			/* the present input code value */
+   bitfile_t *file;			/* I/O stream */
+} arith_t;
+
+enum interval {LOW = 0x0000, FIRST_QUARTER = 0x4000, HALF = 0x8000,
+	       THIRD_QUARTER = 0xc000, HIGH = 0xffff};
+
+arith_t *
+alloc_encoder (bitfile_t *file);
+void
+free_encoder (arith_t *arith);
+real_t
+encode_symbol (unsigned symbol, arith_t *arith, model_t *model);
+void
+encode_array (bitfile_t *output, const unsigned *data, const unsigned *context,
+	      const unsigned *c_symbols, unsigned n_context, unsigned n_data,
+	      unsigned scaling);
+arith_t *
+alloc_decoder (bitfile_t *input);
+void
+free_decoder (arith_t *arith);
+unsigned
+decode_symbol (arith_t *arith, model_t *model);
+unsigned *
+decode_array (bitfile_t *input, const unsigned *context,
+	      const unsigned *c_symbols, unsigned n_context,
+	      unsigned n_data, unsigned scaling);
+model_t *
+alloc_model (unsigned m, unsigned scale, unsigned n, unsigned *totals);
+void
+free_model (model_t *model);
+
+#define RESCALE_INPUT_INTERVAL  for (;;)                                      \
+                                   if ((high >= HALF) && (low < HALF) &&      \
+                                      ((low & FIRST_QUARTER) != FIRST_QUARTER \
+				       || (high & FIRST_QUARTER) != 0))       \
+                                   {                                          \
+                                      break;                                  \
+                                   }                                          \
+                                   else if ((high < HALF) || (low >= HALF))   \
+                                   {                                          \
+                                      low  <<= 1;                             \
+                                      high <<= 1;                             \
+                                      high  |= 1;                             \
+                                      code <<= 1;                             \
+                                      code  += get_bit (input);               \
+                                   }                                          \
+                                   else                                       \
+                                   {                                          \
+                                      code  ^= FIRST_QUARTER;                 \
+                                      low   &= FIRST_QUARTER - 1;             \
+                                      low  <<= 1;                             \
+                                      high <<= 1;                             \
+                                      high  |= HALF + 1;                      \
+                                      code <<= 1;                             \
+                                      code  += get_bit (input);               \
+                                   }                                          
+        								   
+#define RESCALE_OUTPUT_INTERVAL  for (;;)                                     \
+                                 {                                            \
+                                    if (high < HALF)                          \
+                                    {                                         \
+                                       put_bit (output, 0);                   \
+                                       for (; underflow; underflow--)         \
+                                          put_bit (output, 1);                \
+                                    }                                         \
+                                    else if (low >= HALF)                     \
+                                    {                                         \
+                                       put_bit (output, 1);                   \
+                                       for (; underflow; underflow--)         \
+                                          put_bit (output, 0);                \
+                                    }                                         \
+                                    else if (high < THIRD_QUARTER &&          \
+                                             low >= FIRST_QUARTER)            \
+                                    {                                         \
+                                       underflow++;                           \
+                                       high |= FIRST_QUARTER;                 \
+                                       low  &= FIRST_QUARTER - 1;             \
+                                    }                                         \
+                                    else                                      \
+                                       break;                                 \
+                                    high <<= 1;                               \
+                                    high  |= 1;                               \
+                                    low  <<= 1;                               \
+                                 }                                             
+					 
+#endif /* not _ARITH_H */
+
diff --git a/converter/other/fiasco/lib/bit-io.c b/converter/other/fiasco/lib/bit-io.c
new file mode 100644
index 00000000..364a1c05
--- /dev/null
+++ b/converter/other/fiasco/lib/bit-io.c
@@ -0,0 +1,327 @@
+/*
+ *  bit-io.c:       Buffered and bit oriented file I/O 
+ *
+ *  Written by:     Ullrich Hafner
+ *  
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+ 
+/*
+ *  $Date: 2000/06/14 20:49:37 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include "config.h"
+
+#include <string.h>
+#if STDC_HEADERS
+#   include <stdlib.h>
+#endif /* not STDC_HEADERS */
+
+#include "macros.h"
+#include "types.h"
+#include "error.h"
+
+#include "misc.h"
+#include "bit-io.h"
+
+/*****************************************************************************
+
+                 local constants
+  
+*****************************************************************************/
+
+static const unsigned BUFFER_SIZE = 16350;
+
+static const unsigned mask[] = {0x0001, 0x0002, 0x0004, 0x0008,
+                                0x0010, 0x0020, 0x0040, 0x0080,
+                                0x0100, 0x0200, 0x0400, 0x0800,
+                                0x1000, 0x2000, 0x4000, 0x8000};
+
+/*****************************************************************************
+
+                public code
+  
+*****************************************************************************/
+
+FILE *
+open_file (const char *filename, const char *env_var, openmode_e mode)
+/*
+ *  Try to open file 'filename' with mode 'mode' (READ_ACCESS, WRITE_ACCESS).
+ *  Scan the current directory first and then cycle through the
+ *  path given in the environment variable 'env_var', if set.
+ * 
+ *  Return value:
+ *  Pointer to open file on success, else NULL.
+ */
+{
+    char       *path;        /* current path */
+    FILE       *fp;      /* file pointer of I/O stream */
+    char       *ext_filename = NULL; /* full path of file */
+    char       *env_path     = NULL; /* path given by 'env_var' */
+    char const * const PATH_SEP     = " ;:,"; /* path separation characters */
+    char const * const DEFAULT_PATH = "."; /* default for output files */
+    const char * const read_mode  = "rb";
+    const char * const write_mode = "wb";
+
+
+    assert (mode == READ_ACCESS || mode == WRITE_ACCESS);
+
+    /*
+     *  First check for stdin or stdout
+     */
+    if (filename == NULL || streq (filename, "-"))
+    {
+        if (mode == READ_ACCESS)
+            return stdin;
+        else 
+            return stdout;
+    }
+   
+    /*
+     *  Try to open 'readonly' file in the current directory
+     */
+    if (mode == READ_ACCESS && (fp = fopen (filename, read_mode)))
+        return fp; 
+
+    if (mode == WRITE_ACCESS && strchr (filename, '/')) /* contains path */
+        return fopen (filename, write_mode);
+   
+    /*
+     *  Get value of environment variable 'env_var', if set
+     *  else use DEFAULT_PATH ("./")
+     */
+    if (env_var != NULL)
+        env_path = getenv (env_var);
+    if (env_path == NULL) 
+        env_path = strdup (DEFAULT_PATH);
+    else
+        env_path = strdup (env_path);
+   
+    /*
+     *  Try to open file in the directory given by the environment
+     *  variable env_var - individual path components are separated by PATH_SEP 
+     */
+    path = strtok (env_path, PATH_SEP);
+    do 
+    {
+        if (ext_filename) 
+            Free (ext_filename);
+        ext_filename =  Calloc (strlen (path) + strlen (filename) + 2,
+                                sizeof (char));
+        strcpy (ext_filename, path); 
+        if (*(ext_filename + strlen (ext_filename) - 1) != '/')
+            strcat (ext_filename, "/");
+        strcat (ext_filename, filename);
+        fp = fopen (ext_filename, mode == READ_ACCESS ? read_mode : write_mode);
+    }
+    while (fp == NULL && (path = strtok (NULL, PATH_SEP)) != NULL);
+
+    Free (env_path);
+   
+    return fp;
+}
+
+bitfile_t *
+open_bitfile (const char *filename, const char *env_var, openmode_e mode)
+/*
+ *  Bitfile constructor:
+ *  Try to open file 'filename' for buffered bit oriented access with mode
+ *  'mode'. Scan the current directory first and then cycle through the path
+ *  given in the environment variable 'env_var', if set.
+ *
+ *  Return value:
+ *  Pointer to open bitfile on success,
+ *      otherwise the program is terminated.
+ */
+{
+    bitfile_t *bitfile = Calloc (1, sizeof (bitfile_t));
+   
+    bitfile->file = open_file (filename, env_var, mode);
+
+    if (bitfile->file == NULL)
+        file_error (filename);
+
+    if (mode == READ_ACCESS)
+    {
+        bitfile->bytepos  = 0;
+        bitfile->bitpos   = 0;
+        bitfile->mode     = mode;
+        bitfile->filename = filename ? strdup (filename) : strdup ("(stdin)");
+    }
+    else if (mode == WRITE_ACCESS)
+    {
+        bitfile->bytepos  = BUFFER_SIZE - 1;
+        bitfile->bitpos   = 8;
+        bitfile->mode     = mode;
+        bitfile->filename = filename ? strdup (filename) : strdup ("(stdout)");
+    }
+    else
+        error ("Unknow file access mode '%d'.", mode);
+   
+    bitfile->bits_processed = 0;
+    bitfile->buffer         = Calloc (BUFFER_SIZE, sizeof (byte_t));
+    bitfile->ptr            = bitfile->buffer;
+
+    return bitfile;
+}
+
+bool_t
+get_bit (bitfile_t *bitfile)
+/*
+ *  Get one bit from the given stream 'bitfile'.
+ *
+ *  Return value:
+ *   1  H bit
+ *   0  L bit
+ *
+ *  Side effects:
+ *  Buffer of 'bitfile' is modified accordingly.
+ */
+{
+    assert (bitfile);
+   
+    if (!bitfile->bitpos--)      /* use next byte ? */
+    {
+        bitfile->ptr++;
+        if (!bitfile->bytepos--)      /* no more bytes left in the buffer? */
+        {
+            /*
+             *  Fill buffer with new data
+             */
+            int bytes = fread (bitfile->buffer, sizeof (byte_t),
+                               BUFFER_SIZE, bitfile->file) - 1;
+            if (bytes < 0)         /* Error or EOF */
+                error ("Can't read next bit from bitfile %s.", bitfile->filename);
+            else
+                bitfile->bytepos = bytes;
+
+            bitfile->ptr = bitfile->buffer;
+        }
+        bitfile->bitpos = 7;
+    }
+
+    bitfile->bits_processed++;
+
+    return *bitfile->ptr & mask [bitfile->bitpos] ? 1 : 0;
+}
+
+unsigned int
+get_bits (bitfile_t *bitfile, unsigned bits)
+/*
+ *  Get #'bits' bits from the given stream 'bitfile'.
+ *
+ *  Return value:
+ *  composed integer value
+ *
+ *  Side effects:
+ *  Buffer of 'bitfile' is modified.
+ */
+{
+    unsigned value = 0;          /* input value */
+   
+    while (bits--)
+        value = (unsigned) (value << 1) | get_bit (bitfile);
+
+    return value;
+}
+
+void
+put_bit (bitfile_t *bitfile, unsigned value)
+/*     
+ *  Put the bit 'value' to the bitfile buffer.
+ *  The buffer is written to the file 'bitfile->file' if the number of
+ *  buffer bytes exceeds 'BUFFER_SIZE'.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *  Buffer of 'bitfile' is modified.
+ */
+{
+    assert (bitfile);
+   
+    if (!bitfile->bitpos--)      /* use next byte ? */
+    {
+        bitfile->ptr++;
+        if (!bitfile->bytepos--)      /* no more bytes left ? */
+        {
+            /*
+             *  Write buffer to disk and fill buffer with zeros
+             */
+            if (fwrite (bitfile->buffer, sizeof (byte_t),
+                        BUFFER_SIZE, bitfile->file) != BUFFER_SIZE)
+                error ("Can't write next bit of bitfile %s!", bitfile->filename);
+            memset (bitfile->buffer, 0, BUFFER_SIZE);
+            bitfile->bytepos = BUFFER_SIZE - 1;
+            bitfile->ptr     = bitfile->buffer;
+        }
+        bitfile->bitpos = 7;
+    }
+   
+    if (value)
+        *bitfile->ptr |= mask [bitfile->bitpos];
+
+    bitfile->bits_processed++;
+}
+
+void
+put_bits (bitfile_t *bitfile, unsigned value, unsigned bits)
+/*     
+ *  Put #'bits' bits of integer 'value' to the bitfile buffer 'bitfile'.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *  Buffer of 'bitfile' is modified.
+ */
+{
+    while (bits--)
+        put_bit (bitfile, value & mask [bits]);
+}
+
+void
+close_bitfile (bitfile_t *bitfile)
+/*
+ *  Bitfile destructor:
+ *  Close 'bitfile', if 'bitfile->mode' == WRITE_ACCESS write bit buffer
+ *  to disk. 
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *  Structure 'bitfile' is discarded.
+ */
+{
+    assert (bitfile);
+   
+    if (bitfile->mode == WRITE_ACCESS)
+    {
+        unsigned bytes = fwrite (bitfile->buffer, sizeof (byte_t),
+                                 BUFFER_SIZE - bitfile->bytepos, bitfile->file);
+        if (bytes != BUFFER_SIZE - bitfile->bytepos)
+            error ("Can't write remaining %d bytes of bitfile "
+                   "(only %d bytes written)!",
+                   BUFFER_SIZE - bitfile->bytepos, bytes);
+    }
+    fclose (bitfile->file);
+    Free (bitfile->buffer);
+    Free (bitfile->filename);
+    Free (bitfile);
+}
+
+unsigned
+bits_processed (const bitfile_t *bitfile)
+/*
+ *  Return value:
+ *  Number of bits processed up to now
+ */
+{
+    return bitfile->bits_processed;
+}
diff --git a/converter/other/fiasco/lib/bit-io.h b/converter/other/fiasco/lib/bit-io.h
new file mode 100644
index 00000000..d37cc47c
--- /dev/null
+++ b/converter/other/fiasco/lib/bit-io.h
@@ -0,0 +1,58 @@
+/*
+ *  bit-io.h
+ *
+ *  Written by:		Ullrich Hafner
+ *
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:49:37 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _BIT_IO_H
+#define _BIT_IO_H
+
+#include <stdio.h>
+#include "types.h"
+
+#define OUTPUT_BYTE_ALIGN(bfile) while ((bfile)->bitpos) put_bit (bfile, 0);
+#define INPUT_BYTE_ALIGN(bfile)  while ((bfile)->bitpos) get_bit (bfile);
+
+typedef enum {READ_ACCESS, WRITE_ACCESS} openmode_e;
+
+typedef struct bitfile
+{
+   FILE	      *file;			/* associated filepointer */
+   char	      *filename;		/* corresponding filename */
+   byte_t     *buffer;			/* stream buffer */
+   byte_t     *ptr;			/* pointer to current buffer pos */
+   unsigned    bytepos;			/* current I/O byte */
+   unsigned    bitpos;			/* current I/O bit */
+   unsigned    bits_processed;		/* number of bits already processed */
+   openmode_e  mode;			/* access mode */
+} bitfile_t;
+
+FILE *
+open_file (const char *filename, const char *env_var, openmode_e mode);
+bitfile_t *
+open_bitfile (const char *filename, const char *env_var, openmode_e mode);
+void
+put_bit (bitfile_t *bitfile, unsigned value);
+void
+put_bits (bitfile_t *bitfile, unsigned value, unsigned bits);
+bool_t
+get_bit (bitfile_t *bitfile);
+unsigned 
+get_bits (bitfile_t *bitfile, unsigned bits);
+void
+close_bitfile (bitfile_t *bitfile);
+unsigned
+bits_processed (const bitfile_t *bitfile);
+
+#endif /* not _BIT_IO_H */
+
diff --git a/converter/other/fiasco/lib/dither.c b/converter/other/fiasco/lib/dither.c
new file mode 100644
index 00000000..c7f9ebab
--- /dev/null
+++ b/converter/other/fiasco/lib/dither.c
@@ -0,0 +1,1892 @@
+/*
+ *  dither.c:		Various dithering routines 	
+ *
+ *  Adapted by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ * Copyright (c) 1995 Erik Corry
+ * 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 ERIK CORRY 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 ERIK CORRY HAS BEEN ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * ERIK CORRY 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 ERIK CORRY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
+ * UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*
+ *  $Date: 2000/11/27 20:22:51 $
+ *  $Author: hafner $
+ *  $Revision: 5.3 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#if HAVE_STRING_H
+#	include <string.h>
+#else /* not HAVE_STRING_H */
+#	include <strings.h>
+#endif /* not HAVE_STRING_H */
+#if STDC_HEADERS
+#	include <stdlib.h>
+#endif /* not STDC_HEADERS */
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "fiasco.h"
+#include "image.h"
+#include "misc.h"
+#include "dither.h"
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static int 
+display_16_bit (const struct fiasco_renderer *this, unsigned char *ximage,
+		const fiasco_image_t *fiasco_image);
+static int 
+display_24_bit_bgr (const struct fiasco_renderer *this, unsigned char *ximage,
+		    const fiasco_image_t *fiasco_image);
+static int 
+display_24_bit_rgb (const struct fiasco_renderer *this, unsigned char *ximage,
+		    const fiasco_image_t *fiasco_image);
+static int 
+display_32_bit (const struct fiasco_renderer *this, unsigned char *ximage,
+		const fiasco_image_t *fiasco_image);
+static int
+free_bits_at_bottom (unsigned long a);
+static int
+free_bits_at_top (unsigned long a);
+static int
+number_of_bits_set (unsigned long a);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+fiasco_renderer_t *
+fiasco_renderer_new (unsigned long red_mask, unsigned long green_mask,
+		     unsigned long blue_mask, unsigned bpp,
+		     int double_resolution)
+/*
+ *  FIASCO renderer constructor.
+ *  Allocate memory for the FIASCO renderer structure and
+ *  initialize values.
+ *  `red_mask', `green_mask', and `blue_mask' are the corresponding masks
+ *  of the X11R6 XImage structure. 
+ *  `bpp' gives the depth of the image in bits per pixel (16, 24, or 32).
+ *  If `double_resolution' is not 0 the the image width and height is doubled.
+ *  (fast pixel doubling, no interpolation!)
+ *
+ *  Return value:
+ *	pointer to the new structure or NULL on error
+ */
+{
+   if (bpp != 16 && bpp != 24 && bpp !=32)
+   {
+      set_error (_("Rendering depth of XImage must be 16, 24, or 32 bpp."));
+      return NULL;
+   }
+   else
+   {
+      fiasco_renderer_t  *render    = calloc (1, sizeof (fiasco_renderer_t));
+      renderer_private_t *private   = calloc (1, sizeof (renderer_private_t));
+      bool_t 	       	  twopixels = (bpp == 16 && double_resolution);
+      int 		  crval, cbval, i; /* counter */
+
+      if (!render || !private)
+      {
+	 set_error (_("Out of memory."));
+	 return NULL;
+      }
+      switch (bpp)
+      {
+	 case 16:
+	    render->render = display_16_bit;
+	    break;
+	 case 24:
+	    if (red_mask > green_mask)
+	       render->render = display_24_bit_rgb;
+	    else
+	       render->render = display_24_bit_bgr;
+	    break;
+	 case 32:
+	    render->render = display_32_bit;
+	    break;
+	 default:
+	    break;			/* does not happen */
+      }
+      render->private = private;
+      render->delete  = fiasco_renderer_delete;
+
+      private->double_resolution = double_resolution;
+      private->Cr_r_tab = calloc (256 + 2 * 1024, sizeof (int));
+      private->Cr_g_tab = calloc (256 + 2 * 1024, sizeof (int));
+      private->Cb_g_tab = calloc (256 + 2 * 1024, sizeof (int));
+      private->Cb_b_tab = calloc (256 + 2 * 1024, sizeof (int));
+
+      if (!private->Cr_r_tab || !private->Cr_g_tab
+	  || !private->Cb_b_tab || !private->Cb_g_tab)
+      {
+	 set_error (_("Out of memory."));
+	 return NULL;
+      }
+      
+      for (i = 1024; i < 1024 + 256; i++)
+      {
+	 cbval = crval  = i - 128 - 1024;
+
+	 private->Cr_r_tab [i] =  1.4022 * crval + 0.5;
+	 private->Cr_g_tab [i] = -0.7145 * crval + 0.5;
+	 private->Cb_g_tab [i] = -0.3456 * cbval + 0.5; 
+	 private->Cb_b_tab [i] =  1.7710 * cbval + 0.5;
+      }
+      for (i = 0; i < 1024; i++)
+      {
+	 private->Cr_r_tab [i] = private->Cr_r_tab [1024];
+	 private->Cr_g_tab [i] = private->Cr_g_tab [1024];
+	 private->Cb_g_tab [i] = private->Cb_g_tab [1024]; 
+	 private->Cb_b_tab [i] = private->Cb_b_tab [1024];
+      }
+      for (i = 1024 + 256; i < 2048 + 256; i++)
+      {
+	 private->Cr_r_tab [i] = private->Cr_r_tab [1024 + 255];
+	 private->Cr_g_tab [i] = private->Cr_g_tab [1024 + 255];
+	 private->Cb_g_tab [i] = private->Cb_g_tab [1024 + 255]; 
+	 private->Cb_b_tab [i] = private->Cb_b_tab [1024 + 255];
+      }
+
+      private->Cr_r_tab += 1024 + 128;
+      private->Cr_g_tab += 1024 + 128;
+      private->Cb_g_tab += 1024 + 128;
+      private->Cb_b_tab += 1024 + 128;
+   
+      /* 
+       *  Set up entries 0-255 in rgb-to-pixel value tables.
+       */
+      private->r_table = calloc (256 + 2 * 1024, sizeof (unsigned int));
+      private->g_table = calloc (256 + 2 * 1024, sizeof (unsigned int));
+      private->b_table = calloc (256 + 2 * 1024, sizeof (unsigned int));
+      private->y_table = calloc (256 + 2 * 1024, sizeof (unsigned int));
+
+      if (!private->r_table || !private->g_table
+	  || !private->b_table || !private->y_table)
+      {
+	 set_error (_("Out of memory."));
+	 return NULL;
+      }
+      
+      for (i = 0; i < 256; i++)
+      {
+	 private->r_table [i + 1024]
+	    = i >> (8 - number_of_bits_set(red_mask));
+	 private->r_table [i + 1024]
+	    <<= free_bits_at_bottom (red_mask);
+	 private->g_table [i + 1024]
+	    = i >> (8 - number_of_bits_set (green_mask));
+	 private->g_table [i + 1024]
+	    <<= free_bits_at_bottom (green_mask);
+	 private->b_table [i + 1024]
+	    <<= free_bits_at_bottom (blue_mask);
+	 private->b_table [i + 1024]
+	    = i >> (8 - number_of_bits_set (blue_mask));
+	 if (twopixels)
+	 {
+	    private->r_table [i + 1024] = ((private->r_table [i + 1024] << 16)
+					   | private->r_table [i + 1024]);
+	    private->g_table [i + 1024] = ((private->g_table [i + 1024] << 16)
+					   | private->g_table [i + 1024]);
+	    private->b_table [i + 1024] = ((private->b_table [i + 1024] << 16)
+					   | private->b_table [i + 1024]);
+	 }
+	 private->y_table [i + 1024] = (private->r_table [i + 1024]
+					| private->g_table [i + 1024]
+					| private->b_table [i + 1024]);
+      }
+
+      /*
+       * Spread out the values we have to the rest of the array so that
+       * we do not need to check for overflow.
+       */
+      for (i = 0; i < 1024; i++)
+      {
+	 private->r_table [i]              = private->r_table [1024];
+	 private->r_table [i + 1024 + 256] = private->r_table [1024 + 255];
+	 private->g_table [i]              = private->g_table [1024];
+	 private->g_table [i + 1024 + 256] = private->g_table [1024 + 255];
+	 private->b_table [i]              = private->b_table [1024];
+	 private->b_table [i + 1024 + 256] = private->b_table [1024 + 255];
+	 private->y_table [i]              = private->y_table [1024];
+	 private->y_table [i + 1024 + 256] = private->y_table [1024 + 255];
+      }
+
+      private->r_table += 1024;
+      private->g_table += 1024;
+      private->b_table += 1024;
+      private->y_table += 1024 + 128;
+
+      return render;
+   }
+   
+}
+
+void
+fiasco_renderer_delete (fiasco_renderer_t *renderer)
+/*
+ *  FIASCO renderer destructor:
+ *  Free memory of 'renderer' structure.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	structure 'renderer' is discarded.
+ */
+{
+   if (!renderer)
+      return;
+   else
+   {
+      renderer_private_t *private = (renderer_private_t *) renderer->private;
+
+      Free (private->Cr_g_tab - (1024 + 128));
+      Free (private->Cr_r_tab - (1024 + 128));
+      Free (private->Cb_g_tab - (1024 + 128));
+      Free (private->Cb_b_tab - (1024 + 128));
+      Free (private->r_table - 1024);
+      Free (private->g_table - 1024);
+      Free (private->b_table - 1024);
+      Free (private->y_table - (1024 + 128));
+
+      Free (private);
+      Free (renderer);
+   }
+}
+
+int
+fiasco_renderer_render (const fiasco_renderer_t *renderer,
+			unsigned char *ximage,
+			const fiasco_image_t *fiasco_image)
+{
+   if (!renderer)
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "renderer");
+      return 0;
+   }
+   else
+      return renderer->render (renderer, ximage, fiasco_image);
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+/*
+ *  Erik Corry's multi-byte dither routines.
+ *
+ *  The basic idea is that the Init generates all the necessary
+ *  tables.  The tables incorporate the information about the layout
+ *  of pixels in the XImage, so that it should be able to cope with
+ *  16-bit 24-bit (non-packed) and 32-bit (10-11 bits per
+ *  color!) screens.  At present it cannot cope with 24-bit packed
+ *  mode, since this involves getting down to byte level again. It is
+ *  assumed that the bits for each color are contiguous in the
+ *  longword.
+ * 
+ *  Writing to memory is done in shorts or ints. (Unfortunately, short
+ *  is not very fast on Alpha, so there is room for improvement
+ *  here). There is no dither time check for overflow - instead the
+ *  tables have slack at each end. This is likely to be faster than an
+ *  'if' test as many modern architectures are really bad at
+ *  ifs. Potentially, each '&&' causes a pipeline flush!
+ *
+ *  There is no shifting and fixed point arithmetic, as I really doubt
+ *  you can see the difference, and it costs. This may be just my
+ *  bias, since I heard that Intel is really bad at shifting.
+ */
+
+static int
+number_of_bits_set (unsigned long a)
+/*
+ *  How many 1 bits are there in the longword.
+ *  Low performance, do not call often.
+ */
+{
+   if (!a)
+      return 0;
+   if (a & 1)
+      return 1 + number_of_bits_set (a >> 1);
+   else
+      return (number_of_bits_set (a >> 1));
+}
+
+static int
+free_bits_at_top (unsigned long a)
+/*
+ *  How many 0 bits are there at most significant end of longword.
+ *  Low performance, do not call often.
+ */
+{
+   if(!a)				/* assume char is 8 bits */
+      return sizeof (unsigned long) * 8;
+   else if (((long) a) < 0l)		/* assume twos complement */
+      return 0;
+   else
+      return 1 + free_bits_at_top ( a << 1);
+}
+
+static int
+free_bits_at_bottom (unsigned long a)
+/*
+ *  How many 0 bits are there at least significant end of longword.
+ *  Low performance, do not call often.
+ */
+{
+   /* assume char is 8 bits */
+   if (!a)
+      return sizeof (unsigned long) * 8;
+   else if(((long) a) & 1l)
+      return 0;
+   else
+      return 1 + free_bits_at_bottom ( a >> 1);
+}
+
+static int 
+display_16_bit (const struct fiasco_renderer *this, unsigned char *ximage,
+		const fiasco_image_t *fiasco_image)
+/*
+ *  Convert 'image' to 16 bit color bitmap.
+ *  If 'double_resolution' is true then double image size in both directions.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'out[]'	is filled with dithered image
+ */
+{
+   const image_t      *image;
+   renderer_private_t *private;
+   byte_t	      *out;
+   
+   if (!this)
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "this");
+      return 0;
+   }
+   if (!ximage)
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "ximage");
+      return 0;
+   }
+   if (!fiasco_image)
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "fiasco_image");
+      return 0;
+   }
+
+   out 	   = (byte_t *) ximage;
+   image   = cast_image ((fiasco_image_t *) fiasco_image);
+   if (!image)
+      return 0;
+   private = (renderer_private_t *) this->private;
+   
+   if (image->color)
+   {
+      word_t 	   *cbptr, *crptr;	/* pointer to chroma bands */
+      word_t 	   *yptr;		/* pointers to lumincance band */
+      int     	    yval, crval, cbval;	/* pixel value in YCbCr color space */
+      int     	    R, G, B;		/* pixel value in RGB color space */
+      int     	    n;			/* pixel counter */
+      int     	    x, y;		/* pixel coordinates */
+      int 	   *Cr_r_tab, *Cr_g_tab, *Cb_g_tab, *Cb_b_tab;
+      unsigned int *r_table, *g_table, *b_table;
+
+      Cr_g_tab = private->Cr_g_tab;
+      Cr_r_tab = private->Cr_r_tab;
+      Cb_b_tab = private->Cb_b_tab;
+      Cb_g_tab = private->Cb_g_tab;
+      r_table  = private->r_table;
+      g_table  = private->g_table;
+      b_table  = private->b_table;
+      yptr     = image->pixels [Y];
+      cbptr    = image->pixels [Cb];
+      crptr    = image->pixels [Cr];
+
+      if (image->format == FORMAT_4_2_0)
+      {
+	 u_word_t *dst, *dst2;		/* pointers to dithered pixels */
+	 word_t	  *yptr2;		/* pointers to lumincance band */
+
+	 if (private->double_resolution)
+	 {
+	    yptr2 = yptr + image->width;
+	    dst   = (u_word_t *) out;
+	    dst2  = dst + 4 * image->width;
+	    for (y = image->height / 2; y; y--)
+	    {
+	       for (x = image->width / 2; x; x--)
+	       {
+#ifdef HAVE_SIGNED_SHIFT
+		  crval = *crptr++ >> 4;
+		  cbval = *cbptr++ >> 4;
+		  yval  = (*yptr++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  crval = *crptr++ / 16;
+		  cbval = *cbptr++ / 16;
+		  yval  = *yptr++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  R = yval + Cr_r_tab [crval];
+		  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+		  B = yval + Cb_b_tab [cbval];
+		  *dst++ = r_table [R] | g_table [G] | b_table [B];
+		  *dst++ = r_table [R] | g_table [G] | b_table [B];
+	       
+#ifdef HAVE_SIGNED_SHIFT
+		  yval  = (*yptr++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval  = *yptr++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  R = yval + Cr_r_tab [crval];
+		  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+		  B = yval + Cb_b_tab [cbval];
+		  *dst++ = r_table [R] | g_table [G] | b_table [B];
+		  *dst++ = r_table [R] | g_table [G] | b_table [B];
+
+#ifdef HAVE_SIGNED_SHIFT
+		  yval  = (*yptr2++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval  = *yptr2++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  R = yval + Cr_r_tab [crval];
+		  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+		  B = yval + Cb_b_tab [cbval];
+		  *dst2++ = r_table [R] | g_table [G] | b_table [B];
+		  *dst2++ = r_table [R] | g_table [G] | b_table [B];
+
+#ifdef HAVE_SIGNED_SHIFT
+		  yval  = (*yptr2++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval  = *yptr2++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  R = yval + Cr_r_tab [crval];
+		  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+		  B = yval + Cb_b_tab [cbval];
+		  *dst2++ = r_table [R] | g_table [G] | b_table [B];
+		  *dst2++ = r_table [R] | g_table [G] | b_table [B];
+	       }
+	       memcpy (dst, dst - 2 * image->width,
+		       2 * image->width * sizeof (u_word_t));
+	       memcpy (dst2, dst2 - 2 * image->width,
+		       2 * image->width * sizeof (u_word_t));
+	       yptr  += image->width;
+	       yptr2 += image->width;
+	       dst   += 3 * image->width * 2;
+	       dst2  += 3 * image->width * 2;
+	    }
+	 }
+	 else
+	 {
+	    yptr2 = yptr + image->width;
+	    dst  = (u_word_t *) out;
+	    dst2 = dst + image->width;
+	    
+	    for (y = image->height / 2; y; y--)
+	    {
+	       for (x = image->width / 2; x; x--)
+	       {
+#ifdef HAVE_SIGNED_SHIFT
+		  crval = *crptr++ >> 4;
+		  cbval = *cbptr++ >> 4;
+		  yval  = (*yptr++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  crval = *crptr++ / 16;
+		  cbval = *cbptr++ / 16;
+		  yval  = *yptr++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  R = yval + Cr_r_tab [crval];
+		  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+		  B = yval + Cb_b_tab [cbval];
+		  *dst++ = r_table [R] | g_table [G] | b_table [B];
+	       
+#ifdef HAVE_SIGNED_SHIFT
+		  yval  = (*yptr++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval  = *yptr++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  R = yval + Cr_r_tab [crval];
+		  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+		  B = yval + Cb_b_tab [cbval];
+		  *dst++ = r_table [R] | g_table [G] | b_table [B];
+
+#ifdef HAVE_SIGNED_SHIFT
+		  yval  = (*yptr2++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval  = *yptr2++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  R = yval + Cr_r_tab [crval];
+		  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+		  B = yval + Cb_b_tab [cbval];
+		  *dst2++ = r_table [R] | g_table [G] | b_table [B];
+
+#ifdef HAVE_SIGNED_SHIFT
+		  yval  = (*yptr2++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval  = *yptr2++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  R = yval + Cr_r_tab [crval];
+		  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+		  B = yval + Cb_b_tab [cbval];
+		  *dst2++ = r_table [R] | g_table [G] | b_table [B];
+	       }
+	       yptr  += image->width;
+	       yptr2 += image->width;
+	       dst   += image->width;
+	       dst2  += image->width;
+	    }
+	 }
+      }
+      else				/* 4:4:4 format */
+      {
+	 if (private->double_resolution)
+	 {
+	    unsigned int *dst;		/* pointer to dithered pixels */
+	    
+	    dst  = (unsigned int *) out;
+	    
+	    for (y = image->height; y; y--)
+	    {
+	       for (x = image->width; x; x--)
+	       {
+#ifdef HAVE_SIGNED_SHIFT
+		  crval = *crptr++ >> 4;
+		  cbval = *cbptr++ >> 4;
+		  yval  = (*yptr++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  crval = *crptr++ / 16;
+		  cbval = *cbptr++ / 16;
+		  yval  = *yptr++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  R = yval + Cr_r_tab [crval];
+		  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+		  B = yval + Cb_b_tab [cbval];
+		  
+		  *dst++ = r_table [R] | g_table [G] | b_table [B];
+	       }
+	       memcpy (dst, dst - image->width,
+		       image->width * sizeof (unsigned int));
+	       dst += image->width;
+	    }
+	 }
+	 else
+	 {
+	    u_word_t *dst;		/* pointer to dithered pixels */
+
+	    dst  = (u_word_t *) out;
+	    
+	    for (n = image->width * image->height; n; n--)
+	    {
+#ifdef HAVE_SIGNED_SHIFT
+	       crval = *crptr++ >> 4;
+	       cbval = *cbptr++ >> 4;
+	       yval  = (*yptr++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+	       crval = *crptr++ / 16;
+	       cbval = *cbptr++ / 16;
+	       yval  = *yptr++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+	       R = yval + Cr_r_tab [crval];
+	       G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+	       B = yval + Cb_b_tab [cbval];
+
+	       *dst++ = r_table [R] | g_table [G] | b_table [B];
+	    }
+	 }
+      }
+   }
+   else
+   {
+      unsigned int *dst;		/* pointer to dithered pixels */
+      word_t	   *src;		/* current pixel of frame */
+      unsigned int *y_table;
+
+      y_table = private->y_table;
+      dst     = (unsigned int *) out;
+      src     = image->pixels [GRAY];
+      
+      if (private->double_resolution)
+      {
+	 int x, y;			/* pixel coordinates */
+  	    
+	 for (y = image->height; y; y--)
+	 {
+	    for (x = image->width; x; x--)
+	    {
+	       int value;
+	       
+#ifdef HAVE_SIGNED_SHIFT
+	       value = y_table [*src++ >> 4];
+#else /* not HAVE_SIGNED_SHIFT */
+	       value = y_table [*src++ / 16];
+#endif /* not HAVE_SIGNED_SHIFT */
+	       *dst++ = (value << 16) | value;
+	    }
+	    
+	    memcpy (dst, dst - image->width,
+		    image->width * sizeof (unsigned int));
+	    dst += image->width;
+	 }
+      }
+      else
+      {
+	 int n;				/* pixel counter */
+	 
+	 for (n = image->width * image->height / 2; n; n--, src += 2)
+#ifdef HAVE_SIGNED_SHIFT
+#	ifndef WORDS_BIGENDIAN
+	    *dst++ = (y_table [src [1] >> 4] << 16) | y_table [src [0] >> 4];
+#	else /* not WORDS_BIGENDIAN  */
+	    *dst++ = (y_table [src [0] >> 4] << 16) | y_table [src [1] >> 4];
+#	endif
+#else /* not HAVE_SIGNED_SHIFT */
+#	ifndef WORDS_BIGENDIAN
+	    *dst++ = (y_table [src [1] / 16] << 16) | y_table [src [0] / 16];
+#	else /* not WORDS_BIGENDIAN  */
+	    *dst++ = (y_table [src [0] / 16] << 16) | y_table [src [1] / 16];
+#	endif
+#endif /* not HAVE_SIGNED_SHIFT */
+      }
+   }
+
+   return 1;
+}
+
+static int 
+display_24_bit_bgr (const struct fiasco_renderer *this, unsigned char *ximage,
+		    const fiasco_image_t *fiasco_image)
+/*
+ *  Convert 'image' to 16 bit color bitmap.
+ *  If 'double_resolution' is true then double image size in both directions.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'out[]'	is filled with dithered image
+ */
+{
+   unsigned 	      *gray_clip = init_clipping ();
+   const image_t      *image;
+   renderer_private_t *private;
+   byte_t	      *out;
+
+   if (!gray_clip)
+      return 0;
+   if (!this)
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "this");
+      return 0;
+   }
+   if (!ximage)
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "ximage");
+      return 0;
+   }
+   if (!fiasco_image)
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "fiasco_image");
+      return 0;
+   }
+
+   out 	   = (byte_t *) ximage;
+   image   = cast_image ((fiasco_image_t *) fiasco_image);
+   if (!image)
+      return 0;
+   private = (renderer_private_t *) this->private;
+   
+   if (image->color)
+   {
+      word_t 	   *cbptr, *crptr;	/* pointer to chroma bands */
+      word_t 	   *yptr;		/* pointers to lumincance band */
+      int 	   *Cr_r_tab, *Cr_g_tab, *Cb_g_tab, *Cb_b_tab;
+      unsigned int *r_table, *g_table, *b_table;
+
+      Cr_g_tab = private->Cr_g_tab;
+      Cr_r_tab = private->Cr_r_tab;
+      Cb_b_tab = private->Cb_b_tab;
+      Cb_g_tab = private->Cb_g_tab;
+      r_table  = private->r_table;
+      g_table  = private->g_table;
+      b_table  = private->b_table;
+      yptr     = image->pixels [Y];
+      cbptr    = image->pixels [Cb];
+      crptr    = image->pixels [Cr];
+
+      if (image->format == FORMAT_4_2_0)
+      {
+	 if (private->double_resolution)
+	 {
+	    int		  yval1;	/* lumincance pixel */
+	    int 	  crval1, cbval1; /* chroma pixels */
+	    int		  yval2;	/* pixel in YCbCr color space */
+	    unsigned int  R1, G1, B1;	/* pixel in RGB color space */
+	    unsigned int  R2, G2, B2;	/* pixel in RGB color space */
+	    int		  x, y;		/* pixel counter */
+	    unsigned int *dst;		/* pointer to dithered pixels */
+	    unsigned int *dst2;		/* pointers to dithered pixels */
+	    word_t	 *yptr2;	/* pointers to lumincance band */
+	    
+	    dst   = (unsigned int *) out;
+	    dst2  = dst + (image->width >> 1) * 3 * 2;
+	    yptr2 = yptr + image->width;
+	    
+	    for (y = image->height >> 1; y; y--)
+	    {
+	       for (x = image->width >> 1; x; x--)
+	       {
+#ifdef HAVE_SIGNED_SHIFT
+		  yval1  = (*yptr++ >> 4) + 128;
+		  yval2  = (*yptr++ >> 4) + 128;
+		  crval1 = *crptr++ >> 4;
+		  cbval1 = *cbptr++ >> 4;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval1  = *yptr++  / 16 + 128;
+		  yval2  = *yptr++  / 16 + 128;
+		  crval1 = *crptr++ / 16;
+		  cbval1 = *cbptr++ / 16;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+		  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
+		  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
+				 + Cb_g_tab [cbval1]];
+		  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
+		  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
+		  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
+				 + Cb_g_tab [cbval1]];
+		  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];
+
+		  *dst++ = B1 | (G1 << 8) | (R1 << 16) | (B1 << 24);
+		  *dst++ = G1 | (R1 << 8) | (B2 << 16) | (G2 << 24);
+		  *dst++ = R2 | (B2 << 8) | (G2 << 16) | (R2 << 24);
+
+#ifdef HAVE_SIGNED_SHIFT
+		  yval1  = (*yptr2++ >> 4) + 128;
+		  yval2  = (*yptr2++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval1  = *yptr2++  / 16 + 128;
+		  yval2  = *yptr2++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+		  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
+		  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
+				 + Cb_g_tab [cbval1]];
+		  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
+		  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
+		  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
+				 + Cb_g_tab [cbval1]];
+		  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];
+
+		  *dst2++ = B1 | (G1 << 8) | (R1 << 16) | (B1 << 24);
+		  *dst2++ = G1 | (R1 << 8) | (B2 << 16) | (G2 << 24);
+		  *dst2++ = R2 | (B2 << 8) | (G2 << 16) | (R2 << 24);
+	       }
+	       memcpy (dst, dst - (image->width >> 1) * 3,
+		       (image->width >> 1) * 3 * sizeof (unsigned int));
+	       memcpy (dst2, dst2 - (image->width >> 1) * 3, 
+		       (image->width >> 1) * 3 * sizeof (unsigned int));
+	       dst   += (image->width >> 1) * 3 * 3;
+	       dst2  += (image->width >> 1) * 3 * 3;
+	       yptr  += image->width;
+	       yptr2 += image->width;
+	    }
+	 }
+	 else
+	 {
+	    int		  yval1;	/* lumincance pixel */
+	    int 	  crval1, cbval1; /* chroma pixels */
+	    int		  yval2;	/* pixel in YCbCr color space */
+	    unsigned int  R1, G1, B1;	/* pixel in RGB color space */
+	    unsigned int  R2, G2, B2;	/* pixel in RGB color space */
+	    int		  x, y;		/* pixel counter */
+	    unsigned int *dst;		/* pointer to dithered pixels */
+	    unsigned int *dst2;		/* pointers to dithered pixels */
+	    word_t	 *yptr2;	/* pointers to lumincance band */
+	    
+	    dst   = (unsigned int *) out;
+	    dst2  = dst + (image->width >> 2) * 3;
+	    yptr2 = yptr + image->width;
+	    
+	    for (y = image->height >> 1; y; y--)
+	    {
+	       for (x = image->width >> 2; x; x--)
+	       {
+#ifdef HAVE_SIGNED_SHIFT
+		  yval1  = (*yptr++ >> 4) + 128;
+		  yval2  = (*yptr++ >> 4) + 128;
+		  crval1 = *crptr++ >> 4;
+		  cbval1 = *cbptr++ >> 4;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval1  = *yptr++  / 16 + 128;
+		  yval2  = *yptr++  / 16 + 128;
+		  crval1 = *crptr++ / 16;
+		  cbval1 = *cbptr++ / 16;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+		  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
+		  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
+				 + Cb_g_tab [cbval1]];
+		  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
+		  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
+		  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
+				 + Cb_g_tab [cbval1]];
+		  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];
+
+		  *dst++ = B1 | (G1 << 8) | (R1 << 16) | (B2 << 24);
+		  *dst   = G2 | (R2 << 8);
+
+#ifdef HAVE_SIGNED_SHIFT
+		  yval1  = (*yptr2++ >> 4) + 128;
+		  yval2  = (*yptr2++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval1  = *yptr2++  / 16 + 128;
+		  yval2  = *yptr2++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+		  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
+		  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
+				 + Cb_g_tab [cbval1]];
+		  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
+		  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
+		  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
+				 + Cb_g_tab [cbval1]];
+		  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];
+
+		  *dst2++ = B1 | (G1 << 8) | (R1 << 16) | (B2 << 24);
+		  *dst2   = G2 | (R2 << 8);
+	       
+#ifdef HAVE_SIGNED_SHIFT
+		  yval1  = (*yptr++ >> 4) + 128;
+		  yval2  = (*yptr++ >> 4) + 128;
+		  crval1 = *crptr++ >> 4;
+		  cbval1 = *cbptr++ >> 4;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval1  = *yptr++  / 16 + 128;
+		  yval2  = *yptr++  / 16 + 128;
+		  crval1 = *crptr++ / 16;
+		  crval2 = *crptr++ / 16;
+		  cbval1 = *cbptr++ / 16;
+		  cbval2 = *cbptr++ / 16;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+		  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
+		  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
+				 + Cb_g_tab [cbval1]];
+		  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
+		  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
+		  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
+				 + Cb_g_tab [cbval1]];
+		  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];
+
+		  *dst++ |= (B1 << 16) | (G1 << 24);
+		  *dst++ = R1 | (B2 << 8) | (G2 << 16) | (R2 << 24);
+
+#ifdef HAVE_SIGNED_SHIFT
+		  yval1  = (*yptr2++ >> 4) + 128;
+		  yval2  = (*yptr2++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval1  = *yptr2++  / 16 + 128;
+		  yval2  = *yptr2++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+		  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
+		  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
+				 + Cb_g_tab [cbval1]];
+		  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
+		  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
+		  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
+				 + Cb_g_tab [cbval1]];
+		  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];
+
+		  *dst2++ |= (B1 << 16) | (G1 << 24);
+		  *dst2++ = R1 | (B2 << 8) | (G2 << 16) | (R2 << 24);
+	       }
+	       dst   += (image->width >> 2) * 3;
+	       dst2  += (image->width >> 2) * 3;
+	       yptr  += image->width;
+	       yptr2 += image->width;
+	    }
+	 }
+      }
+      else				/* 4:4:4 format */
+      {
+	 if (private->double_resolution)
+	 {
+	    unsigned int R1, G1, B1;		/* pixel1 in RGB color space */
+	    unsigned int R2, G2, B2;		/* pixel2 in RGB color space */
+	    int		 yval1, crval1, cbval1;	/* pixel1 in YCbCr space */
+	    int		 yval2, crval2, cbval2;	/* pixel2 in YCbCr space */
+	    int		 x, y;			/* pixel counter */
+	    unsigned int *dst;		        /* dithered pixel pointer */
+	    
+	    dst = (unsigned int *) out;
+	    
+	    for (y = image->height; y; y--)
+	    {
+	       for (x = image->width >> 1; x; x--)
+	       {
+#ifdef HAVE_SIGNED_SHIFT
+		  yval1  = (*yptr++ >> 4) + 128;
+		  yval2  = (*yptr++ >> 4) + 128;
+		  crval1 = *crptr++ >> 4;
+		  crval2 = *crptr++ >> 4;
+		  cbval1 = *cbptr++ >> 4;
+		  cbval2 = *cbptr++ >> 4;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval1  = *yptr++  / 16 + 128;
+		  yval2  = *yptr++  / 16 + 128;
+		  crval1 = *crptr++ / 16;
+		  crval2 = *crptr++ / 16;
+		  cbval1 = *cbptr++ / 16;
+		  cbval2 = *cbptr++ / 16;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  
+		  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
+		  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
+				 + Cb_g_tab [cbval1]];
+		  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
+		  R2 = gray_clip [yval2 + Cr_r_tab [crval2]];
+		  G2 = gray_clip [yval2 + Cr_g_tab [crval2]
+				 + Cb_g_tab [cbval2]];
+		  B2 = gray_clip [yval2 + Cb_b_tab [cbval2]];
+
+		  *dst++ = B1 | (G1 << 8) | (R1 << 16) | (B1 << 24);
+		  *dst++ = G1 | (R1 << 8) | (B2 << 16) | (G2 << 24);
+		  *dst++ = R2 | (B2 << 8) | (G2 << 16) | (R2 << 24);
+	       }
+	       memcpy (dst, dst - 3 * (image->width >> 1),
+		       3 * (image->width >> 1) * sizeof (unsigned int));
+	       dst += 3 * (image->width >> 1);
+	    }
+	 }
+	 else
+	 {
+	    unsigned int R1, G1, B1;		/* pixel in RGB color space */
+	    unsigned int R2, G2, B2;		/* pixel in RGB color space */
+	    int		 yval1, crval1, cbval1;	/* pixel1 in YCbCr space */
+	    int		 yval2, crval2, cbval2;	/* pixel2 in YCbCr space */
+	    int		 n;			/* pixel counter */
+	    unsigned int *dst;		        /* dithered pixel pointer */
+	    
+	    dst = (unsigned int *) out;
+	    
+	    for (n = (image->width * image->height) >> 2; n; n--)
+	    {
+#ifdef HAVE_SIGNED_SHIFT
+	       yval1  = (*yptr++ >> 4) + 128;
+	       yval2  = (*yptr++ >> 4) + 128;
+	       crval1 = *crptr++ >> 4;
+	       crval2 = *crptr++ >> 4;
+	       cbval1 = *cbptr++ >> 4;
+	       cbval2 = *cbptr++ >> 4;
+#else /* not HAVE_SIGNED_SHIFT */
+	       yval1  = *yptr++  / 16 + 128;
+	       yval2  = *yptr++  / 16 + 128;
+	       crval1 = *crptr++ / 16;
+	       crval2 = *crptr++ / 16;
+	       cbval1 = *cbptr++ / 16;
+	       cbval2 = *cbptr++ / 16;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+	       R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
+	       G1 = gray_clip [yval1 + Cr_g_tab [crval1] + Cb_g_tab [cbval1]];
+	       B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
+	       R2 = gray_clip [yval2 + Cr_r_tab [crval2]];
+	       G2 = gray_clip [yval2 + Cr_g_tab [crval2] + Cb_g_tab [cbval2]];
+	       B2 = gray_clip [yval2 + Cb_b_tab [cbval2]];
+
+	       *dst++ = B1 | (G1 << 8) | (R1 << 16) | (B2 << 24);
+	       *dst   = G2 | (R2 << 8);
+
+#ifdef HAVE_SIGNED_SHIFT
+	       yval1  = (*yptr++ >> 4) + 128;
+	       yval2  = (*yptr++ >> 4) + 128;
+	       crval1 = *crptr++ >> 4;
+	       crval2 = *crptr++ >> 4;
+	       cbval1 = *cbptr++ >> 4;
+	       cbval2 = *cbptr++ >> 4;
+#else /* not HAVE_SIGNED_SHIFT */
+	       yval1  = *yptr++  / 16 + 128;
+	       yval2  = *yptr++  / 16 + 128;
+	       crval1 = *crptr++ / 16;
+	       crval2 = *crptr++ / 16;
+	       cbval1 = *cbptr++ / 16;
+	       cbval2 = *cbptr++ / 16;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+	       R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
+	       G1 = gray_clip [yval1 + Cr_g_tab [crval1] + Cb_g_tab [cbval1]];
+	       B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
+	       R2 = gray_clip [yval2 + Cr_r_tab [crval2]];
+	       G2 = gray_clip [yval2 + Cr_g_tab [crval2] + Cb_g_tab [cbval2]];
+	       B2 = gray_clip [yval2 + Cb_b_tab [cbval2]];
+
+	       *dst++ |= (B1 << 16) | (G1 << 24);
+	       *dst++ = R1 | (B2 << 8) | (G2 << 16) | (R2 << 24);
+	    }
+	 }
+      }
+   }
+   else
+   {
+      unsigned int *dst;		/* pointer to dithered pixels */
+      word_t	   *src;		/* current pixel of frame */
+      unsigned int *y_table;
+
+      y_table = private->y_table;
+      dst     = (unsigned int *) out;
+      src     = image->pixels [GRAY];
+
+      if (private->double_resolution)
+      {
+	 int	   x, y;		/* pixel counter */
+	 unsigned *shift_clipping = gray_clip + 128;
+
+	 for (y = image->height; y; y--)
+	 {
+	    for (x = image->width >> 1; x; x--)
+	    {
+	       unsigned int val1, val2;
+#ifdef HAVE_SIGNED_SHIFT
+	       val1 = shift_clipping [*src++ >> 4];
+	       val2 = shift_clipping [*src++ >> 4];
+#else /* not HAVE_SIGNED_SHIFT */
+	       val1 = shift_clipping [*src++ / 16];
+	       val2 = shift_clipping [*src++ / 16];
+#endif /* not HAVE_SIGNED_SHIFT */
+
+	       *dst++ = val1 | (val1 << 8) | (val1 << 16) | (val1 << 24);
+	       *dst++ = val1 | (val1 << 8) | (val2 << 16) | (val2 << 24); 
+	       *dst++ = val2 | (val2 << 8) | (val2 << 16) | (val2 << 24);
+	    }
+
+	    memcpy (dst, dst - 3 * (image->width >> 1),
+		    3 * (image->width >> 1) * sizeof (unsigned int));
+	    dst += 3 * (image->width >> 1);
+	 }
+      }
+      else
+      {
+	 int	   n;			/* pixel counter */
+	 unsigned *shift_clipping = gray_clip + 128;
+
+	 for (n = (image->width * image->height) >> 2; n; n--)
+	 {
+	    unsigned int val1, val2;
+
+#ifdef HAVE_SIGNED_SHIFT
+	    val1 = shift_clipping [*src++ >> 4];
+	    val2 = shift_clipping [*src++ >> 4];
+#else /* not HAVE_SIGNED_SHIFT */
+	    val1 = shift_clipping [*src++ / 16];
+	    val2 = shift_clipping [*src++ / 16];
+#endif /* not HAVE_SIGNED_SHIFT */
+
+	    *dst++ = val1 | (val1 << 8)
+		     | (val1 << 16) | (val2 << 24);  /* RGBR */
+	    *dst   = val2 | (val2 << 8);             /* GB-- */
+
+#ifdef HAVE_SIGNED_SHIFT
+	    val1 = shift_clipping [*src++ >> 4];
+	    val2 = shift_clipping [*src++ >> 4];
+#else /* not HAVE_SIGNED_SHIFT */
+	    val1 = shift_clipping [*src++ / 16];
+	    val2 = shift_clipping [*src++ / 16];
+#endif /* not HAVE_SIGNED_SHIFT */
+	    
+	    *dst++ |= (val1 << 16) | (val1 << 24);   /* --RG */
+	    *dst++  = val1 | (val2 << 8)
+		      | (val2 << 16) | (val2 << 24); /* BRGB */
+	 }
+      }
+   }
+   
+   return 1;
+}
+
+static int 
+display_24_bit_rgb (const struct fiasco_renderer *this, unsigned char *ximage,
+		    const fiasco_image_t *fiasco_image)
+/*
+ *  Convert 'image' to 16 bit color bitmap.
+ *  If 'double_resolution' is true then double image size in both directions.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'out[]'	is filled with dithered image
+ */
+{
+   unsigned 	      *gray_clip = init_clipping ();
+   const image_t      *image;
+   renderer_private_t *private;
+   byte_t	      *out;
+
+   if (!gray_clip)
+      return 0;
+   if (!this)
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "this");
+      return 0;
+   }
+   if (!ximage)
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "ximage");
+      return 0;
+   }
+   if (!fiasco_image)
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "fiasco_image");
+      return 0;
+   }
+
+   out 	   = (byte_t *) ximage;
+   image   = cast_image ((fiasco_image_t *) fiasco_image);
+   if (!image)
+      return 0;
+   private = (renderer_private_t *) this->private;
+   
+   if (image->color)
+   {
+      word_t 	   *cbptr, *crptr;	/* pointer to chroma bands */
+      word_t 	   *yptr;		/* pointers to lumincance band */
+      int 	   *Cr_r_tab, *Cr_g_tab, *Cb_g_tab, *Cb_b_tab;
+      unsigned int *r_table, *g_table, *b_table;
+
+      Cr_g_tab = private->Cr_g_tab;
+      Cr_r_tab = private->Cr_r_tab;
+      Cb_b_tab = private->Cb_b_tab;
+      Cb_g_tab = private->Cb_g_tab;
+      r_table  = private->r_table;
+      g_table  = private->g_table;
+      b_table  = private->b_table;
+      yptr     = image->pixels [Y];
+      cbptr    = image->pixels [Cb];
+      crptr    = image->pixels [Cr];
+
+      if (image->format == FORMAT_4_2_0)
+      {
+	 if (private->double_resolution)
+	 {
+	    int		  yval1;	/* lumincance pixel */
+	    int 	  crval1, cbval1; /* chroma pixels */
+	    int		  yval2;	/* pixel in YCbCr color space */
+	    unsigned int  R1, G1, B1;	/* pixel in RGB color space */
+	    unsigned int  R2, G2, B2;	/* pixel in RGB color space */
+	    int		  x, y;		/* pixel counter */
+	    unsigned int *dst;		/* pointer to dithered pixels */
+	    unsigned int *dst2;		/* pointers to dithered pixels */
+	    word_t	 *yptr2;	/* pointers to lumincance band */
+	    
+	    dst   = (unsigned int *) out;
+	    dst2  = dst + (image->width >> 1) * 3 * 2;
+	    yptr2 = yptr + image->width;
+	    
+	    for (y = image->height >> 1; y; y--)
+	    {
+	       for (x = image->width >> 1; x; x--)
+	       {
+#ifdef HAVE_SIGNED_SHIFT
+		  yval1  = (*yptr++ >> 4) + 128;
+		  yval2  = (*yptr++ >> 4) + 128;
+		  crval1 = *crptr++ >> 4;
+		  cbval1 = *cbptr++ >> 4;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval1  = *yptr++  / 16 + 128;
+		  yval2  = *yptr++  / 16 + 128;
+		  crval1 = *crptr++ / 16;
+		  cbval1 = *cbptr++ / 16;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+		  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
+		  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
+				  + Cb_g_tab [cbval1]];
+		  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
+		  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
+		  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
+				  + Cb_g_tab [cbval1]];
+		  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];
+
+		  *dst++ = R1 | (G1 << 8) | (B1 << 16) | (R1 << 24);
+		  *dst++ = G1 | (B1 << 8) | (R2 << 16) | (G2 << 24);
+		  *dst++ = B2 | (R2 << 8) | (G2 << 16) | (B2 << 24);
+
+#ifdef HAVE_SIGNED_SHIFT
+		  yval1  = (*yptr2++ >> 4) + 128;
+		  yval2  = (*yptr2++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval1  = *yptr2++  / 16 + 128;
+		  yval2  = *yptr2++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+		  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
+		  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
+				  + Cb_g_tab [cbval1]];
+		  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
+		  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
+		  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
+				  + Cb_g_tab [cbval1]];
+		  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];
+
+		  *dst2++ = R1 | (G1 << 8) | (B1 << 16) | (R1 << 24);
+		  *dst2++ = G1 | (B1 << 8) | (R2 << 16) | (G2 << 24);
+		  *dst2++ = B2 | (R2 << 8) | (G2 << 16) | (B2 << 24);
+	       }
+	       memcpy (dst, dst - (image->width >> 1) * 3,
+		       (image->width >> 1) * 3 * sizeof (unsigned int));
+	       memcpy (dst2, dst2 - (image->width >> 1) * 3, 
+		       (image->width >> 1) * 3 * sizeof (unsigned int));
+	       dst   += (image->width >> 1) * 3 * 3;
+	       dst2  += (image->width >> 1) * 3 * 3;
+	       yptr  += image->width;
+	       yptr2 += image->width;
+	    }
+	 }
+	 else
+	 {
+	    int		  yval1;	/* lumincance pixel */
+	    int 	  crval1, cbval1; /* chroma pixels */
+	    int		  yval2;	/* pixel in YCbCr color space */
+	    unsigned int  R1, G1, B1;	/* pixel in RGB color space */
+	    unsigned int  R2, G2, B2;	/* pixel in RGB color space */
+	    int		  x, y;		/* pixel counter */
+	    unsigned int *dst;		/* pointer to dithered pixels */
+	    unsigned int *dst2;		/* pointers to dithered pixels */
+	    word_t	 *yptr2;	/* pointers to lumincance band */
+	    
+	    dst   = (unsigned int *) out;
+	    dst2  = dst + (image->width >> 2) * 3;
+	    yptr2 = yptr + image->width;
+	    
+	    for (y = image->height >> 1; y; y--)
+	    {
+	       for (x = image->width >> 2; x; x--)
+	       {
+#ifdef HAVE_SIGNED_SHIFT
+		  yval1  = (*yptr++ >> 4) + 128;
+		  yval2  = (*yptr++ >> 4) + 128;
+		  crval1 = *crptr++ >> 4;
+		  cbval1 = *cbptr++ >> 4;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval1  = *yptr++  / 16 + 128;
+		  yval2  = *yptr++  / 16 + 128;
+		  crval1 = *crptr++ / 16;
+		  cbval1 = *cbptr++ / 16;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+		  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
+		  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
+				  + Cb_g_tab [cbval1]];
+		  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
+		  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
+		  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
+				  + Cb_g_tab [cbval1]];
+		  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];
+
+		  *dst++ = R1 | (G1 << 8) | (B1 << 16) | (R2 << 24);
+		  *dst   = G2 | (B2 << 8);
+
+#ifdef HAVE_SIGNED_SHIFT
+		  yval1  = (*yptr2++ >> 4) + 128;
+		  yval2  = (*yptr2++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval1  = *yptr2++  / 16 + 128;
+		  yval2  = *yptr2++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+		  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
+		  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
+				  + Cb_g_tab [cbval1]];
+		  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
+		  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
+		  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
+				  + Cb_g_tab [cbval1]];
+		  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];
+
+		  *dst2++ = R1 | (G1 << 8) | (B1 << 16) | (R2 << 24);
+		  *dst2   = G2 | (B2 << 8);
+	       
+#ifdef HAVE_SIGNED_SHIFT
+		  yval1  = (*yptr++ >> 4) + 128;
+		  yval2  = (*yptr++ >> 4) + 128;
+		  crval1 = *crptr++ >> 4;
+		  cbval1 = *cbptr++ >> 4;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval1  = *yptr++  / 16 + 128;
+		  yval2  = *yptr++  / 16 + 128;
+		  crval1 = *crptr++ / 16;
+		  crval2 = *crptr++ / 16;
+		  cbval1 = *cbptr++ / 16;
+		  cbval2 = *cbptr++ / 16;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+		  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
+		  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
+				  + Cb_g_tab [cbval1]];
+		  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
+		  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
+		  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
+				  + Cb_g_tab [cbval1]];
+		  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];
+
+		  *dst++ |= (R1 << 16) | (G1 << 24);
+		  *dst++ = B1 | (R2 << 8) | (G2 << 16) | (B2 << 24);
+
+#ifdef HAVE_SIGNED_SHIFT
+		  yval1  = (*yptr2++ >> 4) + 128;
+		  yval2  = (*yptr2++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval1  = *yptr2++  / 16 + 128;
+		  yval2  = *yptr2++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+		  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
+		  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
+				  + Cb_g_tab [cbval1]];
+		  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
+		  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
+		  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
+				  + Cb_g_tab [cbval1]];
+		  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];
+
+		  *dst2++ |= (R1 << 16) | (G1 << 24);
+		  *dst2++ = B1 | (R2 << 8) | (G2 << 16) | (B2 << 24);
+	       }
+	       dst   += (image->width >> 2) * 3;
+	       dst2  += (image->width >> 2) * 3;
+	       yptr  += image->width;
+	       yptr2 += image->width;
+	    }
+	 }
+      }
+      else				/* 4:4:4 format */
+      {
+	 if (private->double_resolution)
+	 {
+	    unsigned int R1, G1, B1;		/* pixel1 in RGB color space */
+	    unsigned int R2, G2, B2;		/* pixel2 in RGB color space */
+	    int		 yval1, crval1, cbval1;	/* pixel1 in YCbCr space */
+	    int		 yval2, crval2, cbval2;	/* pixel2 in YCbCr space */
+	    int		 x, y;			/* pixel counter */
+	    unsigned int *dst;		        /* dithered pixel pointer */
+	    
+	    dst = (unsigned int *) out;
+	    
+	    for (y = image->height; y; y--)
+	    {
+	       for (x = image->width >> 1; x; x--)
+	       {
+#ifdef HAVE_SIGNED_SHIFT
+		  yval1  = (*yptr++ >> 4) + 128;
+		  yval2  = (*yptr++ >> 4) + 128;
+		  crval1 = *crptr++ >> 4;
+		  crval2 = *crptr++ >> 4;
+		  cbval1 = *cbptr++ >> 4;
+		  cbval2 = *cbptr++ >> 4;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval1  = *yptr++  / 16 + 128;
+		  yval2  = *yptr++  / 16 + 128;
+		  crval1 = *crptr++ / 16;
+		  crval2 = *crptr++ / 16;
+		  cbval1 = *cbptr++ / 16;
+		  cbval2 = *cbptr++ / 16;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  
+		  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
+		  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
+				  + Cb_g_tab [cbval1]];
+		  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
+		  R2 = gray_clip [yval2 + Cr_r_tab [crval2]];
+		  G2 = gray_clip [yval2 + Cr_g_tab [crval2]
+				  + Cb_g_tab [cbval2]];
+		  B2 = gray_clip [yval2 + Cb_b_tab [cbval2]];
+
+		  *dst++ = R1 | (G1 << 8) | (B1 << 16) | (R1 << 24);
+		  *dst++ = G1 | (B1 << 8) | (R2 << 16) | (G2 << 24);
+		  *dst++ = B2 | (R2 << 8) | (G2 << 16) | (B2 << 24);
+	       }
+	       memcpy (dst, dst - 3 * (image->width >> 1),
+		       3 * (image->width >> 1) * sizeof (unsigned int));
+	       dst += 3 * (image->width >> 1);
+	    }
+	 }
+	 else
+	 {
+	    unsigned int R1, G1, B1;		/* pixel in RGB color space */
+	    unsigned int R2, G2, B2;		/* pixel in RGB color space */
+	    int		 yval1, crval1, cbval1;	/* pixel1 in YCbCr space */
+	    int		 yval2, crval2, cbval2;	/* pixel2 in YCbCr space */
+	    int		 n;			/* pixel counter */
+	    unsigned int *dst;		        /* dithered pixel pointer */
+	    
+	    dst = (unsigned int *) out;
+	    
+	    for (n = (image->width * image->height) >> 2; n; n--)
+	    {
+#ifdef HAVE_SIGNED_SHIFT
+	       yval1  = (*yptr++ >> 4) + 128;
+	       yval2  = (*yptr++ >> 4) + 128;
+	       crval1 = *crptr++ >> 4;
+	       crval2 = *crptr++ >> 4;
+	       cbval1 = *cbptr++ >> 4;
+	       cbval2 = *cbptr++ >> 4;
+#else /* not HAVE_SIGNED_SHIFT */
+	       yval1  = *yptr++  / 16 + 128;
+	       yval2  = *yptr++  / 16 + 128;
+	       crval1 = *crptr++ / 16;
+	       crval2 = *crptr++ / 16;
+	       cbval1 = *cbptr++ / 16;
+	       cbval2 = *cbptr++ / 16;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+	       R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
+	       G1 = gray_clip [yval1 + Cr_g_tab [crval1] + Cb_g_tab [cbval1]];
+	       B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
+	       R2 = gray_clip [yval2 + Cr_r_tab [crval2]];
+	       G2 = gray_clip [yval2 + Cr_g_tab [crval2] + Cb_g_tab [cbval2]];
+	       B2 = gray_clip [yval2 + Cb_b_tab [cbval2]];
+
+	       *dst++ = R1 | (G1 << 8) | (B1 << 16) | (R2 << 24);
+	       *dst   = G2 | (B2 << 8);
+
+#ifdef HAVE_SIGNED_SHIFT
+	       yval1  = (*yptr++ >> 4) + 128;
+	       yval2  = (*yptr++ >> 4) + 128;
+	       crval1 = *crptr++ >> 4;
+	       crval2 = *crptr++ >> 4;
+	       cbval1 = *cbptr++ >> 4;
+	       cbval2 = *cbptr++ >> 4;
+#else /* not HAVE_SIGNED_SHIFT */
+	       yval1  = *yptr++  / 16 + 128;
+	       yval2  = *yptr++  / 16 + 128;
+	       crval1 = *crptr++ / 16;
+	       crval2 = *crptr++ / 16;
+	       cbval1 = *cbptr++ / 16;
+	       cbval2 = *cbptr++ / 16;
+#endif /* not HAVE_SIGNED_SHIFT */
+
+	       R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
+	       G1 = gray_clip [yval1 + Cr_g_tab [crval1] + Cb_g_tab [cbval1]];
+	       B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
+	       R2 = gray_clip [yval2 + Cr_r_tab [crval2]];
+	       G2 = gray_clip [yval2 + Cr_g_tab [crval2] + Cb_g_tab [cbval2]];
+	       B2 = gray_clip [yval2 + Cb_b_tab [cbval2]];
+
+	       *dst++ |= (R1 << 16) | (G1 << 24);
+	       *dst++ = B1 | (R2 << 8) | (G2 << 16) | (B2 << 24);
+	    }
+	 }
+      }
+   }
+   else
+   {
+      unsigned int *dst;		/* pointer to dithered pixels */
+      word_t	   *src;		/* current pixel of frame */
+      unsigned int *y_table;
+
+      y_table = private->y_table;
+      dst     = (unsigned int *) out;
+      src     = image->pixels [GRAY];
+
+      if (private->double_resolution)
+      {
+	 int	   x, y;		/* pixel counter */
+	 unsigned *shift_clipping = gray_clip + 128;
+
+	 for (y = image->height; y; y--)
+	 {
+	    for (x = image->width >> 1; x; x--)
+	    {
+	       unsigned int val1, val2;
+#ifdef HAVE_SIGNED_SHIFT
+	       val1 = shift_clipping [*src++ >> 4];
+	       val2 = shift_clipping [*src++ >> 4];
+#else /* not HAVE_SIGNED_SHIFT */
+	       val1 = shift_clipping [*src++ / 16];
+	       val2 = shift_clipping [*src++ / 16];
+#endif /* not HAVE_SIGNED_SHIFT */
+
+	       *dst++ = val1 | (val1 << 8) | (val1 << 16) | (val1 << 24);
+	       *dst++ = val1 | (val1 << 8) | (val2 << 16) | (val2 << 24); 
+	       *dst++ = val2 | (val2 << 8) | (val2 << 16) | (val2 << 24);
+	    }
+
+	    memcpy (dst, dst - 3 * (image->width >> 1),
+		    3 * (image->width >> 1) * sizeof (unsigned int));
+	    dst += 3 * (image->width >> 1);
+	 }
+      }
+      else
+      {
+	 int	   n;			/* pixel counter */
+	 unsigned *shift_clipping = gray_clip + 128;
+
+	 for (n = (image->width * image->height) >> 2; n; n--)
+	 {
+	    unsigned int val1, val2;
+
+#ifdef HAVE_SIGNED_SHIFT
+	    val1 = shift_clipping [*src++ >> 4];
+	    val2 = shift_clipping [*src++ >> 4];
+#else /* not HAVE_SIGNED_SHIFT */
+	    val1 = shift_clipping [*src++ / 16];
+	    val2 = shift_clipping [*src++ / 16];
+#endif /* not HAVE_SIGNED_SHIFT */
+
+	    *dst++ = val1 | (val1 << 8)
+		     | (val1 << 16) | (val2 << 24);  /* RGBR */
+	    *dst   = val2 | (val2 << 8);             /* GB-- */
+
+#ifdef HAVE_SIGNED_SHIFT
+	    val1 = shift_clipping [*src++ >> 4];
+	    val2 = shift_clipping [*src++ >> 4];
+#else /* not HAVE_SIGNED_SHIFT */
+	    val1 = shift_clipping [*src++ / 16];
+	    val2 = shift_clipping [*src++ / 16];
+#endif /* not HAVE_SIGNED_SHIFT */
+	    
+	    *dst++ |= (val1 << 16) | (val1 << 24);   /* --RG */
+	    *dst++  = val1 | (val2 << 8)
+		      | (val2 << 16) | (val2 << 24); /* BRGB */
+	 }
+      }
+   }
+   
+   return 1;
+}
+
+static int 
+display_32_bit (const struct fiasco_renderer *this, unsigned char *ximage,
+		const fiasco_image_t *fiasco_image)
+/*
+ *  Convert 'image' to 16 bit color bitmap.
+ *  If 'double_resolution' is true then double image size in both directions.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'out[]'	is filled with dithered image
+ */
+{
+   const image_t      *image;
+   renderer_private_t *private;
+   byte_t	      *out;
+   
+   if (!this)
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "this");
+      return 0;
+   }
+   if (!ximage)
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "ximage");
+      return 0;
+   }
+   if (!fiasco_image)
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "fiasco_image");
+      return 0;
+   }
+
+   out 	   = (byte_t *) ximage;
+   private = (renderer_private_t *) this->private;
+   image   = cast_image ((fiasco_image_t *) fiasco_image);
+   if (!image)
+      return 0;
+   
+   if (image->color)
+   {
+      word_t 	   *cbptr, *crptr;	/* pointer to chroma bands */
+      word_t 	   *yptr;		/* pointers to lumincance band */
+      int     	    yval, crval, cbval;	/* pixel value in YCbCr color space */
+      int     	    R, G, B;		/* pixel value in RGB color space */
+      int     	    n;			/* pixel counter */
+      int     	    x, y;		/* pixel coordinates */
+      int 	   *Cr_r_tab, *Cr_g_tab, *Cb_g_tab, *Cb_b_tab;
+      unsigned int *r_table, *g_table, *b_table;
+
+      Cr_g_tab = private->Cr_g_tab;
+      Cr_r_tab = private->Cr_r_tab;
+      Cb_b_tab = private->Cb_b_tab;
+      Cb_g_tab = private->Cb_g_tab;
+      r_table  = private->r_table;
+      g_table  = private->g_table;
+      b_table  = private->b_table;
+      yptr  = image->pixels [Y];
+      cbptr = image->pixels [Cb];
+      crptr = image->pixels [Cr];
+
+      if (image->format == FORMAT_4_2_0)
+      {
+	 unsigned int	*dst, *dst2;	/* pointers to dithered pixels */
+	 word_t		*yptr2;		/* pointers to lumincance band */
+
+	 if (private->double_resolution)
+	 {
+	    yptr2 = yptr + image->width;
+	    dst  = (unsigned int *) out;
+	    dst2 = dst + 4 * image->width;
+	    for (y = image->height / 2; y; y--)
+	    {
+	       for (x = image->width / 2; x; x--)
+	       {
+#ifdef HAVE_SIGNED_SHIFT
+		  crval = *crptr++ >> 4;
+		  cbval = *cbptr++ >> 4;
+		  yval  = (*yptr++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  crval = *crptr++ / 16;
+		  cbval = *cbptr++ / 16;
+		  yval  = *yptr++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  R = yval + Cr_r_tab [crval];
+		  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+		  B = yval + Cb_b_tab [cbval];
+		  *dst++ = r_table [R] | g_table [G] | b_table [B];
+		  *dst++ = r_table [R] | g_table [G] | b_table [B];
+	       
+#ifdef HAVE_SIGNED_SHIFT
+		  yval  = (*yptr++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval  = *yptr++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  R = yval + Cr_r_tab [crval];
+		  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+		  B = yval + Cb_b_tab [cbval];
+		  *dst++ = r_table [R] | g_table [G] | b_table [B];
+		  *dst++ = r_table [R] | g_table [G] | b_table [B];
+
+#ifdef HAVE_SIGNED_SHIFT
+		  yval  = (*yptr2++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval  = *yptr2++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  R = yval + Cr_r_tab [crval];
+		  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+		  B = yval + Cb_b_tab [cbval];
+		  *dst2++ = r_table [R] | g_table [G] | b_table [B];
+		  *dst2++ = r_table [R] | g_table [G] | b_table [B];
+
+#ifdef HAVE_SIGNED_SHIFT
+		  yval  = (*yptr2++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval  = *yptr2++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  R = yval + Cr_r_tab [crval];
+		  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+		  B = yval + Cb_b_tab [cbval];
+		  *dst2++ = r_table [R] | g_table [G] | b_table [B];
+		  *dst2++ = r_table [R] | g_table [G] | b_table [B];
+	       }
+	       memcpy (dst, dst - 2 * image->width,
+		       2 * image->width * sizeof (unsigned int));
+	       memcpy (dst2, dst2 - 2 * image->width,
+		       2 * image->width * sizeof (unsigned int));
+	       yptr  += image->width;
+	       yptr2 += image->width;
+	       dst   += 3 * image->width * 2;
+	       dst2  += 3 * image->width * 2;
+	    }
+	 }
+	 else
+	 {
+	    yptr2 = yptr + image->width;
+	    dst   = (unsigned int *) out;
+	    dst2  = dst + image->width;
+	    
+	    for (y = image->height / 2; y; y--)
+	    {
+	       for (x = image->width / 2; x; x--)
+	       {
+#ifdef HAVE_SIGNED_SHIFT
+		  crval = *crptr++ >> 4;
+		  cbval = *cbptr++ >> 4;
+		  yval  = (*yptr++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  crval = *crptr++ / 16;
+		  cbval = *cbptr++ / 16;
+		  yval  = *yptr++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  R = yval + Cr_r_tab [crval];
+		  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+		  B = yval + Cb_b_tab [cbval];
+		  *dst++ = r_table [R] | g_table [G] | b_table [B];
+	       
+#ifdef HAVE_SIGNED_SHIFT
+		  yval  = (*yptr++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval  = *yptr++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  R = yval + Cr_r_tab [crval];
+		  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+		  B = yval + Cb_b_tab [cbval];
+		  *dst++ = r_table [R] | g_table [G] | b_table [B];
+
+#ifdef HAVE_SIGNED_SHIFT
+		  yval  = (*yptr2++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval  = *yptr2++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  R = yval + Cr_r_tab [crval];
+		  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+		  B = yval + Cb_b_tab [cbval];
+		  *dst2++ = r_table [R] | g_table [G] | b_table [B];
+
+#ifdef HAVE_SIGNED_SHIFT
+		  yval  = (*yptr2++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  yval  = *yptr2++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  R = yval + Cr_r_tab [crval];
+		  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+		  B = yval + Cb_b_tab [cbval];
+		  *dst2++ = r_table [R] | g_table [G] | b_table [B];
+	       }
+	       yptr  += image->width;
+	       yptr2 += image->width;
+	       dst   += image->width;
+	       dst2  += image->width;
+	    }
+	 }
+      }
+      else				/* 4:4:4 format */
+      {
+	 if (private->double_resolution)
+	 {
+	    unsigned int *dst;		/* pointer to dithered pixels */
+	    
+	    dst = (unsigned int *) out;
+	    
+	    for (y = image->height; y; y--)
+	    {
+	       for (x = image->width; x; x--)
+	       {
+#ifdef HAVE_SIGNED_SHIFT
+		  crval = *crptr++ >> 4;
+		  cbval = *cbptr++ >> 4;
+		  yval  = (*yptr++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+		  crval = *crptr++ / 16;
+		  cbval = *cbptr++ / 16;
+		  yval  = *yptr++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+		  R = yval + Cr_r_tab [crval];
+		  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+		  B = yval + Cb_b_tab [cbval];
+		  
+		  *dst++ = r_table [R] | g_table [G] | b_table [B];
+		  *dst++ = r_table [R] | g_table [G] | b_table [B];
+	       }
+	       memcpy (dst, dst - 2 * image->width,
+		       2 * image->width * sizeof (unsigned int));
+	       dst += image->width * 2;
+	    }
+	 }
+	 else
+	 {
+	    unsigned int *dst;		/* pointer to dithered pixels */
+
+	    dst  = (unsigned int *) out;
+	    
+	    for (n = image->width * image->height; n; n--)
+	    {
+#ifdef HAVE_SIGNED_SHIFT
+	       crval = *crptr++ >> 4;
+	       cbval = *cbptr++ >> 4;
+	       yval  = (*yptr++ >> 4) + 128;
+#else /* not HAVE_SIGNED_SHIFT */
+	       crval = *crptr++ / 16;
+	       cbval = *cbptr++ / 16;
+	       yval  = *yptr++  / 16 + 128;
+#endif /* not HAVE_SIGNED_SHIFT */
+	       R = yval + Cr_r_tab [crval];
+	       G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
+	       B = yval + Cb_b_tab [cbval];
+
+	       *dst++ = r_table [R] | g_table [G] | b_table [B];
+	    }
+	 }
+      }
+   }
+   else
+   {
+      unsigned int *dst;		/* pointer to dithered pixels */
+      word_t	   *src;		/* current pixel of frame */
+      unsigned int *y_table;
+
+      y_table = private->y_table;
+      dst     = (unsigned int *) out;
+      src     = image->pixels [GRAY];
+      
+      if (private->double_resolution)
+      {
+	 int x, y;			/* pixel coordinates */
+	 
+	 for (y = image->height; y; y--)
+	 {
+	    for (x = image->width; x; x--)
+	    {
+	       int value;
+
+#ifdef HAVE_SIGNED_SHIFT
+	       value = y_table [*src++ >> 4];
+#else /* not HAVE_SIGNED_SHIFT */
+	       value = y_table [*src++ / 16];
+#endif /* not HAVE_SIGNED_SHIFT */
+	       *dst++ = value;
+	       *dst++ = value;
+	    }
+
+	    memcpy (dst, dst - 2 * image->width,
+		    2 * image->width * sizeof (unsigned int));
+	    dst += 2 * image->width;
+	 }
+      }
+      else
+      {
+	 int n;				/* pixel counter */
+	 
+	 for (n = image->width * image->height; n; n--)
+#ifdef HAVE_SIGNED_SHIFT
+	    *dst++ = y_table [*src++ >> 4];
+#else /* not HAVE_SIGNED_SHIFT */
+	    *dst++ = y_table [*src++ / 16];
+#endif /* not HAVE_SIGNED_SHIFT */
+      }
+   }
+   
+   return 1;
+}
+
+
+ 
diff --git a/converter/other/fiasco/lib/dither.h b/converter/other/fiasco/lib/dither.h
new file mode 100644
index 00000000..71f9d3c3
--- /dev/null
+++ b/converter/other/fiasco/lib/dither.h
@@ -0,0 +1,27 @@
+/*
+ *  dither.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:49:37 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _DITHER_H
+#define _DITHER_H
+
+typedef struct renderer_private
+{
+   int 	       	*Cr_r_tab, *Cr_g_tab, *Cb_g_tab, *Cb_b_tab;
+   unsigned int *r_table, *g_table, *b_table, *y_table;
+   bool_t	double_resolution;
+} renderer_private_t;
+
+#endif /* _DITHER_H */
diff --git a/converter/other/fiasco/lib/error.c b/converter/other/fiasco/lib/error.c
new file mode 100644
index 00000000..b858badf
--- /dev/null
+++ b/converter/other/fiasco/lib/error.c
@@ -0,0 +1,326 @@
+/*
+ *  error.c:		Error handling
+ *
+ *  Written by:		Stefan Frank
+ *			Ullrich Hafner
+ *  
+ *  Credits:	Modelled after variable argument routines from Jef
+ *		Poskanzer's pbmplus package. 
+ *
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+
+    "int dummy = " change to int dummy; dummy =" for Netpbm to avoid 
+    unused variable warning.
+
+ */
+
+/*
+ *  $Date: 2000/06/14 20:49:37 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#define _ERROR_C
+
+#include "config.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+#if STDC_HEADERS
+#	include <stdarg.h>
+#	define VA_START(args, lastarg) va_start(args, lastarg)
+#else  /* not STDC_HEADERS */
+#	include <varargs.h>
+#	define VA_START(args, lastarg) va_start(args)
+#endif /* not STDC_HEADERS */
+#if HAVE_STRING_H
+#	include <string.h>
+#else /* not HAVE_STRING_H */
+#	include <strings.h>
+#endif /* not HAVE_STRING_H */
+
+#if HAVE_SETJMP_H
+#	include <setjmp.h>
+#endif /* HAVE_SETJMP_H */
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "misc.h"
+#include "fiasco.h"
+
+/*****************************************************************************
+
+			     local variables
+  
+*****************************************************************************/
+
+static fiasco_verbosity_e  verboselevel  = FIASCO_SOME_VERBOSITY;
+static char 	      	  *error_message = NULL;
+
+#if HAVE_SETJMP_H
+jmp_buf env;
+#endif /* HAVE_SETJMP_H */
+
+/*****************************************************************************
+
+			       public code
+  
+*****************************************************************************/
+
+void
+set_error (const char *format, ...)
+/*
+ *  Set error text to given string.
+ */
+{
+   va_list     args;
+   unsigned    len = 0;
+   const char *str = format;
+   
+   VA_START (args, format);
+
+   len = strlen (format);
+   while ((str = strchr (str, '%')))
+   {
+      str++;
+      if (*str == 's')
+      {
+	 char *vstring = va_arg (args, char *);
+	 len += strlen (vstring);
+      }
+      else if (*str == 'd')
+      {
+	 int dummy;
+     dummy = va_arg (args, int);
+	 len += 10;
+      }
+      else if (*str == 'c')
+      {
+	 int dummy;
+     dummy = va_arg (args, int);
+	 len += 1;
+      }
+      else
+	 return;
+      str++;
+   }
+   va_end(args);
+
+   VA_START (args, format);
+
+   if (error_message)
+      Free (error_message);
+   error_message = Calloc (len, sizeof (char));
+   
+#if HAVE_VPRINTF
+   vsprintf (error_message, format, args);
+#elif HAVE_DOPRNT
+   _doprnt (format, args, stderr);
+#endif /* HAVE_DOPRNT */
+
+   va_end (args);
+}
+
+void
+error (const char *format, ...)
+/*
+ *  Set error text to given string.
+ */
+{
+   va_list     args;
+   unsigned    len = 0;
+   const char *str = format;
+   
+   VA_START (args, format);
+
+   len = strlen (format);
+   while ((str = strchr (str, '%')))
+   {
+      str++;
+      if (*str == 's')
+      {
+	 char *vstring = va_arg (args, char *);
+	 len += strlen (vstring);
+      }
+      else if (*str == 'd')
+      {
+	 int dummy;
+     dummy = va_arg (args, int);
+	 len += 10;
+      }
+      else if (*str == 'c')
+      {
+	 int dummy;
+     dummy = va_arg (args, int);
+	 len += 1;
+      }
+      else
+      {
+#if HAVE_SETJMP_H
+	 longjmp (env, 1);
+#else /* not HAVE_SETJMP_H */
+	 exit (1);
+#endif /* HAVE_SETJMP_H */
+      };
+      
+      str++;
+   }
+   va_end(args);
+
+   VA_START (args, format);
+
+   if (error_message)
+      Free (error_message);
+   error_message = Calloc (len, sizeof (char));
+   
+#if HAVE_VPRINTF
+   vsprintf (error_message, format, args);
+#elif HAVE_DOPRNT
+   _doprnt (format, args, stderr);
+#endif /* HAVE_DOPRNT */
+
+   va_end (args);
+   
+#if HAVE_SETJMP_H
+   longjmp (env, 1);
+#else /* not HAVE_SETJMP_H */
+   exit (1);
+#endif /* HAVE_SETJMP_H */
+}
+
+const char *
+fiasco_get_error_message (void)
+/*
+ *  Return value:
+ *	Last error message of FIASCO library.
+ */
+{
+   return error_message ? error_message : "";
+}
+
+const char *
+get_system_error (void)
+{
+   return strerror (errno);
+}
+
+void
+file_error (const char *filename)
+/*
+ *  Print file error message and exit.
+ *
+ *  No return value.
+ */
+{
+   error ("File `%s': I/O Error - %s.", filename, get_system_error ());
+}
+
+void 
+warning (const char *format, ...)
+/*
+ *  Issue a warning and continue execution.
+ *
+ *  No return value.
+ */
+{
+   va_list	args;
+
+   VA_START (args, format);
+
+   if (verboselevel == FIASCO_NO_VERBOSITY)
+      return;
+	
+   fprintf (stderr, "Warning: ");
+#if HAVE_VPRINTF
+   vfprintf (stderr, format, args);
+#elif HAVE_DOPRNT
+   _doprnt (format, args, stderr);
+#endif /* HAVE_DOPRNT */
+   fputc ('\n', stderr);
+
+   va_end (args);
+}
+
+void 
+message (const char *format, ...)
+/*
+ *  Print a message to stderr.
+ */
+{
+   va_list args;
+
+   VA_START (args, format);
+
+   if (verboselevel == FIASCO_NO_VERBOSITY)
+      return;
+
+#if HAVE_VPRINTF
+   vfprintf (stderr, format, args);
+#elif HAVE_DOPRNT
+   _doprnt (format, args, stderr);
+#endif /* HAVE_DOPRNT */
+   fputc ('\n', stderr);
+   va_end (args);
+}
+
+void 
+debug_message (const char *format, ...)
+/*
+ *  Print a message to stderr.
+ */
+{
+   va_list args;
+
+   VA_START (args, format);
+
+   if (verboselevel < FIASCO_ULTIMATE_VERBOSITY)
+      return;
+
+   fprintf (stderr, "*** ");
+#if HAVE_VPRINTF
+   vfprintf (stderr, format, args);
+#elif HAVE_DOPRNT
+   _doprnt (format, args, stderr);
+#endif /* HAVE_DOPRNT */
+   fputc ('\n', stderr);
+   va_end (args);
+}
+
+void
+info (const char *format, ...)
+/*
+ *  Print a message to stderr. Do not append a newline.
+ */
+{
+   va_list args;
+
+   VA_START (args, format);
+
+   if (verboselevel == FIASCO_NO_VERBOSITY)
+      return;
+
+#if HAVE_VPRINTF
+   vfprintf (stderr, format, args);
+#elif HAVE_DOPRNT
+   _doprnt (format, args, stderr);
+#endif /* HAVE_DOPRNT */
+   fflush (stderr);
+   va_end (args);
+}
+
+void
+fiasco_set_verbosity (fiasco_verbosity_e level)
+{
+   verboselevel = level;
+}
+
+fiasco_verbosity_e
+fiasco_get_verbosity (void)
+{
+   return verboselevel;
+}
diff --git a/converter/other/fiasco/lib/error.h b/converter/other/fiasco/lib/error.h
new file mode 100644
index 00000000..288b25f4
--- /dev/null
+++ b/converter/other/fiasco/lib/error.h
@@ -0,0 +1,39 @@
+/*
+ *  error.h
+ *  
+ *  Written by:     Stefan Frank
+ *          Ullrich Hafner
+ *
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+#ifndef ERROR_H_INCLUDED
+#define ERROR_H_INCLUDED
+
+void
+set_error (const char *format, ...);
+void
+error (const char *format, ...);
+void
+file_error (const char *filename);
+void
+message (const char *format, ...);
+void 
+debug_message (const char *format, ...);
+void
+warning (const char *format, ...);
+void 
+info (const char *format, ...);
+const char *
+get_system_error (void);
+
+#include <setjmp.h>
+extern jmp_buf env;
+
+#define try         if (setjmp (env) == 0)
+#define catch           else
+
+#include <assert.h>
+
+#endif
diff --git a/converter/other/fiasco/lib/image.c b/converter/other/fiasco/lib/image.c
new file mode 100644
index 00000000..019ba03c
--- /dev/null
+++ b/converter/other/fiasco/lib/image.c
@@ -0,0 +1,512 @@
+/*
+ *  image.c:		Input and output of PNM images.
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/15 17:21:30 $
+ *  $Author: hafner $
+ *  $Revision: 5.2 $
+ *  $State: Exp $
+ */
+
+#include "pnm.h"
+
+#include <string.h>
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "fiasco.h"
+#include "misc.h"
+#include "image.h"
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static void
+init_chroma_tables (void);
+
+/*****************************************************************************
+
+				local variables
+  
+*****************************************************************************/
+static int *Cr_r_tab = NULL;
+static int *Cr_g_tab = NULL;
+static int *Cb_g_tab = NULL;
+static int *Cb_b_tab = NULL;
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+fiasco_image_t *
+fiasco_image_new (const char *filename)
+/*
+ *  FIASCO image constructor.
+ *  Allocate memory for the FIASCO image structure and
+ *  load the specified image `filename'. The image has to be in
+ *  raw pgm or ppm format.
+ *
+ *  Return value:
+ *	pointer to the new image structure
+ *	or NULL in case of an error
+ */
+{
+   try
+   {
+      fiasco_image_t *image = Calloc (1, sizeof (fiasco_image_t));
+
+      image->private 	= read_image (filename);
+      image->delete  	= fiasco_image_delete;
+      image->get_width  = fiasco_image_get_width;
+      image->get_height = fiasco_image_get_height;
+      image->is_color  	= fiasco_image_is_color;
+
+      return image;
+   }
+   catch
+   {
+      return NULL;
+   }
+}
+
+void
+fiasco_image_delete (fiasco_image_t *image)
+/*
+ *  FIASCO image destructor.
+ *  Free memory of FIASCO image struct.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	structure 'image' is discarded.
+ */
+{
+   image_t *this = cast_image (image);
+
+   if (!this)
+      return;
+
+   try
+   {
+      free_image (this);
+   }
+   catch
+   {
+      return;
+   }
+}
+
+unsigned
+fiasco_image_get_width (fiasco_image_t *image)
+{
+   image_t *this = cast_image (image);
+
+   if (!this)
+      return 0;
+   else
+      return this->width;
+}
+
+unsigned
+fiasco_image_get_height (fiasco_image_t *image)
+{
+   image_t *this = cast_image (image);
+
+   if (!this)
+      return 0;
+   else
+      return this->width;
+}
+
+int
+fiasco_image_is_color (fiasco_image_t *image)
+{
+   image_t *this = cast_image (image);
+
+   if (!this)
+      return 0;
+   else
+      return this->color;
+}
+
+image_t *
+cast_image (fiasco_image_t *image)
+/*
+ *  Cast pointer `image' to type image_t.
+ *  Check whether `image' is a valid object of type image_t.
+ *
+ *  Return value:
+ *	pointer to dfiasco_t struct on success
+ *      NULL otherwise
+ */
+{
+   image_t *this = (image_t *) image->private;
+   if (this)
+   {
+      if (!streq (this->id, "IFIASCO"))
+      {
+	 set_error (_("Parameter `image' doesn't match required type."));
+	 return NULL;
+      }
+   }
+   else
+   {
+      set_error (_("Parameter `%s' not defined (NULL)."), "image");
+   }
+
+   return this;
+}
+
+image_t *
+alloc_image (unsigned width, unsigned height, bool_t color, format_e format)
+/*
+ *  Image constructor:
+ *  Allocate memory for the image_t structure.
+ *  Image size is given by 'width' and 'height'.
+ *  If 'color' == YES then allocate memory for three color bands (Y, Cb, Cr).
+ *  otherwise just allocate memory for a grayscale image.
+ *  'format' specifies whether image pixels of color images
+ *  are stored in 4:4:4 or 4:2:0 format.
+ *
+ *  Return value:
+ *	pointer to the new image structure.
+ */
+{
+   image_t *image;
+   color_e band;
+
+   if ((width & 1) || (height & 1))
+      error ("Width and height of images must be even numbers.");
+   if (!color)
+      format = FORMAT_4_4_4;
+
+   image         	  = Calloc (1, sizeof (image_t));
+   image->width  	  = width;
+   image->height 	  = height;
+   image->color  	  = color;
+   image->format 	  = format;
+   image->reference_count = 1;
+   
+   strcpy (image->id, "IFIASCO");
+
+   for (band = first_band (color); band <= last_band (color); band++)
+      if (format == FORMAT_4_2_0 && band != Y)
+	 image->pixels [band] = Calloc ((width * height) >> 2,
+					sizeof (word_t));
+      else
+	 image->pixels [band] = Calloc (width * height, sizeof (word_t));
+   
+   return image;
+}
+
+image_t *
+clone_image (image_t *image)
+/*
+ *  Copy constructor:
+ *  Construct new image by copying the given `image'.
+ *
+ *  Return value:
+ *	pointer to the new image structure.
+ */
+{
+   image_t *new = alloc_image (image->width, image->height, image->color,
+			       image->format);
+   color_e band;
+   
+   for (band = first_band (new->color); band <= last_band (new->color); band++)
+      if (new->format == FORMAT_4_2_0 && band != Y)
+      {
+	 memcpy (new->pixels [band], image->pixels [band],
+		 ((new->width * new->height) >> 2) * sizeof (word_t));
+      }
+      else
+      {
+	 memcpy (new->pixels [band], image->pixels [band],
+		 new->width * new->height * sizeof (word_t));
+      }
+
+   return new;
+}
+
+void
+free_image (image_t *image)
+/*
+ *  Image destructor:
+ *  Free memory of 'image' struct and pixel data.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	structure 'image' is discarded.
+ */
+{
+   if (image != NULL)
+   {
+      if (--image->reference_count)
+	 return;			/* image is still referenced */
+      else
+      {
+	 color_e band;
+
+	 for (band  = first_band (image->color);
+	      band <= last_band (image->color); band++)
+	    if (image->pixels [band])
+	       Free (image->pixels [band]);
+	 Free (image);
+      }
+   }
+   else
+      warning ("Can't free image <NULL>.");
+}
+
+
+static void 
+read_image_data(image_t * const image, FILE *input, const bool_t color,
+                const int width, const int height, const xelval maxval,
+                const int format) {
+   int row;
+   int i;      /* Cursor into image->pixels arrays */
+   xel * xelrow;
+   /* The following are just the normal rgb -> YCbCr conversion matrix,
+      except normalization to maxval 4095 (12 bit color) is built in
+      */
+   const double coeff_lu_r = +0.2989 / maxval * 4095;
+   const double coeff_lu_g = +0.5866 / maxval * 4095;
+   const double coeff_lu_b = +0.1145 / maxval * 4095;
+   const double coeff_cb_r = -0.1687 / maxval * 4095;
+   const double coeff_cb_g = -0.3312 / maxval * 4095;
+   const double coeff_cb_b = +0.5000 / maxval * 4095;
+   const double coeff_cr_r = +0.5000 / maxval * 4095;
+   const double coeff_cr_g = -0.4183 / maxval * 4095;
+   const double coeff_cr_b = -0.0816 / maxval * 4095;
+
+   xelrow = pnm_allocrow(width);
+
+   i = 0; 
+   for (row = 0; row < height; row++) {
+       int col;
+       pnm_readpnmrow(input, xelrow, width, maxval, format);
+       for (col = 0; col < width; col++) {
+           if (color) {
+               image->pixels[Y][i] = 
+                   coeff_lu_r * PPM_GETR(xelrow[col]) 
+                   + coeff_lu_g * PPM_GETG(xelrow[col])
+                   + coeff_lu_b * PPM_GETB(xelrow[col]) - 2048;
+               image->pixels[Cb][i] = 
+                   coeff_cb_r * PPM_GETR(xelrow[col]) 
+                   + coeff_cb_g * PPM_GETG(xelrow[col])
+                   + coeff_cb_b * PPM_GETB(xelrow[col]);
+               image->pixels[Cr][i] = 
+                   coeff_cr_r * PPM_GETR(xelrow[col]) 
+                   + coeff_cr_g * PPM_GETG(xelrow[col])
+                   + coeff_cr_b * PPM_GETB(xelrow[col]);
+
+               i++;
+           } else 
+               image->pixels[GRAY][i++] =
+                   PNM_GET1(xelrow[col]) * 4095 / maxval - 2048;
+       }
+   }
+
+   free(xelrow);
+}
+
+
+
+image_t *
+read_image (const char *image_name)
+/*
+ *  Read image 'image_name'.
+ *  
+ *  Return value:
+ *	pointer to the image structure.
+ */
+{
+   FILE	    *input;			/* input stream */
+   image_t  *image;			/* pointer to new image structure */
+   int  width, height;		/* image size */
+   xelval   maxval;         /* Maxval of image */
+   int format;              /* Image's format code */
+   bool_t    color;			/* color image ? (YES/NO) */
+
+   if (image_name == NULL)
+       input = stdin;
+   else
+       input = pm_openr((char*)image_name);
+
+   pnm_readpnminit(input, &width, &height, &maxval, &format);
+
+   if (PNM_FORMAT_TYPE(format) == PPM_FORMAT)
+       color = YES;
+   else
+       color = NO;
+
+   if (width < 32)
+       pm_error("Image must have a width of at least 32 pixels.");
+
+   if (height < 32)
+       pm_error("Image must have a height of at least 32 pixels.");
+
+   image = alloc_image (width, height, color, FORMAT_4_4_4);
+
+   read_image_data(image, input, color, width, height, maxval, format);
+
+   pm_close(input);
+   
+   return image;
+}   
+
+void
+write_image (const char *image_name, const image_t *image)
+/*
+ *  Write given 'image' data to the file 'image_name'.
+ *  
+ *  No return value.
+ */
+{
+   FILE	*output;			/* output stream */
+   int format;
+   int row;
+   int i;     /* Cursor into image->pixel arrays */
+   xel * xelrow;
+   unsigned *gray_clip;			/* clipping table */
+
+   assert (image && image_name);
+   
+   if (image->format == FORMAT_4_2_0)
+   {
+      warning ("Writing of images in 4:2:0 format not supported.");
+      return;
+   }
+   
+   if (image_name == NULL)
+       output = stdout;
+   else if (strcmp(image_name, "-") == 0)
+       output = stdout;
+   else
+       output = pm_openw((char*)image_name);
+
+   gray_clip  = init_clipping ();	/* mapping of int -> unsigned */
+   if (!gray_clip)
+      error (fiasco_get_error_message ());
+   init_chroma_tables ();
+
+   format = image->color ? PPM_TYPE : PGM_TYPE;
+   
+   pnm_writepnminit(output, image->width, image->height, 255, format, 0);
+
+   xelrow = pnm_allocrow(image->width);
+   i = 0;
+   for (row = 0; row < image->height; row++) {
+       int col;
+       for (col = 0; col < image->width; col++) {
+           if (image->color) {
+               word_t yval, cbval, crval;
+
+               yval  = image->pixels[Y][i]  / 16 + 128;
+               cbval = image->pixels[Cb][i] / 16;
+               crval = image->pixels[Cr][i] / 16;
+
+               PPM_ASSIGN(xelrow[col], 
+                          gray_clip[yval + Cr_r_tab[crval]],
+                          gray_clip[yval + Cr_g_tab[crval] + Cb_g_tab [cbval]],
+                          gray_clip[yval + Cb_b_tab[cbval]]);
+
+           } else
+               /* The 16 below should be 4095/255 = 16.0588 */
+               PNM_ASSIGN1(xelrow[col], 
+                           gray_clip[image->pixels[GRAY][i]/16+128]);
+           i++;
+       }
+       pnm_writepnmrow(output, xelrow, 
+                       image->width, 255, format, 0);
+   }
+   pnm_freerow(xelrow);
+
+   pm_close(output);
+}
+
+bool_t
+same_image_type (const image_t *img1, const image_t *img2)
+/*
+ *  Check whether the given images 'img1' and `img2' are of the same type.
+ *
+ *  Return value:
+ *	YES	if images 'img1' and `img2' are of the same type
+ *	NO	otherwise.
+ */
+{
+   assert (img1 && img2);
+   
+   return ((img1->width == img2->width)
+	   && (img1->height == img2->height)
+	   && (img1->color == img2->color)
+	   && (img1->format == img2->format));
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static void
+init_chroma_tables (void)
+/*
+ *  Chroma tables are used to perform fast YCbCr->RGB color space conversion.
+ */
+{
+   int crval, cbval, i;
+
+   if (Cr_r_tab != NULL || Cr_g_tab != NULL ||
+       Cb_g_tab != NULL || Cb_b_tab != NULL)
+      return;
+
+   Cr_r_tab = Calloc (768, sizeof (int));
+   Cr_g_tab = Calloc (768, sizeof (int));
+   Cb_g_tab = Calloc (768, sizeof (int));
+   Cb_b_tab = Calloc (768, sizeof (int));
+
+   for (i = 256; i < 512; i++)
+   {
+      cbval = crval  = i - 128 - 256;
+
+      Cr_r_tab[i] =  1.4022 * crval + 0.5;
+      Cr_g_tab[i] = -0.7145 * crval + 0.5;
+      Cb_g_tab[i] = -0.3456 * cbval + 0.5; 
+      Cb_b_tab[i] =  1.7710 * cbval + 0.5;
+   }
+   for (i = 0; i < 256; i++)
+   {
+      Cr_r_tab[i] = Cr_r_tab[256];
+      Cr_g_tab[i] = Cr_g_tab[256];
+      Cb_g_tab[i] = Cb_g_tab[256]; 
+      Cb_b_tab[i] = Cb_b_tab[256];
+   }
+   for (i = 512; i < 768; i++)
+   {
+      Cr_r_tab[i] = Cr_r_tab[511];
+      Cr_g_tab[i] = Cr_g_tab[511];
+      Cb_g_tab[i] = Cb_g_tab[511]; 
+      Cb_b_tab[i] = Cb_b_tab[511];
+   }
+
+   Cr_r_tab += 256 + 128;
+   Cr_g_tab += 256 + 128;
+   Cb_g_tab += 256 + 128;
+   Cb_b_tab += 256 + 128;
+}
+
diff --git a/converter/other/fiasco/lib/image.h b/converter/other/fiasco/lib/image.h
new file mode 100644
index 00000000..958049f6
--- /dev/null
+++ b/converter/other/fiasco/lib/image.h
@@ -0,0 +1,59 @@
+/*
+ *  image.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/10/22 10:43:56 $
+ *  $Author: hafner $
+ *  $Revision: 5.3 $
+ *  $State: Exp $
+ */
+
+#ifndef _IMAGE_H
+#define _IMAGE_H
+
+#include <stdio.h>
+#include "types.h"
+#include "fiasco.h"
+
+typedef enum {FORMAT_4_4_4, FORMAT_4_2_0} format_e;
+
+typedef struct image
+/*
+ *  Image data
+ */
+{
+   char      id [7];
+   unsigned  reference_count;
+   unsigned  width;			/* Width of the image */
+   unsigned  height;			/* Height of the image */
+   bool_t    color;			/* Color or grayscale image */
+   format_e  format;			/* Pixel format 4:4:4 or 4:2:0 */
+   word_t   *pixels [3];		/* Pixels in short format */
+} image_t;
+
+image_t *
+cast_image (fiasco_image_t *image);
+image_t *
+alloc_image (unsigned width, unsigned height, bool_t color, format_e format);
+image_t *
+clone_image (image_t *image);
+void
+free_image (image_t *image);
+FILE *
+read_pnmheader (const char *image_name, unsigned *width, unsigned *height,
+		bool_t *color);
+image_t *
+read_image (const char *image_name);
+void
+write_image (const char *image_name, const image_t *image);
+bool_t
+same_image_type (const image_t *img1, const image_t *img2);
+
+#endif /* not _IMAGE_H */
+
diff --git a/converter/other/fiasco/lib/list.c b/converter/other/fiasco/lib/list.c
new file mode 100644
index 00000000..9f516c2e
--- /dev/null
+++ b/converter/other/fiasco/lib/list.c
@@ -0,0 +1,258 @@
+/*
+ *  list.c:		List operations	
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:49:37 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#if HAVE_STRING_H
+#	include <string.h>
+#else /* not HAVE_STRING_H */
+#	include <strings.h>
+#endif /* not HAVE_STRING_H */
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "misc.h"
+#include "list.h"
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+list_t *
+alloc_list (size_t size_of_element)
+/*
+ *  List constructor:
+ *  Allocate a new list.
+ *  Size of list element values is given by 'size_of_element'.
+ *
+ *  Return value:
+ *	pointer to an empty list
+ */
+{
+   list_t *new_list = Calloc (1, sizeof (list_t));
+
+   assert (size_of_element > 0);
+
+   new_list->head 	     = NULL;
+   new_list->tail 	     = NULL;
+   new_list->size_of_element = size_of_element;
+
+   return new_list;
+}
+
+void
+free_list (list_t *list)
+/*
+ *  List destructor:
+ *  Discard list and its elements.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	struct 'list' is discarded
+ */
+{
+   assert (list);
+   
+   while (list_remove (list, HEAD, NULL))
+      ;
+   Free (list);
+}
+
+void
+list_insert (list_t *list, pos_e pos, const void *data)
+/*
+ *  Insert a new 'list' element at head ('pos' = HEAD) or
+ *  tail ('pos' = TAIL) of 'list'. 
+ *  'data' is a pointer to a memory segment of size
+ *  'list'->size_of_element containing the value to store.
+ *  The value is directly copied - no references are stored.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	lists current tail or head is replaced by the new element
+ */
+{
+   node_t *element;
+
+   assert (list && data);
+
+   element 	  = Calloc (1, sizeof (node_t));
+   element->value = Calloc (1, list->size_of_element);
+   memcpy (element->value, data, list->size_of_element);
+
+   if (pos == TAIL)
+   {
+      element->next = NULL;
+      element->prev = list->tail;
+      if (list->tail)
+	 list->tail->next = element;
+      list->tail = element;
+      if (!list->head)
+	 list->head = element;
+   }
+   else					/* pos == HEAD */
+   {
+      element->prev = NULL;
+      element->next = list->head;
+      if (list->head)
+	 list->head->prev = element;
+      list->head = element;
+      if (!list->tail)
+	 list->tail = element;
+   }
+}
+
+bool_t
+list_remove (list_t *list, pos_e pos, void *data)
+/*
+ *  Remove 'list' element from head or tail of 'list'.
+ *
+ *  Return value:
+ *	TRUE on success,
+ *	FALSE if list is empty or
+ *	      if list value data is NULL
+ *
+ *  Side effects:
+ *	lists current head or tail is removed
+ *	value of the removed list element (if not NULL) is copied to
+ *      'data' (if 'data' is not NULL)
+ */
+{
+   node_t *element;
+   void	  *valueptr;
+
+   assert (list);
+   
+   if (pos == TAIL)
+   {
+      element = list->tail;
+      if (element)
+      {
+	 list->tail = element->prev;
+	 valueptr   = element->value;
+	 Free (element);
+      }
+      else
+	 valueptr = NULL;
+      if (!list->tail)			/* 'element' was last node */
+	 list->head = NULL;
+   }
+   else					/* pos == HEAD */
+   {
+      element = list->head;
+      if (element)
+      {
+	 list->head = element->next;
+	 valueptr   = element->value;
+	 Free (element);
+      }
+      else
+	 valueptr = NULL;
+      if (!list->head)			/* 'element' was last node */
+	 list->tail = NULL;
+   }
+
+   if (valueptr)			/* copy value of node */
+   {
+      if (data)				
+	 memcpy (data, valueptr, list->size_of_element);
+      Free (valueptr);
+   }
+   
+   return valueptr ? TRUE : FALSE;
+}
+
+bool_t
+list_element_n (const list_t *list, pos_e pos, unsigned n, void *data)
+/*
+ *  Get value of 'list' element number 'n'.
+ *  (First element is list head if 'pos' == HEAD
+ *                 or list tail if 'pos' == TAIL.
+ *   Accordingly, traverse the list in ascending or descending order).
+ *  
+ *  Return value:
+ *	TRUE on success, FALSE if there is no element 'n'
+ *
+ *  Side effects:
+ *	value of list element 'n' is copied to 'data' 
+ */
+{
+   node_t *element;
+
+   assert (list && data);
+   
+   if (pos == HEAD)
+      for (element = list->head; element != NULL && n;
+	   element = element->next, n--)
+	 ;
+   else
+      for (element = list->tail; element != NULL && n;
+	   element = element->prev, n--)
+	 ;
+      
+   if (element)
+   {
+      memcpy (data, element->value, list->size_of_element);
+      return TRUE;
+   }
+   else
+      return FALSE;
+}
+
+unsigned
+list_sizeof (const list_t *list)
+/*
+ *  Count number of 'list' elements.
+ *
+ *  Return value:
+ *	number of 'list' elements.
+ */
+{
+   node_t   *element;
+   unsigned  n = 0;
+
+   assert (list);
+   
+   for (element = list->head; element != NULL; element = element->next)
+      n++;
+
+   return n;
+}
+
+void
+list_foreach (const list_t *list, void (*function)(void *, void *), void *data)
+/*
+ *  Call 'function' for each element of the 'list'.
+ *  Parameters given to 'function' are a pointer to the value of the
+ *  current 'list' element and the user pointer 'data'.
+ *
+ *  No return value.
+ */
+{
+   node_t *element;
+
+   assert (list && function && data);
+   
+   for (element = list->head; element; element = element->next)
+      function (element->value, data);
+}
+
diff --git a/converter/other/fiasco/lib/list.h b/converter/other/fiasco/lib/list.h
new file mode 100644
index 00000000..db7c08b2
--- /dev/null
+++ b/converter/other/fiasco/lib/list.h
@@ -0,0 +1,72 @@
+/*
+ *  list.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:49:37 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _LIST_H
+#define _LIST_H
+
+#include <stdio.h>
+#include <stddef.h>
+
+typedef struct node
+{
+   struct node *prev;			/* pointer to prev list element */
+   struct node *next;			/* pointer to next list element */
+   void	       *value;			/* pointer to value of node */
+} node_t;
+
+typedef struct list
+{
+   node_t *head;
+   node_t *tail;
+   size_t  size_of_element;		/* number of bytes to store value */
+} list_t;
+
+typedef enum {TAIL, HEAD} pos_e;
+
+/*
+ *  Alias definitions for queue and stack
+ */
+
+typedef list_t lqueue_t ;
+#define alloc_queue		alloc_list
+#define free_queue		free_list		
+#define queue_append(q, d)	(list_insert ((q), TAIL, (d)))
+#define queue_remove(q, d)	(list_remove ((q), HEAD, (d)))
+
+typedef list_t lstack_t ;
+#define alloc_stack		alloc_list
+#define free_stack		free_list
+#define stack_push(q, d)	(list_insert ((q), TAIL, (d)))
+#define stack_pop(q, d)		(list_remove ((q), TAIL, (d)))
+
+list_t *
+alloc_list (size_t size_of_element);
+void 
+free_list (list_t *list);
+bool_t
+list_element_n (const list_t *list, pos_e pos, unsigned n, void *data);
+void
+list_foreach (const list_t *list, void (*function)(void *, void *),
+	      void *data);
+void
+list_insert (list_t *list, pos_e pos, const void *data);
+bool_t
+list_remove (list_t *list, pos_e pos, void *data);
+unsigned
+list_sizeof (const list_t *list);
+
+#endif /* not _LIST_H */
+
diff --git a/converter/other/fiasco/lib/macros.h b/converter/other/fiasco/lib/macros.h
new file mode 100644
index 00000000..877abeea
--- /dev/null
+++ b/converter/other/fiasco/lib/macros.h
@@ -0,0 +1,70 @@
+/*
+ *  macros.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:49:37 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _MACROS_H
+#define _MACROS_H
+
+#include <string.h>
+/*******************************************************************************
+
+			  System configuration section
+  
+*******************************************************************************/
+
+#ifndef SEEK_CUR
+#   define SEEK_CUR	1
+#endif /* not SEEK_CUR */
+
+#ifdef WIN32
+#undef max
+#undef min
+#endif /* not WIN32 */
+
+/*****************************************************************************
+
+				Various macros
+  
+*****************************************************************************/
+
+#define streq(str1, str2)	(strcmp ((str1), (str2)) == 0)
+#define strneq(str1, str2)	(strcmp ((str1), (str2)) != 0)
+#define square(x)		((x) * (x))
+#define first_band(color)	((unsigned) ((color) ? Y  : GRAY))
+#define last_band(color)        ((unsigned) ((color) ? Cr : GRAY))
+#define width_of_level(l)	((unsigned) (1 << ((l) >> 1)))
+#define height_of_level(l)	((unsigned) (1 << (((l) + 1) >> 1)))
+#define size_of_level(l)	((unsigned) (1 << (l)))
+#define address_of_level(l)	((unsigned) (size_of_level (l) - 1))
+#define size_of_tree(l)		((unsigned) (address_of_level ((l) + 1)))
+#define is_odd(n)		(abs (n) % 2)
+#ifndef max
+#define max(a,b)		((a) < (b) ? (b) : (a))
+#endif
+#ifndef min
+#define min(a,b)	        ((a) > (b) ? (b) : (a))
+#endif
+#define _(x) (x) 
+
+
+#define	MAXSTRLEN 1024
+#define	MAXSTRLEN_SCANF "%1024s"
+
+typedef enum color {GRAY = 0, Y = 0, Cb = 1, Cr = 2} color_e;
+
+#endif /* _MACROS_H */
+
+
+
diff --git a/converter/other/fiasco/lib/misc.c b/converter/other/fiasco/lib/misc.c
new file mode 100644
index 00000000..02a1314f
--- /dev/null
+++ b/converter/other/fiasco/lib/misc.c
@@ -0,0 +1,563 @@
+/*
+ *  misc.c:		Some usefull functions, that don't fit in one of 
+ *			the other files and that are needed by at least
+ *			two modules. 
+ *
+ *  Written by:		Stefan Frank
+ *			Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:49:37 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <ctype.h>
+
+#ifdef TIME_WITH_SYS_TIME
+#	include <sys/time.h>
+#	include <time.h>
+#else  /* not TIME_WITH_SYS_TIME */
+#	if HAVE_SYS_TIME_H
+#		include <sys/time.h>
+#	else /* not HAVE_SYS_TIME_H */
+#		include <time.h>
+#	endif /* not HAVE_SYS_TIME_H */
+#endif /* not TIME_WITH_SYS_TIME */
+
+#if STDC_HEADERS
+#	include <stdlib.h>
+#endif /* not STDC_HEADERS */
+
+#if HAVE_STRING_H
+#	include <string.h>
+#else /* not HAVE_STRING_H */
+#	include <strings.h>
+#endif /* not HAVE_STRING_H */
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "bit-io.h"
+#include "misc.h"
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static void
+remove_comments (FILE *file);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+void *
+Calloc (size_t n, size_t size)
+/*
+ *  Allocate memory like calloc ().
+ *
+ *  Return value: Pointer to the new block of memory on success,
+ *		  otherwise the program is terminated.
+ */
+{
+   void	*ptr;				/* pointer to the new memory block */
+
+   if (n <= 0 || size <= 0)
+      error ("Can't allocate memory for %d items of size %d",
+	     (int) n, (int) size);
+
+   ptr = calloc (n, size);
+   if (!ptr)
+      error ("Out of memory!");
+
+   return ptr;
+}
+
+void
+Free (void *memory)
+/*
+ *  Free memory given by the pointer 'memory'
+ *
+ *  No return value.
+ */
+{
+   if (memory != NULL)
+      free (memory);
+   else
+      warning ("Can't free memory block <NULL>.");
+}
+
+unsigned
+prg_timer (clock_t *last_timer, enum action_e action)
+/*
+ *  If 'action' == START then store current value of system timer.
+ *  If 'action' == STOP	 then compute number of elapsed micro seconds since
+ *			 the last time 'prg_timer' was called
+ *			 with 'action' == START.
+ *
+ *  Return value:
+ *	Number of elapsed micro seconds if 'action' == STOP
+ *	0				if 'action' == START
+ *
+ *  Side effects:
+ *	last_timer is set to current timer if action == START
+ */
+{
+   assert (last_timer);
+   
+   if (action == START)
+   {
+      *last_timer = clock ();
+      return 0;
+   }
+   else
+      return (clock () - *last_timer) / (CLOCKS_PER_SEC / 1000.0);
+}
+
+real_t 
+read_real (FILE *infile)
+/* 
+ *  Read one real value from the given input stream 'infile'.
+ *  
+ *  Return value:
+ *	real value on success
+ */
+{
+   float input;
+
+   assert (infile);
+   
+   remove_comments (infile);
+   if (fscanf(infile, "%f", &input) != 1)
+      error("Can't read float value!");
+
+   return (real_t) input;
+}
+
+int 
+read_int (FILE *infile)
+/* 
+ *  Read one integer value from the given input stream 'infile'.
+ *
+ *  Return value:
+ *	integer value on success
+ */
+{
+   int input;				/* integer */
+
+   assert (infile);
+   
+   remove_comments (infile);
+   if (fscanf(infile, "%d", &input) != 1)
+      error("Can't read integer value!");
+
+   return input;
+}
+   
+static void
+remove_comments (FILE *file)
+/*
+ *  Remove shell/pgm style comments (#) from the input 'file'
+ *
+ *  No return value.
+ */
+{
+   int c;				/* current character */
+   
+   assert (file);
+   
+   do
+   {
+      while (isspace(c = getc (file)))
+	 ;
+      if (c == EOF)
+	 error ("EOF reached, input seems to be truncated!");
+      if (c == '#')
+      {
+	 int dummy;
+	 
+	 while (((dummy = getc (file)) != '\n') && dummy != EOF)
+	    ;
+	 if (dummy == EOF)
+	    error ("EOF reached, input seems to be truncated!");
+      }
+      else 
+	 ungetc (c, file);
+   } while (c == '#');
+}
+
+void
+write_rice_code (unsigned value, unsigned rice_k, bitfile_t *output)
+/*
+ *  Write 'value' to the stream 'output' using Rice coding with base 'rice_k'.
+ *
+ *  No return value.
+ */
+{
+   unsigned unary;			/* unary part of Rice Code */
+
+   assert (output);
+   
+   for (unary = value >> rice_k; unary; unary--)
+      put_bit (output, 1);
+   put_bit (output, 0);
+   put_bits (output, value & ((1 << rice_k) - 1), rice_k);
+}
+
+unsigned
+read_rice_code (unsigned rice_k, bitfile_t *input)
+/*
+ *  Read a Rice encoded integer (base 'rice_k') from the stream 'input'.
+ *
+ *  Return value:
+ *	decoded integer
+ */
+{
+   unsigned unary;			/* unary part of Rice code */
+   
+   assert (input);
+   
+   for (unary = 0; get_bit (input); unary++) /* unary part */
+      ;
+
+   return (unary << rice_k) | get_bits (input, rice_k);
+}
+
+void
+write_bin_code (unsigned value, unsigned maxval, bitfile_t *output)
+/*
+ *  Write 'value' to the stream 'output' using an adjusted binary code
+ *  based on given 'maxval'.
+ *
+ *  No return value.
+ */
+{
+   unsigned k;
+   unsigned r;
+   
+   assert (output && maxval && value <= maxval);
+
+   k = log2 (maxval + 1);
+   r = (maxval + 1) % (1 << k);
+
+   if (value < maxval + 1 - 2 * r)	/* 0, ... , maxval - 2r */
+      put_bits (output, value, k);
+   else					/* maxval - 2r + 1, ..., maxval */
+      put_bits (output, value + maxval + 1 - 2 * r, k + 1);
+}
+
+unsigned
+read_bin_code (unsigned maxval, bitfile_t *input)
+/*
+ *  Read a bincode encoded integer from the stream 'input'.
+ *
+ *  Return value:
+ *	decoded integer
+ */
+{
+   unsigned k;
+   unsigned r;
+   unsigned value;
+   
+   assert (input);
+
+   k = log2 (maxval + 1);
+   r = (maxval + 1) % (1 << k);
+
+   value = get_bits (input, k);
+   if (value < maxval + 1 - 2 * r)
+      return value;
+   else
+   {
+      value <<= 1;
+      if (get_bit (input))
+	 value++;
+      return value - maxval - 1 + 2 * r;
+   }
+}
+
+unsigned
+bits_rice_code (unsigned value, unsigned rice_k)
+/*
+ *  Compute number of bits needed for coding integer 'value'
+ *  with given Rice code 'rice_k'.
+ *
+ *  Return value:
+ *	number of bits
+ */
+{
+   unsigned unary;
+   unsigned bits = 0;
+   
+   for (unary = value >> rice_k; unary; unary--)
+      bits++;
+   bits += rice_k + 1;
+
+   return bits;
+}
+
+unsigned
+bits_bin_code (unsigned value, unsigned maxval)
+/*
+ *  Compute number of bits needed for coding integer 'value'
+ *  with adjusted binary code of given maximum value 'maxval'.
+ *
+ *  Return value:
+ *	number of bits
+ */
+{
+   unsigned k;
+   unsigned r;
+
+   assert (maxval && value <= maxval);
+
+   k = log2 (maxval + 1);
+   r = (maxval + 1) % (1 << k);
+
+   return value < maxval + 1 - 2 * r ? k : k + 1;
+}
+
+unsigned *
+init_clipping (void)
+/*
+ *  Initialize the clipping tables
+ *
+ *  Return value:
+ *	pointer to clipping table
+ */
+{
+   static unsigned *gray_clip = NULL;	/* clipping array */
+
+   if (gray_clip == NULL)		/* initialize clipping table */
+   {
+      int i;				/* counter */
+
+      gray_clip  = calloc (256 * 3, sizeof (unsigned));
+      if (!gray_clip)
+      {
+	 set_error (_("Out of memory."));
+	 return NULL;
+      }
+      gray_clip += 256;
+
+      for (i = -256; i < 512; i++)
+	 if (i < 0)
+	    gray_clip [i] = 0;
+	 else if (i > 255)
+	    gray_clip [i] = 255;
+	 else
+	    gray_clip [i] = i;
+   }
+
+   return gray_clip;
+}
+
+#ifndef HAVE_MEMMOVE
+void *
+memmove (void *v_dst, const void *v_src, size_t n)
+/*
+ *  Copy 'n' bytes from memory area 'src' to memory area 'dest'.
+ *  The memory areas may overlap.
+ *
+ *  Return value:
+ *	pointer 'dest'
+ */
+{
+   byte_t	*to, *dst = (byte_t *) v_dst;
+   const byte_t	*from, *src = (byte_t *) v_src;
+   
+   assert (v_dst && v_src);
+   
+   if (dst <= src)
+   {
+      from = src;
+      to   = dst;
+      for (; n; n--)
+	 *to++ = *from++;
+   }
+   else
+   { 
+      from = src + (n - 1);
+      to   = dst + (n - 1);
+      for (; n; n--)
+	 *to-- = *from--;
+   }
+   
+   return v_dst;
+}
+#endif /* not HAVE_MEMMOVE */
+
+#ifndef HAVE_STRDUP
+char *
+strdup (const char *s)
+/*
+ *  Duplicate given string 's'.
+ *
+ *  Return value:
+ *	pointer to new string value
+ */
+{
+   assert (s);
+   
+   return strcpy (Calloc (strlen (s) + 1, sizeof (char)), s);
+}
+#endif /* not HAVE_STRDUP */
+
+/* Note that some systems have a "log2()" in the math library and some
+   have a "log2" macro.  So we name ours Log2.  But to avoid lots of
+   differences from the original fiasco source code, we define a
+   macro log2 in config.h to expand to Log2.
+   */
+double
+Log2 (double x)
+/*
+ *  Return value:
+ *	base-2 logarithm of 'x'
+ */
+{
+   return log (x) / 0.69314718;
+}
+
+#ifndef HAVE_STRCASECMP
+bool_t
+strcaseeq (const char *s1, const char *s2)
+/*
+ *  Compare strings 's1' and 's2', ignoring  the  case of the characters.
+ *
+ *  Return value:
+ *	TRUE if strings match, else FALSE
+ */
+{
+   bool_t  matched;
+   char	  *ls1, *ls2, *ptr;
+
+   assert (s1 && s2);
+   
+   ls1 = strdup (s1);
+   ls2 = strdup (s2);
+   
+   for (ptr = ls1; *ptr; ptr++)
+      *ptr = tolower (*ptr);
+   for (ptr = ls2; *ptr; ptr++)
+      *ptr = tolower (*ptr);
+
+   matched = streq (ls1, ls2) ? YES : NO;
+
+   Free (ls1);
+   Free (ls2);
+   
+   return matched;
+}
+#endif /* not HAVE_STRCASECMP */
+
+real_t
+variance (const word_t *pixels, unsigned x0, unsigned y0,
+	  unsigned width, unsigned height, unsigned cols)
+/*
+ *  Compute variance of subimage ('x0', y0', 'width', 'height') of 
+ *  the image data given by 'pixels' ('cols' is the number of pixels
+ *  in one row of the image).
+ *
+ *  Return value:
+ *	variance
+ */
+{
+   real_t   average;			/* average of pixel values */
+   real_t   variance;			/* variance of pixel values */
+   unsigned x, y;			/* pixel counter */
+   unsigned n;				/* number of pixels */
+
+   assert (pixels);
+   
+   for (average = 0, n = 0, y = y0; y < y0 + height; y++)
+      for (x = x0; x < min (x0 + width, cols); x++, n++)
+	 average += pixels [y * cols + x] / 16;
+
+   average /= n;
+
+   for (variance = 0, y = y0; y < y0 + height; y++)
+      for (x = x0; x < min (x0 + width, cols); x++)
+	 variance += square ((pixels [y * cols + x] / 16) - average);
+
+   return variance;
+}
+
+int
+sort_asc_word (const void *value1, const void *value2)
+/*
+ *  Sorting function for quicksort.
+ *  Smallest values come first.
+ */
+{
+   if (* (word_t *) value1 < * (word_t *) value2)
+      return -1;
+   else if (* (word_t *) value1 > * (word_t *) value2)
+      return +1;
+   else
+      return 0;
+}
+
+int
+sort_desc_word (const void *value1, const void *value2)
+/*
+ *  Sorting function for quicksort.
+ *  Largest values come first.
+ */
+{
+   if (* (word_t *) value1 > * (word_t *) value2)
+      return -1;
+   else if (* (word_t *) value1 < * (word_t *) value2)
+      return +1;
+   else
+      return 0;
+}
+
+int
+sort_asc_pair (const void *value1, const void *value2)
+/*
+ *  Sorting function for quicksort.
+ *  Smallest values come first.
+ */
+{
+   word_t v1 = ((pair_t *) value1)->key;
+   word_t v2 = ((pair_t *) value2)->key;
+   
+   if (v1 < v2)
+      return -1;
+   else if (v1 > v2)
+      return +1;
+   else
+      return 0;
+}
+
+int
+sort_desc_pair (const void *value1, const void *value2)
+/*
+ *  Sorting function for quicksort.
+ *  Largest values come first.
+ */
+{
+   word_t v1 = ((pair_t *) value1)->key;
+   word_t v2 = ((pair_t *) value2)->key;
+
+   if (v1 > v2)
+      return -1;
+   else if (v1 < v2)
+      return +1;
+   else
+      return 0;
+}
diff --git a/converter/other/fiasco/lib/misc.h b/converter/other/fiasco/lib/misc.h
new file mode 100644
index 00000000..29456590
--- /dev/null
+++ b/converter/other/fiasco/lib/misc.h
@@ -0,0 +1,98 @@
+/*
+ *  misc.h
+ *
+ *  Written by:		Stefan Frank
+ *			Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:49:37 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _MISC_H
+#define _MISC_H
+
+#include "config.h"
+
+#if TIME_WITH_SYS_TIME
+#	include <sys/time.h>
+#	include <time.h>
+#else  /* not TIME_WITH_SYS_TIME */
+#	if HAVE_SYS_TIME_H
+#		include <sys/time.h>
+#	else /* not HAVE_SYS_TIME_H */
+#		include <time.h>
+#	endif /* not HAVE_SYS_TIME_H */
+#endif /* not TIME_WITH_SYS_TIME */
+
+#include <stdio.h>
+#include "types.h"
+#include "bit-io.h"
+
+enum action_e {START, STOP};
+
+void *
+Calloc (size_t n, size_t size);
+void
+Free (void *memory);
+unsigned
+prg_timer (clock_t *ptimer, enum action_e action);
+int 
+read_int(FILE *infile);
+real_t 
+read_real(FILE *infile);
+unsigned
+read_rice_code (unsigned rice_k, bitfile_t *input);
+void
+write_rice_code (unsigned value, unsigned rice_k, bitfile_t *output);
+void
+write_bin_code (unsigned value, unsigned maxval, bitfile_t *output);
+unsigned
+bits_bin_code (unsigned value, unsigned maxval);
+unsigned
+bits_rice_code (unsigned value, unsigned rice_k);
+unsigned
+read_bin_code (unsigned maxval, bitfile_t *input);
+unsigned *
+init_clipping (void);
+real_t
+variance (const word_t *pixels, unsigned x0, unsigned y0,
+	  unsigned width, unsigned height, unsigned cols);
+
+#ifndef HAVE_MEMMOVE
+void *
+memmove(void *dest, const void *src, size_t n);
+#endif /* not HAVE_MEMMOVE */
+
+double
+Log2 (double x);
+#ifndef HAVE_STRDUP
+char *
+strdup (const char *s);
+#endif
+#ifndef HAVE_STRCASECMP
+bool_t
+strcaseeq (const char *s1, const char *s2);
+#else  /* HAVE_STRCASECMP */
+int
+strcasecmp (const char *s1, const char *s2);
+#define strcaseeq(s1, s2) (strcasecmp ((s1), (s2)) == 0)
+#endif /* HAVE_STRCASECMP */
+
+int
+sort_asc_word (const void *value1, const void *value2);
+int
+sort_desc_word (const void *value1, const void *value2);
+int
+sort_asc_pair (const void *value1, const void *value2);
+int
+sort_desc_pair (const void *value1, const void *value2);
+
+#endif /* not _MISC_H */
+
diff --git a/converter/other/fiasco/lib/mvcode.c b/converter/other/fiasco/lib/mvcode.c
new file mode 100644
index 00000000..d9ce91e2
--- /dev/null
+++ b/converter/other/fiasco/lib/mvcode.c
@@ -0,0 +1,14 @@
+#include "mvcode.h"
+
+unsigned mv_code_table [33][2] =
+/*
+ *  MPEG's huffman code for vector components. Format: code_value, length
+ */
+{
+   {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}
+};
+
diff --git a/converter/other/fiasco/lib/mvcode.h b/converter/other/fiasco/lib/mvcode.h
new file mode 100644
index 00000000..f43f2081
--- /dev/null
+++ b/converter/other/fiasco/lib/mvcode.h
@@ -0,0 +1,6 @@
+#ifndef MVCODE_H_INCLUDED
+#define MVCODE_H_INCLUDED
+
+extern unsigned mv_code_table [33][2];
+
+#endif
diff --git a/converter/other/fiasco/lib/rpf.c b/converter/other/fiasco/lib/rpf.c
new file mode 100644
index 00000000..ac7d48ca
--- /dev/null
+++ b/converter/other/fiasco/lib/rpf.c
@@ -0,0 +1,223 @@
+/*
+ *  rpf.c:		Conversion of float to reduced precision format values
+ *
+ *  Written by:		Stefan Frank
+ *			Richard Krampfl
+ *			Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:49:37 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "misc.h"
+#include "rpf.h"
+
+/* 
+ * CAUTION: The IEEE float format must be used by your compiler,
+ *          or all following code is void!
+ */
+
+#ifdef WORDS_BIGENDIAN
+/*
+ *  Big-Endian Architecture (e.g. SUN, Motorola)
+ *  Memory representation of integer 0x00112233 is 00,11,22,33
+ */
+
+enum real_bytes {BYTE_0, BYTE_1, BYTE_2, BYTE_3};
+
+#else  /* not WORDS_BIGENDIAN */
+/*
+ *  Little-Endian Architecture (e.g. Intel, VAX, Alpha)
+ *  Memory representation of integer 0x00112233 is 33,22,11,00
+ */
+
+enum real_bytes {BYTE_3, BYTE_2, BYTE_1, BYTE_0};
+
+#endif /* not WORDS_BIGENDIAN */
+
+const int RPF_ZERO = -1;
+
+/*****************************************************************************
+
+			       private code
+  
+*****************************************************************************/
+
+int
+rtob (real_t f, const rpf_t *rpf)
+/*
+ *  Convert real number 'f' into fixed point format.
+ *  The real number in [-'range'; +'range'] is scaled to [-1 ; +1].
+ *  Sign and the first 'precision' - 1 bits of the mantissa are
+ *  packed into one integer.  
+ *
+ *  Return value:
+ *	real value in reduced precision format
+ */
+{  
+   unsigned int	mantissa;
+   int		exponent, sign;
+   union
+   {
+      float f;
+      unsigned char c[4];
+   } v;					/* conversion dummy */
+
+   f  /= rpf->range;			/* scale f to [-1,+1] */	
+   v.f = f;
+
+   /*
+    *  Extract mantissa (23 Bits), exponent (8 Bits) and sign (1 Bit)
+    */
+
+   mantissa = ((((v.c[BYTE_1] & 127) << 8 ) | v.c[BYTE_2]) << 8) | v.c[BYTE_3];
+   exponent = (((v.c[BYTE_0] & 127) << 1) | (v.c[BYTE_1] & 128 ? 1 : 0)) - 126;
+   sign     = v.c[BYTE_0] & 128 ? 1 : 0;
+		
+   /*
+    *  Generate reduced precision mantissa.
+    */
+   mantissa >>= 1;				/* shift 1 into from left */
+   mantissa  |= (1 << 22);
+   if (exponent > 0) 
+      mantissa <<= exponent;
+   else
+      mantissa >>= -exponent;  
+   
+   mantissa >>= (23 - rpf->mantissa_bits - 1);
+
+   mantissa +=  1;			/* Round last bit. */
+   mantissa >>= 1;
+   
+   if (mantissa == 0)			/* close to zero */
+      return RPF_ZERO;
+   else if (mantissa >= (1U << rpf->mantissa_bits)) /* overflow */
+      return sign;
+   else
+      return ((mantissa & ((1U << rpf->mantissa_bits) - 1)) << 1) | sign;
+}
+
+float
+btor (int binary, const rpf_t *rpf)
+/*
+ *  Convert value 'binary' in reduced precision format to a real value.
+ *  For more information refer to function lin_rtob() above.
+ *
+ *  Return value:
+ *	converted value
+ */
+{
+   unsigned int	mantissa;
+   int		sign, exponent;
+   union
+   {
+      float f;
+      unsigned char c[4];
+   } value;
+
+   if (binary == RPF_ZERO)
+      return 0;
+
+   if (binary < 0 || binary >= 1 << (rpf->mantissa_bits + 1))
+      error ("Reduced precision format: value %d out of range.", binary);
+
+   /*
+    *  Restore IEEE float format:
+    *  mantissa (23 Bits), exponent (8 Bits) and sign (1 Bit)
+    */
+   
+   sign       = binary & 1;
+   mantissa   = (binary & ((1 << (rpf->mantissa_bits + 1)) - 1)) >> 1; 
+   mantissa <<= (23 - rpf->mantissa_bits);
+   exponent   = 0;
+
+   if (mantissa == 0)
+   {
+      value.f = (sign ? -1.0 : 1.0);
+   }
+   else
+   {
+      while (!(mantissa & (1 << 22)))	/* normalize mantissa */
+      {
+	 exponent--;
+	 mantissa <<= 1;
+      }
+      mantissa <<= 1;
+
+      value.c[BYTE_0] = (sign << 7) | ((exponent + 126) >> 1);
+      value.c[BYTE_1] = (((exponent + 126) & 1) << 7)
+			| ((mantissa  >> 16) & 127);
+      value.c[BYTE_2] = (mantissa >> 8) & 255;
+      value.c[BYTE_3] = mantissa & 255;
+   }
+   
+   return value.f * rpf->range;		/* expand [ -1 ; +1 ] to
+					   [ -range ; +range ] */
+}
+
+rpf_t *
+alloc_rpf (unsigned mantissa, fiasco_rpf_range_e range)
+/*
+ *  Reduced precision format constructor.
+ *  Allocate memory for the rpf_t structure.
+ *  Number of mantissa bits is given by `mantissa'.
+ *  The range of the real values is in the interval [-`range', +`range'].
+ *  In case of invalid parameters, a structure with default values is
+ *  returned. 
+ *
+ *  Return value
+ *	pointer to the new rpf structure
+ */
+{
+   rpf_t *rpf = Calloc (1, sizeof (rpf_t));
+   
+   if (mantissa < 2)
+   {
+      warning (_("Size of RPF mantissa has to be in the interval [2,8]. "
+		 "Using minimum value 2.\n"));
+      mantissa = 2;
+   }
+   else if (mantissa > 8)
+   {
+      warning (_("Size of RPF mantissa has to be in the interval [2,8]. "
+		 "Using maximum value 8.\n"));
+      mantissa = 2;
+   }
+
+   rpf->mantissa_bits = mantissa;
+   rpf->range_e       = range;
+   switch (range)
+   {
+      case FIASCO_RPF_RANGE_0_75:
+	 rpf->range = 0.75;
+	 break;
+      case FIASCO_RPF_RANGE_1_50:
+	 rpf->range = 1.50;
+	 break;
+      case FIASCO_RPF_RANGE_2_00:
+	 rpf->range = 2.00;
+	 break;
+      case FIASCO_RPF_RANGE_1_00:
+	 rpf->range = 1.00;
+	 break;
+      default:
+	 warning (_("Invalid RPF range specified. Using default value 1.0."));
+	 rpf->range   = 1.00;
+	 rpf->range_e = FIASCO_RPF_RANGE_1_00;
+	 break;
+   }
+   return rpf;
+}
diff --git a/converter/other/fiasco/lib/rpf.h b/converter/other/fiasco/lib/rpf.h
new file mode 100644
index 00000000..ba3ff6be
--- /dev/null
+++ b/converter/other/fiasco/lib/rpf.h
@@ -0,0 +1,47 @@
+/*
+ *  rpf.h
+ *
+ *  Written by:		Stefan Frank
+ *			Richard Krampfl
+ *			Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:49:37 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _RPF_H
+#define _RPF_H
+
+#include "types.h"
+#include "fiasco.h"
+
+typedef struct rpf
+{
+   unsigned    	      mantissa_bits;	/* number of bits used for mantissa */
+   real_t      	      range;		/* scale value to [-range, +range] */
+   fiasco_rpf_range_e range_e;
+} rpf_t;
+
+int
+rtob (real_t real, const rpf_t *rpf);
+real_t 
+btor (int b, const rpf_t *rpf);
+rpf_t *
+alloc_rpf (unsigned mantissa, fiasco_rpf_range_e range);
+
+extern const int RPF_ZERO;
+
+#endif /* not _RPF_H */
+
+
+
+
+
+
diff --git a/converter/other/fiasco/lib/types.h b/converter/other/fiasco/lib/types.h
new file mode 100644
index 00000000..16d8028c
--- /dev/null
+++ b/converter/other/fiasco/lib/types.h
@@ -0,0 +1,38 @@
+/*
+ *  types.h
+ *
+ *  Written by:     Ullrich Hafner
+ *      
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:49:37 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _FIASCO_TYPES_H
+#define _FIASCO_TYPES_H
+
+#undef FALSE
+#undef NO
+#undef TRUE
+#undef YES
+
+enum fiasco_boolean { NO = 0, FALSE = 0, YES = 1, TRUE = 1};
+
+typedef float                real_t;
+typedef enum fiasco_boolean  bool_t;
+typedef unsigned char        byte_t;
+typedef short                word_t;
+typedef unsigned short       u_word_t;
+typedef struct pair
+{
+   word_t key;
+   word_t value;
+} pair_t;
+
+#endif
diff --git a/converter/other/fiasco/output/Makefile b/converter/other/fiasco/output/Makefile
new file mode 100644
index 00000000..3bdc4635
--- /dev/null
+++ b/converter/other/fiasco/output/Makefile
@@ -0,0 +1,26 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../../..
+  BUILDDIR = $(SRCDIR)
+endif
+FIASCOSUBDIR = converter/other/fiasco
+SUBDIR = $(FIASCOSUBDIR)/output
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+OBJECTS =  matrices.o mc.o nd.o tree.o weights.o write.o
+
+MERGE_OBJECTS = $(OBJECTS)
+
+INCLUDES = -I$(SRCDIR)/$(FIASCOSUBDIR) \
+	   -I$(SRCDIR)/$(FIASCOSUBDIR)/lib \
+	   -I$(SRCDIR)/$(FIASCOSUBDIR)/codec 
+
+all: libfiasco_output.a
+
+include $(SRCDIR)/Makefile.common
+
+libfiasco_output.a: $(OBJECTS)
+	$(AR) -rc $@ $(OBJECTS)
+	$(RANLIB) $@
+
diff --git a/converter/other/fiasco/output/matrices.c b/converter/other/fiasco/output/matrices.c
new file mode 100644
index 00000000..fd8d31e2
--- /dev/null
+++ b/converter/other/fiasco/output/matrices.c
@@ -0,0 +1,547 @@
+/*
+ *  matrices.c:		Output of transitions matrices
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/* NETPBM: When you call delta_encoding() with last_domain < 4, it
+   crashes.  And we have seen it happen.
+*/
+
+/*
+ *  $Date: 2000/06/14 20:50:31 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#if STDC_HEADERS
+#	include <stdlib.h>
+#endif /* not STDC_HEADERS */
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "wfa.h"
+#include "bit-io.h"
+#include "arith.h"
+#include "misc.h"
+#include "wfalib.h"
+
+#include "matrices.h"
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static unsigned
+delta_encoding (bool_t use_normal_domains, bool_t use_delta_domains,
+		const wfa_t *wfa, unsigned last_domain, bitfile_t *output);
+static unsigned
+column_0_encoding (const wfa_t *wfa, unsigned last_row, bitfile_t *output);
+static unsigned
+chroma_encoding (const wfa_t *wfa, bitfile_t *output);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+unsigned
+write_matrices (bool_t use_normal_domains, bool_t use_delta_domains,
+		const wfa_t *wfa, bitfile_t *output)
+/*
+ *  Write transition matrices of 'wfa' to stream 'output'.
+ *
+ *  Return value:
+ *	number of transitions encoded
+ */
+{
+   unsigned root_state;			/* root of luminance */
+   unsigned total = 0;			/* number of transitions */
+   
+   root_state = wfa->wfainfo->color
+		? wfa->tree [wfa->tree [wfa->root_state][0]][0]
+		: wfa->root_state;
+   
+   total  = column_0_encoding (wfa, root_state, output);
+
+   total += delta_encoding (use_normal_domains, use_delta_domains,
+			    wfa, root_state, output);
+   
+   if (wfa->wfainfo->color)
+      total += chroma_encoding (wfa, output);
+   
+   return total;
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static unsigned
+delta_encoding (bool_t use_normal_domains, bool_t use_delta_domains,
+		const wfa_t *wfa, unsigned last_domain, bitfile_t *output)
+/*
+ *  Write transition matrices with delta coding to stream 'input'.
+ *  'last_domain' is the maximum state number used as domain image.
+ *
+ *  Return value:
+ *	number of non-zero matrix elements (WFA edges)
+ */
+{
+   range_sort_t	rs;			/* ranges are sorted as in the coder */
+   unsigned	max_domain;		/* dummy used for recursion */
+   unsigned	total = 0;
+      
+   /*
+    *  Generate a list of range blocks.
+    *  The order is the same as in the coder.
+    */
+   rs.range_state      = Calloc ((last_domain + 1) * MAXLABELS,
+				 sizeof (u_word_t));
+   rs.range_label      = Calloc ((last_domain + 1) * MAXLABELS,
+				 sizeof (byte_t));
+   rs.range_max_domain = Calloc ((last_domain + 1) * MAXLABELS,
+				 sizeof (u_word_t));
+   rs.range_subdivided = Calloc ((last_domain + 1) * MAXLABELS,
+				 sizeof (bool_t));
+   rs.range_no	       = 0;
+   max_domain 	       = wfa->basis_states - 1;
+   sort_ranges (last_domain, &max_domain, &rs, wfa);
+   
+   /*
+    *  Compute and write distribution of #edges
+    */
+   {
+      unsigned state, label;
+      unsigned edge;
+      unsigned count [MAXEDGES + 1];
+      unsigned n;
+      unsigned edges = 0;
+      unsigned M     = 0;
+      unsigned bits  = bits_processed (output);
+      
+      for (n = 0; n < MAXEDGES + 1; n++)
+	 count [n] = 0;
+      
+      for (state = wfa->basis_states; state <= last_domain; state++)
+	 for (label = 0; label < MAXLABELS; label++)
+	    if (isrange (wfa->tree [state][label]))
+	    {
+	       for (edge = 0; isedge (wfa->into [state][label][edge]); edge++)
+		  ;
+	       count [edge]++;
+	       edges++;
+	       M = max (edge, M);
+	    }
+      write_rice_code (M, 3, output);
+      for (n = 0; n <= M; n++)
+/* NETPBM: The following causes a crash when last_domain < 4, because
+   it requests writing of a negative number of bits.  And we have seen
+   last_domain = 3.  But we have no clue what last_domain means, or 
+   even what a rice code is, so we don't know where the error lies.
+   -Bryan 2001.02.09 
+*/
+	 write_rice_code (count [n], (int) log2 (last_domain) - 2, output);
+
+      /*
+       * Arithmetic coding of values */
+      {
+	 unsigned  range;
+	 model_t  *elements = alloc_model (M + 1, 0, 0, count);
+	 arith_t  *encoder  = alloc_encoder (output);
+	       
+	 for (range = 0; range < rs.range_no; range++)
+	    if (!rs.range_subdivided [range])
+	    {
+	       state = rs.range_state [range];
+	       label = rs.range_label [range];
+	       for (edge = 0; isedge (wfa->into [state][label][edge]); edge++)
+		  ;
+	       
+	       encode_symbol (edge, encoder, elements);
+	    }
+	 free_encoder (encoder);
+	 free_model (elements);
+      }
+      debug_message ("delta-#edges: %5d bits. (%5d symbols => %5.2f bps)",
+		     bits_processed (output) - bits, edges,
+		     edges > 0 ? ((bits_processed (output) - bits) /
+				  (double) edges) : 0);
+   }
+
+   /*
+    *  Write matrix elements
+    */
+   {
+      unsigned	bits  	 = bits_processed (output);
+      u_word_t *mapping1 = Calloc (wfa->states, sizeof (u_word_t));
+      u_word_t *mapping2 = Calloc (wfa->states, sizeof (u_word_t));
+      unsigned	range;
+
+      put_bit (output, use_normal_domains);
+      put_bit (output, use_delta_domains);
+      
+      /*
+       *  Generate array of states which are admitted domains.
+       *  When coding intra frames 'mapping1' == 'mapping2' otherwise
+       *  'mapping1' is a list of 'normal' domains which are admitted for 
+       *             coding intra blocks
+       *  'mapping2' is a list of 'delta' domains which are admitted for
+       *             coding the motion compensated prediction error 
+       */
+      {
+	 unsigned n1, n2, state;
+	 
+	 for (n1 = n2 = state = 0; state < wfa->states; state++)
+	 {
+	    mapping1 [state] = n1;
+	    if (usedomain (state, wfa)
+		&& (state < wfa->basis_states || use_delta_domains
+		    || !wfa->delta_state [state]))
+	       n1++;
+	    
+	    mapping2 [state] = n2;
+	    if (usedomain (state, wfa)
+		&& (state < wfa->basis_states || use_normal_domains
+		    || wfa->delta_state [state]))
+	       n2++;
+	 }
+	 debug_message ("# normal states = %d, # delta states = %d,"
+			" # WFA states = %d", n1, n2, wfa->states);
+      }
+      
+      for (range = 0; range < rs.range_no; range++)
+	 if (!rs.range_subdivided [range])
+	 {
+	    unsigned  state = rs.range_state [range];
+	    unsigned  label = rs.range_label [range];
+	    unsigned  last  = 1;
+	    u_word_t *mapping;
+	    unsigned  max_value;
+	    unsigned  edge;
+	    word_t    domain;
+
+	    if (wfa->delta_state [state] ||
+		wfa->mv_tree [state][label].type != NONE)
+	       mapping = mapping2;
+	    else
+	       mapping = mapping1;
+	    
+	    max_value = mapping [rs.range_max_domain [range]];
+	    
+	    for (edge = 0; isedge (domain = wfa->into [state][label][edge]);
+		 edge++)
+	       if (domain > 0)
+	       {
+		  total++;
+		  if (max_value - last)
+		  {
+		     write_bin_code (mapping [domain] - last,
+				     max_value - last, output);
+		     last = mapping [domain] + 1;
+		  }
+	       }
+	 }
+
+      debug_message ("delta-index:  %5d bits. (%5d symbols => %5.2f bps)",
+		     bits_processed (output) - bits, total,
+		     total > 0 ? ((bits_processed (output) - bits) /
+				  (double) total) : 0);
+      Free (mapping1);
+      Free (mapping2);
+   }
+   
+   Free (rs.range_state);
+   Free (rs.range_label);
+   Free (rs.range_max_domain);
+   Free (rs.range_subdivided);
+
+   return total;
+}
+
+static unsigned
+column_0_encoding (const wfa_t *wfa, unsigned last_row, bitfile_t *output)
+/*
+ *  Write column 0 of the transition matrices of the 'wfa' to stream 'output'
+ *  with quasi arithmetic coding.
+ *  All rows from 'wfa->basis_states' up to 'last_row' are decoded.
+ *
+ *  Return value:
+ *	number of non-zero matrix elements (WFA edges)
+ */
+{
+   u_word_t  high;			/* Start of the current code range */
+   u_word_t  low;			/* End of the current code range */
+   unsigned *prob;			/* probability array */
+   unsigned  row;			/* current matrix row */
+   unsigned  label;			/* current matrix label */
+   unsigned  underflow;			/* Underflow bits */
+   unsigned  index;			/* probability index */
+   unsigned  total = 0;			/* Number of '1' elements */
+   unsigned  bits  = bits_processed (output);
+
+   /*
+    *  Compute the probability array:
+    *  prob[] = { 1/2, 1/2, 1/4, 1/4, 1/4, 1/4,
+    *             1/8, ... , 1/16, ..., 1/(MAXPROB+1)}
+    */
+   {
+      unsigned n;
+      unsigned exp;			/* current exponent */
+      
+      prob = Calloc (1 << (MAX_PROB + 1), sizeof (unsigned));
+   
+      for (index = 0, n = MIN_PROB; n <= MAX_PROB; n++)
+	 for (exp = 0; exp < 1U << n; exp++, index++)
+	    prob [index] = n;
+   }
+   
+   high      = HIGH;			/* 1.0 */
+   low       = LOW;			/* 0.0 */
+   underflow = 0;			/* no underflow bits */
+
+   index = 0;
+
+   /*
+    *  Encode column 0 with a quasi arithmetic coder (QAC).
+    *  Advantage of this QAC with respect to a binary AC:
+    *  Instead of using time consuming multiplications and divisions
+    *  to compute the probability of the most probable symbol (MPS) and
+    *  the range of the interval, a table look up procedure linked
+    *  with a shift operation is used for both computations.
+    */
+   for (row = wfa->basis_states; row <= last_row; row++)
+      for (label = 0; label < MAXLABELS; label++)
+	 if (isrange (wfa->tree [row][label]))
+	 {
+	    if (wfa->into [row][label][0] != 0)
+	    {
+	       /*
+		*  encode the MPS '0'
+		*/
+	       high = high - ((high - low) >> prob [index]) - 1;
+	       RESCALE_OUTPUT_INTERVAL;
+	       
+	       if (index < 1020)
+		  index++;
+	    }
+	    else
+	    {
+	       /*
+		*  encode the LPS '1'
+		*/
+	       low = high - ((high - low) >> prob [index]);
+
+	       RESCALE_OUTPUT_INTERVAL;
+	       
+	       total++;
+	       index >>= 1;
+	    }
+	 }
+   /*
+    *  Flush the quasi-arithmetic encoder
+    */
+   low = high;
+
+   RESCALE_OUTPUT_INTERVAL;
+   
+   OUTPUT_BYTE_ALIGN (output);
+
+   Free (prob);
+
+   debug_message ("delta-state0: %5d bits. (%5d symbols => %5.2f bps)",
+		  bits_processed (output) - bits, total,
+		  total > 0 ? ((bits_processed (output) - bits) /
+			       (double) total) : 0);
+
+   return total;
+}   
+
+static unsigned
+chroma_encoding (const wfa_t *wfa, bitfile_t *output)
+/*
+ *  Write transition matrices of 'wfa' states which are part of the
+ *  chroma channels Cb and Cr to stream 'output'.
+ *
+ *  Return value:
+ *	number of non-zero matrix elements (WFA edges)
+ */
+{
+
+   unsigned  domain;			/* current domain, counter */
+   unsigned  label;			/* current label */
+   unsigned  total = 0;			/* number of '1' elements */
+   u_word_t  high;			/* Start of the current code range */
+   u_word_t  low;			/* End of the current code range */
+   unsigned  underflow;			/* underflow bits */
+   unsigned *prob;			/* probability array */
+   unsigned  index;			/* probability index, counter */
+   unsigned  next_index;		/* probability of last domain */
+   unsigned  row;			/* current matrix row */
+   word_t   *y_domains;
+   unsigned  count = 0;			/* number of transitions for part 1 */
+   unsigned  bits  = bits_processed (output);
+   
+   /*
+    *  Compute the asymmetric probability array
+    *  prob[] = { 1/2, 1/2, 1/4, 1/4, 1/4, 1/4,
+    *                     1/8, ... , 1/16, ..., 1/(MAXPROB+1)}
+    */
+   {
+      unsigned n;
+      unsigned exp;			/* current exponent */
+      
+      prob = Calloc (1 << (MAX_PROB + 1), sizeof (unsigned));
+   
+      for (index = 0, n = MIN_PROB; n <= MAX_PROB; n++)
+	 for (exp = 0; exp < 1U << n; exp++, index++)
+	    prob [index] = n;
+   }
+   
+   high      = HIGH;			/* 1.0 */
+   low       = LOW;			/* 0.0 */
+   underflow = 0;			/* no underflow bits */
+
+   next_index = index = 0;
+
+   y_domains = compute_hits (wfa->basis_states,
+			     wfa->tree [wfa->tree [wfa->root_state][0]][0],
+			     wfa->wfainfo->chroma_max_states, wfa);
+
+   /*
+    *  First of all, read all matrix columns given in the list 'y_domains'
+    *  which note all admitted domains.
+    *  These matrix elements are stored with QAC (see column_0_encoding ()).
+    */
+   for (domain = 0; y_domains [domain] != -1; domain++)
+   {
+      bool_t save_index = YES;		/* YES: store current prob. index */
+      
+      row   = wfa->tree [wfa->tree [wfa->root_state][0]][0] + 1;
+      index = next_index;
+	 
+      for (; row < wfa->states; row++)
+      {
+	 for (label = 0; label < MAXLABELS; label++)
+	    if (isrange (wfa->tree [row][label]))
+	    {
+	       unsigned	edge;
+	       int	into;
+	       bool_t    match;		/* approx with current domain found */
+	       
+	       for (match = NO, edge = 0;
+		    isedge (into = wfa->into [row][label][edge])
+			    && (unsigned) into < row;
+		    edge++)
+		  if (into == y_domains [domain]
+		      && into != wfa->y_state [row][label])
+		     match = YES;
+	       if (!match)
+	       {
+		  /*
+		   *  encode the MPS '0'
+		   */
+		  high = high - ((high - low) >> prob [index]) - 1;
+
+		  RESCALE_OUTPUT_INTERVAL;
+		     
+		  if (index < 1020)
+		     index++;
+	       }
+	       else
+	       {
+		  /*
+		   *  encode the LPS '1'
+		   */
+		  low = high - ((high - low) >> prob [index]);
+
+		  RESCALE_OUTPUT_INTERVAL;
+		     
+		  total++;
+		  index >>= 1;
+	       }
+	    }
+	 if (save_index)
+	 {
+	    next_index = index;
+	    save_index = NO;
+	 }
+      }
+   }
+
+   debug_message ("CbCr_matrix:  %5d bits. (%5d symbols => %5.2f bps)",
+		  bits_processed (output) - bits, total,
+		  total > 0 ? ((bits_processed (output) - bits) /
+			       (double) total) : 0);
+   count = total;
+   bits  = bits_processed (output);
+   
+   /*
+    *  Encode the additional column which indicates whether there
+    *  are transitions to a state with same spatial coordinates
+    *  in the Y component.
+    *
+    *  Again, quasi arithmetic coding is used for this task.
+    */
+
+   next_index = index = 0;
+
+   for (row = wfa->tree [wfa->tree [wfa->root_state][0]][0] + 1;
+	row < wfa->states; row++)
+      for (label = 0; label < MAXLABELS; label++)
+	 if (!wfa->y_column [row][label])
+	 {
+	    /*
+	     *  encode the MPS '0'
+	     */
+	    high = high - ((high - low) >> prob [index]) - 1;
+
+	    RESCALE_OUTPUT_INTERVAL;
+	    
+	    if (index < 1020)
+	       index++;
+	 }
+	 else
+	 {
+	    /*
+	     *  encode the LPS '1'
+	     */
+	    low = high - ((high - low) >> prob [index]);
+
+	    RESCALE_OUTPUT_INTERVAL;
+
+	    index >>= 1;
+	    total++;
+	 }
+
+   /*
+    *  Flush the quasi-arithmetic encoder
+    */
+   low = high;
+
+   RESCALE_OUTPUT_INTERVAL;
+   OUTPUT_BYTE_ALIGN (output);
+
+   debug_message ("Yreferences:  %5d bits. (%5d symbols => %5.2f bps)",
+		  bits_processed (output) - bits, total - count,
+		  total - count > 0 ? ((bits_processed (output) - bits) /
+				       (double) (total - count)) : 0);
+
+   Free (prob);
+   Free (y_domains);
+   
+   return total;
+}
diff --git a/converter/other/fiasco/output/matrices.h b/converter/other/fiasco/output/matrices.h
new file mode 100644
index 00000000..f880fef8
--- /dev/null
+++ b/converter/other/fiasco/output/matrices.h
@@ -0,0 +1,28 @@
+/*
+ *  matrices.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:31 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _MATRICES_H
+#define _MATRICES_H
+
+#include "wfa.h"
+#include "bit-io.h"
+
+unsigned
+write_matrices (bool_t use_normal_domains, bool_t use_delta_domains,
+		const wfa_t *wfa, bitfile_t *output);
+
+#endif /* _MATRICES_H */
+
diff --git a/converter/other/fiasco/output/mc.c b/converter/other/fiasco/output/mc.c
new file mode 100644
index 00000000..afff586b
--- /dev/null
+++ b/converter/other/fiasco/output/mc.c
@@ -0,0 +1,250 @@
+/*
+ *  mc.c:		Output of motion compensation	
+ *
+ *  Written by:		Michael Unger
+ *			Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:31 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "wfa.h"
+#include "bit-io.h"
+#include "mvcode.h"
+
+#include "mc.h"
+
+/*****************************************************************************
+
+			     local variables
+  
+*****************************************************************************/
+
+static unsigned p_frame_codes [4][2] =
+/*
+ *  Code values and bits for P-frame prediction
+ *  NONE,  FORWARD
+ */
+{
+   {1, 1}, {0, 1}, {0, 0}, {0, 0} 
+};
+
+static unsigned b_frame_codes [4][2] =
+/*
+ *  Code values and bits for B-frame prediction
+ *  NONE,  FORWARD,  BACKWARD, INTERPOLATED
+ */
+{
+   {1, 1}, {000, 3}, {001, 3}, {01, 2} 
+};
+
+enum vlc_e {CODE = 0, BITS = 1};
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static void
+encode_mc_tree (unsigned max_state, frame_type_e frame_type, const wfa_t *wfa,
+	       bitfile_t *output);
+static void
+encode_mc_coords (unsigned max_state, const wfa_t *wfa, bitfile_t *output);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+void
+write_mc (frame_type_e frame_type, const wfa_t *wfa, bitfile_t *output)
+{
+   unsigned max_state = wfa->wfainfo->color
+			? wfa->tree[wfa->tree[wfa->root_state][0]][0]
+			: wfa->states;
+
+   encode_mc_tree (max_state, frame_type, wfa, output);
+   encode_mc_coords (max_state, wfa, output);
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static void
+encode_mc_tree (unsigned max_state, frame_type_e frame_type, const wfa_t *wfa,
+		bitfile_t *output)
+/*
+ *  Write tree of motion compensation decisions to the 'output' stream.
+ *  Depending on 'frame_type' different decoding methods are used.
+ *  'max_state' is the last state with motion compensation infos.
+ *
+ *  No return value.
+ */
+{
+   unsigned  label;			/* current label */
+   unsigned  state;			/* current state */
+   unsigned  total = 0;			/* number of motion tree decisions */
+   unsigned  queue [MAXSTATES];		/* state numbers in BFO */
+   unsigned  current;			/* current node to process */
+   unsigned  last;			/* last node (update every new node) */
+   mc_type_e type;			/* type of motion compensation */
+   unsigned	     (*mc_tree_codes)[2]; /* pointer to VLC table */
+   unsigned  bits  = bits_processed (output); /* number of bits used */
+   
+   if (frame_type == P_FRAME)
+      mc_tree_codes = p_frame_codes;	/* binary code */
+   else 
+      mc_tree_codes = b_frame_codes;	/* variable length code */
+   
+   /*
+    *  Traverse tree in breadth first order (starting at
+    *  level 'wfa->p_max_level'). Use a queue to store the childs
+    *  of each node ('last' is the next free queue element).  
+    */
+
+   for (last = 0, state = wfa->basis_states; state < max_state; state++)
+      if (wfa->level_of_state [state] - 1 == (int) wfa->wfainfo->p_max_level)
+	 queue [last++] = state;	/* init level = 'mc_max_level' */
+   
+   for (current = 0; current < last; current++)
+      for (label = 0; label < MAXLABELS; label++)
+      {
+	 state = queue [current];
+	 type  = wfa->mv_tree [state][label].type;
+	 if (wfa->x [state][label]
+	     + width_of_level (wfa->level_of_state [state] - 1)
+	     <= wfa->wfainfo->width
+	     &&
+	     wfa->y [state][label]
+	     + height_of_level (wfa->level_of_state [state] - 1)
+	     <= wfa->wfainfo->height)
+	 {
+	    put_bits (output, mc_tree_codes [type][CODE],
+		      mc_tree_codes [type][BITS]);
+	    total++;
+	 }
+	 if (type == NONE && !isrange (wfa->tree [state][label]) &&
+	     wfa->level_of_state [state] - 1 >=
+	     (int) wfa->wfainfo->p_min_level)
+	    queue [last++] = wfa->tree [state][label]; /* append child */
+	 
+      }
+
+   OUTPUT_BYTE_ALIGN (output);
+   debug_message ("mc-tree:      %5d bits. (%5d symbols => %5.2f bps)",
+		  bits_processed (output) - bits, total,
+		  total > 0 ? ((bits_processed (output) - bits) /
+			       (double) total) : 0);
+}
+
+static void
+encode_mc_coords (unsigned max_state, const wfa_t *wfa, bitfile_t *output)
+/*
+ *  Write motion vector coordinates to the 'output' stream. They are stored
+ *  with the static Huffman code of the MPEG and H.263 standards.
+ *  'max_state' is the last state with motion compensation infos.
+ *
+ *  No return value.
+ */
+{
+   unsigned  state;			/* current state */
+   unsigned  label;			/* current label */
+   unsigned  level_count [MAXLEVEL];	/* number of mv per level */
+   unsigned  level;			/* counter */
+   unsigned  ftotal = 0;		/* #forward motion tree decisions */
+   unsigned  btotal = 0;		/* #backward decisions */
+   unsigned  itotal = 0;		/* #interpolated decisions */
+   unsigned  bits   = bits_processed (output); /* number of bits used */
+   unsigned  sr     = wfa->wfainfo->search_range; /* search range */
+   
+   for (level = wfa->wfainfo->p_max_level;
+	level >= wfa->wfainfo->p_min_level; level--)
+      level_count [level] = 0;
+   
+   for (state = wfa->basis_states; state < max_state; state++)
+      for (label = 0; label < MAXLABELS; label++)
+      {
+	 mv_t *mv = &wfa->mv_tree[state][label]; /* motion vector info */
+	 
+	 if (mv->type != NONE)
+	 {
+	    level_count [wfa->level_of_state [state] - 1]++;
+	    switch (mv->type)
+	    {
+	       case FORWARD:
+		  put_bits (output,
+			    mv_code_table[(mv->fx + sr)][CODE],
+			    mv_code_table[(mv->fx + sr)][BITS]);
+		  put_bits (output,
+			    mv_code_table[(mv->fy + sr)][CODE],
+			    mv_code_table[(mv->fy + sr)][BITS]);
+		  ftotal++;
+		  break;
+	       case BACKWARD:
+		  put_bits (output,
+			    mv_code_table[(mv->bx + sr)][CODE],
+			    mv_code_table[(mv->bx + sr)][BITS]);
+		  put_bits (output,
+			    mv_code_table[(mv->by + sr)][CODE],
+			    mv_code_table[(mv->by + sr)][BITS]);
+		  btotal++;
+		  break;
+	       case INTERPOLATED:
+		  put_bits (output,
+			    mv_code_table[(mv->fx + sr)][CODE],
+			    mv_code_table[(mv->fx + sr)][BITS]);
+		  put_bits (output,
+			    mv_code_table[(mv->fy + sr)][CODE],
+			    mv_code_table[(mv->fy + sr)][BITS]);
+		  put_bits (output,
+			    mv_code_table[(mv->bx + sr)][CODE],
+			    mv_code_table[(mv->bx + sr)][BITS]);
+		  put_bits (output,
+			    mv_code_table[(mv->by + sr)][CODE],
+			    mv_code_table[(mv->by + sr)][BITS]);
+		  itotal++;
+		  break;
+	       default:
+		  break;
+	    }
+	 }
+      }
+
+   OUTPUT_BYTE_ALIGN (output);
+   
+   debug_message ("Motion compensation: %d forward, %d backward, "
+		  "%d interpolated", ftotal, btotal, itotal);
+
+   for (level = wfa->wfainfo->p_max_level;
+	level >= wfa->wfainfo->p_min_level; level--)
+      debug_message ("Level %d: %d motion vectors", level, level_count[level]);
+   
+   {
+      unsigned  total = ftotal * 2 + btotal * 2 + itotal * 4;
+
+      debug_message ("mv-coord:     %5d bits. (%5d symbols => %5.2f bps)",
+		     bits_processed (output) - bits, total,
+		     total > 0 ? ((bits_processed (output) - bits) /
+				  (double) total) : 0);
+   }
+
+   return;
+}
diff --git a/converter/other/fiasco/output/mc.h b/converter/other/fiasco/output/mc.h
new file mode 100644
index 00000000..b7843fd8
--- /dev/null
+++ b/converter/other/fiasco/output/mc.h
@@ -0,0 +1,28 @@
+/*
+ *  mc.h
+ *
+ *  Written by:		Michael Unger
+ *			Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:31 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _MC_H
+#define _MC_H
+
+#include "wfa.h"
+#include "bit-io.h"
+
+void
+write_mc (frame_type_e frame_type, const wfa_t *wfa, bitfile_t *output);
+
+#endif /* not _MC_H */
+
diff --git a/converter/other/fiasco/output/nd.c b/converter/other/fiasco/output/nd.c
new file mode 100644
index 00000000..a09ff762
--- /dev/null
+++ b/converter/other/fiasco/output/nd.c
@@ -0,0 +1,244 @@
+/*
+ *  nd.c:		Output of prediction tree	
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:31 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "wfa.h"
+#include "arith.h"
+#include "misc.h"
+#include "bit-io.h"
+#include "rpf.h"
+#include "list.h"
+
+#include "nd.h"
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static unsigned
+encode_nd_tree (const wfa_t *wfa, bitfile_t *output);
+static void
+encode_nd_coefficients (unsigned total, const wfa_t *wfa, bitfile_t *output);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+void
+write_nd (const wfa_t *wfa, bitfile_t *output)
+/*
+ *  Write prediction information of 'wfa' to given stream 'output'.
+ *  Coefficients are quantized with model 'p_rpf'.
+ *
+ *  No return value.
+ */
+{
+   unsigned total = encode_nd_tree (wfa, output);
+   
+   if (total > 0)
+      encode_nd_coefficients (total, wfa, output);
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static unsigned
+encode_nd_tree (const wfa_t *wfa, bitfile_t *output)
+/*
+ *  Write prediction tree of 'wfa' to given stream 'output'. 
+ *
+ *  No return value.
+ */
+{
+   lqueue_t *queue;			/* queue of states */
+   int	     state, next;		/* state and its current child */
+   unsigned  used, not_used;		/* counter ND used/not used */
+   u_word_t  low;			/* Start of the current code range */
+   u_word_t  high;			/* End of the current code range */
+   u_word_t  underflow;			/* Number of underflow bits pending */
+   u_word_t  sum0, sum1;		/* Probability model */
+   unsigned  bits = bits_processed (output);
+
+   used = not_used = 0;
+   
+   /*
+    *  Initialize arithmetic coder
+    */
+   low       = 0;
+   high      = 0xffff;
+   underflow = 0;
+   sum0      = 1;
+   sum1      = 11;
+   
+   queue = alloc_queue (sizeof (int));
+   state = wfa->root_state;
+   queue_append (queue, &state);
+   
+   /*
+    *  Traverse the WFA tree in breadth first order (using a queue).
+    */
+   while (queue_remove (queue, &next))
+   {
+      unsigned label;
+      
+      if (wfa->level_of_state [next] > wfa->wfainfo->p_max_level + 1)
+      {
+	 /*
+	  *  Nondetermismn is not allowed at levels larger than
+	  *  'wfa->wfainfo->p_max_level'.
+	  */
+	 for (label = 0; label < MAXLABELS; label++)
+	    if (ischild (state = wfa->tree [next][label]))
+	       queue_append (queue, &state); /* continue with childs */
+      }
+      else if (wfa->level_of_state [next] > wfa->wfainfo->p_min_level)
+      {
+	 for (label = 0; label < MAXLABELS; label++)
+	    if (ischild (state = wfa->tree [next][label]))
+	    {
+	       unsigned range;		/* Current interval range */
+
+	       if (isedge (wfa->into [next][label][0])) /* prediction used */
+	       {
+		  used++;
+		  
+		  /*
+		   *  Encode a '1' symbol
+		   */
+		  range =  (high - low) + 1;
+		  low   = low + (u_word_t) ((range * sum0) / sum1);
+		  RESCALE_OUTPUT_INTERVAL;
+	       }
+	       else			/* no predict., continue with childs */
+	       {
+		  not_used++;
+		  if (wfa->level_of_state [state] > wfa->wfainfo->p_min_level)
+		     queue_append (queue, &state);
+		  
+		  /*
+		   *  Encode a '0' symbol
+		   */
+		  range =  (high - low) + 1;
+		  high  = low + (u_word_t) ((range * sum0) / sum1 - 1);
+		  RESCALE_OUTPUT_INTERVAL;
+		  sum0++;
+	       }
+	       /*
+		*  Update the frequency counts
+		*/
+	       sum1++;
+	       if (sum1 > 50)		/* Scale the symbol frequencies */
+	       {
+		  sum0 >>= 1;
+		  sum1 >>= 1;
+		  if (!sum0)
+		     sum0 = 1;
+		  if (sum0 >= sum1)
+		     sum1 = sum0 + 1;
+	       }
+	    }
+	 
+      }
+   }
+   free_queue (queue);
+
+   /*
+    *  Flush the quasi-arithmetic encoder
+    */
+   low = high;
+   RESCALE_OUTPUT_INTERVAL;
+   OUTPUT_BYTE_ALIGN (output);
+
+   debug_message ("%d nd fields: %d used nd, %d used not nd", used + not_used,
+		  used, not_used);
+   {
+      unsigned total = used + not_used;
+      
+      debug_message ("nd-tree:      %5d bits. (%5d symbols => %5.2f bps)",
+		     bits_processed (output) - bits, total,
+		     total > 0 ? ((bits_processed (output) - bits) /
+				  (double) total) : 0);
+   }
+
+   return used;
+}
+
+static void
+encode_nd_coefficients (unsigned total, const wfa_t *wfa, bitfile_t *output)
+/*
+ *  Write #'total' weights of nondeterministic part of 'wfa' to given 'output'
+ *  stream. Coefficients are stored with arithmetic coding (the model is
+ *  given by 'p_rpf').
+ *
+ *  No return value.
+ */
+{
+   unsigned bits = bits_processed (output);
+
+   {
+      unsigned *coefficients;		/* array of factors to encode */
+      unsigned *ptr;			/* pointer to current factor */
+      unsigned	state, label, edge;
+      word_t	domain;
+      
+      ptr = coefficients  = Calloc (total, sizeof (unsigned));
+
+      for (state = wfa->basis_states; state < wfa->states; state++)
+	 for (label = 0; label < MAXLABELS; label++)
+	    if (ischild (wfa->tree [state][label])
+		&& isedge (wfa->into [state][label][0]))
+	       for (edge = 0; isedge (domain = wfa->into [state][label][edge]);
+		    edge++)
+	       {
+		  if (ptr - coefficients >= (int) total)
+		     error ("Can't write more than %d coefficients.", total);
+
+		  *ptr++ = rtob (wfa->weight [state][label][edge],
+				 wfa->wfainfo->dc_rpf);
+	       }
+
+      /*
+       *  Encode array of coefficients with arithmetic coding
+       */
+      {
+	 const int scaling = 50;	/* scaling factor of prob. model */
+	 unsigned  c_symbols = 1 << (wfa->wfainfo->dc_rpf->mantissa_bits + 1);
+
+	 encode_array (output, coefficients, NULL, &c_symbols, 1,
+		       total, scaling);
+      }
+      
+      debug_message ("nd-factors:   %5d bits. (%5d symbols => %5.2f bps)",
+		     bits_processed (output) - bits, total,
+		     total ? ((bits_processed (output) - bits)
+			      / (double) total) : 0);
+      Free (coefficients);
+   }
+}
+
+
diff --git a/converter/other/fiasco/output/nd.h b/converter/other/fiasco/output/nd.h
new file mode 100644
index 00000000..600b3d73
--- /dev/null
+++ b/converter/other/fiasco/output/nd.h
@@ -0,0 +1,27 @@
+/*
+ *  nd.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:31 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _ND_H
+#define _ND_H
+
+#include "wfa.h"
+#include "bit-io.h"
+
+void
+write_nd (const wfa_t *wfa, bitfile_t *output);
+
+#endif /* not _ND_H */
+
diff --git a/converter/other/fiasco/output/tree.c b/converter/other/fiasco/output/tree.c
new file mode 100644
index 00000000..0056d7dd
--- /dev/null
+++ b/converter/other/fiasco/output/tree.c
@@ -0,0 +1,176 @@
+/*
+ *  tree.c:		Output of bintree partitioning
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:31 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "wfa.h"
+#include "bit-io.h"
+#include "arith.h"
+#include "misc.h"
+
+#include "tree.h"
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static void
+encode_tree (bitfile_t *output, const byte_t *data, unsigned n_data,
+	     unsigned scaling, u_word_t sum0, u_word_t sum1);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+void
+write_tree (const wfa_t *wfa, bitfile_t *output)
+/*
+ *  Write bintree to stream 'output'.
+ *  Traverse tree in breadth first order and save a '1' for each child
+ *  and a '0' for each range image.
+ *
+ *  No return value.
+ */
+{
+   unsigned  queue [MAXSTATES];		/* state numbers in BFO */
+   unsigned  current;			/* current node to process */
+   unsigned  last;			/* last node (update every new node) */
+   unsigned  label;			/* current label */
+   int	     into;			/* next child */
+   byte_t   *tree_string;		/* bitstring to encode */
+   unsigned  total = 0;			/* number of ranges */
+   unsigned  bits  = bits_processed (output); /* number of bits */
+
+   /*
+    *  Traverse tree in breadth first order. Use a queue to store
+    *  the childs of each node ('last' is the next free queue element).
+    *  The first element ('current') of this queue will get the new parent
+    *  node. 
+    */
+   tree_string = Calloc (MAXSTATES * MAXLABELS, sizeof (byte_t));
+   queue [0] = wfa->root_state;
+   for (last = 1, current = 0; current < last; current++)
+      for (label = 0; label < MAXLABELS; label++)
+	 if (!isrange (into = wfa->tree [queue[current]][label])) /* child ? */
+	 {
+	    queue [last++]        = into;
+	    tree_string [total++] = 1;
+	 }
+	 else				/* or range ? */
+	    tree_string [total++] = 0;
+
+   if (total != (wfa->states - wfa->basis_states) * MAXLABELS)
+      error ("total [%d] != (states - basis_states) * 2 [%d]", total,
+	     (wfa->states - wfa->basis_states) * MAXLABELS);
+   
+   {
+      unsigned scale = total / 20 ;
+
+      encode_tree (output, tree_string, total, scale, 1, 11);
+   }
+
+   Free (tree_string);
+   
+   debug_message ("tree:         %5d bits. (%5d symbols => %5.2f bps)",
+		  bits_processed (output) - bits, total,
+		  total > 0 ? ((bits_processed (output) - bits)
+			       / (double) total) : 0);
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static void
+encode_tree (bitfile_t *output, const byte_t *data, unsigned n_data,
+	     unsigned scaling, u_word_t sum0, u_word_t sum1)
+/*
+ *  Encode bintree data with adaptive binary arithmetic coding.
+ *  Write 'n_data' output symbols stored in 'data' to stream 'output'.
+ *  Rescale probability model after every 'scaling' symbols.
+ *  Initial counts are given by 'sum0' and 'sum1'.
+ *
+ *  No return value.
+ */
+{
+   u_word_t low;			/* Start of the current code range */
+   u_word_t high;			/* End of the current code range */
+   u_word_t underflow;			/* Number of underflow bits pending */
+   unsigned n;				/* Data counter */
+
+   low       = 0;
+   high      = 0xffff;
+   underflow = 0;
+
+   for (n = n_data; n; n--)
+   {
+      unsigned range;			/* Current interval range */
+      
+      if (!*data++)
+      {
+	 /*
+	  *  encode a '0'
+	  */
+	 range =  (high - low) + 1;
+	 high  = low + (u_word_t) ((range * sum0) / sum1 - 1);
+
+	 RESCALE_OUTPUT_INTERVAL;
+
+	 sum0++;
+      }
+      else
+      {
+	 /*
+	  *  encode a '1'
+	  */
+	 range =  (high - low) + 1;
+	 low   = low + (u_word_t) ((range * sum0) / sum1);
+
+	 RESCALE_OUTPUT_INTERVAL;
+      }
+      /*
+       *  Update the frequency counts
+       */
+      sum1++;
+      if (sum1 > scaling) /* Scale the symbol frequencies */
+      {
+	 sum0 >>= 1;
+	 sum1 >>= 1;
+	 if (!sum0)
+	    sum0 = 1;
+	 if (sum0 >= sum1)
+	    sum1 = sum0 + 1;
+      }
+   }
+   /*
+    *  Flush the quasi-arithmetic encoder
+    */
+   low = high;
+
+   RESCALE_OUTPUT_INTERVAL;
+
+   OUTPUT_BYTE_ALIGN (output);
+}
diff --git a/converter/other/fiasco/output/tree.h b/converter/other/fiasco/output/tree.h
new file mode 100644
index 00000000..6f8a3800
--- /dev/null
+++ b/converter/other/fiasco/output/tree.h
@@ -0,0 +1,27 @@
+/*
+ *  tree.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:31 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _TREE_H
+#define _TREE_H
+
+#include "wfa.h"
+#include "bit-io.h"
+
+void
+write_tree (const wfa_t *wfa, bitfile_t *output);
+
+#endif /* not _TREE_H */
+
diff --git a/converter/other/fiasco/output/weights.c b/converter/other/fiasco/output/weights.c
new file mode 100644
index 00000000..085a1f00
--- /dev/null
+++ b/converter/other/fiasco/output/weights.c
@@ -0,0 +1,200 @@
+/*
+ *  weights.c:		Output of weights
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:31 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "wfa.h"
+#include "misc.h"
+#include "bit-io.h"
+#include "arith.h"
+#include "wfalib.h"
+
+#include "weights.h"
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+void
+write_weights (unsigned total, const wfa_t *wfa, bitfile_t *output)
+/*
+ *  Traverse the transition matrices of the 'wfa' and write #'total'
+ *  weights != 0 to stream 'output'.
+ *
+ *  No return value.
+ */
+{
+   unsigned  state, label;		/* current label */
+   unsigned  offset1, offset2;		/* model offsets. */
+   unsigned  offset3, offset4;		/* model offsets. */
+   unsigned *weights_array;		/* array of weights to encode */
+   unsigned *wptr;			/* pointer to current weight */
+   unsigned *level_array;		/* array of corresponding levels */
+   unsigned *lptr;			/* pointer to current corr. level */
+   int	     min_level, max_level;	/* min and max range level */
+   int	     d_min_level, d_max_level; 	/* min and max delta range level */
+   bool_t    dc, d_dc;			/* true if dc or delta dc are used */
+   bool_t    delta_approx = NO;		/* true if delta has been used */
+   unsigned  delta_count  = 0;		/* number of delta ranges */
+   unsigned  bits 	  = bits_processed (output);
+   
+   /*
+    *  Check whether delta approximation has been used
+    */
+   for (state = wfa->basis_states; state < wfa->states; state++)
+      if (wfa->delta_state [state])
+      {
+	 delta_approx = YES;
+	 break;
+      }
+   
+   /*
+    *  Generate array of corresponding levels (context of probability model)
+    */
+   min_level = d_min_level = MAXLEVEL;
+   max_level = d_max_level = 0;
+   dc 	     = d_dc	   = NO;
+   
+   for (state = wfa->basis_states; state < wfa->states; state++)
+      for (label = 0; label < MAXLABELS; label++)
+         if (isrange (wfa->tree [state][label]))
+	 {
+	    if (delta_approx && wfa->delta_state [state]) /* delta approx. */
+	    {
+	       d_min_level = min (d_min_level,
+				  wfa->level_of_state [state] - 1);
+	       d_max_level = max (d_max_level,
+				  wfa->level_of_state [state] - 1);
+	       if (wfa->into [state][label][0] == 0)
+		  d_dc = YES;
+	    }
+	    else
+	    {
+	       min_level = min (min_level, wfa->level_of_state [state] - 1);
+	       max_level = max (max_level, wfa->level_of_state [state] - 1);
+	       if (wfa->into [state][label][0] == 0)
+		  dc = YES;
+	    }
+	 }
+   if (min_level > max_level)		/* no lc found */
+      max_level = min_level - 1;
+   if (d_min_level > d_max_level)
+      d_max_level = d_min_level - 1;
+
+   /*
+    *  Context model:
+    *		0		DC weight
+    *		1		Delta DC weight
+    *		2-k		normal weights per level
+    *		k+1 - m		Delta weights per level
+    */
+
+   offset1 = dc ? 1 : 0;
+   offset2 = offset1 + (d_dc ? 1 : 0);
+   offset3 = offset2 + (max_level - min_level + 1);
+   offset4 = offset3 + (d_max_level - d_min_level + 1);
+   
+   /*
+    *  Weights are encoded as follows:
+    *  all weights of state n
+    *     sorted by label
+    *        sorted by domain number
+    */
+
+   wptr = weights_array = Calloc (total, sizeof (unsigned));
+   lptr = level_array   = Calloc (total, sizeof (unsigned));
+
+   for (state = wfa->basis_states; state < wfa->states; state++)
+      for (label = 0; label < MAXLABELS; label++)
+         if (isrange (wfa->tree [state][label]))
+	 {
+	    int	edge;			/* current edge */
+	    int	domain;			/* current domain (context of model) */
+	    
+            for (edge = 0; isedge (domain = wfa->into [state][label][edge]);
+		 edge++)
+            {
+	       if (wptr - weights_array >= (int) total)
+		  error ("Can't write more than %d weights.", total);
+	       if (domain)		/* not DC component */
+	       {
+		  if (delta_approx && wfa->delta_state [state]) /* delta */
+		  {
+		     *wptr++ = rtob (wfa->weight [state][label][edge],
+				     wfa->wfainfo->d_rpf);
+		     *lptr++ = offset3
+			       + wfa->level_of_state [state] - 1 - d_min_level;
+		     delta_count++;
+		  }
+		  else
+		  {
+		     *wptr++ = rtob (wfa->weight [state][label][edge],
+				     wfa->wfainfo->rpf);
+		     *lptr++ = offset2
+			       + wfa->level_of_state [state] - 1 - min_level;
+		  }
+	       }
+	       else			/* DC component */
+	       {
+		  if (delta_approx && wfa->delta_state [state]) /* delta */
+		  {
+		     *wptr++ = rtob (wfa->weight [state][label][edge],
+				     wfa->wfainfo->d_dc_rpf);
+		     *lptr++ = offset1;
+		  }
+		  else
+		  {
+		     *wptr++ = rtob (wfa->weight [state][label][edge],
+				     wfa->wfainfo->dc_rpf);
+		     *lptr++ = 0;
+		  }
+	       }
+            }
+	 }
+
+   {
+      unsigned	 i;
+      unsigned	*c_symbols = Calloc (offset4, sizeof (int));
+      const int	 scale 	   = 500;	/* scaling of probability model */
+
+      c_symbols [0] = 1 << (wfa->wfainfo->dc_rpf->mantissa_bits + 1);
+      if (offset1 != offset2)
+	 c_symbols [offset1] = 1 << (wfa->wfainfo->d_dc_rpf->mantissa_bits
+				     + 1);
+      for (i = offset2; i < offset3; i++)
+	 c_symbols [i] = 1 << (wfa->wfainfo->rpf->mantissa_bits + 1);
+      for (; i < offset4; i++)
+	 c_symbols [i] = 1 << (wfa->wfainfo->d_rpf->mantissa_bits + 1);
+      
+      encode_array (output, weights_array, level_array, c_symbols, offset4,
+		    total, scale);
+      Free (c_symbols);
+   }
+   
+   debug_message ("%d delta weights out of %d.", delta_count, total);
+   debug_message ("weights:      %5d bits. (%5d symbols => %5.2f bps)",
+		  bits_processed (output) - bits, total,
+		  (bits_processed (output) - bits) / (double) total);
+
+   Free (weights_array);
+   Free (level_array);
+}
diff --git a/converter/other/fiasco/output/weights.h b/converter/other/fiasco/output/weights.h
new file mode 100644
index 00000000..271203ad
--- /dev/null
+++ b/converter/other/fiasco/output/weights.h
@@ -0,0 +1,27 @@
+/*
+ *  weights.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:50:31 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef _WEIGHTS_H
+#define _WEIGHTS_H
+
+#include "wfa.h"
+#include "bit-io.h"
+
+void
+write_weights (unsigned total, const wfa_t *wfa, bitfile_t *output);
+
+#endif /* not _WEIGHTS_H */
+
diff --git a/converter/other/fiasco/output/write.c b/converter/other/fiasco/output/write.c
new file mode 100644
index 00000000..e6185ad3
--- /dev/null
+++ b/converter/other/fiasco/output/write.c
@@ -0,0 +1,250 @@
+/*
+ *  write.c:        Output of WFA files
+ *
+ *  Written by:     Ullrich Hafner
+ *      
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/07/18 15:44:59 $
+ *  $Author: hafner $
+ *  $Revision: 5.3 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+
+#include "types.h"
+#include "macros.h"
+#include "error.h"
+
+#include "cwfa.h"
+#include "image.h"
+#include "misc.h"
+#include "bit-io.h"
+#include "rpf.h"
+
+#include "tree.h"
+#include "matrices.h"
+#include "weights.h"
+#include "mc.h"
+#include "nd.h"
+#include "write.h"
+ 
+/*****************************************************************************
+
+                prototypes
+  
+*****************************************************************************/
+
+static void
+write_tiling (const tiling_t *tiling, bitfile_t *output);
+
+/*****************************************************************************
+
+                public code
+
+                
+*****************************************************************************/
+
+
+void
+write_next_wfa (const wfa_t *wfa, const coding_t *c, bitfile_t *output)
+/*
+ *  Write 'wfa' to stream 'output'. If the first frame should be written
+ *  then also store the header information of the coding struct 'c'.
+ *
+ *  No return value.
+ */
+{
+   unsigned edges = 0;          /* number of transitions */
+   unsigned bits;
+   
+   debug_message ("--------------------------------------"
+          "--------------------------------------");
+
+   if (c->mt->number == 0)              /* first WFA */
+      write_header (wfa->wfainfo, output);
+  
+   bits = bits_processed (output);
+   
+   /*
+    *  Frame header information
+    */
+   {
+      const int rice_k = 8;     /* parameter of Rice Code */
+
+      write_rice_code (wfa->states, rice_k, output);      
+      write_rice_code (c->mt->frame_type, rice_k, output); 
+      write_rice_code (c->mt->number, rice_k, output);     
+   }
+   
+   OUTPUT_BYTE_ALIGN (output);
+
+   debug_message ("frame-header: %5d bits.", bits_processed (output) - bits);
+
+   if (c->tiling->exponent)     /* write tiling permutation */
+   {
+      put_bit (output, 1);
+      write_tiling (c->tiling, output);
+   }
+   else
+      put_bit (output, 0);
+
+   OUTPUT_BYTE_ALIGN (output);
+
+   write_tree (wfa, output);
+
+   if (c->options.prediction)       /* write nondeterministic approx. */
+   {
+      put_bit (output, 1); 
+      write_nd (wfa, output);
+   }
+   else
+      put_bit (output, 0);
+
+   if (c->mt->frame_type != I_FRAME)    /* write motion compensation info */
+      write_mc (c->mt->frame_type, wfa, output);
+   
+   edges = write_matrices (c->options.normal_domains,
+               c->options.delta_domains, wfa, output);
+
+   if (edges)               /* found at least one approximation */
+      write_weights (edges, wfa, output);
+
+   debug_message ("--------------------------------------"
+          "--------------------------------------");
+}
+
+void
+write_header (const wfa_info_t *wi, bitfile_t *output)
+/*
+ *  Write the header information describing the type of 'wfa'
+ *  to stream 'output'.
+ *
+ *  No return value.
+ */
+{
+   const unsigned rice_k = 8;       /* parameter of Rice Code */
+   unsigned   bits   = bits_processed (output);
+   const char *text;            /* next character to write */
+
+   /*
+    *  Write magic number and name of initial basis
+    */
+   for (text = FIASCO_MAGIC; *text; text++)
+      put_bits (output, *text, 8);
+   put_bits (output, '\n', 8);
+   for (text = wi->basis_name; *text; text++)
+      put_bits (output, *text, 8);
+   put_bits (output, *text, 8);
+   
+   write_rice_code (FIASCO_BINFILE_RELEASE, rice_k, output);
+
+   write_rice_code (HEADER_TITLE, rice_k, output);
+   for (text = wi->title;
+    text && *text && text - wi->title < MAXSTRLEN - 2;
+    text++)
+      put_bits (output, *text, 8);
+   put_bits (output, 0, 8);
+   
+   write_rice_code (HEADER_COMMENT, rice_k, output);
+   for (text = wi->comment;
+    text && *text && text - wi->comment < MAXSTRLEN - 2;
+    text++)
+      put_bits (output, *text, 8);
+   put_bits (output, 0, 8);
+   
+   write_rice_code (HEADER_END, rice_k, output);
+   
+   write_rice_code (wi->max_states, rice_k, output); 
+   put_bit (output, wi->color ? 1 : 0); 
+   write_rice_code (wi->width, rice_k, output);
+   write_rice_code (wi->height, rice_k, output);
+   if (wi->color)
+      write_rice_code (wi->chroma_max_states, rice_k, output); 
+   write_rice_code (wi->p_min_level, rice_k, output); 
+   write_rice_code (wi->p_max_level, rice_k, output); 
+   write_rice_code (wi->frames, rice_k, output);
+   write_rice_code (wi->smoothing, rice_k, output);
+
+   put_bits (output, wi->rpf->mantissa_bits - 2, 3);
+   put_bits (output, wi->rpf->range_e, 2);
+   if (wi->rpf->mantissa_bits != wi->dc_rpf->mantissa_bits ||
+       wi->rpf->range != wi->dc_rpf->range)
+   {
+      put_bit (output, YES);
+      put_bits (output, wi->dc_rpf->mantissa_bits - 2, 3);
+      put_bits (output, wi->dc_rpf->range_e, 2);
+   }
+   else
+      put_bit (output, NO);
+   if (wi->rpf->mantissa_bits != wi->d_rpf->mantissa_bits ||
+       wi->rpf->range != wi->d_rpf->range)
+   {
+      put_bit (output, YES);
+      put_bits (output, wi->d_rpf->mantissa_bits - 2, 3);
+      put_bits (output, wi->d_rpf->range_e, 2);
+   }
+   else
+      put_bit (output, NO);
+   if (wi->dc_rpf->mantissa_bits != wi->d_dc_rpf->mantissa_bits ||
+       wi->dc_rpf->range != wi->d_dc_rpf->range)
+   {
+      put_bit (output, YES);
+      put_bits (output, wi->d_dc_rpf->mantissa_bits - 2, 3);
+      put_bits (output, wi->d_dc_rpf->range_e, 2);
+   }
+   else
+      put_bit (output, NO);
+
+   if (wi->frames > 1)          /* motion compensation stuff */
+   {
+      write_rice_code (wi->fps, rice_k, output); 
+      write_rice_code (wi->search_range, rice_k, output); 
+      put_bit (output, wi->half_pixel ? 1 : 0); 
+      put_bit (output, wi->B_as_past_ref ? 1 : 0);
+   }
+
+   OUTPUT_BYTE_ALIGN (output);
+   debug_message ("header:         %d bits.", bits_processed (output) - bits);
+}
+
+/*****************************************************************************
+
+                private code
+  
+*****************************************************************************/
+
+static void
+write_tiling (const tiling_t *tiling, bitfile_t *output)
+/*
+ *  Write image tiling information given by 'tiling' to stream 'output'.
+ *
+ *  No return value.
+ */
+{
+   const unsigned rice_k = 8;       /* parameter of Rice Code */
+   unsigned       bits   = bits_processed (output);
+   
+   write_rice_code (tiling->exponent, rice_k, output);
+   if (tiling->method == FIASCO_TILING_VARIANCE_ASC
+       || tiling->method == FIASCO_TILING_VARIANCE_DSC)
+   {
+      unsigned tile;            /* current image tile */
+
+      put_bit (output, 1);      
+      for (tile = 0; tile < 1U << tiling->exponent; tile++)
+     if (tiling->vorder [tile] != -1) /* image tile is visible */
+        put_bits (output, tiling->vorder [tile], tiling->exponent);
+   }
+   else
+   {
+      put_bit (output, 0);      
+      put_bit (output, tiling->method == FIASCO_TILING_SPIRAL_ASC);
+   }
+
+   debug_message ("tiling:        %4d bits.", bits_processed (output) - bits);
+}
diff --git a/converter/other/fiasco/output/write.h b/converter/other/fiasco/output/write.h
new file mode 100644
index 00000000..a3ede1f4
--- /dev/null
+++ b/converter/other/fiasco/output/write.h
@@ -0,0 +1,28 @@
+/*
+ *  write.h
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/07/15 17:27:30 $
+ *  $Author: hafner $
+ *  $Revision: 5.2 $
+ *  $State: Exp $
+ */
+
+#ifndef _WRITE_H
+#define _WRITE_H
+
+#include "cwfa.h"
+#include "bit-io.h"
+
+void
+write_next_wfa (const wfa_t *wfa, const coding_t *c, bitfile_t *output);
+void
+write_header (const wfa_info_t *wi, bitfile_t *output);
+
+#endif /* not _WRITE_H */
diff --git a/converter/other/fiasco/params.c b/converter/other/fiasco/params.c
new file mode 100644
index 00000000..3d0a0252
--- /dev/null
+++ b/converter/other/fiasco/params.c
@@ -0,0 +1,727 @@
+/*
+ *  params.c:		Parameter file and command line parsing
+ *
+ *  Written by:		Stefan Frank
+ *			Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/07/15 17:24:21 $
+ *  $Author: hafner $
+ *  $Revision: 5.2 $
+ *  $State: Exp $
+ */
+
+#define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>			/* strtod() on SUN sparc */
+
+#include <stdlib.h>
+#include <string.h>
+ 
+#include <getopt.h>			/* system or ../lib */
+
+#include "nstring.h"
+
+#include "types.h"
+#include "macros.h"
+#include "bit-io.h"
+#include "misc.h"
+#include "fiasco.h"
+
+#include "binerror.h"
+
+#include "params.h"
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static void
+read_parameter_file (param_t *params, FILE *file);
+static int
+get_parameter_index (const param_t *params, const char *search_string);
+static void
+set_parameter (param_t *parameter, const char *value);
+static void 
+usage (const param_t *params, const char *progname, const char *synopsis,
+       const char *comment, const char *non_opt_string,
+       bool_t show_all_options, const char *sys_file_name,
+       const char *usr_file_name);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+
+int
+parseargs (param_t *usr_params, 
+           int argc, char **argv, 
+           const char *synopsis,
+           const char *comment, 
+           const char *non_opt_string, 
+           const char *path,
+           const char *sys_file_name, 
+           const char *usr_file_name)
+/*
+ *  Perform the command line parsing.
+ *  List of allowed parameters is given by 'usr_params'.
+ *  Command line and number of parameters are given by 'argv' and 'argc'.
+ *  'synopsis' contains a brief description of the program and
+ *  'comment' may contain some additional advice.
+ *  Initialization order of parameters:
+ *	1.) Default values given by the param_t struct
+ *	2.) System parameter-file ('path'/'sys_file_name')
+ *	3.) User parameter-file ($HOME/'usr_file_name')
+ *	4.) Command line parameters
+ *	5.) Parameter-file forced by option -f (--config-file)
+ *
+ *  Return value:
+ *	index in ARGV of the first ARGV-element that is not an option.
+ *
+ *  Side effects:
+ *	the elements of ARGV are permuted
+ *      usr_params [].value is modified 
+ */
+{
+   extern int optind;			/* index in ARGV of the 1st element
+					   that is not an option */
+   bool_t     detailed_help = NO;	/* NO if all parameters can be modified
+					   with short options too */
+   unsigned   n1;			/* number of user parameters */
+   unsigned   n2;			/* number of system parameters */
+   bool_t     read_config_file = NO;	/* will override command line */
+   param_t    *params;			/* array of user and system params */
+   param_t    *sys_params;		/* array of system parameters */
+   param_t    detailed_sys_params [] =  /* detailed system parameters */
+   {
+      {"version", NULL, 'v', PFLAG, {0}, NULL,
+       "Print program version number, then exit."},
+      {"verbose", "NUM", 'V', PINT, {0}, "1",
+       "Set level of verbosity to `%s'."},
+      {"config", "FILE", 'f', PSTR, {0}, NULL,
+       "Load `%s' to initialize parameters."},
+      {"info", NULL, 'h', PFLAG, {0}, NULL,
+       "Print brief help, then exit."},
+      {"help", NULL, 'H', PFLAG, {0}, NULL,
+       "Print detailed help, then exit."},
+      {NULL, NULL, 0, PSTR, {0}, NULL, NULL }
+   };
+   param_t    short_sys_params [] =	/* short system parameters */
+   {
+      {"version", NULL, 'v', PFLAG, {0}, NULL,
+       "Print program version number, then exit."},
+      {"verbose", "NUM", 'V', PINT, {0}, "1",
+       "Set level of verbosity to `%s'."},
+      {"config", "FILE", 'f', PSTR, {0}, NULL,
+       "Load `%s' to initialize parameters."},
+      {"help", NULL, 'h', PFLAG, {0}, NULL,
+       "Print this help, then exit."},
+      {NULL, NULL, 0, PSTR, {0}, NULL, NULL }
+   };
+   char *sys_path;			/* path to system config file */
+
+   sys_path = calloc (strlen (path) + strlen (sys_file_name) + 2,
+		      sizeof (char));
+   if (!sys_path)
+      error ("Out of memory.");
+   sprintf (sys_path, "%s/%s", path, sys_file_name);
+
+   /*
+    *  Set parameters defaults
+    */
+   {
+       param_t *p;
+      
+       for (p = usr_params; p->name != NULL; p++)
+       {
+           set_parameter (p, p->default_value);
+           if (p->optchar == '\0')
+               detailed_help = YES;
+       }
+
+      sys_params = detailed_help ? detailed_sys_params : short_sys_params;
+      
+      for (p = sys_params; p->name != NULL; p++)
+          set_parameter (p, p->default_value);
+   }
+   /*
+    *  Append system command line option to user parameters
+    */
+   for (n1 = 0; usr_params [n1].name != NULL; n1++)
+      ;
+   for (n2 = 0; sys_params [n2].name != NULL; n2++)
+      ;
+   params = calloc (n1 + n2 + 1, sizeof (param_t));
+   if (!params)
+      error ("Out of memory.");
+
+   memcpy (params, usr_params, n1 * sizeof (param_t));
+   memcpy (params + n1, sys_params, (n2 + 1) * sizeof (param_t));
+   /*
+    *  Try to open the system resource file 'path'/'sys_file_name'
+    */
+   {
+      FILE *parameter_file = open_file (sys_path, NULL, READ_ACCESS);
+      if (parameter_file == NULL)
+/*
+	 warning ("No system resource file found.");
+*/ {}
+      else
+      {
+	 read_parameter_file (params, parameter_file);
+	 fclose (parameter_file);
+      }
+   }
+   /*
+    *  Try to read user resource file $HOME/'usr_file_name'
+    */
+   {
+      FILE *parameter_file = open_file (usr_file_name, "HOME", READ_ACCESS);
+      if (parameter_file != NULL)
+      {
+	 read_parameter_file (params, parameter_file);
+	 fclose (parameter_file);
+      }
+   }
+   /*
+    *  Parse command line options
+    */
+   {
+      extern char   *optarg;		/* argument of current option */
+      struct option *long_options;	/* array of long options */
+      int	     option_index = 0;
+      char	     optstr [MAXSTRLEN]; /* string containing the legitimate
+					    option characters */
+      int	     optchar;		/* found option character */
+
+      /*
+       *  Build short option string for getopt_long (). 
+       */
+      {
+	 param_t *p;			/* counter */
+	 char	 *ptr_optstr;		/* pointer to position in string */
+	 
+	 ptr_optstr = optstr;
+	 for (p = params; p->name != NULL; p++)
+	    if (p->optchar != '\0')
+	    {
+	       *ptr_optstr++ = p->optchar;
+	       if (p->type == POSTR)
+	       {
+		  *ptr_optstr++ = ':';
+		  *ptr_optstr++ = ':';
+	       }
+	       else if (p->type != PFLAG)
+		  *ptr_optstr++ = ':';
+	    }
+	 *ptr_optstr = '\0';
+      }
+      
+      /*
+       *  Build long option string for getopt_long (). 
+       */
+      {
+	 int i;
+	 
+	 long_options = calloc (n1 + n2 + 1, sizeof (struct option));
+	 if (!long_options)
+	    error ("Out of memory.");
+	 for (i = 0; params [i].name != NULL; i++)
+	 {
+	    long_options [i].name    = params [i].name;
+	    switch (params [i].type)
+	    {
+	       case PFLAG:
+		  long_options [i].has_arg = 0;
+		  break;
+	       case POSTR:
+		  long_options [i].has_arg = 2;
+		  break;
+	       case PINT:
+	       case PSTR:
+	       case PFLOAT:
+	       default:
+		  long_options [i].has_arg = 1;
+		  break;
+	    }
+	    long_options [i].has_arg = params [i].type != PFLAG;
+	    long_options [i].flag    = NULL;
+	    long_options [i].val     = 0;
+	 }
+      }
+      
+      /*
+       *  Parse comand line
+       */
+      while ((optchar = getopt_long (argc, argv, optstr, long_options,
+				     &option_index)) != EOF)
+      {
+	 int param_index = -1;
+	 
+	 switch (optchar)
+	 {
+	    case 0:
+	       param_index = option_index;
+	       break;
+	    case ':':
+	       if (detailed_help)
+		  fprintf (stderr,
+			   "Try `%s -h' or `%s --help' for "
+			   "more information.\n",
+			   argv [0], argv [0]);
+	       else
+		  fprintf (stderr, "Try `%s --help' for more information.\n",
+			   argv [0]);
+	       exit (2);
+	       break;
+	    case '?':
+	       if (detailed_help)
+		  fprintf (stderr,
+			   "Try `%s -h' or `%s --help' "
+			   "for more information.\n",
+			   argv [0], argv [0]);
+	       else
+		  fprintf (stderr, "Try `%s --help' for more information.\n",
+			   argv [0]);
+	       exit (2);
+	       break;
+	    default:
+	       {
+		  int i;
+		  
+		  for (i = 0; params [i].name != NULL; i++)
+		     if (params [i].optchar == optchar)
+		     {
+			param_index = i;
+			break;
+		     }
+	       }
+	 }
+	 /*
+	  *  Check for system options
+	  */
+	 if (param_index >= 0)
+	 {
+	    set_parameter (params + param_index, optarg ? optarg : "");
+	    if (streq (params [param_index].name, "help"))
+	       usage (params, argv [0], synopsis, comment, non_opt_string,
+		      YES, sys_path, usr_file_name);
+	    else if (streq (params [param_index].name, "info"))
+	       usage (params, argv [0], synopsis, comment, non_opt_string,
+		      NO, sys_path, usr_file_name);
+	    else if (streq (params [param_index].name, "version"))
+	    {
+	       fprintf (stderr, "%s " VERSION "\n", argv [0]);
+	       exit (2);
+	    }
+	    else if (streq (params [param_index].name, "verbose"))
+	       fiasco_set_verbosity (
+               * (fiasco_verbosity_e *) parameter_value (params,
+                                                         "verbose"));
+	    else if (streq (params [param_index].name, "config"))
+	       read_config_file = YES;
+	    param_index = -1;		/* clear index flag */
+	 }
+      }
+
+      free (long_options);
+   }
+   
+   /*
+    *  Read config-file if specified by option -f
+    */
+   if (read_config_file)
+   {
+      char *filename;
+
+      if ((filename = (char *) parameter_value (params, "config")) != NULL)
+      {
+	 FILE *parameter_file;		/* input file */
+	 
+	 warning ("Options set in file `%s' will override"
+		  " command line options.", filename);
+	 parameter_file = open_file (filename, NULL, READ_ACCESS);
+	 if (parameter_file != NULL)
+	 {
+	    read_parameter_file (params, parameter_file);
+	    fclose (parameter_file);
+	 }
+	 else
+	    file_error (filename);
+      }
+      else
+	 error ("Invalid config filename.");
+   }
+
+   memcpy (usr_params, params, n1 * sizeof (param_t)); /* fill user struct */
+   free (sys_path);
+   
+   return optind;
+}
+ 
+void *
+parameter_value (const param_t *params, const char *name)
+/*
+ *  Extract value of parameter 'name.' of the given parameters 'params'.
+ *
+ *  Return value:
+ *	value of given parameter
+ */
+{
+   int pind = get_parameter_index (params, name);
+
+   if (pind < 0)
+      error ("Invalid parameter `%s'.", name);
+
+   if (params [pind].type == PSTR || params [pind].type == POSTR)
+      return (void *) params [pind].value.s;
+      
+   return (void *) &(params [pind].value);
+}
+
+void
+ask_and_set (param_t *params, const char *name, const char *msg)
+/*
+ *  Ask user (print given message 'msg') for missing mandatory
+ *  parameter 'name' of the given parameters 'params'.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'params ['name'].value' is changed
+ */
+{ 
+   char answer [MAXSTRLEN];
+   int  index = get_parameter_index (params, name);
+
+   if (index < 0)
+      error ("Invalid parameter %s.", name);
+
+   if (msg)
+      fprintf (stderr, "%s\n", msg);
+  
+   switch (params [index].type)
+   {
+      case PFLAG:			/* Unusual, at least. */
+	 warning ("Flags should be initialized and set on demand, "
+		  "not request");
+      case PINT:
+      case PSTR:
+      case POSTR:
+      case PFLOAT:
+	 scanf (MAXSTRLEN_SCANF, answer);
+	 set_parameter (&params [index], answer);
+	 break;
+      default:
+	 error ("Invalid parameter type for %s", name);
+   }
+} 
+
+void
+write_parameters (const param_t *params, FILE *output)
+/*
+ *  Write all parameter settings to 'output'.
+ *
+ *  No return value.
+ */
+{
+   int pind;
+
+   if (!params || !output)
+      error ("Parameters must be not NULL.");
+
+   for (pind = 0; params [pind].name != NULL; pind++)
+   {
+      fprintf (output, "# %s = ", params [pind].name);
+      switch (params [pind].type)
+      {
+	 case PFLAG:
+	    fprintf (output, "%s\n", params [pind].value.b ? "TRUE" : "FALSE");
+	    break;
+	 case PINT:
+	    fprintf (output, "%d\n", params [pind].value.i);
+	    break;
+	 case PFLOAT:
+	    fprintf (output, "%.4f\n", (double) params [pind].value.f);
+	    break;
+	 case PSTR:
+	 case POSTR:
+	    fprintf (output, "%s\n", params [pind].value.s);
+	    break;
+	 default:
+	    error ("Invalid type %d for parameter %s",
+		   params [pind].type, params [pind].name);
+      }
+   }
+   fputc ('\n', output);
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static void
+set_parameter (param_t *parameter, const char *value)
+/*
+ *  Set value of 'parameter' to 'value'.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'parameter.value' is changed accordingly
+ */
+{
+   assert (parameter);
+   
+   switch (parameter->type)
+   {
+      case PFLAG:
+	 if (value != NULL && *value != '\0')
+	 {
+	    if (strcaseeq (value, "TRUE"))
+	       parameter->value.b = YES;
+	    else if (strcaseeq (value, "FALSE"))
+	       parameter->value.b = NO;
+	    else if (strcaseeq (value, "YES"))
+	       parameter->value.b = YES;
+	    else if (strcaseeq (value, "NO"))
+	       parameter->value.b = NO;
+	    else
+	    {
+	       long int	data;
+	       char	*endptr;
+	    
+	       data = strtol (value, &endptr, 0);
+	       if (*endptr != '\0' || endptr == value)
+		  warning ("Invalid value `%s' converted to %d",
+			   value, (int) data);
+	       parameter->value.b = data ? YES : NO;
+	    }
+	 }
+	 else
+	    parameter->value.b = !parameter->value.b;
+	 break;
+      case PINT:
+	 {
+	    long int  data;
+	    char     *endptr;
+	    
+	    data = strtol (value, &endptr, 0);
+	    if (*endptr != '\0' || endptr == value)
+	       warning ("Invalid value `%s' converted to %d",
+			value, (int) data);
+	    parameter->value.i = data;
+	 }
+	 break;
+      case PFLOAT:
+	 {
+	    double	data;
+	    char	*endptr;
+	    
+	    data = strtod (value, &endptr);
+	    if (*endptr != '\0' || endptr == value)
+	       warning ("Invalid value `%s' converted to %f",
+			value, (double) data);
+	    parameter->value.f = data;
+	 }
+	 break;
+      case PSTR:
+      case POSTR:
+	 parameter->value.s = value ? strdup (value) : NULL;
+	 break;
+      default:				
+	 error ("Invalid parameter type for %s", parameter->name);
+   }
+}
+
+static int
+get_parameter_index (const param_t *params, const char *search_string)
+/*
+ *  Search for parameter with name 'search_string' in parameter struct.
+ *
+ *  Return value:
+ *	index of parameter or -1 if no matching parameter has been found
+ */
+{
+   int n;
+   int index = -1;
+
+   assert (params && search_string);
+
+   for (n = 0; params [n].name != NULL; n++)
+      if (strcaseeq (params [n].name, search_string))
+      {
+	 index = n;
+	 break;
+      }
+
+   return index;
+}
+
+static void
+read_parameter_file (param_t *params, FILE *file)
+/*
+ *  Read parameter settings from 'file'.
+ *
+ *  No return value.
+ *
+ *  Side effects:
+ *	'params [].value' are changed if specified in 'file'
+ */
+{
+   char buffer [MAXSTRLEN];
+   int  n = 0;
+
+   assert (params && file);
+
+   while (fgets (buffer, MAXSTRLEN, file) != NULL)
+   {
+      char *b;				/* temporary variable */
+      char *name;			/* parameter name */
+      char *value;			/* parameter value */
+      int   pind;			/* current argument number */
+      
+      b = strchr (buffer, '#');
+      if (b != NULL)			/* Strip comments. */
+	 *b = '\0';
+
+      b = strchr (buffer, '=');
+      if (b == NULL)			/* Strip lines that contain no '=' */
+	 continue;
+      *b = '\0';			/* Replace '=' by string terminator */
+
+      /*
+       *  Extract value of parameter
+       */
+      for (value = b + 1; ISSPACE (*value); value++) 
+	 ;				/* Delete leading spaces */
+
+      for (b = value + strlen (value) - 1; b >= value && ISSPACE (*b); b--)
+	 *b = '\0';			/* Delete trailing spaces. */
+
+      /*
+       *  Extract parameter name
+       */
+      for (name = buffer; ISSPACE (*name); name++) 
+	 ;				/* Delete leading spaces */
+
+      for (b = name + strlen (name) - 1; b >= name && ISSPACE (*b); b--)
+	 *b = '\0';			/* Delete trailing spaces. */
+
+      pind = get_parameter_index (params, name);
+      if (pind >= 0)
+	 set_parameter (&params [pind], value);
+      
+      n++;
+   }
+}   
+
+static void 
+usage (const param_t *params, const char *progname, const char *synopsis,
+       const char *comment, const char *non_opt_string,
+       bool_t show_all_options, const char *sys_file_name,
+       const char *usr_file_name)
+/*
+ *  Generates and prints command line description from param_t struct 'params'.
+ *  'progname' is the name of the excecutable, 'synopsis' a short program
+ *  description, and 'comment' some more advice.
+ *  If flag 'show_all_options' is set then print also options that are not
+ *  associated with a short option character.
+ *  'sys_file_name' and 'usr_file_name' are filenames to parameter files.
+ *
+ *  No return value.
+ */
+{
+   int	  i;
+   size_t width = 0;
+   
+   fprintf (stderr, "Usage: %s [OPTION]...%s\n", progname,
+	    non_opt_string ? non_opt_string : " ");
+   if (synopsis != NULL)
+      fprintf (stderr, synopsis);
+   fprintf (stderr, "\n\n");
+   fprintf (stderr, "Mandatory or optional arguments to long options "
+	    "are mandatory or optional\nfor short options too. "
+	    "Default values are surrounded by {}.\n");
+   for (i = 0; params [i].name != NULL; i++)
+      if (params [i].optchar != '\0' || show_all_options)
+      {
+	 if (params [i].type == POSTR)
+	    width = max (width, (strlen (params [i].name)
+				 + strlen (params [i].argument_name) + 2));
+	 else if (params [i].type != PFLAG)
+	    width = max (width, (strlen (params [i].name)
+				 + strlen (params [i].argument_name)));
+	 else
+	    width = max (width, (strlen (params [i].name)) - 1);
+      }
+   
+   for (i = 0; params [i].name != NULL; i++)
+      if (params [i].optchar != '\0' || show_all_options)
+      {
+	 if (params [i].optchar != '\0')
+	    fprintf (stderr, "  -%c, --", params [i].optchar);
+	 else
+	    fprintf (stderr, "      --");
+	 
+	 if (params [i].type == POSTR)
+	    fprintf (stderr, "%s=[%s]%-*s  ", params [i].name,
+		     params [i].argument_name,
+		     max (0, (width - 2 - strlen (params [i].name)
+			   - strlen (params [i].argument_name))), "");
+	 else if (params [i].type != PFLAG)
+	    fprintf (stderr, "%s=%-*s  ", params [i].name,
+		  width - strlen (params [i].name),
+		  params [i].argument_name);
+	 else
+	    fprintf (stderr, "%-*s  ", width + 1, params [i].name);
+
+	 fprintf (stderr, params [i].use, params [i].argument_name);
+	 
+	 switch (params [i].type)
+	 {
+	    case PFLAG:
+	       break;
+	    case PINT:
+	       fprintf (stderr, "{%d}", params [i].value.i);
+	       break;
+	    case PFLOAT:
+	       fprintf (stderr, "{%.2f}", (double) params [i].value.f);
+	       break;
+	    case PSTR:
+	    case POSTR:
+	       if (params [i].value.s)
+		  fprintf (stderr, "{%s}", params [i].value.s);
+	       break;
+	    default:
+	       error ("type %d for %s invalid",
+		      params [i].type, params [i].name);
+	 }
+	 fprintf (stderr, "\n");
+      }
+   fprintf (stderr, "\n");
+   fprintf (stderr, "Parameter initialization order:\n");
+   fprintf (stderr,
+	    "1.) %s\n2.) $HOME/%s\t 3.) command line\t 4.) --config=file",
+	    sys_file_name, usr_file_name);
+   fprintf (stderr, "\n\n");
+   if (comment != NULL)
+      fprintf (stderr, "%s\n", comment);
+
+   exit (1);
+}
+
diff --git a/converter/other/fiasco/params.h b/converter/other/fiasco/params.h
new file mode 100644
index 00000000..810a9ff0
--- /dev/null
+++ b/converter/other/fiasco/params.h
@@ -0,0 +1,61 @@
+/*
+ *  params.h
+ *
+ *  Written by:     Stefan Frank
+ *          Ullrich Hafner
+ *      
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+
+/*
+ *  $Date: 2000/06/14 20:51:17 $
+ *  $Author: hafner $
+ *  $Revision: 5.1 $
+ *  $State: Exp $
+ */
+
+#ifndef PARAMS_H
+#define PARAMS_H
+
+#include <stdio.h>
+
+typedef union pdata_t           /* Allow for different */
+{                   /* parameter types. */
+    int    b;
+    int    i;
+    float  f;
+    char  *s;
+} pdata_t;
+
+typedef enum {PFLAG = 1, PINT, PFLOAT, PSTR, POSTR} param_e;
+
+typedef struct param_t
+{
+    const char  *name;           /* Parameter name */
+    const char  *argument_name;      /* Argument name */
+    char         optchar;            /* Corresponding command line switch */
+    param_e      type;           /* Parameter type */
+    pdata_t      value;          /* Parameter value */
+    const char  *default_value;      /* Parameters default value */
+    const char  *use;            /* One line usage. Must contain %s,
+                                    which will be replaced by 'name'. */
+} param_t;
+
+int
+parseargs (param_t *usr_params, 
+           int argc, char **argv, 
+           const char *synopsis,
+           const char *comment, 
+           const char *non_opt_string, 
+           const char *path,
+           const char *sys_file_name, 
+           const char *usr_file_name);
+void
+write_parameters (const param_t *params, FILE *output);
+void
+ask_and_set (param_t *params, const char *name, const char *msg);
+void *
+parameter_value (const param_t *params, const char *name);
+
+#endif /* not PARAMS_H */
diff --git a/converter/other/fiasco/pnmtofiasco.c b/converter/other/fiasco/pnmtofiasco.c
new file mode 100644
index 00000000..2218256d
--- /dev/null
+++ b/converter/other/fiasco/pnmtofiasco.c
@@ -0,0 +1,411 @@
+/*
+ *  cwfa.c:		FIASCO coder
+ *
+ *  Written by:		Ullrich Hafner
+ *		
+ *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+ */
+ 
+/*
+ *  $Date: 2000/10/28 17:39:29 $
+ *  $Author: hafner $
+ *  $Revision: 5.4 $
+ *  $State: Exp $
+ */
+
+#include "config.h"
+#include "pnm.h"
+
+#if STDC_HEADERS
+#	include <stdlib.h>
+#	include <string.h>
+#else /* not STDC_HEADERS */
+#	if HAVE_STRING_H
+#		include <string.h>
+#	else /* not HAVE_STRING_H */
+#		include <strings.h>
+#	endif /* not HAVE_STRING_H */
+#endif /* not STDC_HEADERS */
+
+#include "types.h"
+#include "macros.h"
+
+#include "binerror.h"
+#include "misc.h"
+#include "params.h"
+#include "fiasco.h"
+
+/*****************************************************************************
+
+			     local variables
+  
+*****************************************************************************/
+
+static param_t params [] =
+{
+  /*
+   *  Options for standard user
+   */
+  {"image-name", "FILE", 'i', PSTR, {0}, NULL,
+   "Compress raw PPM/PGM image(s) `%s'."},
+  {"output-name", "FILE", 'o', PSTR, {0}, "-",
+   "Write automaton to `%s' (`-' means stdout)."},
+  {"quality", "REAL", 'q', PFLOAT, {0}, "20.0",
+   "Set quality of compression to `%s'."},
+  {"title", "NAME", 't', PSTR, {0}, "",
+   "Set title of FIASCO stream to `%s'."},
+  {"comment", "NAME", 'c', PSTR, {0}, "",
+   "Set comment of FIASCO stream to `%s'."},
+  {"chroma-qfactor", "REAL", '\0', PFLOAT, {0}, "2",
+   "Decrease chroma band quality `%s' times."},
+  {"basis-name", "FILE", '\0', PSTR, {0}, "small.fco",
+   "Preload basis `%s' into FIASCO."},
+  {"optimize", "NUM", 'z', PINT, {0}, "0",
+   "Set optimization level to `%s'."},
+  {"dictionary-size", "NUM", '\0', PINT, {0}, "10000",
+   "Set max# size of dictionary to `%s'."},
+  {"chroma-dictionary", "NUM", '\0', PINT, {0}, "40",
+   "Set max# size of chroma dictionary to `%s'.."},
+  {"min-level", "NUM", '\0', PINT, {0}, "6",
+   "Start prediction on block level `%s'."},
+  {"max-level", "NUM", '\0', PINT, {0}, "10",
+   "Stop prediction on block level `%s'."},
+  {"tiling-exponent", "NUM", '\0', PINT, {0}, "4",
+   "Set exponent of image permutation to `%s'."},
+  {"tiling-method", "NAME", '\0', PSTR, {0}, "desc-variance",
+   "Set type of permutation to `%s'."},
+  {"rpf-range", "REAL", '\0', PFLOAT, {0}, "1.5",
+   "Set quantization range to `%s'."},
+  {"rpf-mantissa", "NUM", '\0', PINT, {0}, "3",
+   "Set quantization mantissa to `%s' bits."},
+  {"dc-rpf-range", "REAL", '\0', PFLOAT, {0}, "1",
+   "Set quant. range (DC part) to `%s'."},
+  {"dc-rpf-mantissa", "NUM", '\0', PINT, {0}, "5",
+   "Set quant. mantissa (DC part) to `%s' bits."},
+  {"pattern", "NAME", '\0', PSTR, {0}, "ippppppppp",
+   "Set frame type sequence to `%s'."},
+  {"fps", "NUM", '\0', PINT, {0}, "25",
+   "Set display rate to `%s' frames per second."},
+  {"half-pixel", NULL, '\0', PFLAG, {0}, "FALSE",
+   "Use half-pixel precision for mc."},
+  {"cross-B-search", NULL, '\0', PFLAG, {0}, "FALSE",
+   "Use cross-B-search for interpolated mc."},
+  {"B-as-past-ref", NULL, '\0', PFLAG, {0}, "FALSE",
+   "Use B-frames as reference images." },
+  {"prediction", NULL, '\0', PFLAG, {0}, "FALSE",
+   "Use additional predictive coding."},
+  {"progress-meter", "NUM", '\0', PINT, {0}, "2",
+   "Set type of progress meter to `%s'."},
+  {"smooth", "NUM", '\0', PINT, {0}, "70",
+   "Smooth image(s) by factor `%s' (0-100)"},
+#if 0
+  /*
+   *  Options currently not activated (maybe in future versions of FIASCO)
+   */
+  {"min-level", "NUM", 'm', PINT, {0}, "4",
+   "Start compression on block level `%s'."},
+  {"max-level", "NUM", 'M', PINT, {0}, "12",
+   "Stop compression on block level `%s'."},
+  {"max-elements", "NUM", 'N', PINT, {0}, "8",
+   "Set max# of elements in an approx. to `%s'." },
+  {"domain-pool", "NAME", '\0', PSTR, {0}, "rle",
+   "Set domain pool of r-lc to `%s'."},
+  {"coeff", "NAME", '\0', PSTR, {0}, "adaptive",
+   "Set coefficients model to `%s'."},
+  /*  DELTA APPROXIATION  */
+  {"d-domain-pool", "NAME", '\0', PSTR, {0}, "rle",
+   "Set domain pool of d-lc to `%s'."},
+  {"d-coeff", "NAME", '\0', PSTR, {0}, "adaptive",
+   "Set d coefficients model to `%s'."},
+  {"d-range", "REAL", '\0', PFLOAT, {0}, "1.5",
+   "Set range of RPF for delta lc to `%s'."},
+  {"d-mantissa", "NUM", '\0', PINT, {0}, "3",
+   "Set #m-bits of RPF for delta lc to `%s'."},
+  {"d-dc-range", "REAL", '\0', PFLOAT, {0}, "1",
+   "Set DC range of RPF of delta lc to `%s'."},
+  {"d-dc-mantissa", "NUM", '\0', PINT, {0}, "5",
+   "Set #m-bits of delta RPF for DC domain to `%s'."},
+  /*  ADVANCED  */
+  {"images-level", "NUM", '\0', PINT, {0}, "5",
+   "Compute state images up to level `%s'."},
+  {"delta-domains", NULL, '\0', PFLAG, {0}, "FALSE",
+   "Use delta domains every time."},
+  {"normal-domains", NULL, '\0', PFLAG, {0}, "FALSE",
+   "Use normal domains every time."},
+  /*  VIDEO COMPRESSION  */
+  {"smooth", "REAL", 's', PFLOAT, {0}, "1.0",
+   "Smooth frames by factor `%s' (0.5 - 1.0)"},
+  {"reference-frame", "FILE", '\0', PSTR, {0}, NULL,
+   "Use PPM/PGM image `%s' as reference frame."},
+#endif  
+  {NULL, NULL, 0, PSTR, {0}, NULL, NULL }
+};
+
+/*****************************************************************************
+
+				prototypes
+  
+*****************************************************************************/
+
+static void 
+checkargs (int argc, char **argv, char const ***image_template,
+	   char **wfa_name, float *quality, fiasco_c_options_t **options);
+
+/*****************************************************************************
+
+				public code
+  
+*****************************************************************************/
+ 
+int 
+main (int argc, char **argv)
+{
+   char const 	      **image_template;	/* template for input image files */
+   char	       	       *wfa_name;	/* filename of output WFA */
+   float	      	quality;	/* approximation quality */
+   fiasco_c_options_t  *options;	/* additional coder options */
+   
+   pnm_init(&argc, argv);
+   
+   init_error_handling (argv [0]);
+
+   checkargs (argc, argv, &image_template, &wfa_name, &quality, &options);
+
+   if (fiasco_coder (image_template, wfa_name, quality, options))
+      return 0;
+   else
+   {
+      fprintf (stderr, fiasco_get_error_message ());
+      fprintf (stderr, "\n");
+      return 1;
+   }
+}
+
+/*****************************************************************************
+
+				private code
+  
+*****************************************************************************/
+
+static void 
+checkargs (int argc, char **argv, char const ***image_template,
+	   char **wfa_name, float *quality, fiasco_c_options_t **options)
+/*
+ *  Check validness of command line parameters and of the parameter files.
+ *
+ *  Return value:
+ *	1 on success
+ *	0 otherwise
+ *  
+ *
+ *  Side effects:
+ *	'image_template', 'wfa_name', 'quality' and 'options' are set.
+ */
+{
+   int	 optind;			/* last processed commandline param */
+   char	*image_name;			/* filename given by option '-i' */
+   int	 i;				/* counter */
+   
+   optind = parseargs (params, argc, argv,
+		       "Compress raw PPM/PGM image FILEs to a FIASCO file.",
+		       "With no image FILE, or if FILE is -, "
+		       "read standard input.\n"
+		       "FILE must be either a filename"
+		       " or an image template of the form:\n"
+		       "`prefix[start-end{+,-}step]suffix'\n"
+		       "e.g., img0[12-01-1].pgm is substituted by"
+		       " img012.pgm ... img001.pgm\n\n"
+		       "Environment:\n"
+		       "FIASCO_DATA   Search and save path for FIASCO files. "
+		       "Default: ./\n"
+		       "FIASCO_IMAGES Search path for image files. "
+		       "Default: ./", " [FILE]...",
+		       FIASCO_SHARE, "system.fiascorc", ".fiascorc");
+
+   /*
+    *  Default options ...
+    */
+   image_name = (char *) parameter_value (params, "image-name"); 
+   *wfa_name  = (char *) parameter_value (params, "output-name");
+   for (;;)
+   {
+      *quality = * (float *) parameter_value (params, "quality");
+      if (*quality > 100)
+	 fprintf (stderr, "Typical range of quality: (0,100].\n"
+		  "Expect some trouble on slow machines.\n");
+      if (*quality > 0)
+	 break;
+      ask_and_set (params, "quality",
+		   "Please enter coding quality 'q' ('q' > 0): ");
+   }
+   
+   if (optind < argc)			/* Additional command line param */
+   {
+      if (image_name)
+	 error ("Multiple image_template arguments."
+		"\nOption -i %s already specified!", image_name);
+
+      *image_template = calloc (argc - optind + 1, sizeof (char *));
+      if (!*image_template)
+	 error ("Out of memory.");
+      for (i = 0; optind < argc; i++, optind++)
+	 (*image_template) [i] = argv [optind];
+      (*image_template) [i] = NULL;
+   }
+   else					/* option -i image_name */
+   {
+      *image_template = calloc (2, sizeof (char *));
+      if (!*image_template)
+	 error ("Out of memory.");
+      (*image_template) [0] = image_name;
+      (*image_template) [1] = NULL;
+   }
+   /*
+    *  Additional options ... (have to be set with the fiasco_set_... methods)
+    */
+   {
+      *options = fiasco_c_options_new ();
+      
+      {
+	 char *pattern = (char *) parameter_value (params, "pattern");
+
+	 if (!fiasco_c_options_set_frame_pattern (*options, pattern))
+	    error (fiasco_get_error_message ());
+      }
+
+      {
+	 char *basis = (char *) parameter_value (params, "basis-name");
+	 
+	 if (!fiasco_c_options_set_basisfile (*options, basis))
+	    error (fiasco_get_error_message ());
+      }
+
+      {
+	 int   n = * (int *) parameter_value (params, "chroma-dictionary");
+	 float q = * (float *) parameter_value (params, "chroma-qfactor");
+      
+	 if (!fiasco_c_options_set_chroma_quality (*options, q, max (0, n)))
+	    error (fiasco_get_error_message ());
+      }
+      
+      {
+	 int n = *((int *) parameter_value (params, "smooth"));
+	 
+	 if (!fiasco_c_options_set_smoothing (*options, max (0, n)))
+	    error (fiasco_get_error_message ());
+      }
+      
+      {
+          int n = * (int *) parameter_value (params, "progress-meter");
+          fiasco_progress_e type = (n < 0) ? 
+              FIASCO_PROGRESS_NONE : (fiasco_progress_e) n;
+      
+          if (!fiasco_c_options_set_progress_meter (*options, type))
+              error (fiasco_get_error_message ());
+      }
+      
+      {
+	 char *t = (char *) parameter_value (params, "title");
+	 
+	 if (strlen (t) > 0 && !fiasco_c_options_set_title (*options, t))
+	    error (fiasco_get_error_message ());
+      }
+      
+      {
+	 char *c = (char *) parameter_value (params, "comment");
+
+	 if (strlen (c) > 0 && !fiasco_c_options_set_comment (*options, c))
+	    error (fiasco_get_error_message ());
+      }
+      
+      {
+	 fiasco_tiling_e method = FIASCO_TILING_VARIANCE_DSC;
+	 int   e  = * (int *) parameter_value (params, "tiling-exponent");
+	 char *m  = (char *) parameter_value (params, "tiling-method");
+
+	 if (strcaseeq (m, "desc-variance"))
+	    method = FIASCO_TILING_VARIANCE_DSC;
+	 else if (strcaseeq (m, "asc-variance"))
+	    method = FIASCO_TILING_VARIANCE_ASC;
+	 else if (strcaseeq (m, "asc-spiral"))
+	    method = FIASCO_TILING_SPIRAL_ASC;
+	 else if (strcaseeq (m, "dsc-spiral"))
+	    method = FIASCO_TILING_SPIRAL_DSC;
+	 else
+	    error (_("Invalid tiling method `%s' specified."), m);
+
+	 if (!fiasco_c_options_set_tiling (*options, method, max (0, e)))
+	    error (fiasco_get_error_message ());
+      }
+      
+      {
+	 int M/*  = * (int *) parameter_value (params, "max-level") */;
+	 int m/*  = * (int *) parameter_value (params, "min-level") */;
+	 int N/*  = * (int *) parameter_value (params, "max-elements") */;
+	 int D = * (int *) parameter_value (params, "dictionary-size");
+	 int o = * (int *) parameter_value (params, "optimize");
+
+	 if (o <= 0)
+	 {
+	    o = 0;
+	    M = 10;
+	    m = 6;
+	    N = 3;
+	 }
+	 else
+	 {
+	    o -= 1;
+	    M = 12;
+	    m = 4;
+	    N = 5;
+	 }
+	 
+	 if (!fiasco_c_options_set_optimizations (*options, m, M, N,
+						  max (0, D), o))
+	    error (fiasco_get_error_message ());
+      }
+      {
+	 int M = * (int *) parameter_value (params, "max-level");
+	 int m = * (int *) parameter_value (params, "min-level");
+	 int p = * (int *) parameter_value (params, "prediction");
+	 
+	 if (!fiasco_c_options_set_prediction (*options,
+					       p, max (0, m), max (0, M)))
+	    error (fiasco_get_error_message ());
+      }
+      {
+	 float r    = * (float *) parameter_value (params, "rpf-range");
+	 float dc_r = * (float *) parameter_value (params, "dc-rpf-range");
+	 int   m    = * (int *)   parameter_value (params, "rpf-mantissa");
+	 int   dc_m = * (int *)   parameter_value (params, "dc-rpf-mantissa");
+	 fiasco_rpf_range_e range, dc_range;
+	 
+	 if (r < 1)
+	    range = FIASCO_RPF_RANGE_0_75;
+	 else if (r < 1.5)
+	    range = FIASCO_RPF_RANGE_1_00;
+	 else if (r < 2.0)
+	    range = FIASCO_RPF_RANGE_1_50;
+	 else
+	    range = FIASCO_RPF_RANGE_2_00;
+	    
+	 if (dc_r < 1)
+	    dc_range = FIASCO_RPF_RANGE_0_75;
+	 else if (dc_r < 1.5)
+	    dc_range = FIASCO_RPF_RANGE_1_00;
+	 else if (dc_r < 2.0)
+	    dc_range = FIASCO_RPF_RANGE_1_50;
+	 else
+	    dc_range = FIASCO_RPF_RANGE_2_00;
+	    
+	 if (!fiasco_c_options_set_quantization (*options,
+						 max (0, m), range,
+						 max (0, dc_m), dc_range))
+	    error (fiasco_get_error_message ());
+      }
+
+      if (fiasco_get_verbosity () == FIASCO_ULTIMATE_VERBOSITY)
+	 write_parameters (params, stderr);
+   }
+}	
diff --git a/converter/other/fiasco/system.fiascorc b/converter/other/fiasco/system.fiascorc
new file mode 100644
index 00000000..86ff2da2
--- /dev/null
+++ b/converter/other/fiasco/system.fiascorc
@@ -0,0 +1,120 @@
+#
+#  system.wfarc:	Resource file WFA coder
+# 
+#  Written by:		Ullrich Hafner
+#		
+#  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+#  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
+
+#
+#  $Date: 2000/06/25 16:38:01 $
+#  $Author: hafner $
+#  $Revision: 5.2 $
+#  $State: Exp $
+
+#
+# Options for FIASCO coder `cfiasco':
+#
+
+# Initial basis to start with
+# small.fco:  domains 1, x, y
+# medium.fco: domains 1, x, y and 129 additional images
+# large.fco: domains 1, x, y and 226 additional images
+basis-name = small.fco
+
+# Limit the number of elements in the dictionary by `dictionary-size' 
+# The smaller the value is the faster the coder runs and the worse
+# the image quality will be.  
+dictionary-size = 10000
+
+# Optimization level
+#  0: standard approximation method, process blocks of level [6, 10],
+#     use up to 3 dictionary vectors
+#  1: standard approximation method, process blocks of level [4, 12],
+#     use up to 5 dictionary vectors
+#  2: significantly increases the approximation quality,
+#     running time is twice as high as with the standard method
+#  3: hardly increases the approximation quality of method 2, 
+#     running time is twice as high as with method 21
+#     (this method just remains for completeness)
+optimize = 0
+
+# Approximation quality (typical range = [1-100]) 
+quality = 20.0                    
+
+# For compression of chroma bands the dictionary can be reduced to
+# the best #`chroma-dictionary' elements.
+# Furthermore, the quality of the approximation is decreased
+# 'chroma-qfactor' times.
+chroma-dictionary = 40
+chroma-qfactor    = 2
+
+# Verbosity level
+# 0 no debug output
+# 1 some verbosity and statistics of the output size
+# 2 lots of debug information and log-files are written
+verbose = 1
+
+# Set exponent and method of image tiling
+# 0   image is processed in normal bintree order
+# >0  image is subdivided into 2^`tiling-exponent' tiles. 
+# Set type of image tiling
+# asc-variance  Tiles with large variances are processed first
+# desc-variance Tiles with small variances are processed first
+# asc-spiral    Tiles are process in spiral order starting in the middle
+# desc-spiral   Tiles are process in spiral order starting at the border
+tiling-exponent = 4
+tiling-method   = desc-variance
+
+# Quantization parameters define the accuracy of coefficients quantization.
+# DC coefficients (of the constant dictionary vector f(x,y) = 1) are quantized
+# to values of the interval [-`dc-rpf-range', `dc-rpf-range'] using
+# #`dc-rpf-mantissa' bits. All other quantized coefficients are quantized in
+# an analogous way using the parameters `rpf-range' and `rpf-mantissa'.
+rpf-mantissa    = 3
+rpf-range       = 1.5
+dc-rpf-mantissa = 5
+dc-rpf-range    = 1
+
+# Search for prediction (coarse approximation or motion compensation)
+# on all levels between minlevel and maxlevel
+min-level = 6
+max-level = 10
+
+# Set various parameters used for video compensation.
+# 'fps' defines the frame rate which should be
+# used when the video is decoded. This value has no effect during coding,
+# it is just passed to the FIASCO output file.
+# If 'half-pixel' is set then half pixel precise
+# motion compensated prediction is used.
+# If 'cross-B-search' is set then the fast Cross-B-Search algorithm is
+# used to determine the motion vectors of interpolated prediction. Otherwise
+# exhaustive search (in the given search range) is used.
+# If 'B-as-past-ref' is set then B frames are allowed to be used
+# for B frame predicion.
+fps            = 25
+half-pixel     = NO
+cross-B-search = NO
+B-as-past-ref  = NO
+
+# Set `pattern' of input frames.
+# `pattern' has to be a sequence of the following
+# characters (case insensitive):
+# 'i' intra frame
+# 'p' predicted frame
+# 'b' bidirectional predicted frame
+# E.g. pattern = 'IBBPBBPBB'
+#
+# When coding video frames the prediction type of input frame N is determined
+# by reading `pattern' [N] (`pattern' is periodically extended).
+
+pattern = ippppppppp
+
+#
+# Options for FIASCO decoder `dfiasco':
+#
+
+double  = NO
+reduced = NO
+panel   = NO
+enlarge = 0
diff --git a/converter/other/fitstopnm.c b/converter/other/fitstopnm.c
new file mode 100644
index 00000000..796ca489
--- /dev/null
+++ b/converter/other/fitstopnm.c
@@ -0,0 +1,494 @@
+ /* fitstopnm.c - read a FITS file and produce a portable anymap
+ **
+ ** 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.
+ **
+ ** Hacked up version by Daniel Briggs  (dbriggs@nrao.edu)  20-Oct-92
+ **
+ ** Include floating point formats, more or less.  Will only work on
+ ** machines that understand IEEE-754.  Added -scanmax -printmax
+ ** -min -max and -noraw.  Ignore axes past 3, instead of error (many packages
+ ** use pseudo axes).  Use a finite scale when max=min.  NB: Min and max
+ ** are the real world FITS values (scaled), so watch out when bzer & bscale
+ ** are not 0 & 1.  Datamin & datamax interpreted correctly in scaled case,
+ ** and initialization changed to less likely values.  If datamin & max are
+ ** not present in the header, the a first pass is made to determine them
+ ** from the array values.
+ **
+ ** Modified by Alberto Accomazzi (alberto@cfa.harvard.edu), Dec 1, 1992.
+ **
+ ** Added understanding of 3-plane FITS files, the program is renamed
+ ** fitstopnm.  Fixed some non-ansi declarations (DBL_MAX and FLT_MAX
+ ** replace MAXDOUBLE and MAXFLOAT), fixed some scaling parameters to
+ ** map the full FITS data resolution to the maximum PNM resolution,
+ ** disabled min max scanning when reading from stdin.
+ */
+
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "pnm.h"
+
+/* Do what you have to, to get a sensible value here.  This may not be so */
+/* portable as the rest of the program.  We want to use MAXFLOAT and */
+/* MAXDOUBLE, so you could define them manually if you have to. */
+/* #include <values.h> */
+#include <float.h>
+
+struct FITS_Header {
+  int simple;       /* basic format or not */
+  int bitpix;       /* number of bits per pixel */
+  int naxis;        /* number of axes */
+  int naxis1;       /* number of points on axis 1 */
+  int naxis2;       /* number of points on axis 2 */
+  int naxis3;       /* number of points on axis 3 */
+  double datamin;   /* min # (Physical value!) */
+  double datamax;   /* max #     "       "     */
+  double bzer;      /* Physical value = Array value*bscale + bzero */
+  double bscale;
+};
+
+static void read_fits_header ARGS(( FILE* fp, struct FITS_Header* hP ));
+static void read_card ARGS(( FILE* fp, char* buf ));
+static void read_val ARGS(( FILE* fp, int bitpix, double* vp ));
+     
+
+
+static void
+scanImageForMinMax(FILE *       const ifP,
+                   unsigned int const images,
+                   int          const cols,
+                   int          const rows,
+                   unsigned int const bitpix,
+                   double       const bscale,
+                   double       const bzer,
+                   unsigned int const imagenum,
+                   bool         const multiplane,
+                   double *     const dataminP,
+                   double *     const datamaxP) {
+
+    double dmax, dmin;
+    unsigned int image;
+    pm_filepos rasterPos;
+    double fmaxval;
+    
+    pm_tell2(ifP, &rasterPos, sizeof(rasterPos));
+
+    pm_message("Scanning file for scaling parameters");
+
+    switch (bitpix) {
+    case   8: fmaxval = 255.0;        break;
+    case  16: fmaxval = 65535.0;      break;
+    case  32: fmaxval = 4294967295.0; break;
+    case -32: fmaxval = FLT_MAX;      break;
+    case -64: fmaxval = DBL_MAX;      break;
+    default:
+        pm_error("unusual bits per pixel (%u), can't read", bitpix);
+    }
+
+    dmax = -fmaxval;
+    dmin = fmaxval;
+    for (image = 1; image <= images; ++image) {
+        unsigned int row;
+        for (row = 0; row < rows; ++row) {
+            unsigned int col;
+            for (col = 0; col < cols; ++col) {
+                double val;
+                read_val(ifP, bitpix, &val);
+                if (image == imagenum || multiplane ) {
+                    dmax = MAX(dmax, val);
+                    dmin = MIN(dmin, val);
+                }
+            }
+        }
+        if (bscale < 0.0) {
+            double const origDmax = dmax;
+            dmax = dmin;
+            dmin = origDmax;
+        }
+    }
+    *dataminP = dmin * bscale + bzer;
+    *datamaxP = dmax * bscale + bzer;
+
+    pm_message("Scan results: min=%f max=%f", *dataminP, *datamaxP);
+
+    pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
+}
+
+
+
+static void
+computeMinMax(FILE *             const ifP,
+              unsigned int       const images,
+              int                const cols,
+              int                const rows,
+              struct FITS_Header const h,
+              unsigned int       const imagenum,
+              bool               const multiplane,
+              bool               const forcemin,
+              bool               const forcemax,
+              double             const frmin,
+              double             const frmax,
+              double *           const dataminP,
+              double *           const datamaxP) {
+
+    double datamin, datamax;
+
+    datamin = -DBL_MAX;  /* initial assumption */
+    datamax = DBL_MAX;   /* initial assumption */
+
+    if (forcemin)
+        datamin = frmin;
+    if (forcemax)
+        datamax = frmax;
+
+    if (datamin == -DBL_MAX)
+        datamin = h.datamin;
+    if (datamax == DBL_MAX)
+        datamax = h.datamax;
+
+    if (datamin == -DBL_MAX || datamax == DBL_MAX) {
+        double scannedDatamin, scannedDatamax;
+        scanImageForMinMax(ifP, images, cols, rows, h.bitpix, h.bscale, h.bzer,
+                           imagenum, multiplane,
+                           &scannedDatamin, &scannedDatamax);
+
+        if (datamin == -DBL_MAX)
+            datamin = scannedDatamin;
+        if (datamax == DBL_MAX)
+            datamax = scannedDatamax;
+    }
+    *dataminP = datamin;
+    *datamaxP = datamax;
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    FILE* ifp;
+    int argn, imagenum, image, row;
+    register int col;
+    xelval maxval;
+    double val, frmin, frmax, scale, t;
+    double datamin, datamax;
+    int rows, cols, images, format;
+    struct FITS_Header h;
+    xel** pnmarray;
+    xelval tx, txv[4];
+    const char* fits_name;
+    const char* const usage = "[-image N] [-scanmax] [-printmax] [-min f] [-max f] [FITSfile]";
+
+    int doscan = 0;
+    int forceplain = 0;
+    int forcemin = 0;
+    int forcemax = 0;
+    int printmax = 0;
+    bool multiplane;
+  
+    pnm_init( &argc, argv );
+  
+    argn = 1;
+    imagenum = 0;
+  
+    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
+    {
+        if ( pm_keymatch( argv[argn], "-image", 2 ) )
+        {
+            ++argn;
+            if ( argn == argc || sscanf( argv[argn], "%d", &imagenum ) != 1 )
+                pm_usage( usage );
+        }
+        else if ( pm_keymatch( argv[argn], "-max", 3 ) )
+        {
+            ++argn;
+            forcemax = 1;
+            if ( argn == argc || sscanf( argv[argn], "%lf", &frmax ) != 1 )
+                pm_usage( usage );
+        }
+        else if ( pm_keymatch( argv[argn], "-min", 3 ) )
+        {
+            ++argn;
+            forcemin = 1;
+            if ( argn == argc || sscanf( argv[argn], "%lf", &frmin ) != 1 )
+                pm_usage( usage );
+        }
+        else if ( pm_keymatch( argv[argn], "-scanmax", 2 ) )
+            doscan = 1;
+        else if ( pm_keymatch( argv[argn], "-noraw", 2 ) )
+            /* This is for backward compatibility only.  Use the common option
+               -plain now.  (pnm_init() processes -plain).
+            */
+            forceplain = 1;
+        else if ( pm_keymatch( argv[argn], "-printmax", 2 ) )
+            printmax = 1;
+        else
+            pm_usage( usage );
+        ++argn;
+    }
+  
+    if ( argn < argc )
+    {
+        fits_name = argv[argn];
+        ++argn;
+    }
+    else
+        fits_name = "-";
+
+    if ( argn != argc )
+        pm_usage( usage );
+
+    ifp = pm_openr_seekable(fits_name);
+  
+    read_fits_header( ifp, &h );
+  
+    if ( ! h.simple )
+        pm_error( "FITS file is not in simple format, can't read" );
+    if ( h.naxis != 2 && h.naxis != 3 )
+        pm_message( "Warning: FITS file has %d axes", h.naxis );
+    cols = h.naxis1;
+    rows = h.naxis2;
+    if ( h.naxis == 2 )
+        images = imagenum = 1;
+    else
+        images = h.naxis3;
+    if ( imagenum > images )
+        pm_error( "only %d plane%s in this file", 
+                  images, images > 1 ? "s" : "" );
+    if ( images != 3 && images > 1 && imagenum == 0 )
+        pm_error( "need to specify a plane using the -imagenum flag" );
+
+    multiplane = ( images == 3 && imagenum == 0 );
+
+    computeMinMax(ifp, images, cols, rows, h, imagenum, multiplane,
+                  forcemin, forcemax, frmin, frmax,
+                  &datamin, &datamax);
+
+    if (h.bitpix < 0) {
+        /* samples are floating point, which means the resolution could be
+           anything.  So we just pick a convenient maxval of 255.  We should
+           have a program option to choose the maxval.  Before Netpbm 10.20
+           (January 2004), we did maxval = max - min for floating point as
+           well as integer samples.
+        */
+        maxval = 255;
+    } else
+        maxval = MAX(1, MIN(PNM_OVERALLMAXVAL, datamax - datamin));
+
+    if ( datamax - datamin == 0 )
+        scale = 1.0;
+    else
+        scale = maxval / ( datamax - datamin );
+
+    /* If printmax option is true, just print and exit. */
+    /* For use in shellscripts.  Ex:                    */
+    /* eval `fitstopnm -printmax $filename | \          */
+    /*   awk '{min = $1; max = $2}\                     */
+    /*         END {print "min=" min; " max=" max}'`    */
+    if (printmax) {
+        printf( "%f %f\n", datamin, datamax);
+        pm_close( ifp );
+        pm_close( stdout );
+        exit( 0 );
+    }
+
+    if (multiplane)
+        format = PPM_FORMAT;
+    else
+        format = PGM_FORMAT;
+
+    pnmarray = pnm_allocarray( cols, rows );
+
+    switch( PNM_FORMAT_TYPE( format ))
+    {
+    case PGM_TYPE:
+        pm_message( "writing PGM file" );
+        for ( image = 1; image <= imagenum; ++image )
+        {
+            if ( image != imagenum )
+                pm_message( "skipping image plane %d of %d", image, images );
+            else if ( images > 1 )
+                pm_message( "reading image plane %d of %d", image, images );
+            for ( row = 0; row < rows; ++row)
+                for ( col = 0; col < cols; ++col )
+                {
+                    read_val (ifp, h.bitpix, &val);
+                    t = scale * ( val * h.bscale + h.bzer - datamin );
+                    tx = MAX( 0, MIN( t, maxval ) );
+                    if ( image == imagenum )
+                        PNM_ASSIGN1( pnmarray[row][col], tx );
+                }
+        }
+        break;
+    case PPM_TYPE:
+        pm_message( "writing PPM file" );
+        for ( image = 1; image <= images; image++ )
+        {
+            pm_message( "reading image plane %d of %d", image, images );
+            for ( row = 0; row < rows; row++ )
+                for ( col = 0; col < cols; col++ )
+                {
+                    read_val (ifp, h.bitpix, &val);
+                    txv[1] = PPM_GETR( pnmarray[row][col] );
+                    txv[2] = PPM_GETG( pnmarray[row][col] );
+                    txv[3] = PPM_GETB( pnmarray[row][col] );
+                    t = scale * ( val * h.bscale + h.bzer - datamin );
+                    txv[image] = MAX( 0, MIN( t, maxval ));
+                    PPM_ASSIGN( pnmarray[row][col], txv[1], txv[2], txv[3] );
+                }
+        }
+        break;
+    default:
+        pm_error( "can't happen" );
+        break;
+    }
+
+    pnm_writepnm( stdout, pnmarray, cols, rows, maxval, format, forceplain );
+    pnm_freearray( pnmarray, rows );
+
+    pm_close( ifp );
+    pm_close( stdout );
+  
+    exit( 0 );
+}
+
+/*
+ ** This code will deal properly with integers, no matter what the byte order
+ ** or integer size of the host machine.  Sign extension is handled manually
+ ** to prevent problems with signed/unsigned characters.  Floating point
+ ** values will only be read properly when the host architecture is IEEE-754
+ ** conformant.  If you need to tweak this code for other machines, you might
+ ** want to snag a copy of the FITS documentation from nssdca.gsfc.nasa.gov
+ */
+
+static void
+read_val (fp, bitpix, vp)
+    FILE *fp;
+    int bitpix;
+    double *vp;
+
+{
+    int i, ich, ival;
+    long int lval;
+    unsigned char c[8];
+  
+    switch ( bitpix )
+    {
+        /* 8 bit FITS integers are unsigned */
+    case 8:
+        ich = getc( fp );
+        if ( ich == EOF )
+            pm_error( "EOF / read error" );
+        *vp = ich;
+        break;
+      
+    case 16:
+        ich = getc( fp );
+        if ( ich == EOF )
+            pm_error( "EOF / read error" );
+        c[0] = ich;
+        ich = getc( fp );
+        if ( ich == EOF )
+            pm_error( "EOF / read error" );
+        c[1] = ich;
+        if (c[0] & 0x80)
+            ival = ~0xFFFF | c[0]<<8 | c[1];
+        else
+            ival = c[0]<<8 | c[1];
+        *vp = ival;
+        break;
+      
+    case 32:
+        for (i=0; i<4; i++) {
+            ich = getc( fp );
+            if ( ich == EOF )
+                pm_error( "EOF / read error" );
+            c[i] = ich;
+        }
+        if (c[0] & 0x80)
+            lval = ~0xFFFFFFFF |
+                c[0]<<24 | c[1]<<16 | c[2]<<8 | c[3];
+        else
+            lval = c[0]<<24 | c[1]<<16 | c[2]<<8 | c[3];
+        *vp = lval;
+      
+    case -32:
+        for (i=0; i<4; i++) {
+            ich = getc( fp );
+            if ( ich == EOF )
+                pm_error( "EOF / read error" );
+            c[i] = ich;
+        }
+        *vp = *( (float *) c);
+        break;
+      
+    case -64:
+        for (i=0; i<8; i++) {
+            ich = getc( fp );
+            if ( ich == EOF )
+                pm_error( "EOF / read error" );
+            c[i] = ich;
+        }
+        *vp = *( (double *) c);
+        break;
+      
+    default:
+        pm_error( "Strange bitpix in read_value" );
+    }
+}
+
+static void
+read_fits_header( fp, hP )
+    FILE* fp;
+    struct FITS_Header* hP;
+{
+    char buf[80];
+    int seen_end;
+    int i;
+    char c;
+  
+    seen_end = 0;
+    hP->simple = 0;
+    hP->bzer = 0.0;
+    hP->bscale = 1.0;
+    hP->datamin = - DBL_MAX;
+    hP->datamax = DBL_MAX;
+  
+    while ( ! seen_end )
+        for ( i = 0; i < 36; ++i )
+        {
+            read_card( fp, buf );
+    
+            if ( sscanf( buf, "SIMPLE = %c", &c ) == 1 )
+            {
+                if ( c == 'T' || c == 't' )
+                    hP->simple = 1;
+            }
+            else if ( sscanf( buf, "BITPIX = %d", &(hP->bitpix) ) == 1 );
+            else if ( sscanf( buf, "NAXIS = %d", &(hP->naxis) ) == 1 );
+            else if ( sscanf( buf, "NAXIS1 = %d", &(hP->naxis1) ) == 1 );
+            else if ( sscanf( buf, "NAXIS2 = %d", &(hP->naxis2) ) == 1 );
+            else if ( sscanf( buf, "NAXIS3 = %d", &(hP->naxis3) ) == 1 );
+            else if ( sscanf( buf, "DATAMIN = %lf", &(hP->datamin) ) == 1 );
+            else if ( sscanf( buf, "DATAMAX = %lf", &(hP->datamax) ) == 1 );
+            else if ( sscanf( buf, "BZERO = %lf", &(hP->bzer) ) == 1 );
+            else if ( sscanf( buf, "BSCALE = %lf", &(hP->bscale) ) == 1 );
+            else if ( strncmp( buf, "END ", 4 ) == 0 ) seen_end = 1;
+        }
+}
+
+static void
+read_card( fp, buf )
+    FILE* fp;
+    char* buf;
+{
+    if ( fread( buf, 1, 80, fp ) == 0 )
+        pm_error( "error reading header" );
+}
diff --git a/converter/other/gemtopnm.c b/converter/other/gemtopnm.c
new file mode 100644
index 00000000..aac74793
--- /dev/null
+++ b/converter/other/gemtopnm.c
@@ -0,0 +1,305 @@
+/*
+ * Convert a GEM .img file to PBM
+ *
+ * Author: Diomidis D. Spinellis
+ * (C) Copyright 1988 Diomidis D. Spinellis.
+ *
+ * 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.
+ *
+ * Comments and additions should be sent to the author:
+ *
+ *                     Diomidis D. Spinellis
+ *                     1 Myrsinis Str.
+ *                     GR-145 62 Kifissia
+ *                     GREECE
+ *
+ * 92/07/11 Johann Haider
+ * Changed to read from stdin if file is omitted
+ * Changed to handle line length not a multipe of 8
+ *
+ * 94/01/31 Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de)
+ * Changed to remove architecture dependency and conform to
+ * PBM coding standard.
+ * Added more tests for garbage.
+ *
+ * 2000/04/30 John Elliott <jce@seasip.demon.co.uk> Added ability to
+ * read 4-plane color IMG files.  Therefore changed from PBM to PPM.
+ * Bryan changed it further to use the PNM facilities so it outputs
+ * both PBM and PPM in the Netpbm tradition.  Name changed from
+ * gemtopbm to gemtopnm.  
+ */
+
+#include <assert.h>
+#include "pnm.h"
+
+#define MAXVAL 3
+#define LIGHT  2
+#define DARK   1
+#define BLACK  0
+
+char pattern[8];
+
+static void getinit ARGS ((FILE *file, int *colsP, int *rowsP, int *padrightP,
+			   int *patlenP, int *planesP));
+
+int
+main(argc, argv)
+	int             argc;
+	char           *argv[];
+{
+	int     debug = 0;
+	FILE    *f;
+    int     row;
+	int     rows, cols, padright, patlen, planes;
+      /* attributes of input image */
+    int type;  /* The format type (PBM/PPM) of the output image */
+	bit	*bitrow[4];
+      /* One row of input, one or four planes.  (If one, only [0] is defined)*/
+    xel * xelrow;  /* One row of output */
+	const char * const usage = "[-debug] [gem IMG file]";
+	int argn;
+
+/* Process multiple planes by maintaining a separate row of bits for each
+ * plane. In a single-plane image, all we have to do is write out the 
+ * first plane; in a multiple-plane image, we combine them just before writing
+ * out the row.
+ */
+	pnm_init( &argc, argv );
+    
+    argn = 1;
+
+	while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0')
+	  {
+	    if (pm_keymatch(argv[1], "-debug", 2))
+	      debug = 1;
+	    else
+	      pm_usage (usage);
+	    ++argn;
+	  }
+
+	if (argc == argn)
+		f = stdin;
+	else {
+		f = pm_openr (argv[argn]);
+		++argn;
+	}
+
+	if (argn != argc)
+	  pm_usage (usage);
+
+	getinit (f, &cols, &rows, &padright, &patlen, &planes);
+
+    if (planes == 1) 
+        type = PBM_TYPE;
+    else 
+        type = PPM_TYPE;
+
+	pnm_writepnminit( stdout, cols, rows, MAXVAL, type, 0 );
+
+    { 
+        /* allocate input row data structure */
+        int plane;
+        for (plane = 0; plane < planes; plane++) 
+            bitrow[plane] = malloc (cols + padright);
+    }
+    xelrow = pnm_allocrow(cols+padright);   /* Output row */
+
+	for (row = 0; row < rows; ) {
+      int linerep;
+      int plane;
+
+	  linerep = 1;
+	  for (plane = 0; plane < planes; plane++) {
+        int col;
+		col = 0;
+		while (col < cols) {
+            int c;
+			switch (c = getc(f)) {
+			case 0x80:	/* Bit String */
+            {
+                int j;
+				c = getc(f);	/* Byte count */
+				if (debug)
+                  pm_message("bit string of %d bytes", c);
+				
+				if (col + c * 8 > cols + padright)
+				  pm_error ("bad byte count");
+				for (j = 0; j < c; ++j) {
+                    int cc, k;
+					cc = getc(f);
+					for (k = 0x80; k; k >>= 1) {
+						bitrow[plane][col] = (k & cc) ? 0 : 1;
+						++col;
+					}
+				}
+            }
+            break;
+			case 0:		/* Pattern run */
+            {
+                int j, l;
+				c = getc(f);	/* Repeat count */
+				if (debug)
+					pm_message("pattern run of %d repetitions",	c);
+                /* line repeat */
+                if (c == 0) {
+                    c = getc(f);
+                    if (c != 0x00ff)
+                        pm_error( "badly formed line repeat" );
+                    linerep = getc(f);
+                    break;
+                }
+				fread (pattern, 1, patlen, f);
+				if (col + c * patlen * 8 > cols + padright)
+				  pm_error ("bad pattern repeat count");
+				for (j = 0; j < c; ++j)
+					for (l = 0; l < patlen; ++l) {
+                        int k;
+						for (k = 0x80; k; k >>= 1) {
+							bitrow[plane][col] = (k & pattern[l]) ? 0 : 1;
+							++col;
+						}
+                    }
+            }
+            break;
+
+			default:	/* Solid run */
+            {
+                int l, j;
+				if (debug)
+					pm_message("solid run of %d bytes %s", c & 0x7f,
+                               c & 0x80 ? "on" : "off" );
+                /* each byte had eight bits DSB */
+                l = (c & 0x80) ? 0: 1;
+                c = (c & 0x7f) * 8;
+                if (col + c > cols + padright)
+                    pm_error ("bad solid run repeat count");
+                for (j = 0; j < c; ++j) {
+                    bitrow[plane][col] = l;
+					++col;
+                }
+            }
+				break;
+
+			case EOF:	/* End of file */
+				pm_error( "end of file reached" );
+
+			}
+		}
+                if ( debug )
+                        pm_message( "EOL plane %d row %d", plane, row );
+                if (col != cols + padright)
+                        pm_error( "EOL beyond edge" );
+	  }
+
+	  if (planes == 4) {
+          /* Construct a pixel from the 4 planes of bits for this row */
+          int col;
+          for (col = 0; col < cols; col++) {
+            int r, g, b, i;
+
+            const int r_bit = !bitrow[0][col];
+            const int g_bit = !bitrow[1][col];
+            const int b_bit = !bitrow[2][col];
+            i = bitrow[3][col];
+
+			/* Deal with weird GEM palette - white/black/gray are
+               encoded oddly 
+            */
+			if (r_bit == g_bit && g_bit == b_bit) {
+                /* It's black, white, or gray */
+				if (r_bit && i) r = LIGHT;
+				else if (r_bit) r = BLACK;
+				else if (i) r = MAXVAL;
+				else r = DARK;
+				g = b = r;	
+			} else {
+                /* It's one of the twelve colored colors */
+                if (!i) {
+                    /* Low intensity */
+                    r = r_bit * LIGHT; 
+                    g = g_bit * LIGHT;
+                    b = b_bit * LIGHT;
+                } else {
+                    /* Normal intensity */
+                    r = r_bit * MAXVAL;
+                    g = g_bit * MAXVAL;
+                    b = b_bit * MAXVAL;
+                }
+            }
+            PPM_ASSIGN(xelrow[col], r, g, b);
+		}
+	  } else {
+          int col;
+          for (col = 0; col < cols; col++) 
+              PNM_ASSIGN1(xelrow[col], bitrow[0][col]);
+      }
+	  while (linerep--) {
+		pnm_writepnmrow( stdout, xelrow, cols, MAXVAL, type, 0 );
+		++row;
+	  }
+	}
+    pnm_freerow(xelrow);
+	pm_close( f );
+	pm_close( stdout );
+	exit(0);
+}
+
+
+static void
+getinit (file, colsP, rowsP, padrightP, patlenP, planesP)
+     FILE *file;
+     int *colsP;
+     int *rowsP;
+     int *padrightP;
+     int *patlenP;
+     int *planesP;
+{
+  short s;
+  short headlen;
+
+  if (pm_readbigshort (file, &s) == -1) /* Image file version */
+    pm_error ("EOF / read error");
+  if (s != 1)
+    pm_error ("unknown version number (%d)", (int) s);
+  if (pm_readbigshort (file, &headlen) == -1) /* Header length in words */
+    pm_error ("EOF / read error");
+  if (headlen < 8)
+    pm_error ("short header (%d)", (int) headlen);
+  if (pm_readbigshort (file, &s) == -1) /* Number of planes */
+    pm_error ("EOF / read error");
+  if (s != 4 && s != 1)
+    pm_error ("This program can interpret IMGs with only 1 or 4 planes");
+  *planesP = s;
+  if (pm_readbigshort (file, &s) == -1) /* Pattern definition length (bytes) */
+    pm_error ("EOF / read error");
+  if (s < 1 || s > 8)
+    pm_error ("illegal pattern length (%d)", (int) s);
+  *patlenP = (int) s;
+  if (pm_readbigshort (file, &s) == -1 /* Pixel height (microns) */
+      || pm_readbigshort (file, &s) == -1 /* Pixel height (microns) */
+      || pm_readbigshort (file, &s) == -1) /* Scan line width */
+    pm_error ("EOF / read error");
+  *colsP = (int) s;
+  if (pm_readbigshort (file, &s) == -1) /* Number of scan line items */
+    pm_error ("EOF / read error");
+  *rowsP = (int) s;
+  *padrightP = 7 - ((*colsP + 7) & 7);
+
+  headlen -= 8;
+  while (headlen-- > 0)
+    {
+      (void) getc (file);
+      (void) getc (file);
+    }
+}
+
diff --git a/converter/other/giftopnm.c b/converter/other/giftopnm.c
new file mode 100644
index 00000000..d3d02fde
--- /dev/null
+++ b/converter/other/giftopnm.c
@@ -0,0 +1,1460 @@
+/* +-------------------------------------------------------------------+ */
+/* | Copyright 1990, 1991, 1993, David Koblas.  (koblas@netcom.com)    | */
+/* |   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.           | */
+/* +-------------------------------------------------------------------+ */
+
+/* There is a copy of the GIF89 specification, as defined by its
+   inventor, Compuserve, in 1989, at http://members.aol.com/royalef/gif89a.txt
+
+   This covers the high level format, but does not cover how the "data"
+   contents of a GIF image represent the raster of color table indices.
+   An appendix describes extensions to Lempel-Ziv that GIF makes (variable
+   length compression codes and the clear and end codes), but does not
+   describe the Lempel-Ziv base.
+*/
+
+#define _BSD_SOURCE   /* Make sure strcasecmp() is in string.h */
+
+#include <string.h>
+#include <assert.h>
+
+#include "pnm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+
+#define GIFMAXVAL 255
+#define MAXCOLORMAPSIZE 256
+
+#define CM_RED 0
+#define CM_GRN 1
+#define CM_BLU 2
+
+#define MAX_LZW_BITS  12
+
+#define INTERLACE      0x40
+#define LOCALCOLORMAP  0x80
+#define BitSet(byte, bit)      (((byte) & (bit)) == (bit))
+
+static __inline__ bool
+ReadOK(FILE *          const fileP,
+       unsigned char * const buffer,
+       size_t          const len) {
+
+    size_t bytesRead;
+
+    bytesRead = fread(buffer, len, 1, fileP);
+
+    return (bytesRead != 0);
+}
+
+
+#define LM_to_uint(a,b)                        (((b)<<8)|(a))
+
+static int const maxnum_lzwCode = (1<<MAX_LZW_BITS);
+
+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;  /* Filespecs of input files */
+    unsigned int verbose;    /* -verbose option */
+    unsigned int comments;   /* -comments option */
+    bool all_images;  /* He wants all the images */
+    unsigned int image_no;
+        /* image number he wants from input, starting at 0.  Undefined
+           if all_images is TRUE
+        */
+    const char * alpha_filename;
+    unsigned int quitearly;
+};
+
+
+
+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 alphaSpec, imageSpec;
+    const char * image;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+    
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "verbose",     OPT_FLAG, NULL, 
+            &cmdlineP->verbose,         0);
+    OPTENT3(0, "comments",    OPT_FLAG, NULL,
+            &cmdlineP->comments,        0);
+    OPTENT3(0, "quitearly",    OPT_FLAG, NULL,
+            &cmdlineP->quitearly,       0);
+    OPTENT3(0, "image",       OPT_STRING, &image,
+            &imageSpec,                 0);
+    OPTENT3(0, "alphaout",    OPT_STRING, &cmdlineP->alpha_filename, 
+            &alphaSpec,                 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 (!imageSpec) {
+        cmdlineP->image_no = 0;
+        cmdlineP->all_images = FALSE;
+    } else {
+        if (strcasecmp(image, "all") == 0) 
+            cmdlineP->all_images = TRUE;
+        else {
+            char * tailptr;
+
+            long const imageNo = strtol(image, &tailptr, 10);
+
+            if (*tailptr != '\0')
+                pm_error("Invalid value for '-image' option.  Must be either "
+                         "a number or 'all'.  You specified '%s'", image);
+            else if (imageNo < 0)
+                pm_error("Invalid value for '-image' option.  Must be "
+                         "positive.  You specified %ld", imageNo);
+            else if (imageNo == 0)
+                pm_error("Invalid value for 'image' option.  You specified "
+                         "zero.  The first image is 1.");
+
+            cmdlineP->all_images = FALSE;
+            cmdlineP->image_no = (unsigned int) imageNo - 1;
+        }
+    }
+    
+    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 (!alphaSpec) 
+        cmdlineP->alpha_filename = NULL;
+}
+
+
+typedef unsigned char gifColorMap[3][MAXCOLORMAPSIZE];
+
+struct gifScreen {
+    unsigned int    Width;
+    unsigned int    Height;
+    gifColorMap     ColorMap;
+    unsigned int    ColorMapSize;
+        /* Number of colors in the color map. */
+    unsigned int    ColorResolution;
+    unsigned int    Background;
+    unsigned int    AspectRatio;
+        /* Aspect ratio of each pixel, times 64, minus 15.  (i.e. 1 => 1:4).
+           But Zero means 1:1.
+        */
+    int      hasGray;  
+        /* Boolean: global colormap has at least one gray color
+           (not counting black and white) 
+        */
+    int      hasColor;
+        /* Boolean: global colormap has at least one non-gray,
+           non-black, non-white color 
+        */
+};
+
+struct gif89 {
+       int     transparent;
+       int     delayTime;
+       int     inputFlag;
+       int     disposal;
+};
+
+static void
+initGif89(struct gif89 * const gif89P) {
+    gif89P->transparent = -1;
+    gif89P->delayTime = -1;
+    gif89P->inputFlag = -1;
+    gif89P->disposal = -1;
+}       
+
+
+static int verbose;
+int    showComment;
+
+
+
+static void
+readColorMap(FILE *ifP, const int colormapsize, 
+             unsigned char colormap[3][MAXCOLORMAPSIZE],
+             int *hasGrayP, int * const hasColorP) {
+
+    int             i;
+    unsigned char   rgb[3];
+
+    assert(colormapsize <= MAXCOLORMAPSIZE);
+
+    *hasGrayP = FALSE;  /* initial assumption */
+    *hasColorP = FALSE;  /* initial assumption */
+
+    for (i = 0; i < colormapsize; ++i) {
+        if (! ReadOK(ifP, rgb, sizeof(rgb)))
+            pm_error("Unable to read Color %d from colormap", i);
+
+        colormap[CM_RED][i] = rgb[0] ;
+        colormap[CM_GRN][i] = rgb[1] ;
+        colormap[CM_BLU][i] = rgb[2] ;
+
+        if (rgb[0] == rgb[1] && rgb[1] == rgb[2]) {
+            if (rgb[0] != 0 && rgb[0] != GIFMAXVAL)
+                *hasGrayP = TRUE;
+        } else
+            *hasColorP = TRUE;
+    }
+}
+
+
+
+static bool zeroDataBlock = FALSE;
+    /* the most recently read DataBlock was an EOD marker, i.e. had
+       zero length */
+
+static void
+getDataBlock(FILE *          const ifP, 
+             unsigned char * const buf, 
+             bool *          const eofP,
+             unsigned int *  const lengthP) {
+/*----------------------------------------------------------------------------
+   Read a DataBlock from file 'ifP', return it at 'buf'.
+
+   The first byte of the datablock is the length, in pure binary, of the
+   rest of the datablock.  We return the data portion (not the length byte)
+   of the datablock at 'buf', and its length as *lengthP.
+
+   Except that if we hit EOF or have an I/O error reading the first
+   byte (size field) of the DataBlock, we return *eofP == TRUE and
+   *lengthP == 0.
+
+   We return *eofP == FALSE if we don't hit EOF or have an I/O error.
+
+   If we hit EOF or have an I/O error reading the data portion of the
+   DataBlock, we exit the program with pm_error().
+-----------------------------------------------------------------------------*/
+    unsigned char count;
+    bool successfulRead;
+    
+    long const pos=ftell(ifP);
+    successfulRead = ReadOK(ifP, &count, 1);
+    if (!successfulRead) {
+        pm_message("EOF or error in reading DataBlock size from file" );
+        *eofP = TRUE;
+        *lengthP = 0;
+    } else {
+        if (verbose)
+            pm_message("%d byte block at Position %ld", count, pos);
+        *eofP = FALSE;
+        *lengthP = count;
+
+        if (count == 0) 
+            zeroDataBlock = TRUE;
+        else {
+            bool successfulRead;
+
+            zeroDataBlock = FALSE;
+            successfulRead = ReadOK(ifP, buf, count); 
+            
+            if (!successfulRead) 
+                pm_error("EOF or error reading data portion of %d byte "
+                         "DataBlock from file", count);
+        }
+    }
+}
+
+
+
+static void
+readThroughEod(FILE * const ifP) {
+/*----------------------------------------------------------------------------
+  Read the file 'ifP' through the next EOD marker.  An EOD marker is a
+  a zero length data block.
+
+  If there is no EOD marker between the present file position and EOF,
+  we read to EOF and issue warning message about a missing EOD marker.
+-----------------------------------------------------------------------------*/
+    unsigned char buf[260];
+    bool eod;
+
+    eod = FALSE;  /* initial value */
+    while (!eod) {
+        bool eof;
+        unsigned int count;
+
+        getDataBlock(ifP, buf, &eof, &count);
+        if (eof)
+            pm_message("EOF encountered before EOD marker.  The GIF "
+                       "file is malformed, but we are proceeding "
+                       "anyway as if an EOD marker were at the end "
+                       "of the file.");
+        if (eof || count == 0)
+            eod = TRUE;
+    }
+}
+
+
+
+static void
+doCommentExtension(FILE * const ifP) {
+/*----------------------------------------------------------------------------
+   Read the rest of a comment extension from the input file 'ifP' and handle
+   it.
+   
+   We ought to deal with the possibility that the comment is not text.  I.e.
+   it could have nonprintable characters or embedded nulls.  I don't know if
+   the GIF spec requires regular text or not.
+-----------------------------------------------------------------------------*/
+    char buf[255+1];
+    unsigned int blocklen;  
+    bool done;
+
+    done = FALSE;
+    while (!done) {
+        bool eof;
+        getDataBlock(ifP, (unsigned char*) buf, &eof, &blocklen); 
+        if (blocklen == 0 || eof)
+            done = TRUE;
+        else {
+            buf[blocklen] = '\0';
+            if (showComment) {
+                pm_message("gif comment: %s", buf);
+            }
+        }
+    }
+}
+
+
+
+static void 
+doGraphicControlExtension(FILE *         const ifP,
+                          struct gif89 * const gif89P) {
+
+    bool eof;
+    unsigned int length;
+    static unsigned char buf[256];
+
+    getDataBlock(ifP, buf, &eof, &length);
+    if (eof)
+        pm_error("EOF/error encountered reading "
+                 "1st DataBlock of Graphic Control Extension.");
+    else if (length < 4) 
+        pm_error("graphic control extension 1st DataBlock too short.  "
+                 "It must be at least 4 bytes; it is %d bytes.",
+                 length);
+    else {
+        gif89P->disposal = (buf[0] >> 2) & 0x7;
+        gif89P->inputFlag = (buf[0] >> 1) & 0x1;
+        gif89P->delayTime = LM_to_uint(buf[1],buf[2]);
+        if ((buf[0] & 0x1) != 0)
+            gif89P->transparent = buf[3];
+        readThroughEod(ifP);
+    }
+}
+
+
+
+static void
+doExtension(FILE * const ifP, int const label, struct gif89 * const gif89P) {
+    const char * str;
+    
+    switch (label) {
+    case 0x01:              /* Plain Text Extension */
+        str = "Plain Text";
+#ifdef notdef
+        GetDataBlock(ifP, (unsigned char*) buf, &eof, &length);
+        
+        lpos   = LM_to_uint(buf[0], buf[1]);
+        tpos   = LM_to_uint(buf[2], buf[3]);
+        width  = LM_to_uint(buf[4], buf[5]);
+        height = LM_to_uint(buf[6], buf[7]);
+        cellw  = buf[8];
+        cellh  = buf[9];
+        foreground = buf[10];
+        background = buf[11];
+        
+        while (GetDataBlock(ifP, (unsigned char*) buf) != 0) {
+            PPM_ASSIGN(xels[ypos][xpos],
+                       cmap[CM_RED][v],
+                       cmap[CM_GRN][v],
+                       cmap[CM_BLU][v]);
+            ++index;
+        }
+#else
+        readThroughEod(ifP);
+#endif
+        break;
+    case 0xff:              /* Application Extension */
+        str = "Application";
+        readThroughEod(ifP);
+        break;
+    case 0xfe:              /* Comment Extension */
+        str = "Comment";
+        doCommentExtension(ifP);
+        break;
+    case 0xf9:              /* Graphic Control Extension */
+        str = "Graphic Control";
+        doGraphicControlExtension(ifP, gif89P);
+        break;
+    default: {
+        static char buf[256];
+        str = buf;
+        sprintf(buf, "UNKNOWN (0x%02x)", label);
+        pm_message("Ignoring unrecognized extension (type 0x%02x)", label);
+        readThroughEod(ifP);
+        }
+        break;
+    }
+    if (verbose)
+        pm_message(" got a '%s' extension", str );
+}
+
+
+
+struct getCodeState {
+    unsigned char buf[280];
+        /* This is the buffer through which we read the data from the 
+           stream.  We must buffer it because we have to read whole data
+           blocks at a time, but our client wants one code at a time.
+           The buffer typically contains the contents of one data block
+           plus two bytes from the previous data block.
+        */
+    int bufCount;
+        /* This is the number of bytes of contents in buf[]. */
+    int curbit;
+        /* The bit number (starting at 0) within buf[] of the next bit
+           to be returned.  If the next bit to be returned is not yet in
+           buf[] (we've already returned everything in there), this points
+           one beyond the end of the buffer contents.
+        */
+    bool streamExhausted;
+        /* The last time we read from the input stream, we got an EOD marker
+           or EOF
+        */
+};
+
+
+
+static void
+initGetCode(struct getCodeState * const getCodeStateP) {
+    
+    /* Fake a previous data block */
+    getCodeStateP->buf[0] = 0;
+    getCodeStateP->buf[1] = 0;
+    getCodeStateP->bufCount = 2;
+    getCodeStateP->curbit = getCodeStateP->bufCount * 8;
+    getCodeStateP->streamExhausted = FALSE;
+}
+
+
+
+static void
+getAnotherBlock(FILE * const ifP, 
+                struct getCodeState * const gsP) {
+
+    unsigned int count;
+    unsigned int assumed_count;
+    bool eof;
+
+    /* Shift buffer down so last two bytes are now the
+       first two bytes.  Shift 'curbit' cursor, which must
+       be somewhere in or immediately after those two
+       bytes, accordingly.
+    */
+    gsP->buf[0] = gsP->buf[gsP->bufCount-2];
+    gsP->buf[1] = gsP->buf[gsP->bufCount-1];
+
+    gsP->curbit -= (gsP->bufCount-2)*8;
+    gsP->bufCount = 2;
+        
+    /* Add the next block to the buffer */
+    getDataBlock(ifP, &gsP->buf[gsP->bufCount], &eof, &count);
+    if (eof) {
+        pm_message("EOF encountered in image "
+                   "before EOD marker.  The GIF "
+                   "file is malformed, but we are proceeding "
+                   "anyway as if an EOD marker were at the end "
+                   "of the file.");
+        assumed_count = 0;
+    } else
+        assumed_count = count;
+
+    gsP->streamExhausted = (assumed_count == 0);
+
+    gsP->bufCount += assumed_count;
+}
+
+
+
+static void
+doGetCode(FILE *                const ifP, 
+          int                   const codeSize,
+          struct getCodeState * const gsP,
+          int *                 const retvalP) {
+
+    if ((gsP->curbit+codeSize) > gsP->bufCount*8 && !gsP->streamExhausted) 
+        /* Not enough left in buffer to satisfy request.  Get the next
+           data block into the buffer.
+        */
+        getAnotherBlock(ifP, gsP);
+
+    if ((gsP->curbit+codeSize) > gsP->bufCount*8) {
+        /* If the buffer still doesn't have enough bits in it, that means
+           there were no data blocks left to read.
+        */
+        *retvalP = -1;  /* EOF */
+
+        {
+            int const bitsUnused = gsP->bufCount*8 - gsP->curbit;
+            if (bitsUnused > 0)
+                pm_message("Stream ends with a partial code "
+                           "(%d bits left in file; "
+                           "expected a %d bit code).  Ignoring.",
+                           bitsUnused, codeSize);
+        }
+    } else {
+        int i, j;
+        int code;
+        unsigned char * const buf = gsP->buf;
+
+        code = 0;  /* initial value */
+        for (i = gsP->curbit, j = 0; j < codeSize; ++i, ++j)
+            code |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
+        gsP->curbit += codeSize;
+        *retvalP = code;
+    }
+}
+
+
+
+static int
+getCode(FILE * const ifP, 
+        int    const codeSize, 
+        bool   const init)
+{
+/*----------------------------------------------------------------------------
+   If 'init', initialize the code getter.
+
+   Otherwise, read and return the next lzw code from the file *ifP.
+
+   'codeSize' is the number of bits in the code we are to get.
+
+   Return -1 instead of a code if we encounter the end of the file.
+-----------------------------------------------------------------------------*/
+    static struct getCodeState getCodeState;
+
+    int retval;
+
+    if (init) {
+        initGetCode(&getCodeState);
+        retval = 0;
+    } else 
+        doGetCode(ifP, codeSize, &getCodeState, &retval);
+
+    return retval;
+}
+
+
+struct stack {
+    /* Stack grows from low addresses to high addresses */
+    int * stack;  /* malloc'ed array */
+    int * sp;     /* stack pointer */
+    int * top;    /* next word above top of stack */
+};
+
+
+
+static void 
+initStack(struct stack * const stackP, unsigned int const size) {
+
+    MALLOCARRAY(stackP->stack, size);
+    if (stackP->stack == NULL)
+        pm_error("Unable to allocate %d-word stack.", size);
+    stackP->sp = stackP->stack;
+    stackP->top = stackP->stack + size;
+}
+
+
+
+static void
+pushStack(struct stack * const stackP, int const value) {
+
+    if (stackP->sp >= stackP->top)
+        pm_error("stack overflow");
+
+    *(stackP->sp++) = value;
+}
+
+
+
+static bool
+stackIsEmpty(const struct stack * const stackP) {
+    return stackP->sp == stackP->stack;
+}
+
+
+
+static int
+popStack(struct stack * const stackP) {
+
+    if (stackP->sp <= stackP->stack)
+        pm_error("stack underflow");
+    
+    return *(--stackP->sp);
+}
+
+
+
+static void
+termStack(struct stack * const stackP) {
+    free(stackP->stack);
+    stackP->stack = NULL;
+}
+
+
+/*----------------------------------------------------------------------------
+   Some notes on LZW.
+
+   LZW is an extension of Limpel-Ziv.  The two extensions are:
+
+     1) in Limpel-Ziv, codes are all the same number of bits.  In
+        LZW, they start out small and increase as the stream progresses.
+
+     2) LZW has a clear code that resets the string table and code
+        size.
+
+   The LZW code space is allocated as follows:
+
+   The true data elements are dataWidth bits wide, so the maximum
+   value of a true data element is 2**dataWidth-1.  We call that
+   max_dataVal.  The first byte in the stream tells you what dataWidth
+   is.
+
+   LZW codes 0 - max_dataVal are direct codes.  Each on represents
+   the true data element whose value is that of the LZW code itself.
+   No decompression is required.
+
+   max_dataVal + 1 and up are compression codes.  They encode
+   true data elements:
+
+   max_dataVal + 1 is the clear code.
+         
+   max_dataVal + 2 is the end code.
+
+   max_dataVal + 3 and up are string codes.  Each string code 
+   represents a string of true data elements.  The translation from a
+   string code to the string of true data elements varies as the stream
+   progresses.  In the beginning and after every clear code, the
+   translation table is empty, so no string codes are valid.  As the
+   stream progresses, the table gets filled and more string codes 
+   become valid.
+
+-----------------------------------------------------------------------------*/
+
+
+struct decompressor {
+    struct stack stack;
+    int      fresh;
+        /* The stream is right after a clear code or at the very beginning */
+    int      codeSize;
+        /* The current code size -- each LZW code in this part of the image
+           is this many bits.  Ergo, we read this many bits at a time from
+           the stream.
+        */
+    int      maxnum_code;
+        /* The maximum number of LZW codes that can be represented with the 
+           current code size.  (1 << codeSize)
+        */
+    int      next_tableSlot;
+        /* Index in the code translation table of the next free entry */
+    int      firstcode;
+        /* This is always a true data element code */
+    int      prevcode;
+        /* The code just before, in the image, the one we're processing now */
+    int      table[2][(1 << MAX_LZW_BITS)];
+
+    /* The following are constant for the life of the decompressor */
+    FILE * ifP;
+    int init_codeSize;
+    int max_dataVal;
+    int clear_code;
+    int end_code; 
+};
+
+
+
+static void
+resetDecompressor(struct decompressor * const decompP) {
+
+    decompP->codeSize = decompP->init_codeSize+1;
+    decompP->maxnum_code = 1 << decompP->codeSize;
+    decompP->next_tableSlot = decompP->max_dataVal + 3;
+    decompP->fresh = 1;
+}
+
+
+
+static void
+lzwInit(struct decompressor * const decompP, 
+        FILE *                const ifP,
+        int                   const init_codeSize) {
+
+    if (verbose)
+        pm_message("Image says the initial compression code size is "
+                   "%d bits", 
+                   init_codeSize);
+    
+    decompP->ifP = ifP;
+    decompP->init_codeSize = init_codeSize;
+
+    assert(decompP->init_codeSize < sizeof(decompP->max_dataVal) * 8);
+
+    decompP->max_dataVal = (1 << init_codeSize) - 1;
+    decompP->clear_code = decompP->max_dataVal + 1;
+    decompP->end_code = decompP->max_dataVal + 2;
+
+    if (verbose)
+        pm_message("Initial code size is %u bits; clear code = 0x%x, "
+                   "end code = 0x%x",
+                   decompP->init_codeSize, 
+                   decompP->clear_code, decompP->end_code);
+    
+    /* The entries in the translation table for true data codes are
+       constant throughout the stream.  We set them now and they never
+       change.
+    */
+    {
+        unsigned int i;
+        for (i = 0; i <= decompP->max_dataVal; ++i) {
+            decompP->table[0][i] = 0;
+            decompP->table[1][i] = i;
+        }
+    }
+    resetDecompressor(decompP);
+
+    getCode(decompP->ifP, 0, TRUE);
+    
+    decompP->fresh = TRUE;
+    
+    initStack(&decompP->stack, maxnum_lzwCode * 2);
+}
+
+
+
+static void
+lzwTerm(struct decompressor * const decompP) {
+
+    termStack(&decompP->stack);
+}
+
+
+
+static void
+expandCodeOntoStack(struct decompressor * const decompP,
+                    int                   const incode,
+                    bool *                const errorP) {
+/*----------------------------------------------------------------------------
+   'incode' is an LZW string code.  It represents a string of true data
+   elements, as defined by the string translation table in *decompP.
+
+   Expand the code to a string of LZW direct codes and push them onto the
+   stack such that the leftmost code is on top.
+
+   Also add to the translation table where appropriate.
+
+   Iff the translation table contains a cycle (which means the LZW stream
+   from which it was built is invalid), return *errorP == TRUE.
+-----------------------------------------------------------------------------*/
+    int code;
+    bool error;
+
+    error = FALSE;
+
+    if (incode < decompP->next_tableSlot) 
+        code = incode;
+    else {
+        /* It's a code that isn't in our translation table yet */
+        pushStack(&decompP->stack, decompP->firstcode);
+        code = decompP->prevcode;
+    }
+
+    {
+        /* Get the whole string that this compression code
+           represents and push it onto the code stack so the
+           leftmost code is on top.  Set decompP->firstcode to the
+           first (leftmost) code in that string.
+            */
+
+        unsigned int stringCount;
+        stringCount = 0;
+
+        while (code > decompP->max_dataVal && !error) {
+            if (stringCount > maxnum_lzwCode) {
+                pm_message("Error in GIF image: contains LZW string loop");
+                error = TRUE;
+            } else {
+                ++stringCount;
+                pushStack(&decompP->stack, decompP->table[1][code]);
+                code = decompP->table[0][code];
+            }
+        }
+        decompP->firstcode = decompP->table[1][code];
+        pushStack(&decompP->stack, decompP->firstcode);
+    }
+
+    if (decompP->next_tableSlot < maxnum_lzwCode) {
+        decompP->table[0][decompP->next_tableSlot] = decompP->prevcode;
+        decompP->table[1][decompP->next_tableSlot] = decompP->firstcode;
+        ++decompP->next_tableSlot;
+        if (decompP->next_tableSlot >= decompP->maxnum_code) {
+            /* We've used up all the codes of the current code size.
+               Future codes in the stream will have codes one bit longer.
+               But there's an exception if we're already at the LZW
+               maximum, in which case the codes will simply continue
+               the same size.
+            */
+            if (decompP->codeSize < MAX_LZW_BITS) {
+                ++decompP->codeSize;
+                decompP->maxnum_code = 1 << decompP->codeSize;
+            }
+        }
+    }
+
+    decompP->prevcode = incode;
+    *errorP = error;
+}
+
+
+
+static int
+lzwReadByte(struct decompressor * const decompP) {
+/*----------------------------------------------------------------------------
+  Return the next data element of the decompressed image.  In the context
+  of a GIF, a data element is the color table index of one pixel.
+
+  We read and return the next byte of the decompressed image, or:
+
+    Return -1 if we hit EOF prematurely (i.e. before an "end" code.  We
+    forgive the case that the "end" code is followed by EOF instead of
+    an EOD marker (zero length DataBlock)).
+
+    Return -2 if there are no more bytes in the image.  In that case,
+    make sure the file is positioned immediately after the image (i.e.
+    after the EOD marker that marks the end of the image or EOF).
+
+    Return -3 if we encounter errors in the LZW stream.
+-----------------------------------------------------------------------------*/
+    int retval;
+
+    if (!stackIsEmpty(&decompP->stack))
+        retval = popStack(&decompP->stack);
+    else if (decompP->fresh) {
+        decompP->fresh = FALSE;
+        /* Read off all initial clear codes, read the first non-clear code,
+           and return it.  There are no strings in the table yet, so the next
+           code must be a direct true data code.
+        */
+        do {
+            decompP->firstcode =
+                getCode(decompP->ifP, decompP->codeSize, FALSE);
+            decompP->prevcode = decompP->firstcode;
+        } while (decompP->firstcode == decompP->clear_code);
+        if (decompP->firstcode == decompP->end_code) {
+            if (!zeroDataBlock)
+                readThroughEod(decompP->ifP);
+            retval = -2;
+        } else
+            retval = decompP->firstcode;
+    } else {
+        int code;
+        code = getCode(decompP->ifP, decompP->codeSize, FALSE);
+        if (code == -1)
+            retval = -1;
+        else {
+            assert(code >= 0);  /* -1 is only possible error return */
+            if (code == decompP->clear_code) {
+                resetDecompressor(decompP);
+                retval = lzwReadByte(decompP);
+            } else {
+                if (code == decompP->end_code) {
+                    if (!zeroDataBlock)
+                        readThroughEod(decompP->ifP);
+                    retval = -2;
+                } else {
+                    bool error;
+                    expandCodeOntoStack(decompP, code, &error);
+                    if (error)
+                        retval = -3;
+                    else
+                        retval = popStack(&decompP->stack);
+                }
+            }
+        }
+    }
+    return retval;
+}
+
+
+
+enum pass {MULT8PLUS0, MULT8PLUS4, MULT4PLUS2, MULT2PLUS1};
+
+static void
+bumpRowInterlace(unsigned int * const rowP,
+                 unsigned int   const rows,
+                 enum pass *    const passP) {
+/*----------------------------------------------------------------------------
+   Move *pixelCursorP to the next row in the interlace pattern.
+-----------------------------------------------------------------------------*/
+    /* There are 4 passes:
+       MULT8PLUS0: Rows 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 (*passP) {
+    case MULT8PLUS0:
+        *rowP += 8;
+        break;
+    case MULT8PLUS4:
+        *rowP += 8;
+        break;
+    case MULT4PLUS2:
+        *rowP += 4;
+        break;
+    case MULT2PLUS1:
+        *rowP += 2;
+        break;
+    }
+    /* Set the proper pass for the next read.  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 (*rowP >= rows && *passP != MULT2PLUS1) {
+        switch (*passP) {
+        case MULT8PLUS0:
+            *passP = MULT8PLUS4;
+            *rowP = 4;
+            break;
+        case MULT8PLUS4:
+            *passP = MULT4PLUS2;
+            *rowP = 2;
+            break;
+        case MULT4PLUS2:
+            *passP = MULT2PLUS1;
+            *rowP = 1;
+            break;
+        case MULT2PLUS1:
+            /* We've read the entire image */
+            break;
+        }
+    }
+}
+
+
+
+struct pnmBuffer {
+    xel ** xels;
+    unsigned int col;
+    unsigned int row;
+};
+
+static void
+addPixelToRaster(unsigned int       const cmapIndex,
+                 struct pnmBuffer * const pnmBufferP,
+                 unsigned int       const cols,
+                 unsigned int       const rows,
+                 gifColorMap              cmap, 
+                 unsigned int       const cmapSize,
+                 bool               const interlace,
+                 int                const transparentIndex,
+                 bit **             const alphabits,
+                 enum pass *        const passP) {
+
+    if (cmapIndex >= cmapSize)
+        pm_error("Invalid color index %u in an image that has only "
+                 "%u colors in the color map.", cmapIndex, cmapSize);
+    
+    assert(cmapIndex < MAXCOLORMAPSIZE);
+    
+    PPM_ASSIGN(pnmBufferP->xels[pnmBufferP->row][pnmBufferP->col], 
+               cmap[CM_RED][cmapIndex],
+               cmap[CM_GRN][cmapIndex],
+               cmap[CM_BLU][cmapIndex]);
+    
+    if (alphabits) 
+        alphabits[pnmBufferP->row][pnmBufferP->col] =
+            (cmapIndex == transparentIndex) ? PBM_BLACK : PBM_WHITE;
+    
+    ++pnmBufferP->col;
+    if (pnmBufferP->col == cols) {
+        pnmBufferP->col = 0;
+        if (interlace)
+            bumpRowInterlace(&pnmBufferP->row, rows, passP);
+        else
+            ++pnmBufferP->row;
+    }
+}
+
+
+
+static void
+readImageData(FILE *       const ifP, 
+              xel **       const xels, 
+              unsigned int const cols,
+              unsigned int const rows,
+              gifColorMap        cmap, 
+              unsigned int const cmapSize,
+              bool         const interlace,
+              int          const transparentIndex,
+              bit **       const alphabits) {
+
+    unsigned char lzwMinCodeSize;      
+    enum pass pass;
+    struct decompressor decomp;
+    struct pnmBuffer pnmBuffer;
+    bool gotMinCodeSize;
+
+    pass = MULT8PLUS0;
+
+    pnmBuffer.xels = xels;
+    pnmBuffer.col  = 0;
+    pnmBuffer.row  = 0;
+
+    gotMinCodeSize =  ReadOK(ifP, &lzwMinCodeSize, 1);
+    if (!gotMinCodeSize)
+        pm_error("GIF stream ends (or read error) "
+                 "right after an image separator; no "
+                 "image data follows.");
+
+    if (lzwMinCodeSize > MAX_LZW_BITS)
+        pm_error("Invalid minimum code size value in image data: %u.  "
+                 "Maximum allowable code size in GIF is %u", 
+                 lzwMinCodeSize, MAX_LZW_BITS);
+
+    lzwInit(&decomp, ifP, lzwMinCodeSize);
+
+    while (pnmBuffer.row < rows) {
+        int const rc = lzwReadByte(&decomp);
+
+        switch (rc) {
+        case -3:
+            pm_error("Error in GIF input stream");
+            break;
+        case -2:
+            pm_error("Error in GIF image: Not enough raster data to fill "
+                     "%u x %u dimensions.  Ran out of raster data in "
+                     "row %u", cols, rows, pnmBuffer.row);
+            break;
+        case -1:
+            pm_error("Premature end of file; no proper GIF closing");
+            break;
+        default:
+            addPixelToRaster(rc, &pnmBuffer, cols, rows, cmap, cmapSize,
+                             interlace, transparentIndex, alphabits, &pass);
+        }
+    }
+    if (lzwReadByte(&decomp) >= 0)
+        pm_message("Extraneous data at end of image.  "
+                   "Skipped to end of image");
+
+    lzwTerm(&decomp);
+}
+
+
+
+static void
+writePnm(FILE *outfile, xel ** const xels, 
+         const int cols, const int rows,
+         const int hasGray, const int hasColor) {
+/*----------------------------------------------------------------------------
+   Write a PNM image to the current position of file 'outfile' with
+   dimensions 'cols' x 'rows' and raster 'xels'.
+   
+   Make it PBM, PGM, or PBM according to 'hasGray' and 'hasColor'.
+-----------------------------------------------------------------------------*/
+    int format;
+    const char *format_name;
+           
+    if (hasColor) {
+        format = PPM_FORMAT;
+        format_name = "PPM";
+    } else if (hasGray) {
+        format = PGM_FORMAT;
+        format_name = "PGM";
+    } else {
+        format = PBM_FORMAT;
+        format_name = "PBM";
+    }
+    if (verbose) 
+        pm_message("writing a %s file", format_name);
+    
+    if (outfile) 
+        pnm_writepnm(outfile, xels, cols, rows,
+                     (xelval) GIFMAXVAL, format, FALSE);
+}
+
+
+
+static void
+transparencyMessage(int const transparentIndex, 
+                    gifColorMap cmap) {
+/*----------------------------------------------------------------------------
+   If user wants verbose output, tell him that the color with index
+   'transparentIndex' is supposed to be a transparent background color.
+   
+   If transparentIndex == -1, tell him there is no transparent background
+   color.
+-----------------------------------------------------------------------------*/
+    if (verbose) {
+        if (transparentIndex == -1)
+            pm_message("no transparency");
+        else
+            pm_message("transparent background color: rgb:%02x/%02x/%02x "
+                       "Index %d",
+                       cmap[CM_RED][transparentIndex],
+                       cmap[CM_GRN][transparentIndex],
+                       cmap[CM_BLU][transparentIndex],
+                       transparentIndex
+                );
+    }
+}
+
+static void
+readGifHeader(FILE * const gifFile, struct gifScreen * const gifScreenP) {
+/*----------------------------------------------------------------------------
+   Read the GIF stream header off the file gifFile, which is present
+   positioned to the beginning of a GIF stream.  Return the info from it
+   as *gifScreenP.
+-----------------------------------------------------------------------------*/
+    unsigned char   buf[16];
+    char     version[4];
+
+
+    if (! ReadOK(gifFile,buf,6))
+        pm_error("error reading magic number" );
+    
+    if (strncmp((char *)buf,"GIF",3) != 0)
+        pm_error("File does not contain a GIF stream.  It does not start "
+                 "with 'GIF'.");
+    
+    strncpy(version, (char *)buf + 3, 3);
+    version[3] = '\0';
+    
+    if (verbose)
+        pm_message("GIF format version is '%s'", version);
+    
+    if ((!STREQ(version, "87a")) && (!STREQ(version, "89a")))
+        pm_error("bad version number, not '87a' or '89a'" );
+    
+    if (! ReadOK(gifFile,buf,7))
+        pm_error("failed to read screen descriptor" );
+    
+    gifScreenP->Width           = LM_to_uint(buf[0],buf[1]);
+    gifScreenP->Height          = LM_to_uint(buf[2],buf[3]);
+    gifScreenP->ColorMapSize    = 1 << ((buf[4] & 0x07) + 1);
+    gifScreenP->ColorResolution = (buf[4] & 0x70 >> 3) + 1;
+    gifScreenP->Background      = buf[5];
+    gifScreenP->AspectRatio     = buf[6];
+
+    if (verbose) {
+        pm_message("GIF Width = %d GIF Height = %d "
+                   "Pixel aspect ratio = %d (%f:1)",
+                   gifScreenP->Width, gifScreenP->Height, 
+                   gifScreenP->AspectRatio, 
+                   gifScreenP->AspectRatio == 0 ? 
+                   1 : (gifScreenP->AspectRatio + 15) / 64.0);
+        pm_message("Colors = %d   Color Resolution = %d",
+                   gifScreenP->ColorMapSize, gifScreenP->ColorResolution);
+    }           
+    if (BitSet(buf[4], LOCALCOLORMAP)) {    /* Global Colormap */
+        readColorMap(gifFile, gifScreenP->ColorMapSize, gifScreenP->ColorMap,
+                     &gifScreenP->hasGray, &gifScreenP->hasColor);
+        if (verbose) {
+            pm_message("Color map %s grays, %s colors", 
+                       gifScreenP->hasGray ? "contains" : "doesn't contain",
+                       gifScreenP->hasColor ? "contains" : "doesn't contain");
+        }
+    }
+    
+    if (gifScreenP->AspectRatio != 0 && gifScreenP->AspectRatio != 49) {
+        float   r;
+        r = ( (float) gifScreenP->AspectRatio + 15.0 ) / 64.0;
+        pm_message("warning - input pixels are not square, "
+                   "but we are rendering them as square pixels "
+                   "in the output.  "
+                   "To fix the output, run it through "
+                   "'pnmscale -%cscale %g'",
+                   r < 1.0 ? 'x' : 'y',
+                   r < 1.0 ? 1.0 / r : r );
+    }
+}
+
+
+
+static void
+readExtensions(FILE*          const ifP, 
+               struct gif89 * const gif89P,
+               bool *         const eodP) {
+/*----------------------------------------------------------------------------
+   Read extension blocks from the GIF stream to which the file *ifP is
+   positioned.  Read up through the image separator that begins the
+   next image or GIF stream terminator.
+
+   If we encounter EOD (end of GIF stream) before we find an image 
+   separator, we return *eodP == TRUE.  Else *eodP == FALSE.
+
+   If we hit end of file before an EOD marker, we abort the program with
+   an error message.
+-----------------------------------------------------------------------------*/
+    bool imageStart;
+    bool eod;
+
+    eod = FALSE;
+    imageStart = FALSE;
+
+    /* Read the image descriptor */
+    while (!imageStart && !eod) {
+        unsigned char c;
+
+        if (! ReadOK(ifP,&c,1))
+            pm_error("EOF / read error on image data" );
+
+        if (c == ';') {         /* GIF terminator */
+            eod = TRUE;
+        } else if (c == '!') {         /* Extension */
+            if (! ReadOK(ifP,&c,1))
+                pm_error("EOF / "
+                         "read error on extension function code");
+            doExtension(ifP, c, gif89P);
+        } else if (c == ',') 
+            imageStart = TRUE;
+        else 
+            pm_message("bogus character 0x%02x, ignoring", (int) c );
+    }
+    *eodP = eod;
+}
+
+
+
+static void
+reportImageInfo(unsigned int const cols,
+                unsigned int const rows,
+                bool         const useGlobalColormap,
+                unsigned int const localColorMapSize,
+                bool         const interlaced) {
+
+
+    pm_message("reading %u by %u%s GIF image",
+               cols, rows, interlaced ? " interlaced" : "" );
+    if (useGlobalColormap)
+        pm_message("  Uses global colormap");
+    else
+        pm_message("  Uses local colormap of %u colors", localColorMapSize);
+}
+
+
+
+static void
+convertImage(FILE *           const ifP, 
+             bool             const skipIt, 
+             FILE *           const imageout_file, 
+             FILE *           const alphafile, 
+             struct gifScreen       gifScreen,
+             struct gif89     const gif89) {
+/*----------------------------------------------------------------------------
+   Read a single GIF image from the current position of file 'ifP'.
+
+   If 'skipIt' is TRUE, don't do anything else.  Otherwise, write the
+   image to the current position of files 'imageout_file' and 'alphafile'.
+   If 'alphafile' is NULL, though, don't write any alpha information.
+-----------------------------------------------------------------------------*/
+    unsigned char buf[16];
+    bool useGlobalColormap;
+    xel **xels;  /* The image raster, in libpnm format */
+    bit **alphabits;  
+        /* The image alpha mask, in libpbm format.  NULL if we aren't computing
+           an alpha mask.
+        */
+    unsigned int cols, rows;  /* Dimensions of the image */
+    gifColorMap localColorMap;
+    unsigned int localColorMapSize;
+    bool interlaced;
+
+    if (! ReadOK(ifP,buf,9))
+        pm_error("couldn't read left/top/width/height");
+
+    useGlobalColormap = ! BitSet(buf[8], LOCALCOLORMAP);
+    localColorMapSize = 1u << ((buf[8] & 0x07) + 1);
+    cols = LM_to_uint(buf[4], buf[5]);
+    rows = LM_to_uint(buf[6], buf[7]);
+    interlaced = !!BitSet(buf[8], INTERLACE);
+
+    if (verbose)
+        reportImageInfo(cols, rows, useGlobalColormap, localColorMapSize,
+                        interlaced);
+        
+    xels = pnm_allocarray(cols, rows);
+    if (!xels)
+        pm_error("couldn't alloc space for image" );
+
+    if (alphafile) {
+        alphabits = pbm_allocarray(cols, rows);
+        if (!alphabits)
+            pm_error("couldn't alloc space for alpha image" );
+    } else
+        alphabits = NULL;
+    
+    if (!useGlobalColormap) {
+        int hasGray, hasColor;
+
+        readColorMap(ifP, localColorMapSize, localColorMap, 
+                     &hasGray, &hasColor);
+        transparencyMessage(gif89.transparent, localColorMap);
+        readImageData(ifP, xels, cols, rows, localColorMap, localColorMapSize,
+                      interlaced, gif89.transparent, alphabits);
+        if (!skipIt) {
+            writePnm(imageout_file, xels, cols, rows,
+                     hasGray, hasColor);
+        }
+    } else {
+        transparencyMessage(gif89.transparent, gifScreen.ColorMap);
+        readImageData(ifP, xels, cols, rows, 
+                      gifScreen.ColorMap, gifScreen.ColorMapSize,
+                      interlaced, gif89.transparent, alphabits);
+        if (!skipIt) {
+            writePnm(imageout_file, xels, cols, rows,
+                     gifScreen.hasGray, gifScreen.hasColor);
+        }
+    }
+
+    if (!skipIt && alphafile && alphabits)
+        pbm_writepbm(alphafile, alphabits, cols, rows, FALSE);
+
+    pnm_freearray(xels, rows);
+    if (alphabits)
+        pbm_freearray(alphabits, rows);
+}
+
+
+
+static void
+convertImages(FILE * const ifP, 
+              bool   const allImages,
+              int    const requestedImageSeq, 
+              bool   const drainStream,
+              FILE * const imageout_file, 
+              FILE * const alphafile) {
+/*----------------------------------------------------------------------------
+   Read a GIF stream from file 'ifP' and write one or more images from
+   it as PNM images to file 'imageout_file'.  If the images have transparency
+   and 'alphafile' is non-NULL, write PGM alpha masks to file 'alphafile'.
+
+   'allImages' means Caller wants all the images in the stream.  
+
+   'requestedImageSeq' is meaningful only when 'allImages' is FALSE.  It 
+   is the sequence number of the one image Caller wants from the stream,
+   with the first image being 0.
+
+   'drainInput' means to read the entire GIF stream, even after
+   reading the image Caller asked for.  We read the stream, not just
+   the file it's in, so we still recognize certain errors in the GIF
+   format in the tail of the stream and there may yet be more stuff in
+   the file when we return.
+-----------------------------------------------------------------------------*/
+    int imageSeq;
+        /* Sequence within GIF stream of image we are currently processing.
+           First is 0.
+        */
+    struct gifScreen gifScreen;
+    struct gif89 gif89;
+    bool eod;
+        /* We've read through the GIF terminator character */
+
+    initGif89(&gif89);
+
+    readGifHeader(ifP, &gifScreen);
+
+    for (imageSeq = 0, eod = FALSE;
+         !eod && (imageSeq <= requestedImageSeq || allImages || drainStream);
+         ++imageSeq) {
+
+        readExtensions(ifP, &gif89, &eod);
+
+        if (eod) {
+            /* GIF stream ends before image with sequence imageSeq */
+            if (!allImages && (imageSeq <= requestedImageSeq))
+                pm_error("You requested Image %d, but "
+                         "only %d image%s found in GIF stream",
+                         requestedImageSeq+1,
+                         imageSeq, imageSeq>1?"s":"" );
+        } else {
+            if (verbose)
+                pm_message("Reading Image Sequence %d", imageSeq);
+            convertImage(ifP, !allImages && (imageSeq != requestedImageSeq), 
+                         imageout_file, alphafile, gifScreen, gif89);
+        }
+    }
+}
+
+
+
+int
+main(int argc, char **argv) {
+
+    struct cmdlineInfo cmdline;
+    FILE *ifP;
+    FILE *alpha_file, *imageout_file;
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+    verbose = cmdline.verbose;
+    showComment = cmdline.comments;
+   
+    ifP = pm_openr(cmdline.input_filespec);
+
+    if (cmdline.alpha_filename == NULL)
+        alpha_file = NULL;
+    else
+        alpha_file = pm_openw(cmdline.alpha_filename);
+
+    if (alpha_file && STREQ(cmdline.alpha_filename, "-"))
+        imageout_file = NULL;
+    else
+        imageout_file = stdout;
+
+    convertImages(ifP, cmdline.all_images, cmdline.image_no, 
+                  !cmdline.quitearly, imageout_file, alpha_file);
+
+    pm_close(ifP);
+    if (imageout_file != NULL) 
+        pm_close( imageout_file );
+    if (alpha_file != NULL)
+        pm_close( alpha_file );
+
+    return 0;
+}
diff --git a/converter/other/hdifftopam.c b/converter/other/hdifftopam.c
new file mode 100644
index 00000000..444d6068
--- /dev/null
+++ b/converter/other/hdifftopam.c
@@ -0,0 +1,156 @@
+/******************************************************************************
+                                hdifftopam
+*******************************************************************************
+  This program recovers a PAM image from a horizontal difference images 
+  such as created by Pamtohdiff.
+
+  By Bryan Henderson, San Jose, CA 2002.04.15.
+******************************************************************************/
+#include <string.h>
+#include <stdio.h>
+
+#include "pam.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 *inputFilespec;  /* Filespecs of input files */
+    unsigned int pnm;
+    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 = 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, "pnm",       OPT_FLAG,    NULL, &cmdlineP->pnm,      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 if (argc-1 == 1)
+        cmdlineP->inputFilespec = argv[1];
+    else
+        pm_error("Too many arguments.");
+}
+
+
+
+static void
+makePnm(struct pam * const pamP) {
+
+    switch (pamP->depth) {
+    case 1: 
+        pamP->format = PGM_FORMAT;
+        break;
+    case 3:
+        pamP->format = PPM_FORMAT;
+        break;
+    default:
+        pm_error("Input depth (%d) does not correspond to a PNM format.",
+                 pamP->depth);
+    }
+}
+
+
+
+static void
+    describeOutput(struct pam const pam) {
+
+    pm_message("Output is %d x %d x %d, maxval %u",
+               pam.width, pam.height, pam.depth, (unsigned int) pam.maxval);
+}
+
+
+
+int 
+main(int argc, char *argv[]) {
+    FILE *ifP;
+    struct cmdlineInfo cmdline;
+    struct pam diffpam, outpam;
+    unsigned int row;
+    tuple * diffrow;
+    tuple * outrow;
+    tuple * prevrow;
+
+    pnm_init( &argc, argv );
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    pnm_readpaminit(ifP, &diffpam, PAM_STRUCT_SIZE(tuple_type));
+
+    if (diffpam.format != PAM_FORMAT) 
+        pm_error("Input must be a PAM file, not PNM");
+    else if (!STREQ(diffpam.tuple_type, "hdiff")) 
+        pm_error("Input tuple type is '%s'.  Must be 'hdiff'",
+                 diffpam.tuple_type);
+
+    outpam = diffpam;
+    outpam.file = stdout;
+    strcpy(outpam.tuple_type, "unhdiff");
+
+    if (cmdline.verbose)
+        describeOutput(outpam);
+    if (cmdline.pnm)
+        makePnm(&outpam);
+
+    pnm_writepaminit(&outpam);
+
+    diffrow = pnm_allocpamrow(&diffpam);
+    outrow =  pnm_allocpamrow(&outpam);
+    prevrow = pnm_allocpamrow(&diffpam);
+
+    pnm_setpamrow(&diffpam, prevrow, 0);
+
+    {
+        unsigned int const bias = diffpam.maxval/2;
+        
+        for (row = 0; row < diffpam.height; ++row) {
+            unsigned int col;
+            pnm_readpamrow(&diffpam, diffrow);
+            for (col = 0; col < diffpam.width; ++col) {
+                unsigned int plane;
+                for (plane = 0; plane < diffpam.depth; ++plane) {
+                    sample const prevSample = prevrow[col][plane];
+                    sample const diffSample = diffrow[col][plane];
+                    
+                    outrow[col][plane] = 
+                        (-bias + prevSample + diffSample) % (outpam.maxval+1);
+                    prevrow[col][plane] = outrow[col][plane];
+                }
+            }
+            pnm_writepamrow(&outpam, outrow);
+        }
+    }
+    pnm_freepamrow(prevrow);
+    pnm_freepamrow(outrow);
+    pnm_freepamrow(diffrow);
+
+    exit(0);
+}
+
diff --git a/converter/other/infotopam.c b/converter/other/infotopam.c
new file mode 100644
index 00000000..4f29eb07
--- /dev/null
+++ b/converter/other/infotopam.c
@@ -0,0 +1,543 @@
+/* infotopam:  A program to convert Amiga Info icon files to PAM files
+ * Copyright (C) 2004  Richard Griswold - griswold@acm.org
+ *
+ * Thanks to the following people on comp.sys.amiga.programmer for tips
+ * and pointers on decoding the info file format:
+ *
+ *   Ben Hutchings
+ *   Thomas Richter
+ *   Kjetil Svalastog Matheussen
+ *   Anders Melchiorsen
+ *   Dirk Stoecker
+ *   Ronald V.D.
+ *
+ * The format of the Amiga info file is as follows:
+ *
+ *   DiskObject header            (78 bytes)
+ *   Optional DrawerData header   (56 bytes)
+ *   First icon header            (20 bytes)
+ *   First icon data
+ *   Second icon header           (20 bytes)
+ *   Second icon data
+ *
+ * The DiskObject header contains, among other things, the magic number
+ * (0xE310), the object width and height (inside the embedded Gadget header),
+ * and the version.
+ *
+ * Each icon header contains the icon width and height, which can be smaller
+ * than the object width and height, and the number of bit-planes.
+ *
+ * The icon data has the following format:
+ *
+ *   BIT-PLANE planes, each with HEIGHT rows of (WIDTH +15) / 16 * 2 bytes
+ *   length.
+ *
+ * So if you have a 9x3x2 icon, the icon data will look like this:
+ *
+ *   aaaa aaaa a000 0000
+ *   aaaa aaaa a000 0000
+ *   aaaa aaaa a000 0000
+ *   bbbb bbbb b000 0000
+ *   bbbb bbbb b000 0000
+ *   bbbb bbbb b000 0000
+ *
+ * Where 'a' is a bit for the first bit-plane, 'b' is a bit for the second
+ * bit-plane, and '0' is padding.  Thanks again to Ben Hutchings for his
+ * very helpful post!
+ *
+ * This program uses code from "sidplay" and an older "infotoxpm" program I
+ * wrote, both of which are released under GPL.
+ *
+ *-------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include "pam.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/* Struct to hold miscellaneous icon information */
+typedef struct IconInfo_ {
+    const char  *name;        /* Icon file name */
+    FILE        *fp;          /* Input file pointer */
+
+    bool         forceColor;  /* Convert 1 bitplane icon to color icon */
+    unsigned int numColors;   /* Number of colors to override */
+    bool         selected;    /* Converting selected (second) icon */
+
+    bool         drawerData;  /* Icon has drawer data */
+    unsigned int version;     /* Icon version */
+    unsigned int width;       /* Width in pixels */
+    unsigned int height;      /* Height in pixels */
+    unsigned int depth;       /* Bits of color per pixel */
+    pixel        colors[4];   /* Colors to use for converted icons */
+    unsigned char *icon;      /* Completed icon */
+
+} IconInfo;
+
+/* Header for each icon image */
+typedef struct IconHeader_ { /* 20 bytes */
+    unsigned char pad0[4];        /* Padding (always seems to be zero) */
+    unsigned char iconWidth[2];   /* Width (usually equal to Gadget width) */
+    unsigned char iconHeight[2];  
+    /* Height (usually equal to Gadget height -1) */
+    unsigned char bpp[2];         /* Bits per pixel */
+    unsigned char pad1[10];       /* ??? */
+} IconHeader;
+
+/*
+ * Gadget and DiskObject structs come from the libsidplay 1.36.57 info_.h file
+ * http://www.geocities.com/SiliconValley/Lakes/5147/sidplay/linux.html
+ */
+typedef struct DiskObject_ { /* 78 bytes (including Gadget struct) */
+    unsigned char magic[2];         /* Magic number at the start of the file */
+    unsigned char version[2];       /* Object version number */
+    unsigned char gadget[44];       /* Copy of in memory gadget (44 by */
+    unsigned char type;             /* ??? */
+    unsigned char pad;              /* Pad it out to the next word boundry */
+    unsigned char pDefaultTool[4];  /* Pointer  to default tool */
+    unsigned char ppToolTypes[4];   /* Pointer pointer to tool types */
+    unsigned char currentX[4];      /* Current X position (?) */
+    unsigned char currentY[4];      /* Current Y position (?) */
+    unsigned char pDrawerData[4];   /* Pointer to drawer data */
+    unsigned char pToolWindow[4];   /* Ptr to tool window - only for tools */
+    unsigned char stackSize[4];     /* Stack size - only for tools */
+} DiskObject;
+
+
+
+static void
+parseCommandLine( int              argc,
+                  char *           argv[],
+                  IconInfo * const infoP ) {
+
+    unsigned int numColorArgs,  /* Number of arguments for overriding colors */
+        colorIdx,      /* Color index */
+        i;             /* Argument index */
+    const char  * const colors[4] = { 
+        /* Pixel colors based on original Amiga colors */
+        "#0055AA",    /*   Blue      0,  85, 170 */
+        "#FFFFFF",    /*   White   255, 255, 255 */
+        "#000020",    /*   Black     0,   0,  32 */
+        "#FF8A00"     /*   Orange  255, 138,   0 */
+    };
+
+    /* Option entry variables */
+    optEntry     *option_def;
+    optStruct3    opt;
+    unsigned int  option_def_index;
+    unsigned int numColorsSpec, forceColorSpec, selectedSpec;
+  
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    /* Set command line options */
+    option_def_index = 0;   /* Incremented by OPTENT3 */
+    OPTENT3(0, "forcecolor", OPT_FLAG, NULL, &forceColorSpec, 0);
+    OPTENT3(0, "numcolors",  OPT_UINT, &infoP->numColors, &numColorsSpec, 0);
+    OPTENT3(0, "selected",   OPT_FLAG, NULL, &selectedSpec,   0);
+
+    /* Initialize the iconInfo struct */
+    infoP->name = NULL;
+    infoP->fp = NULL;
+    infoP->drawerData = FALSE;
+    infoP->version = 0;
+    infoP->width = 0;
+    infoP->height = 0;
+    infoP->depth = 0;
+    infoP->icon = NULL;
+    for ( colorIdx = 0; colorIdx < 4; colorIdx++ )
+        infoP->colors[colorIdx] = 
+            ppm_parsecolor( (char*) colors[colorIdx], 0xFF );
+
+    /* Initialize option structure */
+    opt.opt_table     = option_def;
+    opt.short_allowed = FALSE;  /* No short (old-fashioned) options */
+    opt.allowNegNum   = FALSE;  /* No negative number parameters */
+
+    /* Parse the command line */
+    optParseOptions3( &argc, argv, opt, sizeof( opt ), 0 );
+
+    infoP->forceColor = forceColorSpec;
+    infoP->selected = selectedSpec;
+    if (!numColorsSpec)
+        infoP->numColors = 0;
+
+    /* Get colors and file name */
+    numColorArgs = infoP->numColors * 2;
+    if ( ( argc - 1 != numColorArgs ) && ( argc - 1 != numColorArgs + 1 ) ) {
+        pm_error( "Wrong number of arguments for number of colors.  "
+                  "For %u colors, you need %u color arguments, "
+                  "with possibly one more argument for the input file name.",
+                  infoP->numColors, numColorArgs );
+    }
+
+    /* Convert color arguments */
+    for ( i = 1; i < numColorArgs; i += 2 ) {
+        char *       endptr;        /* End pointer for strtol() */
+        unsigned int colorIdx;
+
+        /* Get color index from argument */
+        colorIdx = strtoul( argv[i], &endptr, 0 );
+
+        if ( *endptr != '\0' ) {
+            pm_error( "'%s' is not a valid color index", argv[i] );
+        }
+
+        /* Check color index range (current 0 to 3) */
+        if ( ( colorIdx < 0 ) || ( colorIdx > 3 ) ) {
+            pm_error( "%u is not a valid color index (minimum 0, maximum 3)",
+                      colorIdx );
+        }
+
+        /* Convert the color for this color index */
+        infoP->colors[colorIdx] = ppm_parsecolor( argv[i+1], 0xFF );
+    }
+
+    /* Set file name */
+    if ( i > argc-1 )
+        infoP->name = "-";  /* Read from standard input */
+    else
+        infoP->name = argv[i];
+}
+
+
+
+static void
+getDiskObject( IconInfo * const infoP ) {
+/*-------------------------------------------------------------------------
+ * Get fields from disk object portion of info file
+ *-------------------------------------------------------------------------*/
+    DiskObject  dobj;      /* Disk object structure */
+    size_t      bytesRead;
+
+    /* Read the disk object header */
+    bytesRead = fread( &dobj, 1, sizeof(dobj), infoP->fp );
+    if (ferror(infoP->fp))
+        pm_error( "Cannot read disk object header for file '%s'.  "
+                  "fread() errno = %d (%s)",
+                  infoP->name, errno, strerror( errno ) );
+    else if ( bytesRead != sizeof(dobj) )
+        pm_error( "Cannot read entire disk object header for file '%s'.  "
+                  "Only read 0x%X of 0x%X bytes",
+                  infoP->name, bytesRead, sizeof(dobj) );
+
+    /* Check magic number */
+    if ( ( dobj.magic[0] != 0xE3 ) && ( dobj.magic[1] != 0x10 ) )
+        pm_error( "Wrong magic number for file '%s'.  "
+                  "Expected 0xE310, but got 0x%X%X",
+                  infoP->name, dobj.magic[0], dobj.magic[1] );
+
+    /* Set version info and have drawer data flag */
+    infoP->version     = ( dobj.version[0]     <<  8 ) +
+        ( dobj.version[1]           );
+    infoP->drawerData  = ( dobj.pDrawerData[0] << 24 ) +
+        ( dobj.pDrawerData[1] << 16 ) +
+        ( dobj.pDrawerData[2] <<  8 ) +
+        ( dobj.pDrawerData[3]       ) ? TRUE : FALSE;
+}
+
+
+
+static void 
+getIconHeader( IconInfo * const infoP ) {
+/*-------------------------------------------------------------------------
+ * Get fields from icon header portion of info file
+ *-------------------------------------------------------------------------*/
+    IconHeader  ihead;      /* Icon header structure */
+    size_t      bytesRead;
+
+    /* Read icon header */
+    bytesRead = fread( &ihead, 1, sizeof(ihead), infoP->fp );
+    if ( ferror(infoP->fp ) )
+         pm_error( "Cannot read icon header for file '%s'.  "
+                   "fread() errno = %d (%s)",
+                   infoP->name, errno, strerror( errno ) );
+    else if ( bytesRead != sizeof(ihead) )
+        pm_error( "Cannot read the entire icon header for file '%s'.  "
+                  "Only read 0x%X of 0x%X bytes",
+                  infoP->name, bytesRead, sizeof(ihead) );
+
+    /* Get icon width, heigh, and bitplanes */
+    infoP->width  = ( ihead.iconWidth[0]  << 8 ) + ihead.iconWidth[1];
+    infoP->height = ( ihead.iconHeight[0] << 8 ) + ihead.iconHeight[1];
+    infoP->depth  = ( ihead.bpp[0]        << 8 ) + ihead.bpp[1];
+
+    /* Check number of bit planes */
+    if ( ( infoP->depth > 2 ) || ( infoP->depth < 1 ) )
+        pm_error( "We don't know how to interpret %u bitplanes file '%s'.  ",
+                  infoP->depth, infoP->name );
+}
+
+
+
+static void
+addBitplane(unsigned char * const icon,
+            unsigned int    const bpsize,
+            unsigned char * const buff) {
+/*----------------------------------------------------------------------------
+   Add bitplane to existing icon image
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    unsigned int j;
+    
+    for (i = j = 0; i < bpsize; ++i, j += 8) {
+        icon[j+0] = (icon[j+0] << 1) | ((buff[i] >> 0) & 0x01);
+        icon[j+1] = (icon[j+1] << 1) | ((buff[i] >> 1) & 0x01);
+        icon[j+2] = (icon[j+2] << 1) | ((buff[i] >> 2) & 0x01);
+        icon[j+3] = (icon[j+3] << 1) | ((buff[i] >> 3) & 0x01);
+        icon[j+4] = (icon[j+4] << 1) | ((buff[i] >> 4) & 0x01);
+        icon[j+5] = (icon[j+5] << 1) | ((buff[i] >> 5) & 0x01);
+        icon[j+6] = (icon[j+6] << 1) | ((buff[i] >> 6) & 0x01);
+        icon[j+7] = (icon[j+7] << 1) | ((buff[i] >> 7) & 0x01);
+    }
+}
+
+
+
+static void
+readIconData(FILE *           const fileP,
+             unsigned int     const width,
+             unsigned int     const height, 
+             unsigned int     const depth,
+             unsigned char ** const iconP) {
+/*-------------------------------------------------------------------------
+ * Read icon data from file
+ *-------------------------------------------------------------------------*/
+    int             bitplane; /* Bitplane index */
+    unsigned char * buff;     /* Buffer to hold bits for 1 bitplane */
+    unsigned char * icon;
+
+    unsigned int const bpsize = height * (((width + 15) / 16) * 2);
+        /* Bitplane size in bytes, with padding */
+
+  
+    MALLOCARRAY(buff, bpsize);
+    if ( buff == NULL )
+        pm_error( "Cannot allocate memory to hold icon pixels" );
+
+    MALLOCARRAY(icon, bpsize * 8);
+    if (icon == NULL)
+        pm_error( "Cannot allocate memory to hold icon" );
+
+    /* Initialize to zero */
+    memset(buff, 0, bpsize);
+    memset(icon, 0, bpsize * 8);
+
+    /* Each bitplane is stored independently in the icon file.  This
+     * loop reads one bitplane at a time into buff.  Since fread() may
+     * not read all of the bitplane on the first call, the inner loop
+     * continues until all bytes are read.  The buffer pointer, bp,
+     * points to the next byte in buff to fill in.  When the inner
+     * loop is done, bp points to the end of buff.
+     *
+     * After reading in the entire bitplane, the second inner loop splits the 
+     * eight pixels in each byte of the bitplane into eight separate bytes in 
+     * the icon buffer.  The existing contents of each byte in icon are left 
+     * shifted by one to make room for the next bit. 
+     *
+     * Each byte in the completed icon contains a value from 0 to
+     * 2^depth (0 to 1 for depth of 1 and 0 to 3 for a depth of 3).
+     * This is an index into the colors array in the info struct.  */
+
+    for (bitplane = 0; bitplane < depth; ++bitplane) {
+        /* Read bitplane into buffer */
+        int toread;   /* Number of bytes left to read */
+        unsigned char * buffp;    /* Buffer point for reading data */
+       
+        toread = bpsize; buffp = &buff[0];
+
+        while (toread > 0) {
+            size_t bytesRead;
+
+            bytesRead = fread(buffp, 1, toread, fileP);
+            if (ferror(fileP))
+                pm_error("Cannot read from file info file.  "
+                         "fread() errno = %d (%s)",
+                         errno, strerror(errno));
+            else if (bytesRead == 0)
+                pm_error("Premature end-of-file.  "
+                         "Still have 0x%X bytes to read",
+                         toread );
+            
+            toread -= bytesRead;
+            buffp  += bytesRead;
+        }
+        addBitplane(icon, bpsize, buff);
+    }
+    *iconP = icon;
+
+    free(buff);
+}
+
+
+
+static void
+writeIconData( IconInfo *   const infoP,
+               struct pam * const pamP ) {
+/*-------------------------------------------------------------------------
+ * Write icon data to file
+ *-------------------------------------------------------------------------*/
+    unsigned int const bpwidth = ( ( infoP->width + 15 ) / 16 ) * 16;
+        /* Bitplane width; Width of each row in icon, including padding */
+
+    tuple * row;      /* Output row */
+
+    /* Allocate row */
+    row = pnm_allocpamrow( pamP );
+
+    /* Write icon image to output file */
+    /* Put if check outside for loop to reduce number of times check is made */
+    if ( infoP->depth == 1 ) {
+        if ( infoP->forceColor ) {
+            /* Convert 1 bitplane icon into color PAM */
+            unsigned int i;
+            for ( i = 0; i < infoP->height; ++i ) {
+                unsigned int j;
+                for ( j = 0; j < infoP->width; ++j ) {
+                    /* 1 is black and 0 is white */
+                    unsigned int colorIdx =
+                        infoP->icon[ i * bpwidth + j ] ? 2 : 1;
+                    row[j][PAM_RED_PLANE] =
+                        PPM_GETR( infoP->colors[colorIdx] );
+                    row[j][PAM_GRN_PLANE] =
+                        PPM_GETG( infoP->colors[colorIdx] );
+                    row[j][PAM_BLU_PLANE] =
+                        PPM_GETB( infoP->colors[colorIdx] );
+                }
+                pnm_writepamrow( pamP, row );
+            }
+        } else {
+            /* Convert 1 bitplane icon into bitmap PAM */
+            unsigned int i;
+            for ( i = 0; i < infoP->height; ++i ) {
+                unsigned int j;
+                for ( j = 0; j < infoP->width; j++ ) {
+                    /* 1 is black and 0 is white */
+                    row[j][0] = infoP->icon[ i * bpwidth + j ] ? 0 : 1;
+                }
+                pnm_writepamrow( pamP, row );
+            }
+        }
+    } else {
+        /* Convert color icon into color PAM */
+        unsigned int i;
+        for ( i = 0; i < infoP->height; ++i ) {
+            unsigned int j;
+            for ( j = 0; j < infoP->width; ++j ) {
+                unsigned int const colorIdx = infoP->icon[ i * bpwidth + j ];
+                row[j][PAM_RED_PLANE] = PPM_GETR( infoP->colors[colorIdx] );
+                row[j][PAM_GRN_PLANE] = PPM_GETG( infoP->colors[colorIdx] );
+                row[j][PAM_BLU_PLANE] = PPM_GETB( infoP->colors[colorIdx] );
+            }
+            pnm_writepamrow( pamP, row );
+        }
+    }
+
+    /* Clean up allocated memory */
+    pnm_freepamrow( row );
+}
+
+
+
+int
+main( int argc,
+      char *argv[] ) {
+
+    IconInfo    info;    /* Miscellaneous icon information */
+    struct pam  pam;     /* PAM header */
+    int         skip;    /* Bytes to skip to read next icon header */
+
+    /* Init PNM library */
+    pnm_init( &argc, argv );
+
+    /* Parse command line arguments */
+    parseCommandLine( argc, argv, &info );
+
+    /* Open input file */
+    info.fp = pm_openr( info.name );
+
+    /* Read disk object header */
+    getDiskObject( &info );
+
+    /* Skip drawer data, if any */
+    if ( info.drawerData ) {
+        skip = 56;   /* Draw data size */
+        if ( fseek( info.fp, skip, SEEK_CUR ) < 0 )
+            pm_error( "Cannot skip header information in file '%s'.  "
+                      "fseek() errno = %d (%s)",
+                      info.name, errno, strerror( errno ) );
+    }
+
+    /* Get dimensions for first icon */
+    getIconHeader( &info );
+
+    /* Skip ahead to next header if converting second icon */
+    if ( info.selected ) {
+        skip = info.height * ( ( ( info.width + 15 ) / 16 ) * 2 ) * info.depth;
+
+        if ( fseek( info.fp, skip, SEEK_CUR ) < 0 )
+            pm_error( "Cannot skip to next icon in file '%s'.  "
+                      "fseek() errno = %d (%s)",
+                      info.name, errno, strerror( errno ) );
+
+        /* Get dimensions for second icon */
+        getIconHeader( &info );
+    }
+
+    /* Read icon data */
+    readIconData( info.fp, info.width, info.height, info.depth, &info.icon );
+
+    /* Print icon info */
+    pm_message( "converting %s, version %d, %s icon: %d X %d X %d",
+                info.name, info.version, info.selected ? "second" : "first",
+                info.width, info.height, info.depth );
+
+    /* Write PAM header */
+    pam.size   = sizeof( pam );
+    pam.len    = PAM_STRUCT_SIZE( tuple_type );
+    pam.file   = stdout;
+    pam.height = info.height;
+    pam.width  = info.width;
+    pam.format = PAM_FORMAT;
+
+    if ( ( info.depth == 1 ) && ( info.forceColor == FALSE ) ) {
+        pam.depth  = 1;
+        pam.maxval = 1;
+        strcpy( pam.tuple_type, "BLACKANDWHITE" );
+    } else {
+        pam.depth  = 3;
+        pam.maxval = 0xFF;
+        strcpy( pam.tuple_type, "RGB" );
+    }
+    pnm_writepaminit( &pam );
+
+    /* Write icon data */
+    writeIconData( &info, &pam );
+
+    free( info.icon );
+
+    /* Close input file and return */
+    pm_close( pam.file );
+    pm_close( info.fp );
+
+    return 0;
+}
diff --git a/converter/other/jbig/ANNOUNCE b/converter/other/jbig/ANNOUNCE
new file mode 100644
index 00000000..edbcc3f8
--- /dev/null
+++ b/converter/other/jbig/ANNOUNCE
@@ -0,0 +1,243 @@
+
+Version 1.2 of the JBIG-KIT lossless image compression library available
+------------------------------------------------------------------------
+
+Markus Kuhn -- 2000-04-08
+
+
+The latest release of JBIG-KIT can be downloaded over the Internet
+with anonymous ftp from
+
+  ftp://ftp.informatik.uni-erlangen.de/pub/doc/ISO/JBIG/jbigkit-1.2.tar.gz
+  http://www.cl.cam.ac.uk/~mgk25/download/jbigkit-1.2.tar.gz
+
+and from a number of other servers.
+
+JBIG-KIT implements a highly effective data compression algorithm for
+bi-level high-resolution images such as fax pages or scanned
+documents.
+
+JBIG-KIT provides a portable library of compression and decompression
+functions with a documented interface that you can very easily include
+into your image or document processing software. In addition, JBIG-KIT
+provides ready-to-use compression and decompression programs with a
+simple command line interface (similar to the converters found in Jef
+Poskanzer's PBM graphics file conversion package).
+
+JBIG-KIT implements the specification
+
+  International Standard ISO/IEC 11544:1993 and ITU-T Recommendation
+  T.82(1993), "Information technology - Coded representation of picture
+  and audio information - progressive bi-level image compression",
+  <http://www.itu.ch/itudoc/itu-t/rec/t/t82_23822.html>,
+
+which is commonly referred to as the "JBIG standard". JBIG (Joint
+Bi-level Image experts Group) is the committee which developed this
+international standard for the lossless compression of images using
+arithmetic coding. Like the well-known compression algorithms JPEG and
+MPEG, also JBIG has been developed and published by the International
+Organization for Standardization (ISO) and the International
+Telecommunication Union (ITU). See also
+
+  http://www.jpeg.org/public/jbighomepage.htm
+  http://www.iso.ch/
+  http://www.itu.ch/
+
+The JBIG compression algorithm offers the following features:
+
+  - Close to state-of-the-art lossless compression ratio for high
+    resolution bi-level images.
+
+  - Around 1.1 to 1.5 times better compression ratio on typical
+    scanned documents compared to G4 fax compression (ITU-T T.6),
+    which has been the best compression algorithm for scanned
+    documents available prior to JBIG.
+
+  - Up to 30 times better compression of scanned images with dithered
+    images compared to G4 fax compression.
+
+  - Around 2 times better compression on typical 300 dpi documents
+    compared to 'gzip -9' on raw bitmaps.
+    
+  - Around 3-4 times better compression than GIF on typical 300 dpi
+    documents.
+
+  - Even much better competitive compression results on computer
+    generated images which are free of scanning distortions.
+
+  - JBIG supports hierarchical "progressive" encoding, that means it is
+    possible to encode a low resolution image first, followed by
+    resolution enhancement data. This allows for instance a document
+    browser to display already a good 75 dpi low resolution version of
+    an image, while the data necessary to reconstruct the full 300 dpi
+    version for laser printer reproduction is still arriving (say
+    over a slow network link or mass storage medium).
+
+  - The various resolution layers of a JBIG image in progressive
+    encoding mode together require not much more space than a
+    normal non-progressive mode encoded image (which JBIG also
+    supports).
+
+  - The progressive encoding mode utilizes a very sophisticated
+    resolution reduction algorithm which offers highest quality
+    low resolution versions that preserve the shape of characters as
+    well as the integrity of thin lines and dithered images.
+
+  - JBIG supports multiple bit planes and can this way also be used
+    for greyscale and color images, although the main field of
+    application is compression of bi-level images, i.e. images with
+    only two different pixel values. For greyscale images with up to
+    6 bit per pixel, JBIG performs superior to JPEG's lossless
+    mode.
+
+JBIG-KIT is free software under the GNU General Public License. For
+other license arrangements contact the author. JBIG-KIT provides a
+portable library implemented in ANSI/ISO C for encoding and decoding
+JBIG data streams together with documentation. The library is not
+intended for 8-bit or 16-bit machine architectures (e.g., old MS-DOS C
+compilers) on which a number of very efficient optimization techniques
+used in this software are not possible. For maximum performance, a
+32-bit processor is required (64-bit systems work too, of course). On
+architectures with 16-bit pointer arithmetic, only very small images
+can be processed.
+
+Special features of the JBIG-KIT implementation are:
+
+  - Fully reentrant multithread-capable design (no global or static
+    variables, isolated malloc()/free() calls, etc.).
+
+  - Capable of handling incomplete and growing JBIG data streams in
+    order to allow earliest display of low resolution versions.
+
+  - Capable of handling several incoming data streams simultaneously
+    in one single process and task.
+
+  - Especially designed with applications in mind that want to display
+    incoming data as early as possible (e.g., similar to the way in
+    which Netscape Navigator handles incoming GIF images).
+
+  - Implements all JBIG features and options including progressive and
+    sequential encoding, multiple bit planes, user specified
+    resolution reduction and deterministic prediction tables, adaptive
+    template changes for optimal performance on half-tone images,
+    deterministic prediction, typical prediction in lowest and
+    differential layers, various stripe orderings, etc. Only the SEQ
+    and HITOLO options are currently not supported by the decoder
+    (they are normally never required, but could be added later in
+    case of user requirements).
+
+  - Efficient code, optimized utilization of 32-bit processor
+    registers.
+
+  - Very easy to use documented C library interface.
+
+  - Included Gray code conversion routines for efficient encoding
+    of greyscale images.
+
+  - Ready-to-use pbmtojbg and jbgtopbm converters.
+
+
+Changes in version 1.2 (2000-04-08):
+
+  - bug in the decoder fixed, which caused the rest of the input file
+    to be skipped whenever a comment marker was encountered (special
+    thanks to Ben Rudiak-Gould <benrg@math.berkeley.edu> for
+    reporting this one)
+
+Changes in version 1.1 (1999-11-16):
+
+  - serious bug in the encoder fixed, which for a very small
+    percentage of images has caused an unterminated linked list to be
+    created internally that could have been responsible for
+    segmentation violations or non-terminating encoders
+    (special thanks to Hisashi Saiga <saiga@itl.tnr.sharp.co.jp> for
+    tracking that one down)
+
+  - minor bug in the "jbgtopbm -d" diagnostic output fixed
+
+Changes in version 1.0 (1998-04-11):
+
+  - two bugs fixed that caused the encoder and decoder to fail
+    under certain modes of operation with several bit planes
+
+  - added new functions jbg_split_planes(), jbg_dec_merge_planes(),
+    and jbg_dec_getsize_merged() for easy handling of greyscale
+    images
+
+  - added support for compressing greyscale PGM files to pbmtojbg
+    and jbgtopbm
+
+  - more changes to avoid paranoid compiler warnings
+
+Changes in version 0.9 (1996-01-09):
+
+  - encoder won't break any more on input bitmap data with incorrect
+    zero padding
+
+  - pbmtojbg displays a warning if input file has incorrect zero
+    padding
+
+  - various minor improvements suggested by Stefan Willer
+    <Stefan.Willer@unnet.wupper.DE>
+
+  - many minor changes in order to avoid warnings from paranoid
+    compilers
+
+Changes in version 0.8 (1995-09-20):
+
+  - namespace cleared up, all names externally visible from the library
+    start now with jbg_ or JBG_
+
+  - minor non-critical bug fixed which caused library to fail compatibility
+    test and showed up especially on DEC Alpha systems
+
+  - jbg_dec_gethight() is now called jbg_dec_getheight()
+
+  - filenames conform now to MS-DOS limits
+
+  - Bug in pbmtojbg fixed (handling of ASCII PBM files)
+
+Changes in version 0.7 (1995-06-10):
+
+  - more problems on 16-bit int systems and on Macintosh systems fixed
+    (special thanks to Jean-Pierre Gachen <jpg11@calvanet.calvacom.fr>)
+
+  - global Makefile
+
+Changes in version 0.6 (1995-06-08):
+
+  - memory leak fixed
+
+  - should now also work on systems where int is only 16-bit large
+
+  - changes of the JBIG "Technical Corrigendum 1" included (special
+    thanks to Dr. Sebestyen from Siemens AG for sending me a copy
+    of the draft)
+
+First release: version 0.5 (1995-05-28)
+
+
+Please send all questions, problem reports, patches, suggestions,
+success stories, comments, etc. to
+
+  mkuhn at acm.org
+
+I will try to provide free support and maintenance for this software
+at least for the next few months depending on my available time.
+
+Y2K statement: JBIG-KIT does not handle any date and time related
+data, therefore if JBIG-KIT causes you any problems related to date
+and time overflows, this would indeed be most surprising.
+
+This library has been published in the hope that it will encourage the
+development of good freely available scanned document handling and
+transmission systems for the Internet so that large amounts of scanned
+text can be made available to the global village easily.
+
+Happy compressing ...
+
+Markus Kuhn
+
+--
+Markus G. Kuhn, Security Group, Computer Lab, Cambridge University, UK
+email: mkuhn at acm.org,  home page: <http://www.cl.cam.ac.uk/~mgk25/>
diff --git a/converter/other/jbig/Makefile b/converter/other/jbig/Makefile
new file mode 100644
index 00000000..ca98ef29
--- /dev/null
+++ b/converter/other/jbig/Makefile
@@ -0,0 +1,47 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/other/jbig
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+LIBJBIG_OBJECTS = jbig.o jbig_tab.o
+
+INCLUDES =
+ifneq ($(JBIGHDR_DIR),NONE)
+  INCLUDES += -I$(JBIGHDR_DIR)
+endif
+
+ifneq ($(JBIGHDR_DIR),NONE)
+  ifneq ($(JBIGLIB),NONE)
+    BINARIES = jbigtopnm pnmtojbig
+  endif
+endif
+
+SCRIPTS =
+
+ifeq ($(JBIGLIB),$(BUILDDIR)/$(SUBDIR)/libjbig.a)
+  JBIGLIB_DEP = $(JBIGLIB)
+else
+  # It's not our internal version; user's on his own to make sure it's built
+endif
+
+OBJECTS = $(BINARIES:%=%.o) $(LIBJBIG_OBJECTS)
+MERGE_OBJECTS = $(BINARIES:%=%.o2) $(LIBJBIG_OBJECTS)
+
+all: $(BINARIES)
+
+include $(SRCDIR)/Makefile.common
+
+$(BINARIES): %: %.o $(JBIGLIB_DEP) $(NETPBMLIB) $(LIBOPT)
+	$(LD) $(LDFLAGS) -o $@ $< \
+	  $(shell $(LIBOPT) $(NETPBMLIB) $(JBIGLIB)) $(MATHLIB) $(LDLIBS) \
+	  $(RPATH) $(LADD)
+
+
+$(BUILDDIR)/$(SUBDIR)/libjbig.a: $(LIBJBIG_OBJECTS)
+	$(AR) -rc $@ $^
+	$(RANLIB) $@
+
diff --git a/converter/other/jbig/README.Netpbm b/converter/other/jbig/README.Netpbm
new file mode 100644
index 00000000..3d593b92
--- /dev/null
+++ b/converter/other/jbig/README.Netpbm
@@ -0,0 +1,12 @@
+The jbig tools are derived from the JBIG-KIT package by Marcus Kuhn,
+by Bryan Henderson on 2000.05.11.
+
+The file ANNOUNCE in this directory is from that package and gives
+details.
+
+The Netpbm tools jbigtopbm and pbmtojbig were adapted from JBIG-KIT's
+jbgtopbm and pbmtojbg.  The main difference is that the Netpbm
+versions use the Netpbm libraries.
+
+The jbig.c and jbig_table.c modules are straight from the JBIG_KIT 
+package.  They are what normally are packaged as libjbig.a.
diff --git a/converter/other/jbig/jbig.c b/converter/other/jbig/jbig.c
new file mode 100644
index 00000000..ebd7c08f
--- /dev/null
+++ b/converter/other/jbig/jbig.c
@@ -0,0 +1,2905 @@
+/*
+ *  Portable Free JBIG image compression library
+ *
+ *  Markus Kuhn -- mkuhn@acm.org
+ *
+ *  $Id: jbig.c,v 1.12 2000-04-08 11:42:18+01 mgk25 Rel $
+ *
+ *  This module implements a portable standard C encoder and decoder
+ *  using the JBIG lossless bi-level image compression algorithm as
+ *  specified in International Standard ISO 11544:1993 or equivalently
+ *  as specified in ITU-T Recommendation T.82. See the file jbig.doc
+ *  for usage instructions and application examples.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * 
+ *  If you want to use this program under different license conditions,
+ *  then contact the author for an arrangement.
+ *
+ *  It is possible that certain products which can be built using this
+ *  software module might form inventions protected by patent rights in
+ *  some countries (e.g., by patents about arithmetic coding algorithms
+ *  owned by IBM and AT&T in the USA). Provision of this software by the
+ *  author does NOT include any licences for any patents. In those
+ *  countries where a patent licence is required for certain applications
+ *  of this software module, you will have to obtain such a licence
+ *  yourself.
+ */
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "jbig.h"
+
+
+/* optional export of arithmetic coder functions for test purposes */
+#ifdef TEST_CODEC
+#define ARITH
+#define ARITH_INL
+#else
+#define ARITH      static
+#ifdef __GNUC__
+#define ARITH_INL  static __inline__
+#else
+#define ARITH_INL  static
+#endif
+#endif
+
+#define MX_MAX  23     /* maximal supported mx offset for
+			* adaptive template in the encoder */
+
+#define TPB2CX  0x195  /* contexts for TP special pixels */
+#define TPB3CX  0x0e5
+#define TPDCX   0xc3f
+
+/* marker codes */
+#define MARKER_STUFF    0x00
+#define MARKER_RESERVE  0x01
+#define MARKER_SDNORM   0x02
+#define MARKER_SDRST    0x03
+#define MARKER_ABORT    0x04
+#define MARKER_NEWLEN   0x05
+#define MARKER_ATMOVE   0x06
+#define MARKER_COMMENT  0x07
+#define MARKER_ESC      0xff
+
+/* loop array indices */
+#define STRIPE  0
+#define LAYER   1
+#define PLANE   2
+
+/* special jbg_buf pointers (instead of NULL) */
+#define SDE_DONE ((struct jbg_buf *) -1)
+#define SDE_TODO ((struct jbg_buf *) 0)
+
+/* object code version id */
+
+const char jbg_version[] = 
+" JBIG-KIT " JBG_VERSION " -- Markus Kuhn -- "
+"$Id: jbig.c,v 1.12 2000-04-08 11:42:18+01 mgk25 Rel $ ";
+
+/*
+ * the following array specifies for each combination of the 3
+ * ordering bits, which ii[] variable represents which dimension
+ * of s->sde.
+ */
+static const int index[8][3] = {
+  { 2, 1, 0 },    /* no ordering bit set */
+  { -1, -1, -1},  /* SMID -> illegal combination */
+  { 2, 0, 1 },    /* ILEAVE */
+  { 1, 0, 2 },    /* SMID + ILEAVE */
+  { 0, 2, 1 },    /* SEQ */
+  { 1, 2, 0 },    /* SEQ + SMID */
+  { 0, 1, 2 },    /* SEQ + ILEAVE */
+  { -1, -1, -1 }  /* SEQ + SMID + ILEAVE -> illegal combination */
+};
+
+
+/*
+ * Array [language][message] with text string error messages that correspond
+ * to return values from public functions in this library.
+ */
+#define NEMSG         9  /* number of error codes */
+#define NEMSG_LANG    3  /* number of supported languages */
+static const char *errmsg[NEMSG_LANG][NEMSG] = {
+  /* English (JBG_EN) */
+  {
+    "Everything is ok",                                     /* JBG_EOK */
+    "Reached specified maximum size",                       /* JBG_EOK_INTR */
+    "Unexpected end of data",                               /* JBG_EAGAIN */
+    "Not enough memory available",                          /* JBG_ENOMEM */
+    "ABORT marker found",                                   /* JBG_EABORT */
+    "Unknown marker segment encountered",                   /* JBG_EMARKER */
+    "Incremental BIE does not fit to previous one",         /* JBG_ENOCONT */
+    "Invalid data encountered",                             /* JBG_EINVAL */
+    "Unimplemented features used"                           /* JBG_EIMPL */
+  },
+  /* German (JBG_DE_8859_1) */
+  {
+    "Kein Problem aufgetreten",                             /* JBG_EOK */
+    "Angegebene maximale Bildgr\366\337e erreicht",         /* JBG_EOK_INTR */
+    "Unerwartetes Ende der Daten",                          /* JBG_EAGAIN */
+    "Nicht gen\374gend Speicher vorhanden",                 /* JBG_ENOMEM */
+    "Es wurde eine Abbruch-Sequenz gefunden",               /* JBG_EABORT */
+    "Eine unbekannte Markierungssequenz wurde gefunden",    /* JBG_EMARKER */
+    "Neue Daten passen nicht zu vorangegangenen Daten",     /* JBG_ENOCONT */
+    "Es wurden ung\374ltige Daten gefunden",                /* JBG_EINVAL */
+    "Noch nicht implementierte Optionen wurden benutzt"     /* JBG_EIMPL */
+  },
+  /* German (JBG_DE_UTF_8) */
+  {
+    "Kein Problem aufgetreten",                             /* JBG_EOK */
+    "Angegebene maximale Bildgr\303\266\303\237e erreicht", /* JBG_EOK_INTR */
+    "Unerwartetes Ende der Daten",                          /* JBG_EAGAIN */
+    "Nicht gen\303\274gend Speicher vorhanden",             /* JBG_ENOMEM */
+    "Es wurde eine Abbruch-Sequenz gefunden",               /* JBG_EABORT */
+    "Eine unbekannte Markierungssequenz wurde gefunden",    /* JBG_EMARKER */
+    "Neue Daten passen nicht zu vorangegangenen Daten",     /* JBG_ENOCONT */
+    "Es wurden ung\303\274ltige Daten gefunden",            /* JBG_EINVAL */
+    "Noch nicht implementierte Optionen wurden benutzt"     /* JBG_EIMPL */
+  }
+};
+
+
+
+/*
+ * The following three functions are the only places in this code, were
+ * C library memory management functions are called. The whole JBIG
+ * library has been designed in order to allow multi-threaded
+ * execution. no static or global variables are used, so all fuctions
+ * are fully reentrant. However if you want to use this multi-thread
+ * capability and your malloc, realloc and free are not reentrant,
+ * then simply add the necessary semaphores or mutex primitives below.
+ */
+
+static void *checked_malloc(size_t size)
+{
+  void *p;
+  
+  p = malloc(size);
+  /* Full manual exception handling is ugly here for performance
+   * reasons. If an adequate handling of lack of memory is required,
+   * then use C++ and throw a C++ exception here. */
+  if (!p)
+    abort();
+
+#if 0
+  fprintf(stderr, "%p = malloc(%ld)\n", p, (long) size);
+#endif
+
+  return p;
+}
+
+
+static void *checked_realloc(void *ptr, size_t size)
+{
+  void *p;
+
+  p = realloc(ptr, size);
+  /* Full manual exception handling is ugly here for performance
+   * reasons. If an adequate handling of lack of memory is required,
+   * then use C++ and throw a C++ exception here. */
+  if (!p)
+    abort();
+
+#if 0
+  fprintf(stderr, "%p = realloc(%p, %ld)\n", p, ptr, (long) size);
+#endif
+
+  return p;
+}
+
+
+static void checked_free(void *ptr)
+{
+  free(ptr);
+
+#if 0
+  fprintf(stderr, "free(%p)\n", ptr);
+#endif
+
+}
+
+
+
+/*
+ * The next functions implement the arithmedic encoder and decoder
+ * required for JBIG. The same algorithm is also used in the arithmetic
+ * variant of JPEG.
+ */
+
+#ifdef DEBUG
+static long encoded_pixels = 0;
+#endif
+
+ARITH void arith_encode_init(struct jbg_arenc_state *s, int reuse_st)
+{
+  int i;
+  
+  if (!reuse_st)
+    for (i = 0; i < 4096; s->st[i++] = 0);
+  s->c = 0;
+  s->a = 0x10000L;
+  s->sc = 0;
+  s->ct = 11;
+  s->buffer = -1;    /* empty */
+  
+  return;
+}
+
+
+ARITH void arith_encode_flush(struct jbg_arenc_state *s)
+{
+  unsigned long temp;
+
+#ifdef DEBUG
+  fprintf(stderr, "  encoded pixels = %ld, a = %05lx, c = %08lx\n",
+	  encoded_pixels, s->a, s->c);
+#endif
+
+  /* find the s->c in the coding interval with the largest
+   * number of trailing zero bits */
+  if ((temp = (s->a - 1 + s->c) & 0xffff0000L) < s->c)
+    s->c = temp + 0x8000;
+  else
+    s->c = temp;
+  /* send remaining bytes to output */
+  s->c <<= s->ct;
+  if (s->c & 0xf8000000L) {
+    /* one final overflow has to be handled */
+    if (s->buffer >= 0) {
+      s->byte_out(s->buffer + 1, s->file);
+      if (s->buffer + 1 == MARKER_ESC)
+	s->byte_out(MARKER_STUFF, s->file);
+    }
+    /* output 0x00 bytes only when more non-0x00 will follow */
+    if (s->c & 0x7fff800L)
+      for (; s->sc; --s->sc)
+	s->byte_out(0x00, s->file);
+  } else {
+    if (s->buffer >= 0)
+      s->byte_out(s->buffer, s->file); 
+    /* T.82 figure 30 says buffer+1 for the above line! Typo? */
+    for (; s->sc; --s->sc) {
+      s->byte_out(0xff, s->file);
+      s->byte_out(MARKER_STUFF, s->file);
+    }
+  }
+  /* output final bytes only if they are not 0x00 */
+  if (s->c & 0x7fff800L) {
+    s->byte_out((s->c >> 19) & 0xff, s->file);
+    if (((s->c >> 19) & 0xff) == MARKER_ESC)
+      s->byte_out(MARKER_STUFF, s->file);
+    if (s->c & 0x7f800L) {
+      s->byte_out((s->c >> 11) & 0xff, s->file);
+      if (((s->c >> 11) & 0xff) == MARKER_ESC)
+	s->byte_out(MARKER_STUFF, s->file);
+    }
+  }
+
+  return;
+}
+
+
+ARITH_INL void arith_encode(struct jbg_arenc_state *s, int cx, int pix) 
+{
+  extern short jbg_lsz[];
+  extern unsigned char jbg_nmps[], jbg_nlps[];
+  register unsigned lsz, ss;
+  register unsigned char *st;
+  long temp;
+
+#ifdef DEBUG
+  ++encoded_pixels;
+#endif
+
+  assert(cx >= 0 && cx < 4096);
+  st = s->st + cx;
+  ss = *st & 0x7f;
+  assert(ss < 113);
+  lsz = jbg_lsz[ss];
+
+#if 0
+  fprintf(stderr, "pix = %d, cx = %d, mps = %d, st = %3d, lsz = 0x%04x, "
+	  "a = 0x%05lx, c = 0x%08lx, ct = %2d, buf = 0x%02x\n",
+	  pix, cx, !!(s->st[cx] & 0x80), ss, lsz, s->a, s->c, s->ct,
+	  s->buffer);
+#endif
+
+  if (((pix << 7) ^ s->st[cx]) & 0x80) {
+    /* encode the less probable symbol */
+    if ((s->a -= lsz) >= lsz) {
+      /* If the interval size (lsz) for the less probable symbol (LPS)
+       * is larger than the interval size for the MPS, then exchange
+       * the two symbols for coding efficiency, otherwise code the LPS
+       * as usual: */
+      s->c += s->a;
+      s->a = lsz;
+    }
+    /* Check whether MPS/LPS exchange is necessary
+     * and chose next probability estimator status */
+    *st &= 0x80;
+    *st ^= jbg_nlps[ss];
+  } else {
+    /* encode the more probable symbol */
+    if ((s->a -= lsz) & 0xffff8000L)
+      return;   /* A >= 0x8000 -> ready, no renormalization required */
+    if (s->a < lsz) {
+      /* If the interval size (lsz) for the less probable symbol (LPS)
+       * is larger than the interval size for the MPS, then exchange
+       * the two symbols for coding efficiency: */
+      s->c += s->a;
+      s->a = lsz;
+    }
+    /* chose next probability estimator status */
+    *st &= 0x80;
+    *st |= jbg_nmps[ss];
+  }
+
+  /* renormalization of coding interval */
+  do {
+    s->a <<= 1;
+    s->c <<= 1;
+    --s->ct;
+    if (s->ct == 0) {
+      /* another byte is ready for output */
+      temp = s->c >> 19;
+      if (temp & 0xffffff00L) {
+	/* handle overflow over all buffered 0xff bytes */
+	if (s->buffer >= 0) {
+	  ++s->buffer;
+	  s->byte_out(s->buffer, s->file);
+	  if (s->buffer == MARKER_ESC)
+	    s->byte_out(MARKER_STUFF, s->file);
+	}
+	for (; s->sc; --s->sc)
+	  s->byte_out(0x00, s->file);
+	s->buffer = temp & 0xff;  /* new output byte, might overflow later */
+	assert(s->buffer != 0xff);
+	/* can s->buffer really never become 0xff here? */
+      } else if (temp == 0xff) {
+	/* buffer 0xff byte (which might overflow later) */
+	++s->sc;
+      } else {
+	/* output all buffered 0xff bytes, they will not overflow any more */
+	if (s->buffer >= 0)
+	  s->byte_out(s->buffer, s->file);
+	for (; s->sc; --s->sc) {
+	  s->byte_out(0xff, s->file);
+	  s->byte_out(MARKER_STUFF, s->file);
+	}
+	s->buffer = temp;   /* buffer new output byte (can still overflow) */
+      }
+      s->c &= 0x7ffffL;
+      s->ct = 8;
+    }
+  } while (s->a < 0x8000);
+ 
+  return;
+}
+
+
+ARITH void arith_decode_init(struct jbg_ardec_state *s, int reuse_st)
+{
+  int i;
+  
+  if (!reuse_st)
+    for (i = 0; i < 4096; s->st[i++] = 0);
+  s->c = 0;
+  s->a = 1;
+  s->ct = 0;
+  s->result = JBG_OK;
+  s->startup = 1;
+  return;
+}
+
+
+ARITH_INL int arith_decode(struct jbg_ardec_state *s, int cx)
+{
+  extern short jbg_lsz[];
+  extern unsigned char jbg_nmps[], jbg_nlps[];
+  register unsigned lsz, ss;
+  register unsigned char *st;
+  int pix;
+
+  /* renormalization */
+  while (s->a < 0x8000 || s->startup) {
+    if (s->ct < 1 && s->result != JBG_READY) {
+      /* first we have to move a new byte into s->c */
+      if (s->pscd_ptr >= s->pscd_end) {
+	s->result = JBG_MORE;
+	return -1;
+      }
+      if (*s->pscd_ptr == 0xff) 
+	if (s->pscd_ptr + 1 >= s->pscd_end) {
+	  s->result = JBG_MARKER;
+	  return -1;
+	} else {
+	  if (*(s->pscd_ptr + 1) == MARKER_STUFF) {
+	    s->c |= 0xffL << (8 - s->ct);
+	    s->ct += 8;
+	    s->pscd_ptr += 2;
+	    s->result = JBG_OK;
+	  } else
+	    s->result = JBG_READY;
+	}
+      else {
+	s->c |= (long)*(s->pscd_ptr++) << (8 - s->ct);
+	s->ct += 8;
+	s->result = JBG_OK;
+      }
+    }
+    s->c <<= 1;
+    s->a <<= 1;
+    --s->ct;
+    if (s->a == 0x10000L)
+      s->startup = 0;
+  }
+
+  st = s->st + cx;
+  ss = *st & 0x7f;
+  assert(ss < 113);
+  lsz = jbg_lsz[ss];
+
+#if 0
+  fprintf(stderr, "cx = %d, mps = %d, st = %3d, lsz = 0x%04x, a = 0x%05lx, "
+	  "c = 0x%08lx, ct = %2d\n",
+	  cx, !!(s->st[cx] & 0x80), ss, lsz, s->a, s->c, s->ct);
+#endif
+
+  if ((s->c >> 16) < (s->a -= lsz))
+    if (s->a & 0xffff8000L)
+      return *st >> 7;
+    else {
+      /* MPS_EXCHANGE */
+      if (s->a < lsz) {
+	pix = 1 - (*st >> 7);
+	/* Check whether MPS/LPS exchange is necessary
+	 * and chose next probability estimator status */
+	*st &= 0x80;
+	*st ^= jbg_nlps[ss];
+      } else {
+	pix = *st >> 7;
+	*st &= 0x80;
+	*st |= jbg_nmps[ss];
+      }
+    }
+  else {
+    /* LPS_EXCHANGE */
+    if (s->a < lsz) {
+      s->c -= s->a << 16;
+      s->a = lsz;
+      pix = *st >> 7;
+      *st &= 0x80;
+      *st |= jbg_nmps[ss];
+    } else {
+      s->c -= s->a << 16;
+      s->a = lsz;
+      pix = 1 - (*st >> 7);
+      /* Check whether MPS/LPS exchange is necessary
+       * and chose next probability estimator status */
+      *st &= 0x80;
+      *st ^= jbg_nlps[ss];
+    }
+  }
+
+  return pix;
+}
+
+
+
+/*
+ * Memory management for buffers which are used for temporarily
+ * storing SDEs by the encoder.
+ *
+ * The following functions manage a set of struct jbg_buf storage
+ * containers were each can keep JBG_BUFSIZE bytes. The jbg_buf
+ * containers can be linked to form linear double-chained lists for
+ * which a number of operations are provided. Blocks which are
+ * tempoarily not used any more are returned to a freelist which each
+ * encoder keeps. Only the destructor of the encoder actually returns
+ * the block via checked_free() to the stdlib memory management.
+ */
+
+
+/*
+ * Allocate a new buffer block and initialize it. Try to get it from
+ * the free_list, and if it is empty, call checked_malloc().
+ */
+static struct jbg_buf *jbg_buf_init(struct jbg_buf **free_list)
+{
+  struct jbg_buf *new_block;
+  
+  /* Test whether a block from the free list is available */
+  if (*free_list) {
+    new_block = *free_list;
+    *free_list = new_block->next;
+  } else {
+    /* request a new memory block */
+    new_block = (struct jbg_buf *) checked_malloc(sizeof(struct jbg_buf));
+  }
+  new_block->len = 0;
+  new_block->next = NULL;
+  new_block->previous = NULL;
+  new_block->last = new_block;
+  new_block->free_list = free_list;
+
+  return new_block;
+}
+
+
+/*
+ * Return an entire free_list to the memory management of stdlib.
+ * This is only done by jbg_enc_free().
+ */
+static void jbg_buf_free(struct jbg_buf **free_list)
+{
+  struct jbg_buf *tmp;
+  
+  while (*free_list) {
+    tmp = (*free_list)->next;
+    checked_free(*free_list);
+    *free_list = tmp;
+  }
+  
+  return;
+}
+
+
+/*
+ * Append a single byte to a single list that starts with the block
+ * *(struct jbg_buf *) head. The type of *head is void here in order to
+ * keep the interface of the arithmetic encoder gereric, which uses this
+ * function as a call-back function in order to deliver single bytes
+ * for a PSCD.
+ */
+static void jbg_buf_write(int b, void *head)
+{
+  struct jbg_buf *now;
+
+  now = ((struct jbg_buf *) head)->last;
+  if (now->len < JBG_BUFSIZE - 1) {
+    now->d[now->len++] = b;
+    return;
+  }
+  now->next = jbg_buf_init(((struct jbg_buf *) head)->free_list);
+  now->next->previous = now;
+  now->next->d[now->next->len++] = b;
+  ((struct jbg_buf *) head)->last = now->next;
+
+  return;
+}
+
+
+/*
+ * Remove any trailing zero bytes from the end of a linked jbg_buf list,
+ * however make sure that no zero byte is removed which directly
+ * follows a 0xff byte (i.e., keep MARKER_ESC MARKER_STUFF sequences
+ * intact). This function is used to remove any redundant final zero
+ * bytes from a PSCD.
+ */
+static void jbg_buf_remove_zeros(struct jbg_buf *head)
+{
+  struct jbg_buf *last;
+
+  while (1) {
+    /* remove trailing 0x00 in last block of list until this block is empty */
+    last = head->last;
+    while (last->len && last->d[last->len - 1] == 0)
+      last->len--;
+    /* if block became really empty, remove it in case it is not the
+     * only remaining block and then loop to next block */
+    if (last->previous && !last->len) {
+      head->last->next = *head->free_list;
+      *head->free_list = head->last;
+      head->last = last->previous;
+      head->last->next = NULL;
+    } else
+      break;
+  }
+
+  /*
+   * If the final non-zero byte is 0xff (MARKER_ESC), then we just have
+   * removed a MARKER_STUFF and we will append it again now in order
+   * to preserve PSCD status of byte stream.
+   */
+  if (head->last->len && head->last->d[head->last->len - 1] == MARKER_ESC)
+    jbg_buf_write(MARKER_STUFF, head);
+ 
+  return;
+}
+
+
+/*
+ * The jbg_buf list which starts with block *new_prefix is concatenated
+ * with the list which starts with block **start and *start will then point
+ * to the first block of the new list.
+ */
+static void jbg_buf_prefix(struct jbg_buf *new_prefix, struct jbg_buf **start)
+{
+  new_prefix->last->next = *start;
+  new_prefix->last->next->previous = new_prefix->last;
+  new_prefix->last = new_prefix->last->next->last;
+  *start = new_prefix;
+  
+  return;
+}
+
+
+/*
+ * Send the contents of a jbg_buf list that starts with block **head to
+ * the call back function data_out and return the blocks of the jbg_buf
+ * list to the freelist from which these jbg_buf blocks have been taken.
+ * After the call, *head == NULL.
+ */
+static void jbg_buf_output(struct jbg_buf **head,
+			void (*data_out)(unsigned char *start,
+					 size_t len, void *file),
+			void *file)
+{
+  struct jbg_buf *tmp;
+  
+  while (*head) {
+    data_out((*head)->d, (*head)->len, file);
+    tmp = (*head)->next;
+    (*head)->next = *(*head)->free_list;
+    *(*head)->free_list = *head;
+    *head = tmp;
+  }
+  
+  return;
+}
+
+
+/*
+ * Calculate y = ceil(x/2) applied n times. This function is used to
+ * determine the number of pixels per row or column after n resolution
+ * reductions. E.g. X[d-1] = jbg_ceil_half(X[d], 1) and X[0] =
+ * jbg_ceil_half(X[d], d) as defined in clause 6.2.3 of T.82.
+ */
+unsigned long jbg_ceil_half(unsigned long x, int n)
+{
+  unsigned long mask;
+  
+  mask = (1UL << n) - 1;     /* the lowest n bits are 1 here */
+  return (x >> n) + ((mask & x) != 0);
+}
+
+
+/*
+ * Initialize the status struct for the encoder.
+ */
+void jbg_enc_init(struct jbg_enc_state *s, unsigned long x, unsigned long y,
+                  int planes, unsigned char **p,
+                  void (*data_out)(unsigned char *start, size_t len,
+				   void *file),
+		  void *file)
+{
+  unsigned long l, lx;
+  int i;
+  size_t bufsize;
+
+  extern char jbg_resred[], jbg_dptable[];
+
+  s->xd = x;
+  s->yd = y;
+  s->planes = planes;
+  s->data_out = data_out;
+  s->file = file;
+
+  s->d = 0;
+  s->dl = 0;
+  s->dh = s->d;
+  s->l0 = jbg_ceil_half(s->yd, s->d) / 35;   /* 35 stripes/image */
+  while ((s->l0 << s->d) > 128)              /* but <= 128 lines/stripe */
+    --s->l0;
+  if (s->l0 < 2) s->l0 = 2;
+  s->mx = 8;
+  s->my = 0;
+  s->order = JBG_ILEAVE | JBG_SMID;
+  s->options = JBG_TPBON | JBG_TPDON | JBG_DPON;
+  s->dppriv = jbg_dptable;
+  s->res_tab = jbg_resred;
+  
+  s->highres = checked_malloc(planes * sizeof(int));
+  s->lhp[0] = p;
+  s->lhp[1] = checked_malloc(planes * sizeof(unsigned char *));
+  bufsize = ((jbg_ceil_half(x, 1) + 7) / 8) * jbg_ceil_half(y, 1);
+  for (i = 0; i < planes; i++) {
+    s->highres[i] = 0;
+    s->lhp[1][i] = checked_malloc(sizeof(unsigned char) * bufsize);
+  }
+  
+  s->free_list = NULL;
+  s->s = (struct jbg_arenc_state *) 
+    checked_malloc(s->planes * sizeof(struct jbg_arenc_state));
+  s->tx = (int *) checked_malloc(s->planes * sizeof(int));
+  lx = jbg_ceil_half(x, 1);
+  s->tp = (char *) checked_malloc(lx * sizeof(char));
+  for (l = 0; l < lx; s->tp[l++] = 2);
+  s->sde = NULL;
+
+  return;
+}
+
+
+/*
+ * This function selects the number of differential layers based on
+ * the maximum size requested for the lowest resolution layer. If
+ * possible, a number of differential layers is selected, which will
+ * keep the size of the lowest resolution layer below or equal to the
+ * given width x and height y. However not more than 6 differential
+ * resolution layers will be used. In addition, a reasonable value for
+ * l0 (height of one stripe in the lowest resolution layer) is
+ * selected, which obeys the recommended limitations for l0 in annex A
+ * and C of the JBIG standard. The selected number of resolution layers
+ * is returned. 
+ */
+int jbg_enc_lrlmax(struct jbg_enc_state *s, unsigned long x, 
+		   unsigned long y)
+{
+  for (s->d = 0; s->d < 6; s->d++)
+    if (jbg_ceil_half(s->xd, s->d) <= x && jbg_ceil_half(s->yd, s->d) <= y)
+      break;
+  s->dl = 0;
+  s->dh = s->d;
+
+  s->l0 = jbg_ceil_half(s->yd, s->d) / 35;  /* 35 stripes/image */
+  while ((s->l0 << s->d) > 128)             /* but <= 128 lines/stripe */
+    --s->l0;
+  if (s->l0 < 2) s->l0 = 2;
+
+  return s->d;
+}
+
+
+/*
+ * As an alternative to jbg_enc_lrlmax(), the following function allows
+ * to specify the number of layers directly. The stripe height and layer
+ * range is also adjusted automatically here.
+ */
+void jbg_enc_layers(struct jbg_enc_state *s, int d)
+{
+  if (d < 0 || d > 255)
+    return;
+  s->d  = d;
+  s->dl = 0;
+  s->dh = s->d;
+
+  s->l0 = jbg_ceil_half(s->yd, s->d) / 35;  /* 35 stripes/image */
+  while ((s->l0 << s->d) > 128)             /* but <= 128 lines/stripe */
+    --s->l0;
+  if (s->l0 < 2) s->l0 = 2;
+
+  return;
+}
+
+
+/*
+ * Specify the highest and lowest resolution layers which will be
+ * written to the output file. Call this function not before
+ * jbg_enc_layers() or jbg_enc_lrlmax(), because these two functions
+ * reset the lowest and highest resolution layer to default values.
+ * Negative values are ignored. The total number of layers is returned.
+ */
+int jbg_enc_lrange(struct jbg_enc_state *s, int dl, int dh)
+{
+  if (dl >= 0     && dl <= s->d) s->dl = dl;
+  if (dh >= s->dl && dh <= s->d) s->dh = dh;
+
+  return s->d;
+}
+
+
+/*
+ * The following function allows to specify the bits describing the
+ * options of the format as well as the maximum AT movement window and
+ * the number of layer 0 lines per stripes.
+ */
+void jbg_enc_options(struct jbg_enc_state *s, int order, int options,
+		     long l0, int mx, int my)
+{
+  if (order >= 0 && order <= 0x0f) s->order = order;
+  if (options >= 0) s->options = options;
+  if (l0 >= 0) s->l0 = l0;
+  if (mx >= 0 && my < 128) s->mx = mx;
+  if (my >= 0 && my < 256) s->my = my;
+
+  return;
+}
+
+
+/*
+ * This function actually does all the tricky work involved in producing
+ * a SDE, which is stored in the appropriate s->sde[][][] element
+ * for later output in the correct order.
+ */
+static void encode_sde(struct jbg_enc_state *s,
+		       long stripe, int layer, int plane)
+{
+  unsigned char *hp, *lp1, *lp2, *p0, *p1, *q1, *q2;
+  unsigned long hl, ll, hx, hy, lx, ly, hbpl, lbpl;
+  unsigned long line_h0 = 0, line_h1 = 0;
+  unsigned long line_h2, line_h3, line_l1, line_l2, line_l3;
+  struct jbg_arenc_state *se;
+  unsigned long i, j, y;
+  unsigned t;
+  int ltp, ltp_old, cx;
+  unsigned long c_all, c[MX_MAX + 1], cmin, cmax, clmin, clmax;
+  int tmax, at_determined;
+  int new_tx;
+  long new_tx_line = -1;
+  struct jbg_buf *new_jbg_buf;
+
+#ifdef DEBUG
+  static long tp_lines, tp_exceptions, tp_pixels, dp_pixels;
+  static long encoded_pixels;
+#endif
+
+  /* return immediately if this stripe has already been encoded */
+  if (s->sde[stripe][layer][plane] != SDE_TODO)
+    return;
+
+#ifdef DEBUG
+  if (stripe == 0)
+    tp_lines = tp_exceptions = tp_pixels = dp_pixels = encoded_pixels = 0;
+  fprintf(stderr, "encode_sde: s/d/p = %2ld/%2d/%2d\n",
+	  stripe, layer, plane);
+#endif
+
+  /* number of lines per stripe in highres image */
+  hl = s->l0 << layer;
+  /* number of lines per stripe in lowres image */
+  ll = hl >> 1;
+  /* current line number in highres image */
+  y = stripe * hl;
+  /* number of pixels in highres image */
+  hx = jbg_ceil_half(s->xd, s->d - layer);
+  hy = jbg_ceil_half(s->yd, s->d - layer);
+  /* number of pixels in lowres image */
+  lx = jbg_ceil_half(hx, 1);
+  ly = jbg_ceil_half(hy, 1);
+  /* bytes per line in highres and lowres image */
+  hbpl = (hx + 7) / 8;
+  lbpl = (lx + 7) / 8;
+  /* pointer to first image byte of highres stripe */
+  hp = s->lhp[s->highres[plane]][plane] + stripe * hl * hbpl;
+  lp2 = s->lhp[1 - s->highres[plane]][plane] + stripe * ll * lbpl;
+  lp1 = lp2 + lbpl;
+  
+  /* initialize arithmetic encoder */
+  se = s->s + plane;
+  arith_encode_init(se, stripe != 0);
+  s->sde[stripe][layer][plane] = jbg_buf_init(&s->free_list);
+  se->byte_out = jbg_buf_write;
+  se->file = s->sde[stripe][layer][plane];
+
+  /* initialize adaptive template movement algorithm */
+  c_all = 0;
+  for (t = 0; t <= s->mx; t++)
+    c[t] = 0;
+  if (stripe == 0)
+    s->tx[plane] = 0;
+  new_tx = -1;
+  at_determined = 0;  /* we haven't yet decided the template move */
+  if (s->mx == 0)
+    at_determined = 1;
+
+  /* initialize typical prediction */
+  ltp = 0;
+  if (stripe == 0)
+    ltp_old = 0;
+  else {
+    ltp_old = 1;
+    p1 = hp - hbpl;
+    if (y > 1) {
+      q1 = p1 - hbpl;
+      while (p1 < hp && (ltp_old = (*p1++ == *q1++)) != 0);
+    } else
+      while (p1 < hp && (ltp_old = (*p1++ == 0)) != 0);
+  }
+
+  if (layer == 0) {
+
+    /*
+     *  Encode lowest resolution layer
+     */
+
+    for (i = 0; i < hl && y < hy; i++, y++) {
+
+      /* check whether it is worth to perform an ATMOVE */
+      if (!at_determined && c_all > 2048) {
+	cmin = clmin = 0xffffffffL;
+	cmax = clmax = 0;
+	tmax = 0;
+	for (t = (s->options & JBG_LRLTWO) ? 5 : 3; t <= s->mx; t++) {
+	  if (c[t] > cmax) cmax = c[t];
+	  if (c[t] < cmin) cmin = c[t];
+	  if (c[t] > c[tmax]) tmax = t;
+	}
+	clmin = (c[0] < cmin) ? c[0] : cmin;
+	clmax = (c[0] > cmax) ? c[0] : cmax;
+	if (c_all - cmax < (c_all >> 3) &&
+	    cmax - c[s->tx[plane]] > c_all - cmax &&
+	    cmax - c[s->tx[plane]] > (c_all >> 4) &&
+	    /*                     ^ T.82 says here < !!! Typo ? */
+	    cmax - (c_all - c[s->tx[plane]]) > c_all - cmax &&
+	    cmax - (c_all - c[s->tx[plane]]) > (c_all >> 4) &&
+	    cmax - cmin > (c_all >> 2) &&
+	    (s->tx[plane] || clmax - clmin > (c_all >> 3))) {
+	  /* we have decided to perform an ATMOVE */
+	  new_tx = tmax;
+	  if (!(s->options & JBG_DELAY_AT)) {
+	    new_tx_line = i;
+	    s->tx[plane] = new_tx;
+	  }
+	}
+	at_determined = 1;
+      }
+      
+      /* typical prediction */
+      if (s->options & JBG_TPBON) {
+	ltp = 1;
+	p1 = hp;
+	if (y > 0) {
+	  q1 = hp - hbpl;
+	  while (q1 < hp && (ltp = (*p1++ == *q1++)) != 0);
+	} else
+	  while (p1 < hp + hbpl && (ltp = (*p1++ == 0)) != 0);
+	arith_encode(se, (s->options & JBG_LRLTWO) ? TPB2CX : TPB3CX,
+		     ltp == ltp_old);
+#ifdef DEBUG
+	tp_lines += ltp;
+#endif
+	ltp_old = ltp;
+	if (ltp) {
+	  /* skip next line */
+	  hp += hbpl;
+	  continue;
+	}
+      }
+
+      /*
+       * Layout of the variables line_h1, line_h2, line_h3, which contain
+       * as bits the neighbour pixels of the currently coded pixel X:
+       *
+       *          76543210765432107654321076543210     line_h3
+       *          76543210765432107654321076543210     line_h2
+       *  76543210765432107654321X76543210             line_h1
+       */
+      
+      line_h1 = line_h2 = line_h3 = 0;
+      if (y > 0) line_h2 = (long)*(hp - hbpl) << 8;
+      if (y > 1) line_h3 = (long)*(hp - hbpl - hbpl) << 8;
+      
+      /* encode line */
+      for (j = 0; j < hx; hp++) {
+	line_h1 |= *hp;
+	if (j < hbpl * 8 - 8 && y > 0) {
+	  line_h2 |= *(hp - hbpl + 1);
+	  if (y > 1)
+	    line_h3 |= *(hp - hbpl - hbpl + 1);
+	}
+	if (s->options & JBG_LRLTWO) {
+	  /* two line template */
+	  do {
+	    line_h1 <<= 1;  line_h2 <<= 1;  line_h3 <<= 1;
+	    if (s->tx[plane])
+	      arith_encode(se, (((line_h2 >> 10) & 0x3e0) |
+				((line_h1 >> (4 + s->tx[plane])) & 0x010) |
+				((line_h1 >>  9) & 0x00f)),
+			   (line_h1 >> 8) & 1);
+	    else
+	      arith_encode(se, (((line_h2 >> 10) & 0x3f0) |
+				((line_h1 >>  9) & 0x00f)),
+			   (line_h1 >> 8) & 1);
+#ifdef DEBUG
+	    encoded_pixels++;
+#endif
+	    /* statistics for adaptive template changes */
+	    if (!at_determined && j >= s->mx && j < hx-2) {
+	      c[0] += !(((line_h2 >> 6) ^ line_h1) & 0x100);
+	      for (t = 5; t <= s->mx; t++)
+		c[t] += !(((line_h1 >> t) ^ line_h1) & 0x100);
+	      ++c_all;
+	    }
+	  } while (++j & 7 && j < hx);
+	} else {
+	  /* three line template */
+	  do {
+	    line_h1 <<= 1;  line_h2 <<= 1;  line_h3 <<= 1;
+	    if (s->tx[plane]) 
+	      arith_encode(se, (((line_h3 >>  8) & 0x380) |
+				((line_h2 >> 12) & 0x078) |
+				((line_h1 >> (6 + s->tx[plane])) & 0x004) |
+				((line_h1 >>  9) & 0x003)),
+			   (line_h1 >> 8) & 1);
+	    else
+	      arith_encode(se, (((line_h3 >>  8) & 0x380) |
+				((line_h2 >> 12) & 0x07c) |
+				((line_h1 >>  9) & 0x003)),
+			   (line_h1 >> 8) & 1);
+#ifdef DEBUG
+	    encoded_pixels++;
+#endif
+	    /* statistics for adaptive template changes */
+	    if (!at_determined && j >= s->mx && j < hx-2) {
+	      c[0] += !(((line_h2 >> 6) ^ line_h1) & 0x100);
+	      for (t = 3; t <= s->mx; t++)
+		c[t] += !(((line_h1 >> t) ^ line_h1) & 0x100);
+	      ++c_all;
+	    }
+	  } while (++j & 7 && j < hx);
+	} /* if (s->options & JBG_LRLTWO) */
+      } /* for (j = ...) */
+    } /* for (i = ...) */
+
+  } else {
+
+    /*
+     *  Encode differential layer
+     */
+    
+    for (i = 0; i < hl && y < hy; i++, y++) {
+
+      /* check whether it is worth to perform an ATMOVE */
+      if (!at_determined && c_all > 2048) {
+	cmin = clmin = 0xffffffffL;
+	cmax = clmax = 0;
+	tmax = 0;
+	for (t = 3; t <= s->mx; t++) {
+	  if (c[t] > cmax) cmax = c[t];
+	  if (c[t] < cmin) cmin = c[t];
+	  if (c[t] > c[tmax]) tmax = t;
+	}
+	clmin = (c[0] < cmin) ? c[0] : cmin;
+	clmax = (c[0] > cmax) ? c[0] : cmax;
+	if (c_all - cmax < (c_all >> 3) &&
+	    cmax - c[s->tx[plane]] > c_all - cmax &&
+	    cmax - c[s->tx[plane]] > (c_all >> 4) &&
+	    /*                     ^ T.82 says here < !!! Typo ? */
+	    cmax - (c_all - c[s->tx[plane]]) > c_all - cmax &&
+	    cmax - (c_all - c[s->tx[plane]]) > (c_all >> 4) &&
+	    cmax - cmin > (c_all >> 2) &&
+	    (s->tx[plane] || clmax - clmin > (c_all >> 3))) {
+	  /* we have decided to perform an ATMOVE */
+	  new_tx = tmax;
+	  if (!(s->options & JBG_DELAY_AT)) {
+	    new_tx_line = i;
+	    s->tx[plane] = new_tx;
+	  }
+#ifdef DEBUG
+	  fprintf(stderr, "ATMOVE: line=%ld, tx=%d, c_all=%ld\n",
+		  i, new_tx, c_all);
+#endif
+	}
+	at_determined = 1;
+      }
+      
+      if ((i >> 1) >= ll - 1 || (y >> 1) >= ly - 1)
+	lp1 = lp2;
+
+      /* typical prediction */
+      if (s->options & JBG_TPDON && (i & 1) == 0) {
+	q1 = lp1; q2 = lp2;
+	p0 = p1 = hp;
+	if (i < hl - 1 && y < hy - 1)
+	  p0 = hp + hbpl;
+	if (y > 1)
+	  line_l3 = (long)*(q2 - lbpl) << 8;
+	else
+	  line_l3 = 0;
+	line_l2 = (long)*q2 << 8;
+	line_l1 = (long)*q1 << 8;
+	ltp = 1;
+	for (j = 0; j < lx && ltp; q1++, q2++) {
+	  if (j < lbpl * 8 - 8) {
+	    if (y > 1)
+	      line_l3 |= *(q2 - lbpl + 1);
+	    line_l2 |= *(q2 + 1);
+	    line_l1 |= *(q1 + 1);
+	  }
+	  do {
+	    if ((j >> 2) < hbpl) {
+	      line_h1 = *(p1++);
+	      line_h0 = *(p0++);
+	    }
+	    do {
+	      line_l3 <<= 1;
+	      line_l2 <<= 1;
+	      line_l1 <<= 1;
+	      line_h1 <<= 2;
+	      line_h0 <<= 2;
+	      cx = (((line_l3 >> 15) & 0x007) |
+		    ((line_l2 >> 12) & 0x038) |
+		    ((line_l1 >> 9)  & 0x1c0));
+	      if (cx == 0x000)
+		if ((line_h1 & 0x300) == 0 && (line_h0 & 0x300) == 0)
+		  s->tp[j] = 0;
+		else {
+		  ltp = 0;
+#ifdef DEBUG
+		  tp_exceptions++;
+#endif
+		}
+	      else if (cx == 0x1ff)
+		if ((line_h1 & 0x300) == 0x300 && (line_h0 & 0x300) == 0x300)
+		  s->tp[j] = 1;
+		else {
+		  ltp = 0;
+#ifdef DEBUG
+		  tp_exceptions++;
+#endif
+		}
+	      else
+		s->tp[j] = 2;
+	    } while (++j & 3 && j < lx);
+	  } while (j & 7 && j < lx);
+	} /* for (j = ...) */
+	arith_encode(se, TPDCX, !ltp);
+#ifdef DEBUG
+	tp_lines += ltp;
+#endif
+      }
+
+
+      /*
+       * Layout of the variables line_h1, line_h2, line_h3, which contain
+       * as bits the high resolution neighbour pixels of the currently coded
+       * highres pixel X:
+       *
+       *            76543210 76543210 76543210 76543210     line_h3
+       *            76543210 76543210 76543210 76543210     line_h2
+       *   76543210 76543210 7654321X 76543210              line_h1
+       *
+       * Layout of the variables line_l1, line_l2, line_l3, which contain
+       * the low resolution pixels near the currently coded pixel as bits.
+       * The lowres pixel in which the currently coded highres pixel is
+       * located is marked as Y:
+       *
+       *            76543210 76543210 76543210 76543210     line_l3
+       *            76543210 7654321Y 76543210 76543210     line_l2
+       *            76543210 76543210 76543210 76543210     line_l1
+       */
+      
+
+      line_h1 = line_h2 = line_h3 = line_l1 = line_l2 = line_l3 = 0;
+      if (y > 0) line_h2 = (long)*(hp - hbpl) << 8;
+      if (y > 1) {
+	line_h3 = (long)*(hp - hbpl - hbpl) << 8;
+	line_l3 = (long)*(lp2 - lbpl) << 8;
+      }
+      line_l2 = (long)*lp2 << 8;
+      line_l1 = (long)*lp1 << 8;
+      
+      /* encode line */
+      for (j = 0; j < hx; lp1++, lp2++) {
+	if ((j >> 1) < lbpl * 8 - 8) {
+	  if (y > 1)
+	    line_l3 |= *(lp2 - lbpl + 1);
+	  line_l2 |= *(lp2 + 1);
+	  line_l1 |= *(lp1 + 1);
+	}
+	do {
+
+	  assert(hp - (s->lhp[s->highres[plane]][plane] +
+		       (stripe * hl + i) * hbpl)
+		 == (ptrdiff_t) j >> 3);
+
+	  assert(lp2 - (s->lhp[1-s->highres[plane]][plane] +
+			(stripe * ll + (i>>1)) * lbpl)
+		 == (ptrdiff_t) j >> 4);
+
+	  line_h1 |= *(hp++);
+	  if (j < hbpl * 8 - 8) {
+	    if (y > 0) {
+	      line_h2 |= *(hp - hbpl);
+	      if (y > 1)
+		line_h3 |= *(hp - hbpl - hbpl);
+	    }
+	  }
+	  do {
+	    line_l1 <<= 1;  line_l2 <<= 1;  line_l3 <<= 1;
+	    if (ltp && s->tp[j >> 1] < 2) {
+	      /* pixel are typical and have not to be encoded */
+	      line_h1 <<= 2;  line_h2 <<= 2;  line_h3 <<= 2;
+#ifdef DEBUG
+	      do {
+		++tp_pixels;
+	      } while (++j & 1 && j < hx);
+#else
+	      j += 2;
+#endif
+	    } else
+	      do {
+		line_h1 <<= 1;  line_h2 <<= 1;  line_h3 <<= 1;
+
+		/* deterministic prediction */
+		if (s->options & JBG_DPON) {
+		  if ((y & 1) == 0) {
+		    if ((j & 1) == 0) {
+		      /* phase 0 */
+		      if (s->dppriv[((line_l3 >> 16) & 0x003) |
+				    ((line_l2 >> 14) & 0x00c) |
+				    ((line_h1 >> 5)  & 0x010) |
+				    ((line_h2 >> 10) & 0x0e0)] < 2) {
+#ifdef DEBUG
+			++dp_pixels;
+#endif
+			continue;
+		      }
+		    } else {
+		      /* phase 1 */
+		      if (s->dppriv[(((line_l3 >> 16) & 0x003) |
+				     ((line_l2 >> 14) & 0x00c) |
+				     ((line_h1 >> 5)  & 0x030) |
+				     ((line_h2 >> 10) & 0x1c0)) + 256] < 2) {
+#ifdef DEBUG
+			++dp_pixels;
+#endif
+			continue;
+		      }
+		    }
+		  } else {
+		    if ((j & 1) == 0) {
+		      /* phase 2 */
+		      if (s->dppriv[(((line_l3 >> 16) & 0x003) |
+				     ((line_l2 >> 14) & 0x00c) |
+				     ((line_h1 >> 5)  & 0x010) |
+				     ((line_h2 >> 10) & 0x0e0) |
+				     ((line_h3 >> 7) & 0x700)) + 768] < 2) {
+#ifdef DEBUG
+			++dp_pixels;
+#endif
+			continue;
+		      }
+		    } else {
+		      /* phase 3 */
+		      if (s->dppriv[(((line_l3 >> 16) & 0x003) |
+				     ((line_l2 >> 14) & 0x00c) |
+				     ((line_h1 >> 5)  & 0x030) |
+				     ((line_h2 >> 10) & 0x1c0) |
+				     ((line_h3 >> 7)  & 0xe00)) + 2816] < 2) {
+#ifdef DEBUG
+			++dp_pixels;
+#endif
+			continue;
+		      }
+		    }	
+		  }	
+		}
+
+		/* determine context */
+		if (s->tx[plane])
+		  cx = (((line_h1 >> 9)  & 0x003) |
+			((line_h1 >> (4 + s->tx[plane])) & 0x010) |
+			((line_h2 >> 13) & 0x00c) |
+			((line_h3 >> 11) & 0x020));
+		else
+		  cx = (((line_h1 >> 9)  & 0x003) |
+			((line_h2 >> 13) & 0x01c) |
+			((line_h3 >> 11) & 0x020));
+		if (j & 1)
+		  cx |= (((line_l2 >> 9)  & 0x0c0) |
+			 ((line_l1 >> 7)  & 0x300)) | (1UL << 10);
+		else
+		  cx |= (((line_l2 >> 10) & 0x0c0) |
+			 ((line_l1 >> 8)  & 0x300));
+		cx |= (y & 1) << 11;
+
+		arith_encode(se, cx, (line_h1 >> 8) & 1);
+#ifdef DEBUG
+		encoded_pixels++;
+#endif
+		
+		/* statistics for adaptive template changes */
+		if (!at_determined && j >= s->mx) {
+		  c[0] += !(((line_h2 >> 6) ^ line_h1) & 0x100);
+		  for (t = 3; t <= s->mx; t++)
+		    c[t] += !(((line_h1 >> t) ^ line_h1) & 0x100);
+		  ++c_all;
+		}
+		
+	      } while (++j & 1 && j < hx);
+	  } while (j & 7 && j < hx);
+	} while (j & 15 && j < hx);
+      } /* for (j = ...) */
+
+      /* low resolution pixels are used twice */
+      if ((i & 1) == 0) {
+	lp1 -= lbpl;
+	lp2 -= lbpl;
+      }
+      
+    } /* for (i = ...) */
+  }
+  
+  arith_encode_flush(se);
+  jbg_buf_remove_zeros(s->sde[stripe][layer][plane]);
+  jbg_buf_write(MARKER_ESC, s->sde[stripe][layer][plane]);
+  jbg_buf_write(MARKER_SDNORM, s->sde[stripe][layer][plane]);
+
+  /* add ATMOVE */
+  if (new_tx != -1) {
+    if (s->options & JBG_DELAY_AT) {
+      /* ATMOVE will become active at the first line of the next stripe */
+      s->tx[plane] = new_tx;
+      jbg_buf_write(MARKER_ESC, s->sde[stripe][layer][plane]);
+      jbg_buf_write(MARKER_ATMOVE, s->sde[stripe][layer][plane]);
+      jbg_buf_write(0, s->sde[stripe][layer][plane]);
+      jbg_buf_write(0, s->sde[stripe][layer][plane]);
+      jbg_buf_write(0, s->sde[stripe][layer][plane]);
+      jbg_buf_write(0, s->sde[stripe][layer][plane]);
+      jbg_buf_write(s->tx[plane], s->sde[stripe][layer][plane]);
+      jbg_buf_write(0, s->sde[stripe][layer][plane]);
+    } else {
+      /* ATMOVE has already become active during this stripe
+       * => we have to prefix the SDE data with an ATMOVE marker */
+      new_jbg_buf = jbg_buf_init(&s->free_list);
+      jbg_buf_write(MARKER_ESC, new_jbg_buf);
+      jbg_buf_write(MARKER_ATMOVE, new_jbg_buf);
+      jbg_buf_write((new_tx_line >> 24) & 0xff, new_jbg_buf);
+      jbg_buf_write((new_tx_line >> 16) & 0xff, new_jbg_buf);
+      jbg_buf_write((new_tx_line >> 8) & 0xff, new_jbg_buf);
+      jbg_buf_write(new_tx_line & 0xff, new_jbg_buf);
+      jbg_buf_write(new_tx, new_jbg_buf);
+      jbg_buf_write(0, new_jbg_buf);
+      jbg_buf_prefix(new_jbg_buf, &s->sde[stripe][layer][plane]);
+    }
+  }
+
+#if 0
+  if (stripe == s->stripes - 1)
+    fprintf(stderr, "tp_lines = %ld, tp_exceptions = %ld, tp_pixels = %ld, "
+	    "dp_pixels = %ld, encoded_pixels = %ld\n",
+	    tp_lines, tp_exceptions, tp_pixels, dp_pixels, encoded_pixels);
+#endif
+
+  return;
+}
+
+
+/*
+ * Create the next lower resolution version of an image
+ */
+static void resolution_reduction(struct jbg_enc_state *s, int plane,
+				 int higher_layer)
+{
+  unsigned long hx, hy, lx, ly, hbpl, lbpl;
+  unsigned char *hp1, *hp2, *hp3, *lp;
+  unsigned long line_h1, line_h2, line_h3, line_l2;
+  unsigned long i, j;
+  int pix, k, l;
+
+  /* number of pixels in highres image */
+  hx = jbg_ceil_half(s->xd, s->d - higher_layer);
+  hy = jbg_ceil_half(s->yd, s->d - higher_layer);
+  /* number of pixels in lowres image */
+  lx = jbg_ceil_half(hx, 1);
+  ly = jbg_ceil_half(hy, 1);
+  /* bytes per line in highres and lowres image */
+  hbpl = (hx + 7) / 8;
+  lbpl = (lx + 7) / 8;
+  /* pointers to first image bytes */
+  hp2 = s->lhp[s->highres[plane]][plane];
+  hp1 = hp2 + hbpl;
+  hp3 = hp2 - hbpl;
+  lp = s->lhp[1 - s->highres[plane]][plane];
+  
+#ifdef DEBUG
+  fprintf(stderr, "resolution_reduction: plane = %d, higher_layer = %d\n",
+	  plane, higher_layer);
+#endif
+
+  /*
+   * Layout of the variables line_h1, line_h2, line_h3, which contain
+   * as bits the high resolution neighbour pixels of the currently coded
+   * lowres pixel /\:
+   *              \/
+   *
+   *   76543210 76543210 76543210 76543210     line_h3
+   *   76543210 76543210 765432/\ 76543210     line_h2
+   *   76543210 76543210 765432\/ 76543210     line_h1
+   *
+   * Layout of the variable line_l2, which contains the low resolution
+   * pixels near the currently coded pixel as bits. The lowres pixel
+   * which is currently coded is marked as X:
+   *
+   *   76543210 76543210 76543210 76543210     line_l2
+   *                            X
+   */
+      
+  for (i = 0; i < ly; i++) {
+    if (2*i + 1 >= hy)
+      hp1 = hp2;
+    pix = 0;
+    line_h1 = line_h2 = line_h3 = line_l2 = 0;
+    for (j = 0; j < lbpl * 8; j += 8) {
+      *lp = 0;
+      line_l2 |= i ? lp[-lbpl] : 0;
+      for (k = 0; k < 8 && j + k < lx; k += 4) {
+	if (((j + k) >> 2) < hbpl) {
+	  line_h3 |= i ? *hp3 : 0;
+	  ++hp3;
+	  line_h2 |= *(hp2++);
+	  line_h1 |= *(hp1++);
+	}
+	for (l = 0; l < 4 && j + k + l < lx; l++) {
+	  line_h3 <<= 2;
+	  line_h2 <<= 2;
+	  line_h1 <<= 2;
+	  line_l2 <<= 1;
+	  pix = s->res_tab[((line_h1 >> 8) & 0x007) |
+			   ((line_h2 >> 5) & 0x038) |
+			   ((line_h3 >> 2) & 0x1c0) |
+			   (pix << 9) | ((line_l2 << 2) & 0xc00)];
+	  *lp = (*lp << 1) | pix;
+	}
+      }
+      ++lp;
+    }
+    *(lp - 1) <<= lbpl * 8 - lx;
+    hp1 += hbpl;
+    hp2 += hbpl;
+    hp3 += hbpl;
+  }
+
+#ifdef DEBUG
+  {
+    FILE *f;
+    char fn[50];
+    
+    sprintf(fn, "dbg_d=%02d.pbm", higher_layer - 1);
+    f = fopen(fn, "wb");
+    fprintf(f, "P4\n%lu %lu\n", lx, ly);
+    fwrite(s->lhp[1 - s->highres[plane]][plane], 1, lbpl * ly, f);
+    fclose(f);
+  }
+#endif
+
+  return;
+}
+
+
+/* 
+ * This function is called inside the three loops of jbg_enc_out() in
+ * order to write the next SDE. It has first to generate the required
+ * SDE and all SDEs which have to be encoded before this SDE can be
+ * created. The problem here is that if we want to output a lower
+ * resolution layer, we have to allpy the resolution reduction
+ * algorithm in order to get it. As we try to safe as much memory as
+ * possible, the resolution reduction will overwrite previous higher
+ * resolution bitmaps. Consequently, we have to encode and buffer SDEs
+ * which depend on higher resolution layers before we can start the
+ * resolution reduction. All this logic about which SDE has to be
+ * encoded before resolution reduction is allowed is handled here.
+ * This approach might be a little bit more complex than alternative
+ * ways to do it, but it allows us to do the encoding with the minimal
+ * possible amount of temporary memory.
+ */
+static void output_sde(struct jbg_enc_state *s,
+		       unsigned long stripe, int layer, int plane)
+{
+  int lfcl;     /* lowest fully coded layer */
+  long i;
+  unsigned long u;
+  
+  assert(s->sde[stripe][layer][plane] != SDE_DONE);
+
+  if (s->sde[stripe][layer][plane] != SDE_TODO) {
+#ifdef DEBUG
+    fprintf(stderr, "writing SDE: s/d/p = %2lu/%2d/%2d\n",
+	    stripe, layer, plane);
+#endif
+    jbg_buf_output(&s->sde[stripe][layer][plane], s->data_out, s->file);
+    s->sde[stripe][layer][plane] = SDE_DONE;
+    return;
+  }
+
+  /* Determine the smallest resolution layer in this plane for which
+   * not yet all stripes have been encoded into SDEs. This layer will
+   * have to be completely coded, before we can apply the next
+   * resolution reduction step. */
+  lfcl = 0;
+  for (i = s->d; i >= 0; i--)
+    if (s->sde[s->stripes - 1][i][plane] == SDE_TODO) {
+      lfcl = i + 1;
+      break;
+    }
+  if (lfcl > s->d && s->d > 0 && stripe == 0) {
+    /* perform the first resolution reduction */
+    resolution_reduction(s, plane, s->d);
+  }
+  /* In case HITOLO is not used, we have to encode and store the higher
+   * resolution layers first, although we do not need them right now. */
+  while (lfcl - 1 > layer) {
+    for (u = 0; u < s->stripes; u++)
+      encode_sde(s, u, lfcl - 1, plane);
+    --lfcl;
+    s->highres[plane] ^= 1;
+    if (lfcl > 1)
+      resolution_reduction(s, plane, lfcl - 1);
+  }
+  
+  encode_sde(s, stripe, layer, plane);
+
+#ifdef DEBUG
+  fprintf(stderr, "writing SDE: s/d/p = %2lu/%2d/%2d\n", stripe, layer, plane);
+#endif
+  jbg_buf_output(&s->sde[stripe][layer][plane], s->data_out, s->file);
+  s->sde[stripe][layer][plane] = SDE_DONE;
+  
+  if (stripe == s->stripes - 1 && layer > 0 &&
+      s->sde[0][layer-1][plane] == SDE_TODO) {
+    s->highres[plane] ^= 1;
+    if (layer > 1)
+      resolution_reduction(s, plane, layer - 1);
+  }
+  
+  return;
+}
+
+
+/*
+ * Convert the table which controls the deterministic prediction
+ * process from the internal format into the representation required
+ * for the 1728 byte long DPTABLE element of a BIH.
+ *
+ * The bit order of the DPTABLE format (see also ITU-T T.82 figure 13) is
+ *
+ * high res:   4  5  6     low res:  0  1
+ *             7  8  9               2  3
+ *            10 11 12
+ *
+ * were 4 table entries are packed into one byte, while we here use
+ * internally an unpacked 6912 byte long table indexed by the following
+ * bit order:
+ *
+ * high res:   7  6  5     high res:   8  7  6     low res:  1  0
+ * (phase 0)   4  .  .     (phase 1)   5  4  .               3  2
+ *             .  .  .                 .  .  .
+ *
+ * high res:  10  9  8     high res:  11 10  9
+ * (phase 2)   7  6  5     (phase 3)   8  7  6
+ *             4  .  .                 5  4  .
+ */
+void jbg_int2dppriv(unsigned char *dptable, const char *internal)
+{
+  int i, j, k;
+  int trans0[ 8] = { 1, 0, 3, 2, 7, 6, 5, 4 };
+  int trans1[ 9] = { 1, 0, 3, 2, 8, 7, 6, 5, 4 };
+  int trans2[11] = { 1, 0, 3, 2, 10, 9, 8, 7, 6, 5, 4 };
+  int trans3[12] = { 1, 0, 3, 2, 11, 10, 9, 8, 7, 6, 5, 4 };
+  
+  for (i = 0; i < 1728; dptable[i++] = 0);
+
+#define FILL_TABLE1(offset, len, trans) \
+  for (i = 0; i < len; i++) { \
+    k = 0; \
+    for (j = 0; j < 8; j++) \
+      k |= ((i >> j) & 1) << trans[j]; \
+    dptable[(i + offset) >> 2] |= \
+      (internal[k + offset] & 3) << ((3 - (i&3)) << 1); \
+  }
+
+  FILL_TABLE1(   0,  256, trans0);
+  FILL_TABLE1( 256,  512, trans1);
+  FILL_TABLE1( 768, 2048, trans2);
+  FILL_TABLE1(2816, 4096, trans3);
+
+  return;
+}
+
+
+/*
+ * Convert the table which controls the deterministic prediction
+ * process from the 1728 byte long DPTABLE format into the 6912 byte long
+ * internal format.
+ */
+void jbg_dppriv2int(char *internal, const unsigned char *dptable)
+{
+  int i, j, k;
+  int trans0[ 8] = { 1, 0, 3, 2, 7, 6, 5, 4 };
+  int trans1[ 9] = { 1, 0, 3, 2, 8, 7, 6, 5, 4 };
+  int trans2[11] = { 1, 0, 3, 2, 10, 9, 8, 7, 6, 5, 4 };
+  int trans3[12] = { 1, 0, 3, 2, 11, 10, 9, 8, 7, 6, 5, 4 };
+  
+#define FILL_TABLE2(offset, len, trans) \
+  for (i = 0; i < len; i++) { \
+    k = 0; \
+    for (j = 0; j < 8; j++) \
+      k |= ((i >> j) & 1) << trans[j]; \
+    internal[k + offset] = \
+      (dptable[(i + offset) >> 2] >> ((3 - (i & 3)) << 1)) & 3; \
+  }
+
+  FILL_TABLE2(   0,  256, trans0);
+  FILL_TABLE2( 256,  512, trans1);
+  FILL_TABLE2( 768, 2048, trans2);
+  FILL_TABLE2(2816, 4096, trans3);
+
+  return;
+}
+
+
+/*
+ * Encode one full BIE and pass the generated data to the specified
+ * call-back function
+ */
+void jbg_enc_out(struct jbg_enc_state *s)
+{
+  long bpl;
+  unsigned char bih[20];
+  unsigned long xd, yd, y;
+  long ii[3], is[3], ie[3];    /* generic variables for the 3 nested loops */ 
+  unsigned long stripe;
+  int layer, plane;
+  int order;
+  unsigned char dpbuf[1728];
+  extern char jbg_dptable[];
+
+  /* some sanity checks */
+  s->order &= JBG_HITOLO | JBG_SEQ | JBG_ILEAVE | JBG_SMID;
+  order = s->order & (JBG_SEQ | JBG_ILEAVE | JBG_SMID);
+  if (index[order][0] < 0)
+    s->order = order = JBG_SMID | JBG_ILEAVE;
+  if (s->options & JBG_DPON && s->dppriv != jbg_dptable)
+    s->options |= JBG_DPPRIV;
+  if (s->mx > MX_MAX)
+    s->mx = MX_MAX;
+  s->my = 0;
+  if (s->mx && s->mx < ((s->options & JBG_LRLTWO) ? 5U : 3U))
+    s->mx = 0;
+  if (s->d > 255 || s->d < 0 || s->dh > s->d || s->dh < 0 ||
+      s->dl < 0 || s->dl > s->dh || s->planes < 0 || s->planes > 255)
+    return;
+
+  /* ensure correct zero padding of bitmap at the final byte of each line */
+  if (s->xd & 7) {
+    bpl = (s->xd + 7) / 8;     /* bytes per line */
+    for (plane = 0; plane < s->planes; plane++)
+      for (y = 0; y < s->yd; y++)
+	s->lhp[0][plane][y * bpl + bpl - 1] &= ~((1 << (8 - (s->xd & 7))) - 1);
+  }
+
+  /* calculate number of stripes that will be required */
+  s->stripes = ((s->yd >> s->d) + 
+		((((1UL << s->d) - 1) & s->xd) != 0) + s->l0 - 1) / s->l0;
+
+  /* allocate buffers for SDE pointers */
+  if (s->sde == NULL) {
+    s->sde = (struct jbg_buf ****)
+      checked_malloc(s->stripes * sizeof(struct jbg_buf ***));
+    for (stripe = 0; stripe < s->stripes; stripe++) {
+      s->sde[stripe] = (struct jbg_buf ***)
+	checked_malloc((s->d + 1) * sizeof(struct jbg_buf **));
+      for (layer = 0; layer < s->d + 1; layer++) {
+	s->sde[stripe][layer] = (struct jbg_buf **)
+	  checked_malloc(s->planes * sizeof(struct jbg_buf *));
+	for (plane = 0; plane < s->planes; plane++)
+	  s->sde[stripe][layer][plane] = SDE_TODO;
+      }
+    }
+  }
+
+  /* output BIH */
+  bih[0] = s->dl;
+  bih[1] = s->dh;
+  bih[2] = s->planes;
+  bih[3] = 0;
+  xd = jbg_ceil_half(s->xd, s->d - s->dh);
+  yd = jbg_ceil_half(s->yd, s->d - s->dh);
+  bih[4] = xd >> 24;
+  bih[5] = (xd >> 16) & 0xff;
+  bih[6] = (xd >> 8) & 0xff;
+  bih[7] = xd & 0xff;
+  bih[8] = yd >> 24;
+  bih[9] = (yd >> 16) & 0xff;
+  bih[10] = (yd >> 8) & 0xff;
+  bih[11] = yd & 0xff;
+  bih[12] = s->l0 >> 24;
+  bih[13] = (s->l0 >> 16) & 0xff;
+  bih[14] = (s->l0 >> 8) & 0xff;
+  bih[15] = s->l0 & 0xff;
+  bih[16] = s->mx;
+  bih[17] = s->my;
+  bih[18] = s->order;
+  bih[19] = s->options & 0x7f;
+  s->data_out(bih, 20, s->file);
+  if ((s->options & (JBG_DPON | JBG_DPPRIV | JBG_DPLAST)) ==
+      (JBG_DPON | JBG_DPPRIV)) {
+    /* write private table */
+    jbg_int2dppriv(dpbuf, s->dppriv);
+    s->data_out(dpbuf, 1728, s->file);
+  }
+
+#if 0
+  /*
+   * Encode everything first. This is a simple-minded alternative to
+   * all the tricky on-demand encoding logic in output_sde() for
+   * debugging purposes.
+   */
+  for (layer = s->dh; layer >= s->dl; layer--) {
+    for (plane = 0; plane < s->planes; plane++) {
+      if (layer > 0)
+	resolution_reduction(s, plane, layer);
+      for (stripe = 0; stripe < s->stripes; stripe++)
+	encode_sde(s, stripe, layer, plane);
+      s->highres[plane] ^= 1;
+    }
+  }
+#endif
+
+  /*
+   * Generic loops over all SDEs. Which loop represents layer, plane and
+   * stripe depends on the option flags.
+   */
+
+  /* start and end value vor each loop */
+  is[index[order][STRIPE]] = 0;
+  ie[index[order][STRIPE]] = s->stripes - 1;
+  is[index[order][LAYER]] = s->dl;
+  ie[index[order][LAYER]] = s->dh;
+  is[index[order][PLANE]] = 0;
+  ie[index[order][PLANE]] = s->planes - 1;
+
+  for (ii[0] = is[0]; ii[0] <= ie[0]; ii[0]++)
+    for (ii[1] = is[1]; ii[1] <= ie[1]; ii[1]++)
+      for (ii[2] = is[2]; ii[2] <= ie[2]; ii[2]++) {
+	
+	stripe = ii[index[order][STRIPE]];
+	if (s->order & JBG_HITOLO)
+	  layer = s->dh - (ii[index[order][LAYER]] - s->dl);
+	else
+	  layer = ii[index[order][LAYER]];
+	plane = ii[index[order][PLANE]];
+
+	output_sde(s, stripe, layer, plane);
+
+      }
+
+  return;
+}
+
+
+void jbg_enc_free(struct jbg_enc_state *s)
+{
+  unsigned long stripe;
+  int layer, plane;
+
+#ifdef DEBUG
+  fprintf(stderr, "jbg_enc_free(%p)\n", s);
+#endif
+
+  /* clear buffers for SDEs */
+  if (s->sde) {
+    for (stripe = 0; stripe < s->stripes; stripe++) {
+      for (layer = 0; layer < s->d + 1; layer++) {
+	for (plane = 0; plane < s->planes; plane++)
+	  if (s->sde[stripe][layer][plane] != SDE_DONE &&
+	      s->sde[stripe][layer][plane] != SDE_TODO)
+	    jbg_buf_free(&s->sde[stripe][layer][plane]);
+	checked_free(s->sde[stripe][layer]);
+      }
+      checked_free(s->sde[stripe]);
+    }
+    checked_free(s->sde);
+  }
+
+  /* clear free_list */
+  jbg_buf_free(&s->free_list);
+
+  /* clear memory for arithmetic encoder states */
+  checked_free(s->s);
+
+  /* clear memory for differential-layer typical prediction buffer */
+  checked_free(s->tp);
+
+  /* clear memory for adaptive template pixel offsets */
+  checked_free(s->tx);
+
+  /* clear lowres image buffers */
+  if (s->lhp[1]) {
+    for (plane = 0; plane < s->planes; plane++)
+      checked_free(s->lhp[1][plane]);
+    checked_free(s->lhp[1]);
+  }
+
+  return;
+}
+
+
+/*
+ * Convert the error codes used by jbg_dec_in() into a string
+ * written in the selected language and character set.
+ */
+const char *jbg_strerror(int errnum, int language)
+{
+  if (errnum < 0 || errnum >= NEMSG)
+    return "Unknown error code passed to jbg_strerror()";
+  if (language < 0 || language >= NEMSG_LANG)
+    return "Unknown language code passed to jbg_strerror()";
+
+  return errmsg[language][errnum];
+}
+
+
+/*
+ * The constructor for a decoder 
+ */
+void jbg_dec_init(struct jbg_dec_state *s)
+{
+  s->order = 0;
+  s->d = -1;
+  s->bie_len = 0;
+  s->buf_len = 0;
+  s->dppriv = NULL;
+  s->xmax = 4294967295UL;
+  s->ymax = 4294967295UL;
+  s->dmax = 256;
+  s->s = NULL;
+
+  return;
+}
+
+
+/*
+ * Specify a maximum image size for the decoder. If the JBIG file has
+ * the order bit ILEAVE, but not the bit SEQ set, then the decoder
+ * will abort to decode after the image has reached the maximal
+ * resolution layer which is still not wider than xmax or higher than
+ * ymax.
+ */
+void jbg_dec_maxsize(struct jbg_dec_state *s, unsigned long xmax,
+		     unsigned long ymax)
+{
+  if (xmax > 0) s->xmax = xmax;
+  if (ymax > 0) s->ymax = ymax;
+
+  return;
+}
+
+
+/*
+ * Decode the new len PSDC bytes to which data points and add them to
+ * the current stripe. Return the number of bytes which have actually
+ * been read (this will be less than len if a marker segment was 
+ * part of the data or if the final byte was 0xff were this code
+ * can not determine, whether we have a marker segment.
+ */
+static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
+			  size_t len)
+{
+  unsigned long stripe;
+  unsigned int layer, plane;
+  unsigned long hl, ll, y, hx, hy, lx, ly, hbpl, lbpl;
+  unsigned char *hp, *lp1, *lp2, *p1, *q1;
+  register unsigned long line_h1, line_h2, line_h3;
+  register unsigned long line_l1, line_l2, line_l3;
+  struct jbg_ardec_state *se;
+  unsigned long x;
+  int n;
+  int pix, cx = 0, slntp, shift, tx;
+
+  /* SDE loop variables */
+  stripe = s->ii[index[s->order & 7][STRIPE]];
+  layer = s->ii[index[s->order & 7][LAYER]];
+  plane = s->ii[index[s->order & 7][PLANE]];
+
+  /* forward data to arithmetic decoder */
+  se = s->s[plane] + layer - s->dl;
+  se->pscd_ptr = data;
+  se->pscd_end = data + len;
+  
+  /* number of lines per stripe in highres image */
+  hl = s->l0 << layer;
+  /* number of lines per stripe in lowres image */
+  ll = hl >> 1;
+  /* current line number in highres image */
+  y = stripe * hl + s->i;
+  /* number of pixels in highres image */
+  hx = jbg_ceil_half(s->xd, s->d - layer);
+  hy = jbg_ceil_half(s->yd, s->d - layer);
+  /* number of pixels in lowres image */
+  lx = jbg_ceil_half(hx, 1);
+  ly = jbg_ceil_half(hy, 1);
+  /* bytes per line in highres and lowres image */
+  hbpl = (hx + 7) / 8;
+  lbpl = (lx + 7) / 8;
+  /* pointer to highres and lowres image bytes */
+  hp  = s->lhp[ layer    & 1][plane] + (stripe * hl + s->i) * hbpl +
+    (s->x >> 3);
+  lp2 = s->lhp[(layer-1) & 1][plane] + (stripe * ll + (s->i >> 1)) * lbpl +
+    (s->x >> 4);
+  lp1 = lp2 + lbpl;
+
+  /* restore a few local variables */
+  line_h1 = s->line_h1;
+  line_h2 = s->line_h2;
+  line_h3 = s->line_h3;
+  line_l1 = s->line_l1;
+  line_l2 = s->line_l2;
+  line_l3 = s->line_l3;
+  x = s->x;
+
+  if (s->x == 0 && s->i == 0 &&
+      (stripe == 0 || s->reset[plane][layer - s->dl])) {
+    s->tx[plane][layer - s->dl] = s->ty[plane][layer - s->dl] = 0;
+    if (s->pseudo)
+      s->lntp[plane][layer - s->dl] = 1;
+  }
+
+#ifdef DEBUG
+  if (s->x == 0 && s->i == 0 && s->pseudo)
+    fprintf(stderr, "decode_pscd(%p, %p, %ld): s/d/p = %2lu/%2u/%2u\n",
+	    s, data, (long) len, stripe, layer, plane);
+#endif
+
+  if (layer == 0) {
+
+    /*
+     *  Decode lowest resolution layer
+     */
+
+    for (; s->i < hl && y < hy; s->i++, y++) {
+
+      /* adaptive template changes */
+      if (x == 0)
+	for (n = 0; n < s->at_moves; n++)
+	  if (s->at_line[n] == s->i) {
+	    s->tx[plane][layer - s->dl] = s->at_tx[n];
+	    s->ty[plane][layer - s->dl] = s->at_ty[n];
+#ifdef DEBUG
+	    fprintf(stderr, "ATMOVE: line=%lu, tx=%d, ty=%d.\n", s->i,
+		    s->tx[plane][layer - s->dl], s->ty[plane][layer - s->dl]);
+#endif
+	  }
+      tx = s->tx[plane][layer - s->dl];
+      shift =  tx - ((s->options & JBG_LRLTWO) ? 5 : 3);
+
+      /* typical prediction */
+      if (s->options & JBG_TPBON && s->pseudo) {
+	slntp = arith_decode(se, (s->options & JBG_LRLTWO) ? TPB2CX : TPB3CX);
+	if (se->result == JBG_MORE || se->result == JBG_MARKER)
+	  goto leave;
+	s->lntp[plane][layer - s->dl] =
+	  !(slntp ^ s->lntp[plane][layer - s->dl]);
+	if (s->lntp[plane][layer - s->dl]) {
+	  /* this line is 'not typical' and has to be coded completely */
+	  s->pseudo = 0;
+	} else {
+	  /* this line is 'typical' (i.e. identical to the previous one) */
+	  p1 = hp;
+	  if (s->i == 0 && (stripe == 0 || s->reset[plane][layer - s->dl]))
+	    while (p1 < hp + hbpl) *p1++ = 0;
+	  else {
+	    q1 = hp - hbpl;
+	    while (q1 < hp) *p1++ = *q1++;
+	  }
+	  hp += hbpl;
+	  continue;
+	}
+      }
+      
+      /*
+       * Layout of the variables line_h1, line_h2, line_h3, which contain
+       * as bits the neighbour pixels of the currently decoded pixel X:
+       *
+       *                     76543210 76543210 76543210 76543210     line_h3
+       *                     76543210 76543210 76543210 76543210     line_h2
+       *   76543210 76543210 76543210 76543210 X                     line_h1
+       */
+      
+      if (x == 0) {
+	line_h1 = line_h2 = line_h3 = 0;
+	if (s->i > 0 || (y > 0 && !s->reset[plane][layer - s->dl]))
+	  line_h2 = (long)*(hp - hbpl) << 8;
+	if (s->i > 1 || (y > 1 && !s->reset[plane][layer - s->dl]))
+	  line_h3 = (long)*(hp - hbpl - hbpl) << 8;
+      }
+      
+      /*
+       * Another tiny JBIG standard bug:
+       *
+       * While implementing the line_h3 handling here, I discovered
+       * another problem with the ITU-T T.82(1993 E) specification.
+       * This might be a somewhat pathological case, however. The
+       * standard is unclear about how a decoder should behave in the
+       * following situation:
+       *
+       * Assume we are in layer 0 and all stripes are single lines
+       * (L0=1 allowed by table 9). We are now decoding the first (and
+       * only) line of the third stripe. Assume, the first stripe was
+       * terminated by SDRST and the second stripe was terminated by
+       * SDNORM. While decoding the only line of the third stripe with
+       * the three-line template, we need access to pixels from the
+       * previous two stripes. We know that the previous stripe
+       * terminated with SDNROM, so we access the pixel from the
+       * second stripe. But do we have to replace the pixels from the
+       * first stripe by background pixels, because this stripe ended
+       * with SDRST? The standard, especially clause 6.2.5 does never
+       * mention this case, so the behaviour is undefined here. My
+       * current implementation remembers only the marker used to
+       * terminate the previous stripe. In the above example, the
+       * pixels of the first stripe are accessed despite the fact that
+       * this stripe ended with SDRST. An alternative (only slightly
+       * more complicated) implementation would be to remember the end
+       * marker (SDNORM or SDRST) of the previous two stripes in a
+       * plane/layer and to act accordingly when accessing the two
+       * previous lines. What am I supposed to do here?
+       *
+       * As the standard is unclear about the correct behaviour in the
+       * situation of the above example, I strongly suggest to avoid
+       * the following situation while encoding data with JBIG:
+       *
+       *   LRLTWO = 0, L0=1 and both SDNORM and SDRST appear in layer 0.
+       *
+       * I guess that only a very few if any encoders will switch
+       * between SDNORM and SDRST, so let us hope that this ambiguity
+       * in the standard will never cause any interoperability
+       * problems.
+       *
+       * Markus Kuhn -- 1995-04-30
+       */
+
+      /* decode line */
+      while (x < hx) {
+	if ((x & 7) == 0) {
+	  if (x < hbpl * 8 - 8 &&
+	      (s->i > 0 || (y > 0 && !s->reset[plane][layer - s->dl]))) {
+	    line_h2 |= *(hp - hbpl + 1);
+	    if (s->i > 1 || (y > 1 && !s->reset[plane][layer - s->dl]))
+	      line_h3 |= *(hp - hbpl - hbpl + 1);
+	  }
+	}
+	if (s->options & JBG_LRLTWO) {
+	  /* two line template */
+	  do {
+	    if (tx)
+	      pix = arith_decode(se, (((line_h2 >> 9) & 0x3e0) |
+				      ((line_h1 >> shift) & 0x010) |
+				      (line_h1 & 0x00f)));
+	    else
+	      pix = arith_decode(se, (((line_h2 >> 9) & 0x3f0) |
+				      (line_h1 & 0x00f)));
+	    if (se->result == JBG_MORE || se->result == JBG_MARKER)
+	      goto leave;
+	    line_h1 = (line_h1 << 1) | pix;
+	    line_h2 <<= 1;
+	  } while ((++x & 7) && x < hx);
+	} else {
+	  /* three line template */
+	  do {
+	    if (tx) 
+	      pix = arith_decode(se, (((line_h3 >>  7) & 0x380) |
+				      ((line_h2 >> 11) & 0x078) |
+				      ((line_h1 >> shift) & 0x004) |
+				      (line_h1 & 0x003)));
+	    else
+	      pix = arith_decode(se, (((line_h3 >>  7) & 0x380) |
+				      ((line_h2 >> 11) & 0x07c) |
+				      (line_h1 & 0x003)));
+	    if (se->result == JBG_MORE || se->result == JBG_MARKER)
+	      goto leave;
+	    
+	    line_h1 = (line_h1 << 1) | pix;
+	    line_h2 <<= 1;
+	    line_h3 <<= 1;
+	  } while ((++x & 7) && x < hx);
+	} /* if (s->options & JBG_LRLTWO) */
+	*hp++ = line_h1;
+      } /* while */
+      *(hp - 1) <<= hbpl * 8 - hx;
+      x = 0;
+      s->pseudo = 1;
+    } /* for (i = ...) */
+    
+  } else {
+
+    /*
+     *  Decode differential layer
+     */
+
+    for (; s->i < hl && y < hy; s->i++, y++) {
+
+      /* adaptive template changes */
+      if (x == 0)
+	for (n = 0; n < s->at_moves; n++)
+	  if (s->at_line[n] == s->i) {
+	    s->tx[plane][layer - s->dl] = s->at_tx[n];
+	    s->ty[plane][layer - s->dl] = s->at_ty[n];
+#ifdef DEBUG
+	    fprintf(stderr, "ATMOVE: line=%lu, tx=%d, ty=%d.\n", s->i,
+		    s->tx[plane][layer - s->dl], s->ty[plane][layer - s->dl]);
+#endif
+	  }
+      tx = s->tx[plane][layer - s->dl];
+      shift = tx - 3;
+
+      /* handle lower border of low-resolution image */
+      if ((s->i >> 1) >= ll - 1 || (y >> 1) >= ly - 1)
+	lp1 = lp2;
+
+      /* typical prediction */
+      if (s->options & JBG_TPDON && s->pseudo) {
+	s->lntp[plane][layer - s->dl] = arith_decode(se, TPDCX);
+	if (se->result == JBG_MORE || se->result == JBG_MARKER)
+	  goto leave;
+	s->pseudo = 0;
+      }
+
+
+      /*
+       * Layout of the variables line_h1, line_h2, line_h3, which contain
+       * as bits the high resolution neighbour pixels of the currently
+       * decoded highres pixel X:
+       *
+       *                     76543210 76543210 76543210 76543210     line_h3
+       *                     76543210 76543210 76543210 76543210     line_h2
+       *   76543210 76543210 76543210 76543210 X                     line_h1
+       *
+       * Layout of the variables line_l1, line_l2, line_l3, which contain
+       * the low resolution pixels near the currently decoded pixel as bits.
+       * The lowres pixel in which the currently coded highres pixel is
+       * located is marked as Y:
+       *
+       *                     76543210 76543210 76543210 76543210     line_l3
+       *                     76543210 76543210 Y6543210 76543210     line_l2
+       *                     76543210 76543210 76543210 76543210     line_l1
+       */
+      
+
+      if (x == 0) {
+	line_h1 = line_h2 = line_h3 = line_l1 = line_l2 = line_l3 = 0;
+	if (s->i > 0 || (y > 0 && !s->reset[plane][layer - s->dl])) {
+	  line_h2 = (long)*(hp - hbpl) << 8;
+	  if (s->i > 1 || (y > 1 && !s->reset[plane][layer - s->dl]))
+	    line_h3 = (long)*(hp - hbpl - hbpl) << 8;
+	}
+	if (s->i > 1 || (y > 1 && !s->reset[plane][layer-s->dl]))
+	  line_l3 = (long)*(lp2 - lbpl) << 8;
+	line_l2 = (long)*lp2 << 8;
+	line_l1 = (long)*lp1 << 8;
+      }
+      
+      /* decode line */
+      while (x < hx) {
+	if ((x & 15) == 0)
+	  if ((x >> 1) < lbpl * 8 - 8) {
+	    line_l1 |= *(lp1 + 1);
+	    line_l2 |= *(lp2 + 1);
+	    if (s->i > 1 || 
+		(y > 1 && !s->reset[plane][layer - s->dl]))
+	      line_l3 |= *(lp2 - lbpl + 1);
+	  }
+	do {
+
+	  assert(hp  - (s->lhp[ layer     &1][plane] + (stripe * hl + s->i)
+			* hbpl) == (ptrdiff_t) x >> 3);
+	  assert(lp2 - (s->lhp[(layer-1) &1][plane] + (stripe * ll + (s->i>>1))
+			* lbpl) == (ptrdiff_t) x >> 4);
+
+	  if ((x & 7) == 0)
+	    if (x < hbpl * 8 - 8) {
+	      if (s->i > 0 || (y > 0 && !s->reset[plane][layer - s->dl])) {
+		line_h2 |= *(hp + 1 - hbpl);
+		if (s->i > 1 || (y > 1 && !s->reset[plane][layer - s->dl]))
+		  line_h3 |= *(hp + 1 - hbpl - hbpl);
+	      }
+	    }
+	  do {
+	    if (!s->lntp[plane][layer - s->dl])
+              cx = (((line_l3 >> 14) & 0x007) |
+                    ((line_l2 >> 11) & 0x038) |
+                    ((line_l1 >> 8)  & 0x1c0));
+	    if (!s->lntp[plane][layer - s->dl] &&
+		(cx == 0x000 || cx == 0x1ff)) {
+	      /* pixels are typical and have not to be decoded */
+	      do {
+		line_h1 = (line_h1 << 1) | (cx & 1);
+	      } while ((++x & 1) && x < hx);
+	      line_h2 <<= 2;  line_h3 <<= 2;
+	    } else 
+	      do {
+		
+		/* deterministic prediction */
+		if (s->options & JBG_DPON)
+		  if ((y & 1) == 0)
+		    if ((x & 1) == 0) 
+		      /* phase 0 */
+		      pix = s->dppriv[((line_l3 >> 15) & 0x003) |
+				      ((line_l2 >> 13) & 0x00c) |
+				      ((line_h1 <<  4) & 0x010) |
+				      ((line_h2 >>  9) & 0x0e0)];
+		    else
+		      /* phase 1 */
+		      pix = s->dppriv[(((line_l3 >> 15) & 0x003) |
+				       ((line_l2 >> 13) & 0x00c) |
+				       ((line_h1 <<  4) & 0x030) |
+				       ((line_h2 >>  9) & 0x1c0)) + 256];
+		  else
+		    if ((x & 1) == 0)
+		      /* phase 2 */
+		      pix = s->dppriv[(((line_l3 >> 15) & 0x003) |
+				       ((line_l2 >> 13) & 0x00c) |
+				       ((line_h1 <<  4) & 0x010) |
+				       ((line_h2 >>  9) & 0x0e0) |
+				       ((line_h3 >>  6) & 0x700)) + 768];
+		    else
+		      /* phase 3 */
+		      pix = s->dppriv[(((line_l3 >> 15) & 0x003) |
+				       ((line_l2 >> 13) & 0x00c) |
+				       ((line_h1 <<  4) & 0x030) |
+				       ((line_h2 >>  9) & 0x1c0) |
+				       ((line_h3 >>  6) & 0xe00)) + 2816];
+		else
+		  pix = 2;
+
+		if (pix & 2) {
+		  if (tx)
+		    cx = ((line_h1         & 0x003) |
+			  (((line_h1 << 2) >> shift) & 0x010) |
+			  ((line_h2 >> 12) & 0x00c) |
+			  ((line_h3 >> 10) & 0x020));
+		  else
+		    cx = ((line_h1         & 0x003) |
+			  ((line_h2 >> 12) & 0x01c) |
+			  ((line_h3 >> 10) & 0x020));
+		  if (x & 1)
+		    cx |= (((line_l2 >> 8) & 0x0c0) |
+			   ((line_l1 >> 6) & 0x300)) | (1UL << 10);
+		  else
+		    cx |= (((line_l2 >> 9) & 0x0c0) |
+			   ((line_l1 >> 7) & 0x300));
+		  cx |= (y & 1) << 11;
+
+		  pix = arith_decode(se, cx);
+		  if (se->result == JBG_MORE || se->result == JBG_MARKER)
+		    goto leave;
+		}
+
+		line_h1 = (line_h1 << 1) | pix;
+		line_h2 <<= 1;
+		line_h3 <<= 1;
+		
+	      } while ((++x & 1) && x < hx);
+	    line_l1 <<= 1; line_l2 <<= 1;  line_l3 <<= 1;
+	  } while ((x & 7) && x < hx);
+	  *hp++ = line_h1;
+	} while ((x & 15) && x < hx);
+	++lp1;
+	++lp2;
+      } /* while */
+      x = 0;
+      
+      *(hp - 1) <<= hbpl * 8 - hx;
+      if ((s->i & 1) == 0) {
+	/* low resolution pixels are used twice */
+	lp1 -= lbpl;
+	lp2 -= lbpl;
+      } else
+	s->pseudo = 1;
+      
+    } /* for (i = ...) */
+    
+  }
+
+ leave:
+
+  /* save a few local variables */
+  s->line_h1 = line_h1;
+  s->line_h2 = line_h2;
+  s->line_h3 = line_h3;
+  s->line_l1 = line_l1;
+  s->line_l2 = line_l2;
+  s->line_l3 = line_l3;
+  s->x = x;
+
+  return se->pscd_ptr - data;
+}
+
+
+/*
+ * Provide a new BIE fragment to the decoder.
+ *
+ * If cnt is not NULL, then *cnt will contain after the call the
+ * number of actually read bytes. If the data was not complete, then
+ * the return value will be JBG_EAGAIN and *cnt == len. In case this
+ * function has returned with JBG_EOK, then it has reached the end of
+ * a BIE but it can be called again with data from the next BIE if
+ * there exists one in order to get to a higher resolution layer. In
+ * case the return value was JBG_EOK_INTR then this function can be
+ * called again with the rest of the BIE, because parsing the BIE has
+ * been interrupted by a jbg_dec_maxsize() specification. In both
+ * cases the remaining len - *cnt bytes of the previous block will
+ * have to passed to this function again (if len > *cnt). In case of
+ * any other return value than JBG_EOK, JBG_EOK_INTR or JBG_EAGAIN, a
+ * serious problem has occured and the only function you should call
+ * is jbg_dec_free() in order to remove the mess (and probably
+ * jbg_strerror() in order to find out what to tell the user).
+ */
+int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
+	       size_t *cnt)
+{
+  int i, j, required_length;
+  unsigned long x, y;
+  unsigned long is[3], ie[3];
+  long hsize, lsize;
+  extern char jbg_dptable[];
+  size_t dummy_cnt;
+
+  if (!cnt) cnt = &dummy_cnt;
+  *cnt = 0;
+  if (len < 1) return JBG_EAGAIN;
+
+  /* read in 20-byte BIH */
+  if (s->bie_len < 20) {
+    while (s->bie_len < 20 && *cnt < len)
+      s->buffer[s->bie_len++] = data[(*cnt)++];
+    if (s->bie_len < 20) 
+      return JBG_EAGAIN;
+    if (s->buffer[1] < s->buffer[0])
+      return JBG_EINVAL;
+    /* test whether this looks like a valid JBIG header at all */
+    if (s->buffer[3] != 0 || (s->buffer[18] & 0xf0) != 0 ||
+	(s->buffer[19] & 0x80) != 0)
+      return JBG_EINVAL;
+    if (s->buffer[0] != s->d + 1)
+      return JBG_ENOCONT;
+    s->dl = s->buffer[0];
+    s->d = s->buffer[1];
+    if (s->dl == 0)
+      s->planes = s->buffer[2];
+    else
+      if (s->planes != s->buffer[2])
+	return JBG_ENOCONT;
+    x = (((long) s->buffer[ 4] << 24) | ((long) s->buffer[ 5] << 16) |
+	 ((long) s->buffer[ 6] <<  8) | (long) s->buffer[ 7]);
+    y = (((long) s->buffer[ 8] << 24) | ((long) s->buffer[ 9] << 16) |
+	 ((long) s->buffer[10] <<  8) | (long) s->buffer[11]);
+    if (s->dl != 0 && ((s->xd << (s->d - s->dl + 1)) != x &&
+		       (s->yd << (s->d - s->dl + 1)) != y))
+      return JBG_ENOCONT;
+    s->xd = x;
+    s->yd = y;
+    s->l0 = (((long) s->buffer[12] << 24) | ((long) s->buffer[13] << 16) |
+	     ((long) s->buffer[14] <<  8) | (long) s->buffer[15]);
+    if (!s->planes || !s->xd || !s->yd || !s->l0)
+      return JBG_EINVAL;
+    s->mx = s->buffer[16];
+    if (s->mx > 127)
+      return JBG_EINVAL;
+    s->my = s->buffer[17];
+    if (s->mx > 32 || s->my > 0) 
+      return JBG_EIMPL;
+    s->order = s->buffer[18];
+    if (index[s->order & 7][0] < 0)
+      return JBG_EINVAL;
+    /* HITOLO and SEQ currently not yet implemented */
+    if (s->dl != s->d && (s->order & JBG_HITOLO || s->order & JBG_SEQ))
+      return JBG_EIMPL;
+    s->options = s->buffer[19];
+
+    /* calculate number of stripes that will be required */
+    s->stripes = ((s->yd >> s->d) +
+		  ((((1UL << s->d) - 1) & s->xd) != 0) + s->l0 - 1) / s->l0;
+
+    /* some initialization */
+    s->ii[index[s->order & 7][STRIPE]] = 0;
+    s->ii[index[s->order & 7][LAYER]] = s->dl;
+    s->ii[index[s->order & 7][PLANE]] = 0;
+    /* bytes required for resolution layer D and D-1 */
+    hsize = ((s->xd + 7) / 8) * s->yd;
+    lsize = ((jbg_ceil_half(s->xd, 1) + 7) / 8) *
+      jbg_ceil_half(s->yd, 1);
+    if (s->dl == 0) {
+      s->s = checked_malloc(s->planes * sizeof(struct jbg_ardec_state *));
+      s->tx = checked_malloc(s->planes * sizeof(int *));
+      s->ty = checked_malloc(s->planes * sizeof(int *));
+      s->reset = checked_malloc(s->planes * sizeof(int *));
+      s->lntp = checked_malloc(s->planes * sizeof(int *));
+      s->lhp[0] = checked_malloc(s->planes * sizeof(unsigned char *));
+      s->lhp[1] = checked_malloc(s->planes * sizeof(unsigned char *));
+      for (i = 0; i < s->planes; i++) {
+	s->s[i] = checked_malloc((s->d - s->dl + 1) *
+				 sizeof(struct jbg_ardec_state));
+	s->tx[i] = checked_malloc((s->d - s->dl + 1) * sizeof(int));
+	s->ty[i] = checked_malloc((s->d - s->dl + 1) * sizeof(int));
+	s->reset[i] = checked_malloc((s->d - s->dl + 1) * sizeof(int));
+	s->lntp[i] = checked_malloc((s->d - s->dl + 1) * sizeof(int));
+	s->lhp[s->d    &1][i] = checked_malloc(sizeof(unsigned char) * hsize);
+	s->lhp[(s->d-1)&1][i] = checked_malloc(sizeof(unsigned char) * lsize);
+      }
+    } else {
+      for (i = 0; i < s->planes; i++) {
+	s->s[i] = checked_realloc(s->s[i], (s->d - s->dl + 1) *
+				  sizeof(struct jbg_ardec_state));
+	s->tx[i] = checked_realloc(s->tx[i], (s->d - s->dl + 1) * sizeof(int));
+	s->ty[i] = checked_realloc(s->ty[i], (s->d - s->dl + 1) * sizeof(int));
+	s->reset[i] = checked_realloc(s->reset[i],
+				      (s->d - s->dl +1) * sizeof(int));
+	s->lntp[i] = checked_realloc(s->lntp[i],
+				     (s->d - s->dl +1) * sizeof(int));
+	s->lhp[s->d    &1][i] = checked_realloc(s->lhp[s->d    & 1][i],
+						sizeof(unsigned char) * hsize);
+	s->lhp[(s->d-1)&1][i] = checked_realloc(s->lhp[(s->d-1)&1][i],
+						sizeof(unsigned char) * lsize);
+      }
+    }
+    for (i = 0; i < s->planes; i++)
+      for (j = 0; j <= s->d - s->dl; j++)
+	arith_decode_init(s->s[i] + j, 0);
+    if (s->dl == 0 || (s->options & JBG_DPON && !(s->options & JBG_DPPRIV)))
+      s->dppriv = jbg_dptable;
+    s->comment_skip = 0;
+    s->buf_len = 0;
+    s->x = 0;
+    s->i = 0;
+    s->pseudo = 1;
+    s->at_moves = 0;
+  }
+
+  /* read in DPTABLE */
+  if (s->bie_len < 20 + 1728 && 
+      (s->options & (JBG_DPON | JBG_DPPRIV | JBG_DPLAST)) ==
+      (JBG_DPON | JBG_DPPRIV)) {
+    assert(s->bie_len >= 20);
+    while (s->bie_len < 20 + 1728 && *cnt < len)
+      s->buffer[s->bie_len++ - 20] = data[(*cnt)++];
+    if (s->bie_len < 20 + 1728) 
+      return JBG_EAGAIN;
+    if (!s->dppriv || s->dppriv == jbg_dptable)
+      s->dppriv = checked_malloc(sizeof(char) * 1728);
+    jbg_dppriv2int(s->dppriv, s->buffer);
+  }
+
+  /*
+   * BID processing loop
+   */
+  
+  while (*cnt < len) {
+
+    /* process floating marker segments */
+
+    /* skip COMMENT contents */
+    if (s->comment_skip) {
+      if (s->comment_skip <= len - *cnt) {
+	*cnt += s->comment_skip;
+	s->comment_skip = 0;
+      } else {
+	s->comment_skip -= len - *cnt;
+	*cnt = len;
+      }
+      continue;
+    }
+
+    /* load complete marker segments into s->buffer for processing */
+    if (s->buf_len > 0) {
+      assert(s->buffer[0] == MARKER_ESC);
+      while (s->buf_len < 2 && *cnt < len)
+	s->buffer[s->buf_len++] = data[(*cnt)++];
+      if (s->buf_len < 2) continue;
+      switch (s->buffer[1]) {
+      case MARKER_COMMENT: required_length = 6; break;
+      case MARKER_ATMOVE:  required_length = 8; break;
+      case MARKER_NEWLEN:  required_length = 6; break;
+      case MARKER_ABORT:
+      case MARKER_SDNORM:
+      case MARKER_SDRST:   required_length = 2; break;
+      case MARKER_STUFF:
+	/* forward stuffed 0xff to arithmetic decoder */
+	s->buf_len = 0;
+	decode_pscd(s, s->buffer, 2);
+	continue;
+      default:
+	return JBG_EMARKER;
+      }
+      while (s->buf_len < required_length && *cnt < len)
+	s->buffer[s->buf_len++] = data[(*cnt)++];
+      if (s->buf_len < required_length) continue;
+      /* now the buffer is filled with exactly one marker segment */
+      switch (s->buffer[1]) {
+      case MARKER_COMMENT:
+	s->comment_skip = 
+	  (((long) s->buffer[2] << 24) | ((long) s->buffer[3] << 16) |
+	   ((long) s->buffer[4] <<  8) | (long) s->buffer[5]);
+	break;
+      case MARKER_ATMOVE:
+	if (s->at_moves < JBG_ATMOVES_MAX) {
+	  s->at_line[s->at_moves] =
+	    (((long) s->buffer[2] << 24) | ((long) s->buffer[3] << 16) |
+	     ((long) s->buffer[4] <<  8) | (long) s->buffer[5]);
+	  s->at_tx[s->at_moves] = (signed char) s->buffer[6];
+	  s->at_ty[s->at_moves] = s->buffer[7];
+	  if (s->at_tx[s->at_moves] < - (int) s->mx ||
+	      s->at_tx[s->at_moves] >   (int) s->mx ||
+	      s->at_ty[s->at_moves] >   (int) s->my ||
+	      (s->at_ty[s->at_moves] == 0 && s->at_tx[s->at_moves] < 0))
+	    return JBG_EINVAL;
+	  s->at_moves++;
+	} else
+	  return JBG_EINVAL;
+	break;
+      case MARKER_NEWLEN:
+	y = (((long) s->buffer[2] << 24) | ((long) s->buffer[3] << 16) |
+	     ((long) s->buffer[4] <<  8) | (long) s->buffer[5]);
+	if (y > s->yd || !(s->options & JBG_VLENGTH))
+	  return JBG_EINVAL;
+	s->yd = y;
+	/* calculate again number of stripes that will be required */
+	s->stripes = 
+	  ((s->yd >> s->d) +
+	   ((((1UL << s->d) - 1) & s->xd) != 0) + s->l0 - 1) / s->l0;
+	break;
+      case MARKER_ABORT:
+	return JBG_EABORT;
+	
+      case MARKER_SDNORM:
+      case MARKER_SDRST:
+	/* decode final pixels based on trailing zero bytes */
+	decode_pscd(s, s->buffer, 2);
+
+	arith_decode_init(s->s[s->ii[index[s->order & 7][PLANE]]] + 
+			  s->ii[index[s->order & 7][LAYER]] - s->dl,
+			  s->ii[index[s->order & 7][STRIPE]] != s->stripes - 1
+			  && s->buffer[1] != MARKER_SDRST);
+	
+	s->reset[s->ii[index[s->order & 7][PLANE]]]
+	  [s->ii[index[s->order & 7][LAYER]] - s->dl] =
+	    (s->buffer[1] == MARKER_SDRST);
+	
+	/* prepare for next SDE */
+	s->x = 0;
+	s->i = 0;
+	s->pseudo = 1;
+	s->at_moves = 0;
+	
+	/* increment layer/stripe/plane loop variables */
+	/* start and end value for each loop: */
+	is[index[s->order & 7][STRIPE]] = 0;
+	ie[index[s->order & 7][STRIPE]] = s->stripes - 1;
+	is[index[s->order & 7][LAYER]] = s->dl;
+	ie[index[s->order & 7][LAYER]] = s->d;
+	is[index[s->order & 7][PLANE]] = 0;
+	ie[index[s->order & 7][PLANE]] = s->planes - 1;
+	i = 2;  /* index to innermost loop */
+	do {
+	  j = 0;  /* carry flag */
+	  if (++s->ii[i] > ie[i]) {
+	    /* handling overflow of loop variable */
+	    j = 1;
+	    if (i > 0)
+	      s->ii[i] = is[i];
+	  }
+	} while (--i >= 0 && j);
+
+	s->buf_len = 0;
+	
+	/* check whether this have been all SDEs */
+	if (j) {
+	  s->bie_len = 0;
+	  return JBG_EOK;
+	}
+
+	/* check whether we have to abort because of xmax/ymax */
+	if (index[s->order & 7][LAYER] == 0 && i < 0) {
+	  /* LAYER is the outermost loop and we have just gone to next layer */
+	  if (jbg_ceil_half(s->xd, s->d - s->ii[0]) > s->xmax ||
+	      jbg_ceil_half(s->yd, s->d - s->ii[0]) > s->ymax) {
+	    s->xmax = 4294967295UL;
+	    s->ymax = 4294967295UL;
+	    return JBG_EOK_INTR;
+	  }
+	  if (s->ii[0] > (unsigned long) s->dmax) {
+	    s->dmax = 256;
+	    return JBG_EOK_INTR;
+	  }
+	}
+
+	break;
+      }
+      s->buf_len = 0;
+
+    } else if (data[*cnt] == MARKER_ESC)
+      s->buffer[s->buf_len++] = data[(*cnt)++];
+
+    else {
+
+      /* we have found PSCD bytes */
+      *cnt += decode_pscd(s, data + *cnt, len - *cnt);
+      if (*cnt < len && data[*cnt] != 0xff) {
+#ifdef DEBUG
+	fprintf(stderr, "PSCD was longer than expected, unread bytes "
+		"%02x %02x %02x %02x ...\n", data[*cnt], data[*cnt+1],
+		data[*cnt+2], data[*cnt+3]);
+#endif
+	return JBG_EINVAL;
+      }
+      
+    }
+  }  /* of BID processing loop 'while (*cnt < len) ...' */
+
+  return JBG_EAGAIN;
+}
+
+
+/*
+ * After jbg_dec_in() returned JBG_EOK or JBG_EOK_INTR, you can call this
+ * function in order to find out the width of the image.
+ */
+long jbg_dec_getwidth(const struct jbg_dec_state *s)
+{
+  if (s->d < 0)
+    return -1;
+  if (index[s->order & 7][LAYER] == 0) {
+    if (s->ii[0] < 1)
+      return -1;
+    else
+      return jbg_ceil_half(s->xd, s->d - (s->ii[0] - 1));
+  }
+
+  return s->xd;
+}
+
+
+/*
+ * After jbg_dec_in() returned JBG_EOK or JBG_EOK_INTR, you can call this
+ * function in order to find out the height of the image.
+ */
+long jbg_dec_getheight(const struct jbg_dec_state *s)
+{
+  if (s->d < 0)
+    return -1;
+  if (index[s->order & 7][LAYER] == 0) {
+    if (s->ii[0] < 1)
+      return -1;
+    else
+      return jbg_ceil_half(s->yd, s->d - (s->ii[0] - 1));
+  }
+  
+  return s->yd;
+}
+
+
+/*
+ * After jbg_dec_in() returned JBG_EOK or JBG_EOK_INTR, you can call this
+ * function in order to get a pointer to the image.
+ */
+unsigned char *jbg_dec_getimage(const struct jbg_dec_state *s, int plane)
+{
+  if (s->d < 0)
+    return NULL;
+  if (index[s->order & 7][LAYER] == 0) {
+    if (s->ii[0] < 1)
+      return NULL;
+    else
+      return s->lhp[(s->ii[0] - 1) & 1][plane];
+  }
+  
+  return s->lhp[s->d & 1][plane];
+}
+
+
+/*
+ * After jbg_dec_in() returned JBG_EOK or JBG_EOK_INTR, you can call
+ * this function in order to find out the size in bytes of one
+ * bitplane of the image.
+ */
+long jbg_dec_getsize(const struct jbg_dec_state *s)
+{
+  if (s->d < 0)
+    return -1;
+  if (index[s->order & 7][LAYER] == 0) {
+    if (s->ii[0] < 1)
+      return -1;
+    else
+      return 
+	((jbg_ceil_half(s->xd, s->d - (s->ii[0] - 1)) + 7) / 8) *
+	jbg_ceil_half(s->yd, s->d - (s->ii[0] - 1));
+  }
+  
+  return ((s->xd + 7) / 8) * s->yd;
+}
+
+
+/*
+ * After jbg_dec_in() returned JBG_EOK or JBG_EOK_INTR, you can call
+ * this function in order to find out the size of the image that you
+ * can retrieve with jbg_merge_planes().
+ */
+long jbg_dec_getsize_merged(const struct jbg_dec_state *s)
+{
+  if (s->d < 0)
+    return -1;
+  if (index[s->order & 7][LAYER] == 0) {
+    if (s->ii[0] < 1)
+      return -1;
+    else
+      return 
+	jbg_ceil_half(s->xd, s->d - (s->ii[0] - 1)) *
+	jbg_ceil_half(s->yd, s->d - (s->ii[0] - 1)) *
+	((s->planes + 7) / 8);
+  }
+  
+  return s->xd * s->yd * ((s->planes + 7) / 8);
+}
+
+
+/* 
+ * The destructor function which releases any resources obtained by the
+ * other decoder functions.
+ */
+void jbg_dec_free(struct jbg_dec_state *s)
+{
+  int i;
+
+  if (s->d < 0 || s->s == NULL)
+    return;
+  s->d = -2;
+
+  for (i = 0; i < s->planes; i++) {
+    checked_free(s->s[i]);
+    checked_free(s->tx[i]);
+    checked_free(s->ty[i]);
+    checked_free(s->reset[i]);
+    checked_free(s->lntp[i]);
+    checked_free(s->lhp[0][i]);
+    checked_free(s->lhp[1][i]);
+  }
+  
+  checked_free(s->s);
+  checked_free(s->tx);
+  checked_free(s->ty);
+  checked_free(s->reset);
+  checked_free(s->lntp);
+  checked_free(s->lhp[0]);
+  checked_free(s->lhp[1]);
+
+  s->s = NULL;
+
+  return;
+}
+
+
+/*
+ * Split bigendian integer pixel field into separate bit planes. In the
+ * src array, every pixel is represented by a ((has_planes + 7) / 8) byte
+ * long word, most significant byte first. While has_planes describes
+ * the number of used bits per pixel in the source image, encode_plane
+ * is the number of most significant bits among those that we
+ * actually transfer to dest.
+ */
+void jbg_split_planes(unsigned long x, unsigned long y, int has_planes,
+		      int encode_planes,
+		      const unsigned char *src, unsigned char **dest,
+		      int use_graycode)
+{
+  unsigned bpl = (x + 7) / 8;           /* bytes per line in dest plane */
+  unsigned i, k = 8;
+  int p;
+  unsigned long line;
+  extern void *memset(void *s, int c, size_t n);
+  unsigned prev;     /* previous *src byte shifted by 8 bit to the left */
+  register int bits, msb = has_planes - 1;
+  int bitno;
+
+  /* sanity checks */
+  if (encode_planes > has_planes)
+    encode_planes = has_planes;
+  use_graycode = use_graycode != 0 && encode_planes > 1;
+  
+  for (p = 0; p < encode_planes; p++)
+    memset(dest[p], 0, bpl * y);
+  
+  for (line = 0; line < y; line++) {                 /* lines loop */
+    for (i = 0; i * 8 < x; i++) {                    /* dest bytes loop */
+      for (k = 0; k < 8 && i * 8 + k < x; k++) {     /* pixel loop */
+	prev = 0;
+	for (p = 0; p < encode_planes; p++) {        /* bit planes loop */
+	  /* calculate which bit in *src do we want */
+	  bitno = (msb - p) & 7;
+	  /* put this bit with its left neighbor right adjusted into bits */
+	  bits = (prev | *src) >> bitno;
+	  /* go to next *src byte, but keep old */
+	  if (bitno == 0)
+	    prev = *src++;
+	  /* make space for inserting new bit */
+	  dest[p][bpl * line + i] <<= 1;
+	  /* insert bit, if requested apply Gray encoding */
+	  dest[p][bpl * line + i] |= (bits ^ (use_graycode & (bits>>1))) & 1;
+	  /*
+	   * Theorem: Let b(n),...,b(1),b(0) be the digits of a
+	   * binary word and let g(n),...,g(1),g(0) be the digits of the
+	   * corresponding Gray code word, then g(i) = b(i) xor b(i+1).
+	   */
+	}
+	/* skip unused *src bytes */
+	for (;p < has_planes; p++)
+	  if (((has_planes - 1 - p) & 7) == 0)
+	    src++;
+      }
+    }
+    for (p = 0; p < encode_planes; p++)              /* right padding loop */
+      dest[p][bpl * (line + 1) - 1] <<= 8 - k;
+  }
+  
+  return;
+}
+
+/* 
+ * Merge the separate bit planes decoded by the JBIG decoder into an
+ * integer pixel field. This is essentially the counterpart to
+ * jbg_split_planes(). */
+void jbg_dec_merge_planes(const struct jbg_dec_state *s, int use_graycode,
+			  void (*data_out)(unsigned char *start, size_t len,
+					   void *file), void *file)
+{
+#define BUFLEN 4096
+  int bpp, bpl;
+  unsigned long line;
+  unsigned i, k = 8;
+  int p, q;
+  unsigned char buf[BUFLEN];
+  unsigned char *bp = buf;
+  unsigned char **src;
+  unsigned long x, y;
+  unsigned v;
+
+  /* sanity check */
+  use_graycode = use_graycode != 0;
+  
+  x = jbg_dec_getwidth(s);
+  y = jbg_dec_getheight(s);
+  if (x <= 0 || y <= 0)
+    return;
+  bpp = (s->planes + 7) / 8;   /* bytes per pixel in dest image */
+  bpl = (x + 7) / 8;           /* bytes per line in src plane */
+
+  if (index[s->order & 7][LAYER] == 0)
+    if (s->ii[0] < 1)
+      return;
+    else
+      src = s->lhp[(s->ii[0] - 1) & 1];
+  else
+    src = s->lhp[s->d & 1];
+  
+  for (line = 0; line < y; line++) {                    /* lines loop */
+    for (i = 0; i * 8 < x; i++) {                       /* src bytes loop */
+      for (k = 0; k < 8 && i * 8 + k < x; k++) {        /* pixel loop */
+	for (p = (s->planes-1) & ~7; p >= 0; p -= 8) {  /* dest bytes loop */
+	  v = 0;
+	  for (q = 0; q < 8 && p+q < s->planes; q++)    /* pixel bit loop */
+	    v = (v << 1) |
+	      (((src[p+q][bpl * line + i] >> (7 - k)) & 1) ^
+	       (use_graycode & v));
+	  *bp++ = v;
+	  if (bp - buf == BUFLEN) {
+	    data_out(buf, BUFLEN, file);
+	    bp = buf;
+	  }
+	}
+      }
+    }
+  }
+  
+  if (bp - buf > 0)
+    data_out(buf, bp - buf, file);
+  
+  return;
+}
diff --git a/converter/other/jbig/jbig.doc b/converter/other/jbig/jbig.doc
new file mode 100644
index 00000000..10eeda80
--- /dev/null
+++ b/converter/other/jbig/jbig.doc
@@ -0,0 +1,721 @@
+
+Using the JBIG-KIT library
+--------------------------
+
+Markus Kuhn -- 1998-04-10
+
+
+This text explains how to include the functions provided by the
+JBIG-KIT portable image compression library into your application
+software.
+
+
+1  Introduction to JBIG
+
+Below follows a short introduction into some technical aspects of the
+JBIG standard. More detailed information is provided in the
+"Introduction and overview" section of the JBIG standard. Information
+about how to obtain a copy of the standard is available on the
+Internet from <http://www.itu.ch/> or <http://www.iso.ch/>.
+
+Image data encoded with the JBIG algorithm is separated into planes,
+layers, and stripes. Each plane contains one bit per pixel. The number
+of planes stored in a JBIG data stream is the number of bits per
+pixel. Resolution layers are numbered from 0 to D with 0 being the
+layer with the lowest resolution and D the layer with the highest one.
+Each next higher resolution layer has exactly twice the number of rows
+and columns than the previous one. Layer 0 is encoded independently of
+any other data, all other resolution layers are encoded as only the
+difference between the next lower and the current layer. For
+applications that require very quick access to parts of an image it is
+possible to divide an image into several horizontal stripes. All
+stripes of one resolution layer have equal size except perhaps the
+final one. The number of stripes of an image is equal in all
+resolution layers and in all bit planes.
+
+The compressed data stream specified by the JBIG standard is called a
+bi-level image entity (BIE). A BIE consists of a 20-byte header,
+followed by an optional 1728-byte table (usually not present, except
+in special applications) followed by a sequence of stripe data
+entities (SDE). SDEs are the data blocks of which each encodes the
+content of one single stripe in one plane and resolution layer.
+Between the SDEs, other information blocks (called floating marker
+segments) can also be present, which change certain parameters of the
+algorithm in the middle of an image or contain additional application
+specific information. A BIE looks like this:
+
+
+          +------------------------------------------------+
+          |                                                |
+          |  20-byte header (with image size, #planes,     |
+          |  #layers, stripe size, first layer, options,   |
+          |  SDE ordering, ...)                            |
+          |                                                |
+          +------------------------------------------------+
+          |                                                |
+          |           optional 1728-byte table             |
+          |                                                |
+          +------------------------------------------------+
+          |                                                |
+          |              stripe data entity                |
+          |                                                |
+          +------------------------------------------------+
+          |                                                |
+          |       optional floating marker segments        |
+          |                                                |
+          +------------------------------------------------+
+          |                                                |
+          |              stripe data entity                |
+          |                                                |
+          +------------------------------------------------+
+            ...
+          +------------------------------------------------+
+          |                                                |
+          |              stripe data entity                |
+          |                                                |
+          +------------------------------------------------+
+
+
+One BIE can contain all resolution layers of an image, but it is also
+possible to store various resolution layers in several BIEs. The BIE
+header contains the number of the first and the last resolution layer
+stored in this BIE, as well as the size of the highest resolution
+layer stored in this BIE. Progressive coding is deactivated by simply
+storing the image in one single resolution layer.
+
+Different applications might have different requirements for the order
+in which the SDEs for stripes of various planes and layers are stored
+in the BIE, so all possible sensible orderings are allowed and
+indicated by four bits in the header.
+
+It is possible to use the raw BIE data stream as specified by the JBIG
+standard directly as the format of a file used for storing images.
+This is what the JBIG<->PBM conversion tools that are provided in this
+package as demonstration applications do. However as the BIE format
+has been designed for a large number of very different applications
+and also in order to allow efficient direct processing by special JBIG
+hardware chip implementations, the BIE header contains only the
+minimum amount of information absolutely required by the decompression
+algorithm. A large number of features expected from a good file format
+are missing in the BIE data stream:
+
+  - no "magic code" in the first few bytes to allow identification
+    of the file on a typeless file system as JBIG encoded and to allow
+    automatic distinction from other compression algorithms
+
+  - no standardized way to encode additional information like a textual
+    description, information about the meaning of various bit planes,
+    the physical size and resolution of the document, etc.
+
+  - a checksum that ensures image integrity
+
+  - encryption and signature mechanisms
+
+  - many things more
+
+Raw BIE data streams alone are consequently no suitable format for
+document archiving and exchange. A standard format for this purpose
+would typically combine a BIE representing the image data together
+with an additional header providing auxiliary information into one
+file. Existing established multi-purpose file formats with a rich set
+of auxiliary information attributes like TIFF could be extended easily
+so that they can also contain JBIG compressed data.
+
+On the other hand, in database applications for instance, a BIE might
+be directly stored in a variable length field. Auxiliary information
+on which efficient search operations are required would then be stored
+in other fields of the same record.
+
+
+2  Compressing an image
+
+2.1  Format of the source image
+
+For processing by the library, the image has to be present in memory
+as separate bitmap planes. Each byte of a bitmap contains eight
+pixels, the most significant bit represents the leftmost of these
+pixels. Each line of a bitmap has to be stored in an integral number
+of bytes. If the image width is not an integral multiple of eight,
+then the final byte has to be padded with zero bits.
+
+For example the 23x5 pixels large single plane image:
+
+   .XXXXX..XXX...X...XXX..
+   .....X..X..X..X..X.....
+   .....X..XXX...X..X.XXX.
+   .X...X..X..X..X..X...X.
+   ..XXX...XXX...X...XXX..
+
+is represented by the 15 bytes
+
+   01111100 11100010 00111000
+   00000100 10010010 01000000
+   00000100 11100010 01011100
+   01000100 10010010 01000100
+   00111000 11100010 00111000
+
+or in hexadecimal notation
+
+   7c e2 38 04 92 40 04 e2 5c 44 92 44 38 e2 38
+
+This is the format used in binary PBM files and it can also be be
+handled directly by the Xlib library of the X Window System.
+
+As JBIG can also handle images with several bit planes, the JBIG-KIT
+library functions accept and return always arrays of pointers to
+bitmaps with one pointer per plane.
+
+For single plane images, the standard recommends that a 0 pixel
+represents the background and a 1 pixel represents the foreground
+color of an image, i.e. 0 is white and 1 is black for scanned paper
+documents. For images with several bits per pixel, the JBIG standard
+makes no recommendations about how various colors should be encoded.
+
+For greyscale images, by using a Gray code instead of a simple binary
+weighted representation of the pixel intensity, some increase in
+coding efficiency can be reached.
+
+A Gray code is also a binary representation of integer numbers, but
+has the property that the representations of the integer numbers i and
+(i+1) differ always in exactly one single bit. For example, the
+numbers 0 to 7 can be represented in normal binary code and Gray code
+as in the following table:
+
+                           normal
+              number    binary code     Gray code
+            ---------------------------------------
+                0           000            000
+                1           001            001
+                2           010            011
+                3           011            010
+                4           100            110
+                5           101            111
+                6           110            101
+                7           111            100
+
+The form of Gray code shown above has the property that the second
+half of the code (numbers 4 - 7) is simply the mirrored first half
+(numbers 3 - 0) with the first bit set to one. This way, arbitrarily
+large Gray codes can be generated quickly by mirroring the above
+example and prefixing the first half with zeros and the second half
+with ones as often as required. In greyscale images, it is common
+practise to use the all-0 code for black and the all-1 code for white.
+
+No matter whether a Gray code or a binary code is used for encoding a
+pixel intensity in several bit planes, it always makes sense to store
+the most significant (leftmost) bit in plane 0, which is transmitted
+first. This way, a decoder could increase the precision of the
+displayed pixel intensities while data is still being received and the
+basic structure of the image will become visible as early as possible
+during the transmission.
+
+
+2.2  A simple compression application
+
+In order to use JBIG-KIT in your application, just link libjbig.a to
+your executable (on Unix systems just add -ljbig and -L. to the
+command line options of your compiler, on other systems you will have
+to write a new Makefile anyway), copy the file jbig.h into your source
+directory and put the line
+
+  #include "jbig.h"
+
+into your source code.
+
+The library interface follows the concepts of object-oriented
+programming. You have to declare a variable (object)
+
+  struct jbg_enc_state se;
+
+which contains the current status of an encoder. Then you initialize
+the encoder by calling the constructor function
+
+  void jbg_enc_init(struct jbg_enc_state *s, unsigned long x, unsigned long y,
+                    int pl, unsigned char **p,
+                    void (*data_out)(unsigned char *start, size_t len,
+                                     void *file),
+                    void *file);
+
+The parameters have the following meaning:
+
+  s             A pointer to the jbg_enc_state structure which you want
+                to initialize.
+
+  x             The width of your image.
+
+  y             The height of your image.
+
+  pl            the number of bitmap planes you want to encode.
+
+  p             A pointer to an array of pl pointers, where each is again
+                pointing to the first byte of a bitmap as described in
+                section 2.1.
+
+  data_out      This is a call-back function which will be called during
+                the compression process by libjbig in order to deliver
+                the BIE data to the application. The parameters of the
+                function data_out are a pointer start to the new block of
+                data to be delivered as well as the number len of delivered
+                bytes. The pointer file is transparently delivered to
+                data_out as specified in jbg_enc_init(). Usually, data_out
+                will write the BIE portion to a file, send it to a
+                network connection or append it to some memory buffer.
+
+  file          A pointer parameter which is transparently passed to
+                data_out() and allows data_out() to distinguish by which
+                compression task it has been called in multi-threaded
+                applications.
+
+In the simplest case, the compression is then started by calling the
+function
+
+  void jbg_enc_out(struct jbg_enc_state *s);
+
+which will deliver the complete BIE to data_out(). After this, a call
+to the destructor function
+
+  void jbg_enc_free(struct jbg_enc_state *s);
+
+will release any memory allocated by the previous functions.
+
+
+A minimal example application which sends the BIE of the above example
+bitmap to stdout looks like this:
+
+---------------------------------------------------------------------------
+/* A sample JBIG encoding application */
+
+#include <stdio.h>
+#include "jbig.h"
+
+void output_bie(unsigned char *start, size_t len, void *file)
+{
+  fwrite(start, 1, len, (FILE *) file);
+  
+  return;
+}
+
+int main()
+{
+  unsigned char bitmap[15] = {
+    /* 23 x 5 pixels, "JBIG" */
+    0x7c, 0xe2, 0x38, 0x04, 0x92, 0x40, 0x04, 0xe2,
+    0x5c, 0x44, 0x92, 0x44, 0x38, 0xe2, 0x38
+  };
+  unsigned char *bitmaps[1] = { bitmap };
+  struct jbg_enc_state se;
+  
+  jbg_enc_init(&se, 23, 5, 1, bitmaps, 
+	       output_bie, stdout);              /* initialize encoder */
+  jbg_enc_out(&se);                                    /* encode image */
+  jbg_enc_free(&se);                    /* release allocated resources */
+  
+  return 0;
+}
+---------------------------------------------------------------------------
+
+This software produces a 42 byte long BIE. (JBIG is not very good at
+compressing extremely small images like in this example, because the
+arithmetic encoder requires some startup data in order to generate
+reasonable statistics which influence the compression process and
+because there is some header overhead.)
+
+
+2.3  More about compression
+
+If jbg_enc_out() is called directly after jbg_enc_init(), the
+following default values are used for various compression parameters:
+
+  - Only one single resolution layer is used, i.e. no progressive
+    mode.
+
+  - The number of lines per stripe is selected so that approximately
+    35 stripes per image are used (as recommended in annex C of the
+    standard together with the suggested adaptive template change
+    algorithm). However not less than 2 and not more than 128 lines
+    are used in order to stay within the suggested minimum parameter
+    support range specified in annex A of the standard).
+
+  - All optional parts of the JBIG algorithm are activated (TPBON,
+    TPDON and DPON).
+
+  - The default resolution reduction table and the default deterministic
+    prediction tables are used
+
+  - The maximal vertical offset of the adaptive template pixel is 0
+    and the maximal horizontal offset is 8 (mx = 8, my = 0).
+
+In order to change any of these default parameters, additional
+functions have to be called between jbg_enc_init() and jbg_enc_out().
+
+In order to activate progressive encoding, it is possible to specify
+with 
+
+  void jbg_enc_layers(struct jbg_enc_state *s, int d);
+
+the number d of differential resolution layers which shall be encoded
+in addition to the lowest resolution layer 0. For example, if a 300
+dpi document has to be stored and the lowest resolution layer shall
+have 75 dpi so that a screen previewer can directly decompress only
+the required resolution, then a call
+
+  jbg_enc_layers(&se, 2);
+
+will cause three resolution layers with 75, 150 and 300 dots per inch.
+
+If the application does not know what typical resolutions are used and
+simply wants to ensure that the lowest resolution layer will fit into
+a given maximal window size, then as an alternative, a call to
+
+  int jbg_enc_lrlmax(struct jbg_enc_state *s, unsigned long mwidth,
+                     unsigned long mheight);
+
+will cause the library to automatically determine the suitable number
+of resolutions so that the lowest resolution layer 0 will not be
+larger than mwidth x mheight pixels. E.g. if one wants to ensure that
+systems with a 640 x 480 pixel large screen can decode the required
+resolution directly, then call
+
+  jbg_enc_lrlmax(&se, 640, 480);
+
+The return value is the number of differential layers selected.
+
+After the number of resolution layers has been specified by calls to
+jbg_enc_layers() or jbg_enc_lrlmax(), by default all these layers will
+be written into the BIE. This can be changed with a call to
+
+  int  jbg_enc_lrange(struct jbg_enc_state *s, int dl, int dh);
+
+Parameter dl specifies the lowest resolution layer and dh the highest
+resolution layer that will appear in the BIE. If e.g. only layer 0
+shall be written to the first BIE and layer 1 and 2 shall be written
+to a second one, then before writing the first BIE, one calls
+
+  jbg_enc_lrange(&se, 0, 0);
+
+and before writing the second BIE with jbg_enc_out(), one calls
+
+  jbg_enc_lrange(&se, 1, 2);
+
+If any of the parameters is negative, it will be ignored. The return
+value is the total number of differential layers which will represent
+the input image. This way, jbg_enc_lrange(&se, -1, -1) can be used to
+query the layer of the full image.
+
+A number of other more exotic options of the JBIG algorithm can be
+modified by calling
+
+  void jbg_enc_options(struct jbg_enc_state *s, int order, int options,
+                       long l0, int mx, int my);
+
+before calling jbg_enc_out().
+
+The order parameter can be a combination of the bits JBG_HITOLO,
+JBG_SEQ, JBG_ILEAVE and JBG_SMID and it determines in which order
+the SDEs are stored in the BIE. The bits have the following meaning:
+
+  JBG_HITOLO   Usually, the lower resolution layers are stored before
+               the higher resolution layers, so that a decoder can
+               already start to display a low resolution version of
+               the full image once a prefix of the BIE has been
+               received. When this bit is set however, the BIE will
+               contain the higher layers before the lower layers. This
+               avoids additional buffer memory in the encoder and is
+               intended for applications where the encoder is connected
+               to a database which can easily reorder the SDEs before
+               sending them to a decoder. Warning: JBIG decoders are
+               not expected to support the HITOLO option (e.g. the
+               JBIG-KIT decoder does currently not) so you should
+               normally not use it.
+
+  JBG_SEQ      Usually, at first all stripes of one resolution layer
+               are written to the BIE and then all stripes of the next
+               layer, and so on. When the SEQ bit is set however, then
+               all layers of the first stripe will be written,
+               followed by all layers of the second stripe, etc. This
+               option also should normally never be required and is
+               not supported by the current JBIG-KIT decoder.
+
+  JBG_SMID     In case there exist several bit planes, then the order of
+               the stripes is determined by 3 loops over all stripes,
+               all planes and all layers. When SMID is set, the loop
+               over all stripes is the middle loop.
+
+  JBG_ILEAVE   If this bit is set, then at first all layers of one
+               plane are written before the encoder starts with the next
+               plane.
+
+The above description might be somewhat confusing, but the following
+table (see also Table 11 in ITU-T T.82) makes clear how the three bits
+JBG_SEQ, JBIG_ILEAVE and JBG_SMID influence the ordering of the loops
+over all stripes, planes and layers:
+
+
+                                                 Loops:
+    JBG_SEQ   JBG_ILEAVE   JBG_SMID   |  Outer   Middle    Inner
+  ------------------------------------+---------------------------
+       0           0           0      |    p        d        s
+       0           1           0      |    d        p        s
+       0           1           1      |    d        s        p
+       1           0           0      |    s        p        d
+       1           0           1      |    p        s        d
+       1           1           0      |    s        d        p
+
+                                       p: plane, s: stripe, d: layer
+
+
+By default, the order combination JBG_ILEAVE | JBG_SMID is used.
+
+The options value can contain the following bits, which activate
+some of the optional algorithms defined by JBIG:
+
+  JBG_LRLTWO     Normally, in the lowest resolution layer, pixels
+                 from three lines around the next pixel are used
+                 in order to determine the context in which the next
+                 pixel is encoded. Some people in the JBIG committee
+                 seem to have argued that using only 2 lines will
+                 make software implementations a little bit faster,
+                 however others have argued that using only two lines
+                 will decrease compression efficiency by around 5%.
+                 As you might expect from a committee, now both
+                 alternatives are allowed and if JBG_LRLTWO is set,
+                 the slightly faster but 5% less well compressing two
+                 line alternative is selected. God bless the committees.
+                 Although probably nobody will ever need this option,
+                 it has been implemented in JBIG-KIT and is off by
+                 default.
+
+  JBG_TPDON      This activates the "typical prediction" algorithm
+                 for differential layers which avoids that large
+                 areas of equal color have to be encoded at all.
+                 This is on by default and there is no good reason to
+                 switch it off except for debugging or preparing data
+                 for cheap JBIG hardware which does not support this
+                 option.
+
+  JBG_TPBON      Like JBG_TPDON this activates the "typical prediction"
+                 algorithm in the lowest resolution layer. Also activated
+                 by default.
+
+  JBG_DPON       This bit activates for the differential resolution
+                 layers the "deterministic prediction" algorithm,
+                 which avoids that higher resolution layer pixels are
+                 encoded when their value can already be determined
+                 with the knowledge of the neighbor pixels, the
+                 corresponding lower resolution pixels and the
+                 resolution reduction algorithm. This is also
+                 activated by default and one only might perhaps want
+                 to deactivate it if the default resolution reduction
+                 algorithm is replaced by a new one.
+
+  JBG_DELAY_AT   Use a slightly less efficient algorithm to determine
+                 when an adaptive template change is necessary. With
+                 this bit set, the encoder output is compatible to the
+                 conformance test examples in cause 7.2 of ITU-T T.82.
+                 Then all adaptive template changes are delayed until
+                 the first line of the next stripe. This option is by
+                 default deactivated and only required for passing a
+                 special compatibility test suite.
+
+In addition, parameter l0 in jbg_enc_options() allows you to specify
+the number of lines per stripe in resolution layer 0. The parameters
+mx and my change the maximal offset allowed for the adaptive template
+pixel. The JBIG-KIT implementation allows currently a maximal mx value
+of 23 in the encoder and 32 in the decoder. Parameter my is at the
+moment ignored and always set to 0. As the standard requires of all
+decoder implementations only a maximum supported mx = 16 and my = 0,
+higher values should normally be avoided in order to guarantee
+interoperability. Default is mx = 8 and my = 0. If any of the
+parameters order, options, l0, mx or my is negative, then this value
+is ignored and the current value stays unmodified.
+
+The resolution reduction and deterministic prediction tables can also
+be replaced. However as these options are anyway only for experts,
+please have a look at the source code of jbg_enc_out() and the struct
+members dppriv and res_tab of struct jbg_enc_state for the details of
+how to do this in case you really need it. The functions
+jbg_int2dppriv and jbg_dppriv2int are provided in order to convert the
+DPTABLE data from the format used in the standard into the more
+efficient format used internally by JBIG-KIT.
+
+If you want to encode a greyscale image, you can use the library
+function
+
+  void jbg_split_planes(unsigned long x, unsigned long y, int has_planes,
+                        int encode_planes,
+                        const unsigned char *src, unsigned char **dest,
+                        int use_graycode);
+
+It separates an image in which each pixel is represented by one or
+more bytes into separate bitplanes. The dest array of pointers to
+these bitplanes can then be handed over to jbg_enc_init(). The
+variables x and y specify the width and height of the image in pixels,
+and has_planes specifies how many bits per pixel are used. As each
+pixel is represented by an integral number of consecutive bytes, of
+which each contains up to eight bits, the total length of the input
+image array src[] will therefore be x * y * ((has_planes + 7) / 8)
+bytes. The pixels are stored as usually in English reading order, and
+for each pixel the integer value is stored with the most significant
+byte coming first (Bigendian). This is exactly the format used in raw
+PGM files. In encode_planes, the number of bitplanes that shall be
+extracted can be specified. This allows for instance to extract only
+the most significant 8 bits of a 12-bit image, where each pixel is
+represented by two bytes, by specifying has_planes = 12 and
+encode_planes = 8. If use_graycode is zero, then the binary code of
+the pixel integer values will be used instead of the Gray code. Plane
+0 contains always the most significant bit.
+
+
+3  Decompressing an image
+
+Like with the compression functions, if you want to use the JBIG-KIT
+library, you have to put the line
+
+  #include "jbig.h"
+
+into your source code and link your executable with libjbig.a.
+
+The state of a JBIG decoder is stored completely in a struct and you
+will have to define a variable like
+
+  struct jbg_dec_state sd;
+
+which is initialized by a call to
+
+  void jbg_dec_init(struct jbg_dec_state *s);
+
+After this, you can directly start to pass data from the BIE to the decoder
+by calling the function
+
+  int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
+                 size_t *cnt);
+
+The pointer data points to the first byte of a data block with length
+len, which contains bytes from a BIE. It is not necessary to pass a
+whole BIE at once to jbg_dec_in(), it can arrive fragmented in any way
+by calling jbg_dec_in() several times. It is also possible to send
+several BIEs concatenated to jbg_dec_in(), however these then have to
+fit together. If you send several BIEs to the decoder, the lowest
+resolution layer in each following BIE has to be the highest
+resolution layer in the previous BIE plus one and the image sizes and
+number of planes also have to fit together, otherwise jbg_dec_in()
+will return the error JBG_ENOCONT after the header of the new BIE has
+been received completely.
+
+If pointer cnt is not NULL, then the number of bytes actually read
+from the data block is stored there. In case the data block did not
+contain the end of the BIE, then the value JBG_EAGAIN will be returned
+and *cnt equals len.
+
+Once the end of a BIE has been reached, the return value of
+jbg_dec_in() will be JBG_EOK. After this has happened, the functions
+and macros
+
+  long jbg_dec_getwidth(struct jbg_dec_state *s);
+  long jbg_dec_getheight(struct jbg_dec_state *s);
+  int jbg_dec_getplanes(struct jbg_dec_state *s);
+  unsigned char *jbg_dec_getimage(struct jbg_dec_state *s, int plane);
+  long jbg_dec_getsize(struct jbg_dec_state *s);
+
+can be used to query the dimensions of the now completely decoded
+image and to get a pointer to all bitmap planes. The bitmaps are
+stored as described in section 2.1. The function jbg_dec_getsize()
+calculates the number of bytes which one bitmap requires.
+
+The function
+
+  void jbg_dec_merge_planes(const struct jbg_dec_state *s, int use_graycode,
+                            void (*data_out)(unsigned char *start, size_t len,
+                                             void *file), void *file);
+
+allows you to merge the bitplanes that can be accessed individually
+with jbg_dec_getimage() into an array with one or more bytes per pixel
+(i.e., the format provided to jbg_split_planes()). If use_graycode is
+zero, then a binary encoding will be used. The output array will be
+delivered via the callback function data_out, exactly in the same way
+in which the encoder provides the BIE. The function
+
+  long jbg_dec_getsize_merged(const struct jbg_dec_state *s);
+
+determines how long the data array delivered by jbg_dec_merge_planes()
+is going to be.
+
+Before calling jbg_dec_in() the first time, it is possible to specify with
+a call to
+
+  void jbg_dec_maxsize(struct jbg_dec_state *s, unsigned long xmax,
+                       unsigned long ymax);
+
+an abort criterion for progressively encoded images. For instance if an
+application will display a whole document on a screen which is 1024 x
+768 pixels large, then this application should call
+
+  jbg_dec_maxsize(&sd, 1024, 768);
+
+before the decoding process starts. If the image has been encoded in
+progressive mode (i.e. with several resolution layers), then the
+decoder will stop with a return value JBG_EOK_INTR after the largest
+resolution layer that is still smaller than 1024 x 768. However this
+is no guarantee that the image which can then be read out using
+jbg_dec_getimage(), etc. is really not larger than the specified
+maximal size. The application will have to check the size of the
+image, because the decoder does not automatically apply a resolution
+reduction if no suitable resolution layer is available in the BIE.
+
+If jbg_dec_in() returned JBG_EOK_INTR or JBG_EOK, then it is possible
+to continue calling jbg_dec_in() with the remaining data in order to
+either decode the remaining resolution layers of the current BIE or in
+order to add another BIE with additional resolution layers. In both
+cases, after jbg_dec_in() returned JBG_EOK_INTR or JBG_EOK, *cnt is
+probably not equal to len and the remainder of the data block which
+has not yet been processed by the decoder has to be delivered to
+jbg_dec_in() again.
+
+If any other return value than JBG_EOK, JBG_EOK_INTR or JBG_EAGAIN
+has been returned by jbg_dec_in(), then an error has occurred and
+
+  void jbg_dec_free(struct jbg_dec_state *s);
+
+should be called in order to release any allocated memory. The
+destructor jbg_dec_free() should of course also be called, once the
+decoded bitmap returned by jbg_dec_getimage() is no longer required
+and the memory can be released.
+
+The function
+
+  const char *jbg_strerror(int errnum, int language);
+
+returns a pointer to a short single line test message which explains
+the return value of jbg_dec_in(). This message can be used in order to
+provide the user a brief informative message about what when wrong
+while decompressing the JBIG image. The error messages are available
+in several languages and in several character sets. Currently
+supported are the following values for the language parameter:
+
+  JBG_EN              English messages in ASCII
+  JBG_DE_8859_1       German messages in ISO 8859-1 Latin 1 character set
+  JBG_DE_UTF_8        German messages in ISO 10646/Unicode UTF-8 encoding
+
+
+The current implementation of the JBIG-KIT decoder has the following
+limitations:
+
+  - The maximal horizontal offset mx of the adaptive template pixel
+    must not be larger than 32 and the maximal vertical offset must
+    be zero.
+
+  - HITOLO and SEQ bits must not be set in the order value.
+
+A more detailed description of the JBIG-KIT implementation is
+
+  Markus Kuhn: Effiziente Kompression von bi-level Bilddaten durch
+  kontextsensitive arithmetische Codierung. Studienarbeit, Lehrstuhl
+  für Betriebssysteme, IMMD IV, Universität Erlangen-Nürnberg,
+  Erlangen, July 1995. (German, 62 pages)
+  <http://www.cl.cam.ac.uk/~mgk25/kuhn-sta.pdf>
+
+Please quote the above if you use JBIG-KIT in your research project.
+
+*** Happy compressing ***
+
+[end]
diff --git a/converter/other/jbig/jbig.h b/converter/other/jbig/jbig.h
new file mode 100644
index 00000000..dd9a76f3
--- /dev/null
+++ b/converter/other/jbig/jbig.h
@@ -0,0 +1,267 @@
+/*
+ *  Header file for the portable free JBIG compression library
+ *
+ *  Markus Kuhn -- mkuhn@acm.org
+ *
+ *  $Id: jbig.h,v 1.9 1999-11-16 15:58:45+00 mgk25 Rel $
+ */
+
+#ifndef JBG_H
+#define JBG_H
+
+#include <stddef.h>
+
+/*
+ * JBIG-KIT version number
+ */
+
+#define JBG_VERSION    "1.1"
+
+/*
+ * Buffer block for SDEs which are temporarily stored by encoder
+ */
+
+#define JBG_BUFSIZE 4000
+
+struct jbg_buf {
+  unsigned char d[JBG_BUFSIZE];              /* one block of a buffer list */
+  int len;                             /* length of the data in this block */
+  struct jbg_buf *next;                           /* pointer to next block */
+  struct jbg_buf *previous;                   /* pointer to previous block *
+					       * (unused in freelist)      */
+  struct jbg_buf *last;     /* only used in list head: final block of list */
+  struct jbg_buf **free_list;   /* pointer to pointer to head of free list */
+};
+
+/*
+ * Maximum number of allowed ATMOVEs per stripe
+ */
+
+#define JBG_ATMOVES_MAX  64
+
+/*
+ * Option and order flags
+ */
+
+#define JBG_HITOLO     0x08
+#define JBG_SEQ        0x04
+#define JBG_ILEAVE     0x02
+#define JBG_SMID       0x01
+
+#define JBG_LRLTWO     0x40
+#define JBG_VLENGTH    0x20
+#define JBG_TPDON      0x10
+#define JBG_TPBON      0x08
+#define JBG_DPON       0x04
+#define JBG_DPPRIV     0x02
+#define JBG_DPLAST     0x01
+
+#define JBG_DELAY_AT   0x100  /* delay ATMOVE until the first line of the next
+			       * stripe. Option available for compatibility
+			       * with conformance test example in clause 7.2.*/
+
+
+/*
+ * Possible error code return values
+ */
+
+#define JBG_EOK        0
+#define JBG_EOK_INTR   1
+#define JBG_EAGAIN     2
+#define JBG_ENOMEM     3
+#define JBG_EABORT     4
+#define JBG_EMARKER    5
+#define JBG_ENOCONT    6
+#define JBG_EINVAL     7
+#define JBG_EIMPL      8
+
+/*
+ * Language code for error message strings (based on ISO 639 2-letter
+ * standard language name abbreviations).
+ */ 
+
+#define JBG_EN         0        /* English */
+#define JBG_DE_8859_1  1        /* German in ISO Latin 1 character set */
+#define JBG_DE_UTF_8   2        /* German in Unicode UTF-8 encoding */
+
+/*
+ * Status description of an arithmetic encoder
+ */
+
+struct jbg_arenc_state {
+  unsigned char st[4096];    /* probability status for contexts, MSB = MPS */
+  unsigned long c;                /* C register, base of coding intervall, *
+                                   * layout as in Table 23                 */
+  unsigned long a;      /* A register, normalized size of coding intervall */
+  long sc;        /* counter for buffered 0xff values which might overflow */
+  int ct;  /* bit shift counter, determines when next byte will be written */
+  int buffer;                /* buffer for most recent output byte != 0xff */
+  void (*byte_out)(int, void *); /* function which receives all PSCD bytes */
+  void *file;                              /* parameter passed to byte_out */
+};
+
+
+/*
+ * Status description of an arithmetic decoder
+ */
+
+struct jbg_ardec_state {
+  unsigned char st[4096];    /* probability status for contexts, MSB = MPS */
+  unsigned long c;                /* C register, base of coding intervall, *
+                                   * layout as in Table 25                 */
+  unsigned long a;      /* A register, normalized size of coding intervall */
+  int ct;     /* bit shift counter, determines when next byte will be read */
+  unsigned char *pscd_ptr;               /* pointer to next PSCD data byte */
+  unsigned char *pscd_end;                   /* pointer to byte after PSCD */
+  enum {
+    JBG_OK,                        /* symbol has been successfully decoded */
+    JBG_READY,             /* no more bytes of this PSCD required, marker  *
+		            * encountered, probably more symbols available */
+    JBG_MORE,          /* more PSCD data bytes required to decode a symbol */
+    JBG_MARKER   /* more PSCD data bytes required, ignored final 0xff byte */
+  } result;                              /* result of previous decode call */
+  int startup;                            /* controls initial fill of s->c */
+};
+
+#ifdef TEST_CODEC
+void arith_encode_init(struct jbg_arenc_state *s, int reuse_st);
+void arith_encode_flush(struct jbg_arenc_state *s);
+void arith_encode(struct jbg_arenc_state *s, int cx, int pix);
+void arith_decode_init(struct jbg_ardec_state *s, int reuse_st);
+int arith_decode(struct jbg_ardec_state *s, int cx);
+#endif
+
+ 
+/*
+ * Status of a JBIG encoder
+ */
+
+struct jbg_enc_state {
+  int d;                            /* resolution layer of the input image */
+  unsigned long xd, yd;    /* size of the input image (resolution layer d) */
+  int planes;                         /* number of different bitmap planes */
+  int dl;                       /* lowest resolution layer in the next BIE */
+  int dh;                      /* highest resolution layer in the next BIE */
+  unsigned long l0;                /* number of lines per stripe at lowest *
+                                    * resolution layer 0                   */
+  unsigned long stripes;    /* number of stripes required  (determ. by l0) */
+  unsigned char **lhp[2];    /* pointers to lower/higher resolution images */
+  int *highres;                 /* index [plane] of highres image in lhp[] */
+  int order;                                    /* SDE ordering parameters */
+  int options;                                      /* encoding parameters */
+  unsigned mx, my;                           /* maximum ATMOVE window size */
+  int *tx;       /* array [plane] with x-offset of adaptive template pixel */
+  char *dppriv;         /* optional private deterministic prediction table */
+  char *res_tab;           /* table for the resolution reduction algorithm */
+  struct jbg_buf ****sde;      /* array [stripe][layer][plane] pointers to *
+				* buffers for stored SDEs                  */
+  struct jbg_arenc_state *s;  /* array [planes] for arithm. encoder status */
+  struct jbg_buf *free_list; /* list of currently unused SDE block buffers */
+  void (*data_out)(unsigned char *start, size_t len, void *file);
+                                                    /* data write callback */
+  void *file;                            /* parameter passed to data_out() */
+  char *tp;    /* buffer for temp. values used by diff. typical prediction */
+};
+
+
+/*
+ * Status of a JBIG decoder
+ */
+
+struct jbg_dec_state {
+  /* data from BIH */
+  int d;                             /* resolution layer of the full image */
+  int dl;                            /* first resolution layer in this BIE */
+  unsigned long xd, yd;     /* size of the full image (resolution layer d) */
+  int planes;                         /* number of different bitmap planes */
+  unsigned long l0;                /* number of lines per stripe at lowest *
+				    * resolution layer 0                   */
+  unsigned long stripes;    /* number of stripes required  (determ. by l0) */
+  int order;                                    /* SDE ordering parameters */
+  int options;                                      /* encoding parameters */
+  int mx, my;                                /* maximum ATMOVE window size */
+  char *dppriv;         /* optional private deterministic prediction table */
+
+  /* loop variables */
+  unsigned long ii[3];  /* current stripe, layer, plane (outer loop first) */
+
+  /*
+   * Pointers to array [planes] of lower/higher resolution images.
+   * lhp[d & 1] contains image of layer d. 
+   */
+  unsigned char **lhp[2];
+
+  /* status information */
+  int **tx, **ty;   /* array [plane][layer-dl] with x,y-offset of AT pixel */
+  struct jbg_ardec_state **s;    /* array [plane][layer-dl] for arithmetic *
+				  * decoder status */
+  int **reset;     /* array [plane][layer-dl] remembers if previous stripe *
+		    * in that plane/resolution ended with SDRST.           */
+  unsigned long bie_len;                    /* number of bytes read so far */
+  unsigned char buffer[20]; /* used to store BIH or marker segments fragm. */
+  int buf_len;                                /* number of bytes in buffer */
+  unsigned long comment_skip;      /* remaining bytes of a COMMENT segment */
+  unsigned long x;              /* x position of next pixel in current SDE */
+  unsigned long i; /* line in current SDE (first line of each stripe is 0) */ 
+  int at_moves;                /* number of AT moves in the current stripe */
+  unsigned long at_line[JBG_ATMOVES_MAX];           /* lines at which an   *
+					             * AT move will happen */
+  int at_tx[JBG_ATMOVES_MAX], at_ty[JBG_ATMOVES_MAX]; /* ATMOVE offsets in *
+						       * current stripe    */
+  unsigned long line_h1, line_h2, line_h3;     /* variables of decode_pscd */
+  unsigned long line_l1, line_l2, line_l3;
+  int pseudo;         /* flag for TPBON/TPDON:  next pixel is pseudo pixel */
+  int **lntp;        /* flag [plane][layer-dl] for TP: line is not typical */
+
+  unsigned long xmax, ymax;         /* if possible abort before image gets *
+				     * larger than this size */
+  int dmax;                                      /* abort after this layer */
+};
+
+
+/* some macros (too trivial for a function) */
+
+#define jbg_dec_getplanes(s)     ((s)->planes)
+
+
+/* function prototypes */
+
+void jbg_enc_init(struct jbg_enc_state *s, unsigned long x, unsigned long y,
+		  int planes, unsigned char **p,
+		  void (*data_out)(unsigned char *start, size_t len,
+				   void *file),
+		  void *file);
+int jbg_enc_lrlmax(struct jbg_enc_state *s, unsigned long mwidth,
+		   unsigned long mheight);
+void jbg_enc_layers(struct jbg_enc_state *s, int d);
+int  jbg_enc_lrange(struct jbg_enc_state *s, int dl, int dh);
+void jbg_enc_options(struct jbg_enc_state *s, int order, int options,
+		     long l0, int mx, int my);
+void jbg_enc_out(struct jbg_enc_state *s);
+void jbg_enc_free(struct jbg_enc_state *s);
+
+void jbg_dec_init(struct jbg_dec_state *s);
+void jbg_dec_maxsize(struct jbg_dec_state *s, unsigned long xmax,
+		     unsigned long ymax);
+int  jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
+		size_t *cnt);
+long jbg_dec_getwidth(const struct jbg_dec_state *s);
+long jbg_dec_getheight(const struct jbg_dec_state *s);
+unsigned char *jbg_dec_getimage(const struct jbg_dec_state *s, int plane);
+long jbg_dec_getsize(const struct jbg_dec_state *s);
+void jbg_dec_merge_planes(const struct jbg_dec_state *s, int use_graycode,
+			  void (*data_out)(unsigned char *start, size_t len,
+					   void *file), void *file);
+long jbg_dec_getsize_merged(const struct jbg_dec_state *s);
+void jbg_dec_free(struct jbg_dec_state *s);
+
+const char *jbg_strerror(int errnum, int language);
+void jbg_int2dppriv(unsigned char *dptable, const char *internal);
+void jbg_dppriv2int(char *internal, const unsigned char *dptable);
+unsigned long jbg_ceil_half(unsigned long x, int n);
+void jbg_split_planes(unsigned long x, unsigned long y, int has_planes,
+		      int encode_planes,
+		      const unsigned char *src, unsigned char **dest,
+		      int use_graycode);
+
+#endif /* JBG_H */
diff --git a/converter/other/jbig/jbig_tab.c b/converter/other/jbig/jbig_tab.c
new file mode 100644
index 00000000..55183503
--- /dev/null
+++ b/converter/other/jbig/jbig_tab.c
@@ -0,0 +1,428 @@
+/*
+ *  Probability estimation tables for the arithmetic encoder/decoder
+ *  given by ITU T.82 Table 24.
+ *
+ *  $Id: jbig_tab.c,v 1.6 1998-04-05 18:36:19+01 mgk25 Rel $
+ */
+
+short jbg_lsz[113] = {
+  0x5a1d, 0x2586, 0x1114, 0x080b, 0x03d8, 0x01da, 0x00e5, 0x006f,
+  0x0036, 0x001a, 0x000d, 0x0006, 0x0003, 0x0001, 0x5a7f, 0x3f25,
+  0x2cf2, 0x207c, 0x17b9, 0x1182, 0x0cef, 0x09a1, 0x072f, 0x055c,
+  0x0406, 0x0303, 0x0240, 0x01b1, 0x0144, 0x00f5, 0x00b7, 0x008a,
+  0x0068, 0x004e, 0x003b, 0x002c, 0x5ae1, 0x484c, 0x3a0d, 0x2ef1,
+  0x261f, 0x1f33, 0x19a8, 0x1518, 0x1177, 0x0e74, 0x0bfb, 0x09f8,
+  0x0861, 0x0706, 0x05cd, 0x04de, 0x040f, 0x0363, 0x02d4, 0x025c,
+  0x01f8, 0x01a4, 0x0160, 0x0125, 0x00f6, 0x00cb, 0x00ab, 0x008f,
+  0x5b12, 0x4d04, 0x412c, 0x37d8, 0x2fe8, 0x293c, 0x2379, 0x1edf,
+  0x1aa9, 0x174e, 0x1424, 0x119c, 0x0f6b, 0x0d51, 0x0bb6, 0x0a40,
+  0x5832, 0x4d1c, 0x438e, 0x3bdd, 0x34ee, 0x2eae, 0x299a, 0x2516,
+  0x5570, 0x4ca9, 0x44d9, 0x3e22, 0x3824, 0x32b4, 0x2e17, 0x56a8,
+  0x4f46, 0x47e5, 0x41cf, 0x3c3d, 0x375e, 0x5231, 0x4c0f, 0x4639,
+  0x415e, 0x5627, 0x50e7, 0x4b85, 0x5597, 0x504f, 0x5a10, 0x5522,
+  0x59eb
+};
+
+unsigned char jbg_nmps[113] = {
+    1,   2,   3,   4,   5,   6,   7,   8,
+    9,  10,  11,  12,  13,  13,  15,  16,
+   17,  18,  19,  20,  21,  22,  23,  24,
+   25,  26,  27,  28,  29,  30,  31,  32,
+   33,  34,  35,   9,  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,  32,
+   65,  66,  67,  68,  69,  70,  71,  72,
+   73,  74,  75,  76,  77,  78,  79,  48,
+   81,  82,  83,  84,  85,  86,  87,  71,
+   89,  90,  91,  92,  93,  94,  86,  96,
+   97,  98,  99, 100,  93, 102, 103, 104,
+   99, 106, 107, 103, 109, 107, 111, 109,
+  111
+};
+
+/*
+ * least significant 7 bits (mask 0x7f) of jbg_nlps[] contain NLPS value,
+ * most significant bit (mask 0x80) contains SWTCH bit
+ */
+unsigned char jbg_nlps[113] = {
+  129,  14,  16,  18,  20,  23,  25,  28,
+   30,  33,  35,   9,  10,  12, 143,  36,
+   38,  39,  40,  42,  43,  45,  46,  48,
+   49,  51,  52,  54,  56,  57,  59,  60,
+   62,  63,  32,  33, 165,  64,  65,  67,
+   68,  69,  70,  72,  73,  74,  75,  77,
+   78,  79,  48,  50,  50,  51,  52,  53,
+   54,  55,  56,  57,  58,  59,  61,  61,
+  193,  80,  81,  82,  83,  84,  86,  87,
+   87,  72,  72,  74,  74,  75,  77,  77,
+  208,  88,  89,  90,  91,  92,  93,  86,
+  216,  95,  96,  97,  99,  99,  93, 223,
+  101, 102, 103, 104,  99, 105, 106, 107,
+  103, 233, 108, 109, 110, 111, 238, 112,
+  240
+};
+
+/*
+ * Resolution reduction table given by ITU-T T.82 Table 17
+ */
+
+char jbg_resred[4096] = {
+  0,0,0,1,0,0,0,1,0,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,
+  0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,1,0,0,1,1,1,0,1,1,
+  0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,0,1,1,1,0,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,1,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,
+  1,0,0,1,0,0,1,1,0,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,0,0,0,1,0,0,0,1,0,0,1,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,1,1,1,1,1,0,1,1,1,0,
+  0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,
+  0,0,0,1,0,0,0,1,0,1,1,0,1,0,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,
+  1,1,1,0,1,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,
+  1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,
+  0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,1,1,0,1,0,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,0,1,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,1,0,0,1,1,1,1,1,1,0,1,1,1,1,0,1,1,
+  1,0,0,1,0,0,1,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,
+  0,0,1,0,1,1,1,1,0,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,0,
+  0,0,0,0,1,0,0,1,0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,1,1,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,
+  0,0,1,0,0,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,1,
+  0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,1,0,0,1,0,0,1,1,
+  0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,1,0,1,0,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,1,1,0,1,1,1,
+  0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,1,0,1,0,1,1,0,0,0,1,0,0,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,1,1,0,0,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,
+  0,0,1,0,0,1,1,1,0,0,0,0,1,0,0,1,0,0,0,1,1,1,1,0,1,0,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1,1,0,
+  0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1,1,1,0,1,1,1,
+  0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,1,1,0,1,0,0,0,1,1,0,1,0,0,0,0,1,1,1,1,0,0,1,1,1,0,1,1,0,0,1,1,
+  0,0,0,0,0,0,0,0,1,1,0,1,0,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,1,0,0,0,1,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,
+  0,0,0,1,0,0,1,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,
+  0,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
+  0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,1,1,0,1,1,
+  0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,0,1,0,1,0,1,1,0,1,1,1,0,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,1,0,1,0,1,1,0,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,1,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,
+  1,0,1,0,1,0,0,1,1,0,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,0,1,1,0,1,1,1,
+  0,0,1,0,0,0,0,1,0,0,0,0,0,0,1,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,0,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,1,0,1,0,1,0,1,1,0,1,0,1,0,0,0,1,1,1,1,1,1,1,1,1,
+  1,1,1,0,1,0,0,0,1,1,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,
+  1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,1,1,0,1,1,
+  0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0,0,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,0,1,0,1,0,0,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,1,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,1,0,1,0,0,0,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,
+  1,0,0,0,1,0,0,0,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,1,1,1,0,1,1,0,
+  0,0,1,1,1,1,1,1,0,0,0,0,1,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,1,0,0,0,0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,0,1,0,1,1,0,0,1,0,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,
+  0,0,1,0,1,0,1,1,0,1,1,1,1,1,1,1,0,0,1,1,1,0,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,1,
+  0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,
+  0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,1,0,1,0,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,
+  0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,1,
+  0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,
+  1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,
+  0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,
+  0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,1,
+  0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,0,1,1,0,0,0,1,0,1,1,1,0,1,1,1
+};
+
+/*
+ * Deterministic prediction tables given by ITU-T T.82 tables
+ * 19 to 22. The table below is organized differently, the
+ * index bits are permutated for higher efficiency.
+ */
+
+char jbg_dptable[256 + 512 + 2048 + 4096] = {
+  /* phase 0: offset=0 */
+  0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,0,2,2,2,2,2,2,2,
+  0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,0,2,0,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  /* phase 1: offset=256 */
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,0,2,0,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,2,1,2,1,2,2,2,2,1,1,1,1,2,0,2,0,2,2,2,2,0,2,0,2,2,2,2,2,
+  0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,2,2,0,2,2,2,2,2,2,2,
+  0,2,0,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,0,0,2,2,2,2,2,0,0,2,2,2,2,2,
+  0,2,2,2,2,1,2,1,2,2,2,2,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,
+  1,2,1,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,1,1,2,2,2,2,2,0,2,2,2,2,2,2,
+  2,2,2,2,2,0,2,0,2,2,2,2,0,0,0,0,0,2,0,2,2,2,2,2,0,2,2,2,2,2,2,2,
+  0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,2,0,2,0,2,2,2,2,2,
+  2,2,2,2,2,1,1,1,2,2,2,2,1,1,1,1,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,0,1,2,0,2,0,2,2,2,2,2,0,2,0,2,2,2,2,1,
+  0,2,0,2,2,1,2,1,2,2,2,2,1,1,1,1,0,0,0,0,2,2,2,2,0,2,0,2,2,2,2,1,
+  2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,0,0,0,2,2,2,2,2,
+  2,2,2,2,2,1,2,1,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,1,2,1,2,2,2,2,1,
+  2,2,2,2,2,2,2,2,0,2,0,2,2,1,2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,2,2,2,
+  /* phase 2: offset=768 */
+  2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2,2,2,2,1,1,1,1,
+  0,2,2,2,2,1,2,1,2,2,2,2,1,2,1,2,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,
+  2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,1,2,1,2,2,2,2,2,1,1,1,
+  2,0,2,2,2,1,2,1,0,2,2,2,1,2,1,2,2,2,2,0,2,2,2,2,0,2,0,2,2,2,2,2,
+  0,2,0,0,1,1,1,1,2,2,2,2,1,1,1,1,0,2,0,2,1,1,1,1,2,2,2,2,1,1,1,1,
+  2,2,0,2,2,2,1,2,2,2,2,2,1,2,1,2,2,2,0,2,2,1,2,1,0,2,0,2,1,1,1,1,
+  2,0,0,2,2,2,2,2,0,2,0,2,2,0,2,0,2,0,2,0,2,2,2,1,2,2,0,2,1,1,2,1,
+  2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2,2,2,2,1,1,1,1,
+  0,0,0,0,2,2,2,2,0,0,0,0,2,2,2,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,0,2,2,2,2,1,0,2,2,2,1,1,1,1,2,0,2,2,2,2,2,2,0,2,0,2,2,1,2,1,
+  2,0,2,0,2,2,2,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,
+  0,2,2,2,1,2,1,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,
+  2,2,0,2,2,2,2,2,2,2,2,2,2,2,0,2,2,0,0,2,2,1,2,1,0,2,2,2,1,1,1,1,
+  2,2,2,0,2,2,2,2,2,2,0,2,2,0,2,0,2,1,2,2,2,2,2,2,1,2,1,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,1,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,1,1,1,2,2,2,2,1,1,1,1,
+  2,2,2,1,2,2,2,2,2,2,1,2,0,0,0,0,2,2,0,2,2,1,2,2,2,2,2,2,1,1,1,1,
+  2,0,0,0,2,2,2,2,0,2,2,2,2,2,2,0,2,2,2,0,2,2,2,2,2,0,0,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,0,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,1,
+  0,2,0,2,2,1,1,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,
+  2,0,2,0,2,1,2,1,0,2,0,2,2,2,1,2,2,0,2,0,2,2,2,2,0,2,0,2,2,2,1,2,
+  2,2,2,0,2,2,2,2,2,2,0,2,2,2,2,2,2,2,1,2,2,2,2,2,2,0,1,2,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  0,2,2,2,1,2,1,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,
+  2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,1,2,1,0,2,2,2,1,1,1,1,
+  2,0,2,0,2,1,2,2,0,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,1,2,2,
+  2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,0,2,2,2,2,0,0,0,0,2,1,2,1,
+  2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,0,0,2,2,2,1,2,2,2,
+  0,0,2,0,2,2,2,2,0,2,0,2,2,0,2,0,1,1,1,2,2,2,2,2,2,2,2,2,2,1,1,1,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,1,
+  2,2,0,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2,2,2,2,1,1,1,1,
+  0,2,2,2,1,2,1,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,
+  2,0,0,2,2,2,2,2,0,2,0,2,2,2,2,2,1,0,1,2,2,2,2,1,0,2,2,2,1,1,1,1,
+  2,2,2,2,2,2,2,2,2,2,0,2,2,0,2,0,2,1,2,2,2,2,2,2,2,2,0,2,2,1,2,2,
+  0,2,0,0,1,1,1,1,0,2,2,2,1,1,1,1,2,2,2,2,2,2,2,2,2,0,2,2,1,2,1,1,
+  2,2,0,2,2,1,2,2,2,2,2,2,1,2,2,2,2,0,2,2,2,2,2,2,0,2,0,2,1,2,1,1,
+  2,0,2,0,2,2,2,2,0,2,0,2,2,1,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,2,0,2,0,2,2,2,2,0,0,0,0,2,2,2,2,2,1,1,2,2,2,2,2,1,2,2,2,
+  2,0,2,2,2,1,2,1,0,2,2,2,2,2,1,2,2,0,2,0,2,2,2,2,0,2,0,2,2,1,2,2,
+  0,2,0,0,2,2,2,2,1,2,2,2,2,2,2,0,2,1,2,2,2,2,2,2,1,2,2,2,2,2,2,2,
+  0,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,1,0,2,2,
+  0,0,0,2,2,1,1,1,2,2,2,2,1,2,2,2,2,0,2,0,2,2,2,1,2,2,2,2,1,2,1,2,
+  0,0,0,0,2,2,2,2,2,2,0,2,2,1,2,2,2,1,2,1,2,2,2,2,1,2,1,2,0,2,2,2,
+  2,0,2,0,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  0,2,2,2,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,1,2,2,2,2,2,0,2,2,1,2,2,0,0,0,2,2,2,2,2,1,2,2,0,2,2,2,1,2,1,2,
+  2,0,2,0,2,2,2,2,0,2,0,2,2,1,2,2,0,2,0,0,2,2,2,2,2,2,2,2,2,1,2,2,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,1,
+  1,2,0,2,2,1,2,1,2,2,2,2,1,2,2,2,2,0,2,0,2,2,2,2,2,0,2,2,1,1,1,1,
+  0,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,1,2,1,
+  2,2,0,0,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,
+  2,2,2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,
+  2,0,2,0,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,1,0,2,0,2,2,2,1,2,
+  2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
+  2,0,2,0,2,2,2,2,2,0,2,0,2,2,2,2,2,0,2,0,2,2,2,2,0,0,0,0,2,1,2,1,
+  2,2,2,2,2,1,2,1,0,2,0,2,2,2,2,2,2,0,2,0,2,2,2,2,0,2,0,2,2,2,2,1,
+  2,0,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,0,
+  2,0,2,0,2,2,2,1,2,2,2,0,2,2,2,1,2,0,2,0,2,2,2,2,0,0,0,2,2,2,2,1,
+  2,0,2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,
+  /* phase 3: offset=2816 */
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,1,2,1,2,0,2,0,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,0,2,2,2,1,2,0,2,2,2,1,2,2,2,2,0,2,0,2,1,2,1,0,0,0,0,1,1,1,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,0,2,2,2,1,2,
+  2,2,2,1,2,2,2,0,1,1,1,1,0,0,0,0,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,0,0,0,0,1,1,1,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,0,0,0,0,1,1,1,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,2,
+  2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,2,0,2,0,2,1,2,1,
+  2,0,0,0,2,1,1,1,0,0,0,0,1,1,1,1,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,
+  2,0,2,2,2,1,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,0,0,2,0,1,1,2,1,
+  2,2,2,0,2,2,2,1,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,
+  0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,2,0,2,0,2,1,2,1,0,0,0,0,1,1,1,1,
+  2,0,0,2,2,1,1,2,2,2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,2,1,1,1,2,0,0,0,
+  2,1,2,1,2,0,2,0,1,2,1,2,0,2,0,2,2,2,2,0,2,2,2,1,2,0,2,0,2,1,2,1,
+  2,0,2,0,2,1,2,1,0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,
+  2,2,2,2,2,2,2,2,2,0,0,0,2,1,1,1,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,
+  2,0,0,0,2,1,1,1,0,0,0,0,1,1,1,1,2,0,2,0,2,1,2,1,0,0,2,0,1,1,2,1,
+  2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,0,0,0,2,1,1,1,
+  2,2,2,1,2,2,2,0,2,1,1,1,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,1,2,1,2,0,2,0,2,
+  2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,1,1,1,0,0,0,0,1,1,1,1,
+  2,0,2,2,2,1,2,2,0,0,2,0,1,1,2,1,2,1,2,1,2,0,2,0,2,2,2,2,2,2,2,2,
+  2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,0,0,0,0,1,1,1,1,
+  2,0,0,0,2,1,1,1,0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,2,1,0,2,2,0,1,2,
+  2,2,2,1,2,2,2,0,2,1,1,1,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,
+  2,1,2,1,2,0,2,0,1,2,1,1,0,2,0,0,0,0,2,1,1,1,2,0,0,0,0,0,1,1,1,1,
+  2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,0,2,1,2,1,2,0,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,0,2,2,2,1,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,0,2,2,2,1,2,2,2,0,0,2,2,1,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,
+  2,0,2,0,2,1,2,1,0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,0,0,0,0,1,1,1,1,
+  2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,0,0,0,2,1,1,1,
+  2,2,2,0,2,2,2,1,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
+  2,0,2,2,2,1,2,2,2,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,1,2,1,2,0,2,0,1,2,1,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,1,2,1,2,0,2,0,1,2,1,1,0,2,0,0,2,0,2,2,2,1,2,2,0,2,1,2,1,2,0,2,
+  2,2,2,1,2,2,2,0,2,2,1,2,2,2,0,2,2,1,2,2,2,0,2,2,2,2,0,2,2,2,1,2,
+  0,0,2,0,1,1,2,1,0,0,1,0,1,1,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,0,2,2,2,1,1,2,2,2,0,2,2,2,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,
+  2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,0,0,2,2,1,1,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,
+  2,0,0,0,2,1,1,1,0,0,0,0,1,1,1,1,2,2,2,1,2,2,2,0,2,1,2,1,2,0,2,0,
+  2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,0,2,0,0,1,2,1,1,2,0,0,0,2,1,1,1,
+  2,2,2,2,2,2,2,2,2,1,1,1,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,0,2,2,2,1,2,2,0,2,2,2,1,2,2,1,2,1,2,0,2,0,2,0,2,2,2,1,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,1,1,1,2,0,0,0,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,0,0,1,2,1,1,
+  2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,0,2,2,2,1,2,2,2,
+  2,1,2,1,2,0,2,0,2,1,2,2,2,0,2,2,2,2,2,0,2,2,2,1,2,0,2,0,2,1,2,1,
+  2,0,2,0,2,1,2,1,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
+  2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,0,1,0,0,1,0,1,1,
+  2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,1,2,2,1,0,2,0,2,2,2,1,2,2,2,
+  2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,0,2,2,2,1,2,2,0,2,2,2,1,2,
+  2,0,2,0,2,1,2,1,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
+  0,2,0,0,1,2,1,1,2,0,0,0,2,1,1,1,2,2,2,2,2,2,2,2,1,0,1,2,0,1,0,2,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,1,2,2,2,0,2,2,1,1,2,2,0,0,2,2,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,1,2,1,2,0,2,0,2,1,2,2,2,0,2,2,2,0,2,2,2,1,2,2,0,2,2,2,1,2,2,2,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,1,2,2,2,0,2,2,2,
+  2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,2,
+  0,0,0,0,1,1,1,1,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,2,2,0,2,2,2,1,2,
+  2,0,2,0,2,1,2,1,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,1,2,2,2,0,1,1,2,1,0,0,2,0,2,0,2,2,2,1,2,2,0,2,2,2,1,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,0,2,2,2,1,2,
+  2,0,2,0,2,1,2,1,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,
+  0,2,0,0,1,2,1,1,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,0,0,0,2,1,1,1,2,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,0,0,2,1,1,1,2,0,0,2,2,2,1,2,2,2,
+  2,1,2,1,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,0,1,2,1,1,
+  0,0,2,2,1,1,2,2,0,2,1,2,1,2,0,2,2,1,2,1,2,0,2,0,1,2,1,2,0,2,0,2,
+  2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,2,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,
+  2,2,0,0,2,2,1,1,2,2,0,0,2,2,1,1,2,2,2,2,2,2,2,2,2,2,0,0,2,2,1,1,
+  2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,0,0,1,2,1,1,
+  2,2,2,0,2,2,2,1,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,1,1,1,2,0,0,0,2,
+  2,2,2,2,2,2,2,2,1,1,1,2,0,0,0,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,
+  2,0,2,0,2,1,2,1,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,0,0,0,2,1,1,1,
+  2,0,2,2,2,1,2,2,0,2,2,2,1,2,2,2,2,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,
+  2,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,0,2,0,2,1,2,1,2,1,2,0,2,0,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,0,2,0,2,1,2,1,1,2,1,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,2,2,1,2,1,2,0,2,0,2,2,2,2,2,2,2,2,
+  2,0,2,1,2,1,2,0,0,2,1,2,1,2,0,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,0,2,0,2,1,2,1,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,1,2,1,2,0,2,0,1,1,1,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,0,2,0,2,1,2,1,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+};
diff --git a/converter/other/jbig/jbigtopnm.c b/converter/other/jbig/jbigtopnm.c
new file mode 100644
index 00000000..7a6e95c1
--- /dev/null
+++ b/converter/other/jbig/jbigtopnm.c
@@ -0,0 +1,286 @@
+/*
+    jbigtopnm - JBIG to PNM converter
+  
+    This program was derived from jbgtopbm.c in Markus Kuhn's
+    JBIG-KIT package by Bryan Henderson on 2000.05.11
+
+    The main difference is that this version uses the Netpbm libraries.
+  
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <jbig.h>
+#include "pnm.h"
+
+#define BUFSIZE 8192
+
+
+static void
+collect_image (unsigned char *data, size_t len, void *image) {
+    static int cursor = 0;
+    int i;
+
+    for (i = 0; i < len; i++) {
+        ((unsigned char *)image)[cursor++] = data[i];
+    }
+}
+
+
+
+static void 
+write_pnm (FILE *fout, const unsigned char * const image, const int bpp,
+           const int rows, const int cols, const int maxval,
+           const int format) {
+
+    int row;
+    xel *pnm_row;
+
+    pnm_writepnminit(fout, cols, rows, maxval, format, 0);
+
+    pnm_row = pnm_allocrow(cols);
+
+    for (row = 0; row < rows; row++) {
+        int col;
+        for (col = 0; col < cols; col++) {
+            int j;
+            for (j = 0; j < bpp; j++)
+                PNM_ASSIGN1(pnm_row[col], 
+                            image[(((row*cols)+col) * bpp) + j]);
+        }
+        pnm_writepnmrow(fout, pnm_row, cols, maxval, format, 0);
+    }
+    
+    pnm_freerow(pnm_row);
+}
+
+
+
+static void
+write_raw_pbm(FILE * const fout, 
+              const unsigned char * const binary_image,
+              int                   const cols,
+              int                   const rows) { 
+
+    unsigned int const bytes_per_row = pbm_packed_bytes(cols);
+
+    int row;
+
+    pbm_writepbminit(fout, cols, rows, 0);
+
+    for (row = 0; row < rows; ++row)
+        pbm_writepbmrow_packed(fout, &binary_image[row*bytes_per_row], cols, 
+                               0);
+}
+
+
+
+/*
+ *
+ */
+static void 
+diagnose_bie(FILE *f)
+{
+  unsigned char bih[20];
+  int len;
+  unsigned long xd, yd, l0;
+
+  len = fread(bih, 1, 20, f);
+  if (len < 20) {
+    printf("Input file is %d < 20 bytes long and does therefore not "
+	   "contain an intact BIE header!\n", len);
+    return;
+  }
+
+  printf("Decomposition of BIH:\n\n  DL = %d\n  D  = %d\n  P  = %d\n"
+	 "  -  = %d\n  XD = %lu\n  YD = %lu\n  L0 = %lu\n  MX = %d\n"
+	 "  MY = %d\n",
+	 bih[0], bih[1], bih[2], bih[3],
+	 xd = ((unsigned long) bih[ 4] << 24) | ((unsigned long)bih[ 5] << 16)|
+	 ((unsigned long) bih[ 6] <<  8) | ((unsigned long) bih[ 7]),
+	 yd = ((unsigned long) bih[ 8] << 24) | ((unsigned long)bih[ 9] << 16)|
+	 ((unsigned long) bih[10] <<  8) | ((unsigned long) bih[11]),
+	 l0 = ((unsigned long) bih[12] << 24) | ((unsigned long)bih[13] << 16)|
+	 ((unsigned long) bih[14] <<  8) | ((unsigned long) bih[15]),
+	 bih[16], bih[17]);
+  printf("  order   = %d %s%s%s%s%s\n", bih[18],
+	 bih[18] & JBG_HITOLO ? " HITOLO" : "",
+	 bih[18] & JBG_SEQ ? " SEQ" : "",
+	 bih[18] & JBG_ILEAVE ? " ILEAVE" : "",
+	 bih[18] & JBG_SMID ? " SMID" : "",
+	 bih[18] & 0xf0 ? " other" : "");
+  printf("  options = %d %s%s%s%s%s%s%s%s\n", bih[19],
+	 bih[19] & JBG_LRLTWO ? " LRLTWO" : "",
+	 bih[19] & JBG_VLENGTH ? " VLENGTH" : "",
+	 bih[19] & JBG_TPDON ? " TPDON" : "",
+	 bih[19] & JBG_TPBON ? " TPBON" : "",
+	 bih[19] & JBG_DPON ? " DPON" : "",
+	 bih[19] & JBG_DPPRIV ? " DPPRIV" : "",
+	 bih[19] & JBG_DPLAST ? " DPLAST" : "",
+	 bih[19] & 0x80 ? " other" : "");
+  printf("\n  %lu stripes, %d layers, %d planes\n\n",
+	 ((yd >> bih[1]) +  ((((1UL << bih[1]) - 1) & xd) != 0) + l0 - 1) / l0,
+	 bih[1] - bih[0], bih[2]);
+
+  return;
+}
+
+
+int main (int argc, char **argv)
+{
+    FILE *fin = stdin, *fout = stdout;
+    const char *fnin = "<stdin>", *fnout = "<stdout>";
+    int i, j, result;
+    int all_args = 0, files = 0;
+    struct jbg_dec_state s;
+    char *buffer;
+    unsigned char *p;
+    size_t len, cnt;
+    unsigned long xmax = 4294967295UL, ymax = 4294967295UL;
+    int plane = -1, use_graycode = 1, diagnose = 0;
+
+    pnm_init(&argc, argv);
+
+    buffer = malloc(BUFSIZE);
+    if (!buffer)
+        pm_error("Sorry, not enough memory available!");
+
+    /* parse command line arguments */
+    for (i = 1; i < argc; i++) {
+        if (!all_args && argv[i][0] == '-') {
+            if (argv[i][1] == '\0' && files == 0)
+                ++files;
+            else {
+                for (j = 1; j > 0 && argv[i][j]; j++) {
+                    switch(tolower(argv[i][j])) {
+                    case '-' :
+                        all_args = 1;
+                        break;
+                    case 'b':
+                        use_graycode = 0;
+                        break;
+                    case 'd':
+                        diagnose = 1;
+                        break;
+                    case 'x':
+                        if (++i >= argc)
+                            pm_error("-x needs a value");
+                        xmax = atol(argv[i]);
+                        j = -1;
+                        break;
+                    case 'y':
+                        if (++i >= argc)
+                            pm_error("-y needs a value");
+                        ymax = atol(argv[i]);
+                        j = -1;
+                        break;
+                    case 'p':
+                        if (++i >= argc)
+                            pm_error("-p needs a value");
+                        plane = atoi(argv[i]);
+                        j = -1;
+                        break;
+                    default:
+                        pm_error("Unrecognized option: %c", argv[i][j]);
+                    }
+                }
+            }
+        } else {
+            switch (files++) {
+            case 0:
+                if (argv[i][0] != '-' || argv[i][1] != '\0') {
+                    fnin = argv[i];
+                    fin = fopen(fnin, "rb");
+                    if (!fin)
+                        pm_error("Can't open input file '%s'", fnin);
+                }
+                if (diagnose) {
+                    diagnose_bie(fin);
+                    exit(0);
+                }
+                break;
+            case 1:
+                fnout = argv[i];
+                fout = fopen(fnout, "wb");
+                if (!fout)
+                    pm_error("Can't open output file '%s'", fnout);
+                break;
+            default:
+                pm_error("Too many non-option arguments");
+            }
+        }
+    }
+
+    /* send input file to decoder */
+    jbg_dec_init(&s);
+    jbg_dec_maxsize(&s, xmax, ymax);
+    result = JBG_EAGAIN;
+    do {
+        len = fread(buffer, 1, BUFSIZE, fin);
+        if (!len) break;
+        cnt = 0;
+        p = (unsigned char *) buffer;
+        while (len > 0 && (result == JBG_EAGAIN || result == JBG_EOK)) {
+            result = jbg_dec_in(&s, p, len, &cnt);
+            p += cnt;
+            len -= cnt;
+        }
+    } while (result == JBG_EAGAIN || result == JBG_EOK);
+    if (ferror(fin)) 
+        pm_error("Problem while reading input file '%s", fnin);
+    if (result != JBG_EOK && result != JBG_EOK_INTR) 
+        pm_error("Problem with input file '%s': %s\n", 
+                 fnin, jbg_strerror(result, JBG_EN));
+    if (plane >= 0 && jbg_dec_getplanes(&s) <= plane) 
+        pm_error("Image has only %d planes!\n", jbg_dec_getplanes(&s));
+
+    {
+        /* Write it out */
+
+        int rows, cols;
+        int maxval;
+        int bpp;
+        int plane_to_write;
+
+        cols = jbg_dec_getwidth(&s);
+        rows = jbg_dec_getheight(&s);
+        maxval = pm_bitstomaxval(jbg_dec_getplanes(&s));
+        bpp = (jbg_dec_getplanes(&s)+7)/8;
+
+        if (jbg_dec_getplanes(&s) == 1) 
+            plane_to_write = 0;
+        else 
+            plane_to_write = plane;
+
+        if (plane_to_write >= 0) {
+            /* Write just one plane */
+            unsigned char * binary_image;
+
+            pm_message("WRITING PBM FILE");
+
+            binary_image=jbg_dec_getimage(&s, plane_to_write);
+            write_raw_pbm(fout, binary_image, cols, rows);
+        } else {
+            unsigned char *image;
+            pm_message("WRITING PGM FILE");
+
+            /* Write out all the planes */
+            /* What jbig.doc doesn't tell you is that jbg_dec_merge_planes
+               delivers the image in chunks, in consecutive calls to 
+               the data-out callback function.  And a row can span two
+               chunks.
+            */
+            image = malloc(cols*rows*bpp);
+            jbg_dec_merge_planes(&s, use_graycode, collect_image, image);
+            write_pnm(fout, image, bpp, rows, cols, maxval, PGM_TYPE);
+            free(image);
+        }
+    }
+  
+    pm_close(fout);
+
+    jbg_dec_free(&s);
+
+    return 0;
+}
diff --git a/converter/other/jbig/pnmtojbig.c b/converter/other/jbig/pnmtojbig.c
new file mode 100644
index 00000000..9dbef3fa
--- /dev/null
+++ b/converter/other/jbig/pnmtojbig.c
@@ -0,0 +1,463 @@
+/*
+    pnmtojbig - PNM to JBIG converter
+  
+    This program was derived from pbmtojbg.c in Markus Kuhn's
+    JBIG-KIT package by Bryan Henderson on 2000.05.11
+
+    The main difference is that this version uses the Netpbm libraries.
+
+ */
+
+/*
+     The JBIG standard doesn't say which end of the scale is white and
+     which end is black in a BIE.  It has a recommendation in terms of
+     foreground and background (a concept which does not exist in the
+     Netpbm formats) for single-plane images, and is silent for
+     multi-plane images.
+
+     Kuhn's implementation of the JBIG standard says if the BIE has a
+     single plane, then in that plane a zero bit means white and a one
+     bit means black.  But if it has multiple planes, a composite zero
+     value means black and a composite maximal value means white.
+
+     Actually, Kuhn's pbmtojbg doesn't even implement this, but rather
+     bases the distinction on whether the input file was PBM or PGM.
+     This means that if you convert a PGM file with maxval 1 to a JBIG
+     file and then back, the result (which is a PBM file) is the
+     inverse of what you started with.  Same if the PGM file has
+     maxval > 1 but you use a -t option to write only one plane.  We
+     assume this is just a bug in pbmtojpg and that hardly anybody does
+     this.  So we adopt the implementation described above.
+
+     This means that after jbg_split_planes() hands us a set of bitmap
+     planes, if there is only one of them, we have to invert all the
+     bits in it.
+
+*/
+  
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <jbig.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "pnm.h"
+
+static unsigned long total_length = 0;  
+  /* used for determining output file length */
+
+/*
+ * malloc() with exception handler
+ */
+static void
+*checkedmalloc(size_t n)
+{
+  void *p;
+  
+  if ((p = malloc(n)) == NULL) {
+    fprintf(stderr, "Sorry, not enough memory available!\n");
+    exit(1);
+  }
+  
+  return p;
+}
+
+
+/*
+ * Callback procedure which is used by JBIG encoder to deliver the
+ * encoded data. It simply sends the bytes to the output file.
+ */
+static void data_out(unsigned char *start, size_t len, void *file)
+{
+  fwrite(start, len, 1, (FILE *) file);
+  total_length += len;
+  return;
+}
+
+
+
+static void
+readPbm(FILE *            const fin,
+        unsigned int      const cols,
+        unsigned int      const rows,
+        unsigned char *** const bitmapP) {
+
+    unsigned int const bytes_per_line = pbm_packed_bytes(cols);
+
+    unsigned char ** bitmap;
+
+    /* Read the input image into bitmap[] */
+    /* Shortcut for PBM */
+    int row;
+    bitmap = (unsigned char **) checkedmalloc(sizeof(unsigned char *));
+    bitmap[0] = (unsigned char *) checkedmalloc(bytes_per_line * rows);
+    
+    for (row = 0; row < rows; row++)
+        pbm_readpbmrow_packed(fin, &bitmap[0][row*bytes_per_line],
+                              cols, RPBM_FORMAT);
+
+    *bitmapP = bitmap;
+} 
+
+
+
+static void
+readImage(FILE * const fin,
+          unsigned int     const cols,
+          unsigned int     const rows,
+          xelval           const maxval,
+          int              const format,
+          unsigned int     const bpp,
+          unsigned char ** const imageP) {
+/*----------------------------------------------------------------------------
+  Read the input image and put it into *imageP;
+
+  Although the PBM case is separated, this logic works also for
+  PBM, bpp=1.
+-----------------------------------------------------------------------------*/
+    unsigned char *image;
+        /* This is a representation of the entire image with 'bpp' bytes per
+           pixel.  The 'bpp' bytes for each pixel are arranged MSB first
+           and its numerical value is the value from the PNM input.
+           The pixels are laid out in row-major format in this rectangle.
+           
+           The point of this data structure is it is what jbg_split_planes()
+           wants for input.
+        */
+    xel* pnm_row;
+    unsigned int row;
+
+    pnm_row = pnm_allocrow(cols);  /* row buffer */
+    MALLOCARRAY_NOFAIL(image, cols * rows * bpp);
+    
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        pnm_readpnmrow(fin, pnm_row, cols, maxval, format);
+        for (col = 0; col < cols; col++) {
+            unsigned int j;
+            /* Move each byte of the sample into image[], MSB first */
+            for (j = 0; j < bpp; ++j)
+                image[(((row*cols)+col) * bpp) + j] = (unsigned char)
+                    PNM_GET1(pnm_row[col]) >> ((bpp-1-j) * 8);
+        }
+    }
+    pnm_freerow(pnm_row);
+    *imageP = image;
+}
+      
+
+
+static void
+convertImageToBitmap(unsigned char *   const image,
+                     unsigned char *** const bitmapP,
+                     unsigned int      const encode_planes,
+                     unsigned int      const bytes_per_line,
+                     unsigned int      const lines) {
+    
+    /* Convert image[] into bitmap[]  */
+    
+    unsigned char ** bitmap;
+    unsigned int i;
+    
+    MALLOCARRAY_NOFAIL(bitmap, encode_planes);
+    for (i = 0; i < encode_planes; ++i)
+        MALLOCARRAY_NOFAIL(bitmap[i], bytes_per_line * lines);
+
+    *bitmapP = bitmap;
+}
+
+
+
+static void
+readPnm(FILE *            const fin,
+        unsigned int      const cols,
+        unsigned int      const rows,
+        xelval            const maxval,
+        int               const format,
+        unsigned int      const bpp,
+        unsigned int      const planes,
+        unsigned int      const encode_planes,
+        bool              const use_graycode,
+        unsigned char *** const bitmapP) {
+
+    unsigned int const bytes_per_line = pbm_packed_bytes(cols);
+
+    unsigned char * image;
+    unsigned char ** bitmap;
+
+    readImage(fin, cols, rows, maxval, format, bpp, &image);
+
+    convertImageToBitmap(image, &bitmap, encode_planes, bytes_per_line, rows);
+
+    jbg_split_planes(cols, rows, planes, encode_planes, image, bitmap,
+                     use_graycode);
+    free(image);
+    
+    /* Invert the image if it is just one plane.  See top of this file
+       for an explanation why.  Due to the separate handling of PBM,
+       this is for exceptional PGM files.  
+    */
+
+    if (encode_planes == 1) {
+        unsigned int row;
+        for (row = 0; row < rows; ++row) {
+            unsigned int i;
+            for (i = 0; i < bytes_per_line; i++)
+                bitmap[0][(row*bytes_per_line) + i] ^= 0xff;
+
+            if (cols % 8 > 0) {   
+                bitmap[0][ (row+1)*bytes_per_line  -1] >>= 8-cols%8;
+                bitmap[0][ (row+1)*bytes_per_line  -1] <<= 8-cols%8;
+            }
+        }
+    }
+    *bitmapP = bitmap;
+}
+
+
+
+int
+main(int argc, char **argv) {
+    FILE *fin = stdin, *fout = stdout;
+    const char *fnin = "<stdin>", *fnout = "<stdout>";
+    int i;
+    int all_args = 0, files = 0;
+    int bpp, planes, encode_planes = -1;
+    int cols, rows;
+    xelval maxval;
+    int format;
+    unsigned char **bitmap;
+    /* This is an array of the planes of the image.  Each plane is a
+       two-dimensional array of pixels laid out in row-major format.
+       format with each pixel being one bit.  A byte in the array 
+       contains 8 pixels left to right, msb to lsb.
+    */
+
+    struct jbg_enc_state s;
+    int verbose = 0, delay_at = 0, use_graycode = 1;
+    long mwidth = 640, mheight = 480;
+    int dl = -1, dh = -1, d = -1, l0 = -1, mx = -1;
+    int options = JBG_TPDON | JBG_TPBON | JBG_DPON;
+    int order = JBG_ILEAVE | JBG_SMID;
+
+    pbm_init(&argc, argv);
+
+    /* parse command line arguments */
+    for (i = 1; i < argc; ++i) {
+        int j;
+        if (!all_args && argv[i][0] == '-') {
+            if (argv[i][1] == '\0' && files == 0)
+                ++files;
+            else {
+                for (j = 1; j > 0 && argv[i][j]; j++) {
+                    switch(tolower(argv[i][j])) {
+                    case '-' :
+                        all_args = 1;
+                        break;
+                    case 'v':
+                        verbose = 1;
+                        break;
+                    case 'b':
+                        use_graycode = 0;
+                        break;
+                    case 'c':
+                        delay_at = 1;
+                        break;
+                    case 'x':
+                        if (++i >= argc)
+                            pm_error("-x needs a value");
+                        j = -1;
+                        mwidth = atol(argv[i]);
+                        break;
+                    case 'y':
+                        if (++i >= argc)
+                            pm_error("-y needsa  value");
+                        j = -1;
+                        mheight = atol(argv[i]);
+                        break;
+                    case 'o':
+                        if (++i >= argc)
+                            pm_error("-o needs a value");
+                        j = -1;
+                        order = atoi(argv[i]);
+                        break;
+                    case 'p':
+                        if (++i >= argc)
+                            pm_error("-p needs a value");
+                        j = -1;
+                        options = atoi(argv[i]);
+                        break;
+                    case 'l':
+                        if (++i >= argc)
+                            pm_error("-l needs a value");
+                        j = -1;
+                        dl = atoi(argv[i]);
+                        break;
+                    case 'h':
+                        if (++i >= argc)
+                            pm_error("-h needs a value");
+                        j = -1;
+                        dh = atoi(argv[i]);
+                        break;
+                    case 'q':
+                        d = 0;
+                        break;
+                    case 'd':
+                        if (++i >= argc)
+                            pm_error("-d needs a value");
+                        j = -1;
+                        d = atoi(argv[i]);
+                        break;
+                    case 's':
+                        if (++i >= argc)
+                            pm_error("-s needs a value");
+                        j = -1;
+                        l0 = atoi(argv[i]);
+                        break;
+                    case 't':
+                        if (++i >= argc)
+                            pm_error("-t needs a value");
+                        j = -1;
+                        encode_planes = atoi(argv[i]);
+                        break;
+                    case 'm':
+                        if (++i >= argc)
+                            pm_error("-m needs a value");
+                        j = -1;
+                        mx = atoi(argv[i]);
+                        break;
+                    default:
+                        pm_error("Unrecognized option: %c", argv[i][j]);
+                    }
+                }
+            }
+        } else {
+            switch (files++) {
+            case 0:
+                if (argv[i][0] != '-' || argv[i][1] != '\0') {
+                    fnin = argv[i];
+                    fin = fopen(fnin, "rb");
+                    if (!fin) {
+                        fprintf(stderr, "Can't open input file '%s", fnin);
+                        perror("'");
+                        exit(1);
+                    }
+                }
+                break;
+            case 1:
+                fnout = argv[i];
+                fout = fopen(fnout, "wb");
+                if (!fout) {
+                    fprintf(stderr, "Can't open input file '%s", fnout);
+                    perror("'");
+                    exit(1);
+                }
+                break;
+            default:
+                pm_error("too many non-option arguments");
+            }
+        }
+    }
+
+    pnm_readpnminit(fin, &cols, &rows, &maxval, &format);
+
+    if (PNM_FORMAT_TYPE(format) != PGM_TYPE &&
+        PNM_FORMAT_TYPE(format) != PBM_TYPE)
+        pm_error("This program accepts PBM and PGM input only.  "
+                 "Try Ppmtopgm.");
+
+    planes = pm_maxvaltobits(maxval);
+
+    /* In a JBIG file, maxvals are determined only by the number of planes,
+       so must be a power of 2 minus 1
+    */
+  
+    if ((1UL << planes)-1 != maxval) 
+        pm_error("Input image has unacceptable maxval: %d.  JBIG files must "
+                 "have a maxval which is a power of 2 minus 1.  Use "
+                 "Ppmdepth to adjust the image's maxval", maxval);
+
+    bpp = (planes + 7) / 8;
+
+    if (encode_planes < 0 || encode_planes > planes)
+        encode_planes = planes;
+
+    if (bpp == 1 && PNM_FORMAT_TYPE(format) == PBM_TYPE)
+        readPbm(fin, cols, rows, &bitmap);
+    else
+        readPnm(fin, cols, rows, maxval, format, bpp, 
+                planes, encode_planes, use_graycode, 
+                &bitmap);
+
+    /* Apply JBIG algorithm and write BIE to output file */
+
+  /* initialize parameter struct for JBIG encoder*/
+    jbg_enc_init(&s, cols, rows, encode_planes, bitmap, data_out, fout);
+
+    /* Select number of resolution layers either directly or based
+   * on a given maximum size for the lowest resolution layer */
+    if (d >= 0)
+        jbg_enc_layers(&s, d);
+    else
+        jbg_enc_lrlmax(&s, mwidth, mheight);
+
+  /* Specify a few other options (each is ignored if negative) */
+    if (delay_at)
+        options |= JBG_DELAY_AT;
+    jbg_enc_lrange(&s, dl, dh);
+    jbg_enc_options(&s, order, options, l0, mx, -1);
+
+  /* now encode everything and send it to data_out() */
+    jbg_enc_out(&s);
+
+    /* give encoder a chance to free its temporary data structures */
+    jbg_enc_free(&s);
+
+    /* check for file errors and close fout */
+    if (ferror(fout) || fclose(fout)) {
+        fprintf(stderr, "Problem while writing output file '%s", fnout);
+        perror("'");
+        exit(1);
+    }
+
+    /* In case the user wants to know all the gory details ... */
+    if (verbose) {
+        fprintf(stderr, "Information about the created JBIG bi-level image entity "
+                "(BIE):\n\n");
+        fprintf(stderr, "              input image size: %ld x %ld pixel\n",
+                s.xd, s.yd);
+        fprintf(stderr, "                    bit planes: %d\n", s.planes);
+        if (s.planes > 1)
+            fprintf(stderr, "                      encoding: %s code, MSB first\n",
+                    use_graycode ? "Gray" : "binary");
+        fprintf(stderr, "                       stripes: %ld\n", s.stripes);
+        fprintf(stderr, "   lines per stripe in layer 0: %ld\n", s.l0);
+        fprintf(stderr, "  total number of diff. layers: %d\n", s.d);
+        fprintf(stderr, "           lowest layer in BIE: %d\n", s.dl);
+        fprintf(stderr, "          highest layer in BIE: %d\n", s.dh);
+        fprintf(stderr, "             lowest layer size: %lu x %lu pixel\n",
+                jbg_ceil_half(s.xd, s.d - s.dl), jbg_ceil_half(s.yd, s.d - s.dl));
+        fprintf(stderr, "            highest layer size: %lu x %lu pixel\n",
+                jbg_ceil_half(s.xd, s.d - s.dh), jbg_ceil_half(s.yd, s.d - s.dh));
+        fprintf(stderr, "                   option bits:%s%s%s%s%s%s%s\n",
+                s.options & JBG_LRLTWO  ? " LRLTWO" : "",
+                s.options & JBG_VLENGTH ? " VLENGTH" : "",
+                s.options & JBG_TPDON   ? " TPDON" : "",
+                s.options & JBG_TPBON   ? " TPBON" : "",
+                s.options & JBG_DPON    ? " DPON" : "",
+                s.options & JBG_DPPRIV  ? " DPPRIV" : "",
+                s.options & JBG_DPLAST  ? " DPLAST" : "");
+        fprintf(stderr, "                    order bits:%s%s%s%s\n",
+                s.order & JBG_HITOLO ? " HITOLO" : "",
+                s.order & JBG_SEQ    ? " SEQ" : "",
+                s.order & JBG_ILEAVE ? " ILEAVE" : "",
+                s.order & JBG_SMID   ? " SMID" : "");
+        fprintf(stderr, "           AT maximum x-offset: %d\n"
+                "           AT maximum y-offset: %d\n", s.mx, s.my);
+        fprintf(stderr, "         length of output file: %lu byte\n\n",
+                total_length);
+    }
+
+    return 0;
+}
diff --git a/converter/other/jpeg2000/Makefile b/converter/other/jpeg2000/Makefile
new file mode 100644
index 00000000..70cfafb7
--- /dev/null
+++ b/converter/other/jpeg2000/Makefile
@@ -0,0 +1,77 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/other/jpeg2000
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+SUBDIRS = libjasper
+
+include $(BUILDDIR)/Makefile.config
+
+INCLUDES =
+ifneq ($(JASPERHDR_DIR),NONE)
+  INCLUDES += -I$(JASPERHDR_DIR)
+endif
+
+
+# INTERNAL_JASPERLIB must be relative to the current directory, because it
+# may end up in MERGE_OBJECTS, which must be relative.
+INTERNAL_JASPERLIB = libjasper/libjasper.a
+INTERNAL_JASPERHDR_DIR = libjasper/include
+
+ifeq ($(JASPERLIB),$(INTERNAL_JASPERLIB))
+  ifeq ($(HAVE_INT64),Y)
+    JASPERLIB_DEP = $(JASPERLIB)
+    JASPERLIB_USE = $(JASPERLIB)
+  else
+    # He wants the internal library, but doesn't have a 64 bit compiler,
+    # so we have no way to build it.  Ergo, he doesn't have a Jasper
+    # library.
+    JASPERLIB_USE = NONE
+  endif
+else
+  # It's not our internal version; user's on his own to make sure it's built
+  JASPERLIB_USE = $(JASPERLIB)
+endif
+
+
+ifneq ($(JASPERHDR_DIR),NONE)
+  ifneq ($(JASPERLIB_USE),NONE)
+    BINARIES = pamtojpeg2k jpeg2ktopam
+  endif
+endif
+
+
+OBJECTS = $(BINARIES:%=%.o)
+MERGE_OBJECTS = $(BINARIES:%=%.o2) 
+ifeq ($(JASPERLIB),$(INTERNAL_JASPERLIB))
+  # MERGE_OBJECTS contains relative paths, so $(INTERNAL_JASPERLIB) had better
+  # be relative to the current relative to the current directory.
+  MERGE_OBJECTS += $(JASPERLIB)
+endif
+MERGEBINARIES = $(BINARIES)
+
+.PHONY: all
+all: $(BINARIES)
+
+include $(SRCDIR)/Makefile.common
+
+LIBOPTS = $(shell $(LIBOPT) $(NETPBMLIB) $(JASPERLIB_USE))
+
+$(BINARIES): %: %.o $(JASPERLIB_DEP) $(NETPBMLIB) $(LIBOPT)
+	$(LD) $(LDFLAGS) $(MATHLIB) $(LDLIBS) -o $@ $< \
+	  $(LIBOPTS) $(JASPERDEPLIBS) -lm $(RPATH) $(LADD)
+
+$(INTERNAL_JASPERLIB): FORCE
+	$(MAKE) -f $(SRCDIR)/$(SUBDIR)/libjasper/Makefile \
+	   -C $(dir $@) $(notdir $@)
+
+clean: localclean
+
+.PHONY: localclean
+localclean:
+	$(MAKE) -f $(SRCDIR)/$(SUBDIR)/libjasper/Makefile -C libjasper clean
+
+.PHONY: FORCE
+FORCE:
diff --git a/converter/other/jpeg2000/jpeg2ktopam.c b/converter/other/jpeg2000/jpeg2ktopam.c
new file mode 100644
index 00000000..d6ea580c
--- /dev/null
+++ b/converter/other/jpeg2000/jpeg2ktopam.c
@@ -0,0 +1,498 @@
+/*****************************************************************************
+                              jpeg2kopam
+******************************************************************************
+
+  Convert a JPEG-2000 code stream image to a PNM or PAM
+
+  By Bryan Henderson, San Jose CA  2002.10.26
+
+*****************************************************************************/
+
+#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 "pam.h"
+#include "shhopt.h"
+#include "nstring.h"
+#include "mallocvar.h"
+
+#include <jasper/jasper.h>
+#include "libjasper_compat.h"
+
+enum compmode {COMPMODE_INTEGER, COMPMODE_REAL};
+
+enum progression {PROG_LRCP, PROG_RLCP, PROG_RPCL, PROG_PCRL, PROG_CPRL};
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    char * inputFilename;
+    unsigned int debuglevel;  /* Jasper library debug level */
+    unsigned int verbose;
+};
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   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;
+    optStruct3 opt;
+
+    unsigned int debuglevelSpec;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "verbose",      OPT_FLAG,   NULL, 
+            &cmdlineP->verbose,   0);
+    OPTENT3(0, "debuglevel",   OPT_UINT,   &cmdlineP->debuglevel,
+            &debuglevelSpec,      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 (!debuglevelSpec)
+        cmdlineP->debuglevel = 0;
+
+    if (argc - 1 == 0)
+        cmdlineP->inputFilename = strdup("-");  /* he wants stdin */
+    else if (argc - 1 == 1)
+        cmdlineP->inputFilename = strdup(argv[1]);
+    else 
+        pm_error("Too many arguments.  The only argument accepted\n"
+                 "is the input file specification");
+
+}
+
+
+
+static void
+readJpc(const char *   const inputFilename, 
+        jas_image_t ** const jasperPP) {
+
+    jas_image_t * jasperP;
+    jas_stream_t *instream;
+    const char * options;
+
+    if ( strcmp(inputFilename, "-") == 0) {
+        /* The input image is to be read from standard input. */
+        instream = jas_stream_fdopen(fileno(stdin), "rb");
+        if (instream == NULL)
+            pm_error("error: cannot reopen standard input");
+    } else {
+        instream = jas_stream_fopen(inputFilename, "rb");
+        if (instream == NULL )
+            pm_error("cannot open input image file '%s'", inputFilename);
+    } 
+
+    if (jas_image_getfmt(instream) != jas_image_strtofmt((char*)"jpc"))
+        pm_error("Input is not JPEG-2000 code stream");
+
+    options = "";
+
+    jasperP = jas_image_decode(instream, jas_image_strtofmt((char*)"jpc"), 
+                               (char*)options);
+    if (jasperP == NULL)
+        pm_error("Unable to interpret JPEG-2000 input.  "
+                 "The Jasper library jas_image_decode() subroutine failed.");
+
+	jas_stream_close(instream);
+
+    *jasperPP = jasperP;
+}
+
+
+
+static void
+getRgbComponents(int jasperCmpnt[], jas_image_t * const jasperP) {
+
+    {
+        int const rc = 
+            jas_image_getcmptbytype(jasperP,
+                                    JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_R));
+        if (rc < 0)
+            pm_error("Input says it has RGB color space, but contains "
+                     "no red component");
+        else
+            jasperCmpnt[PAM_RED_PLANE] = rc;
+
+        if (jas_image_cmptsgnd(jasperP, rc)) 
+            pm_error("Input image says it is RGB, but has signed values "
+                     "for what should be the red intensities.");
+    }
+    {
+        int const rc = 
+            jas_image_getcmptbytype(jasperP,
+                                    JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_G));
+        if (rc < 0)
+            pm_error("Input says it has RGB color space, but contains "
+                     "no green component");
+        else
+            jasperCmpnt[PAM_GRN_PLANE] = rc;
+
+        if (jas_image_cmptsgnd(jasperP, rc)) 
+            pm_error("Input image says it is RGB, but has signed values "
+                     "for what should be the green intensities.");
+    }
+    {
+        int const rc = 
+            jas_image_getcmptbytype(jasperP,
+                                    JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_B));
+        if (rc < 0)
+            pm_error("Input says it has RGB color space, but contains "
+                     "no blue component");
+        else
+            jasperCmpnt[PAM_BLU_PLANE] = rc;
+
+        if (jas_image_cmptsgnd(jasperP, rc)) 
+            pm_error("Input image says it is RGB, but has signed values "
+                     "for what should be the blue intensities.");
+    }
+}            
+
+
+
+static void
+getGrayComponent(int * jasperCmptP, jas_image_t * const jasperP) {
+
+    int const rc = 
+        jas_image_getcmptbytype(jasperP,
+                                JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_GRAY_Y));
+    if (rc < 0)
+        pm_error("Input says it has Grayscale color space, but contains "
+                 "no gray intensity component");
+    else
+        *jasperCmptP = rc;
+    if (jas_image_cmptsgnd(jasperP, 0)) 
+        pm_error("Input image says it is grayscale, but has signed values "
+                 "for what should be the gray levels.");
+}
+
+
+
+static void
+validateComponentsAlike(jas_image_t * const jasperP) {
+/*----------------------------------------------------------------------------
+   JPC allows each component to have its own width and height.  But
+   PAM requires all planes to the same shape.  So we validate now that
+   all the channels are the same, and abort the program if not.
+-----------------------------------------------------------------------------*/
+    int cmptNo;
+    
+    for (cmptNo = 0; cmptNo < jas_image_numcmpts(jasperP); ++cmptNo) {
+        if (jas_image_cmptwidth(jasperP, cmptNo) != 
+            jas_image_cmptwidth(jasperP, 0))
+            pm_message("Input image does not have components all the same "
+                       "width.");
+        if (jas_image_cmptheight(jasperP, cmptNo) != 
+            jas_image_cmptheight(jasperP, 0))
+            pm_message("Input image does not have components all the same "
+                       "height.");
+    }
+}
+
+
+
+static unsigned int
+maxJasperComponentPrecision(jas_image_t * const jasperP) {
+
+    int cmptNo;
+    unsigned int max;
+    
+    max = 1;
+
+    for (cmptNo = 0; cmptNo < jas_image_numcmpts(jasperP); ++cmptNo)
+        max = MAX(max, jas_image_cmptprec(jasperP, cmptNo));
+
+    return max;
+}
+
+
+
+static void
+computeOutputParm(jas_image_t * const jasperP,
+                  struct pam *  const outpamP,
+                  int **        const jasperCmptNoP) {
+
+    int * jasperCmptNo;
+       /* Malloc'ed array.  jaspercmptNo[P] is the component number for use
+          with the Jasper library that corresponds to Plane P of the PAM.
+       */
+
+	switch (jas_clrspc_fam(jas_image_clrspc(jasperP))) {
+	case JAS_CLRSPC_FAM_GRAY:
+        outpamP->depth = 1;
+        MALLOCARRAY_NOFAIL(jasperCmptNo, 1);
+        getGrayComponent(&jasperCmptNo[0], jasperP);
+        if (jas_image_cmptprec(jasperP, jasperCmptNo[0]) == 1) {
+            outpamP->format = RPBM_FORMAT;
+            strcpy(outpamP->tuple_type, PAM_PBM_TUPLETYPE);
+        } else {
+            outpamP->format = RPGM_FORMAT;
+            strcpy(outpamP->tuple_type, PAM_PGM_TUPLETYPE);
+        }
+        break;
+	case JAS_CLRSPC_FAM_RGB:
+        outpamP->depth = 3;
+        MALLOCARRAY_NOFAIL(jasperCmptNo, 3);
+        getRgbComponents(jasperCmptNo, jasperP);
+        outpamP->format = RPPM_FORMAT;
+        strcpy(outpamP->tuple_type, PAM_PPM_TUPLETYPE);
+        break;
+    default:
+        outpamP->format = PAM_FORMAT;
+        outpamP->depth = jas_image_numcmpts(jasperP);
+        {
+            unsigned int plane;
+
+            MALLOCARRAY_NOFAIL(jasperCmptNo, outpamP->depth);
+            for (plane = 0; plane < outpamP->depth; ++plane)
+                jasperCmptNo[plane] = plane;
+        }
+        strcpy(outpamP->tuple_type, "");
+        if (jas_image_cmptsgnd(jasperP, 0)) 
+            pm_message("Warning: Input image has signed sample values.  "
+                       "They will be represented in the PAM output in "
+                       "two's complement.");
+    }
+    outpamP->plainformat = FALSE;
+
+	outpamP->width = jas_image_cmptwidth(jasperP, 0);
+	outpamP->height = jas_image_cmptheight(jasperP, 0);
+
+    validateComponentsAlike(jasperP);
+
+    {
+        unsigned int const maxPrecision = maxJasperComponentPrecision(jasperP);
+
+        outpamP->maxval = pm_bitstomaxval(maxPrecision);
+        
+        outpamP->bytes_per_sample = (maxPrecision + 7)/8;
+    }
+    *jasperCmptNoP = jasperCmptNo;
+}
+
+
+
+static void
+createMatrices(struct pam * const outpamP, jas_matrix_t *** matrixP) {
+
+    jas_matrix_t ** matrix; 
+    unsigned int plane;
+
+    MALLOCARRAY_NOFAIL(matrix, outpamP->depth);
+
+    for (plane = 0; plane < outpamP->depth; ++plane) {
+        matrix[plane] = jas_matrix_create(1, outpamP->width);
+
+        if (matrix[plane] == NULL)
+            pm_error("Unable to create matrix for plane %u.  "
+                     "jas_matrix_create() failed.", plane);
+    }   
+    *matrixP = matrix;
+}
+
+
+
+static void
+destroyMatrices(struct pam *    const outpamP, 
+                jas_matrix_t ** const matrix ) {
+
+    unsigned int plane;
+
+    for (plane = 0; plane < outpamP->depth; ++plane)
+        jas_matrix_destroy(matrix[plane]);
+    free(matrix);
+}    
+
+
+
+static void
+computeComponentMaxval(struct pam *  const outpamP,
+                       jas_image_t * const jasperP,
+                       int           const jasperCmpt[],
+                       sample **     const jasperMaxvalP,
+                       bool *        const singleMaxvalP) {
+    
+    sample * jasperMaxval;
+    unsigned int plane;
+
+    MALLOCARRAY(jasperMaxval, outpamP->depth);
+
+    *singleMaxvalP = TRUE;  /* initial assumption */
+    for (plane = 0; plane < outpamP->depth; ++plane) {
+        jasperMaxval[plane] = 
+            pm_bitstomaxval(jas_image_cmptprec(jasperP, jasperCmpt[plane]));
+        if (jasperMaxval[plane] != jasperMaxval[0])
+            *singleMaxvalP = FALSE;
+    }
+    *jasperMaxvalP = jasperMaxval;
+}
+
+                       
+
+static void
+copyRowSingleMaxval(jas_seqent_t ** const jasperRow,
+                    tuple *         const tuplerow,
+                    struct pam *    const outpamP) {
+/*----------------------------------------------------------------------------
+   Copy row from Jasper library representation to Netpbm library
+   representation, assuming all Jasper components have the same precision,
+   which corresponds to the maxval of the output PAM, which means we don't
+   have to do any maxval scaling.
+
+   This is significantly faster than copyRowAnyMaxval().
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+    
+    for (col = 0; col < outpamP->width; ++col) {
+        unsigned int plane;
+        for (plane = 0; plane < outpamP->depth; ++plane) 
+            tuplerow[col][plane] = jasperRow[plane][col];
+    }
+}
+
+
+
+static void
+copyRowAnyMaxval(jas_seqent_t ** const jasperRow,
+                 tuple *         const tuplerow,
+                 struct pam *    const outpamP,
+                 sample          const jasperMaxval[]) {
+/*----------------------------------------------------------------------------
+   Copy row from Jasper library representation to Netpbm library
+   representation, allowing for each Jasper library component to have a
+   different precision (number of bits) and for those precisions to be
+   unrelated to the PAM maxval.
+
+   This is significantly slower than copyRowSingleMaxval().
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+            
+    for (col = 0; col < outpamP->width; ++col) {
+        unsigned int plane;
+        for (plane = 0; plane < outpamP->depth; ++plane) 
+            tuplerow[col][plane] = 
+                jasperRow[plane][col] * 
+                outpamP->maxval / jasperMaxval[plane];
+    }
+}
+
+
+
+static void
+convertToPamPnm(struct pam *  const outpamP,
+                jas_image_t * const jasperP,
+                int           const jasperCmptNo[]) {
+
+    jas_matrix_t ** matrix;  /* malloc'ed */
+        /* matrix[X] is the data for Plane X of the current row */
+    sample * jasperMaxval;
+    unsigned int row;
+    tuple * tuplerow;
+    jas_seqent_t ** jasperRow;   /* malloc'ed */
+       /* A row of a plane of the raster from the Jasper library 
+          This is an array of pointers into the 'matrix' data structures.
+       */
+    bool singleMaxval;
+
+    createMatrices(outpamP, &matrix);
+
+    computeComponentMaxval(outpamP, jasperP, jasperCmptNo,
+                           &jasperMaxval, &singleMaxval);
+
+    MALLOCARRAY(jasperRow, outpamP->depth);
+    if (jasperRow == NULL)
+        pm_error("Out of memory");
+
+    tuplerow = pnm_allocpamrow(outpamP);
+
+    for (row = 0; row < outpamP->height; ++row) {
+        unsigned int plane;
+
+        for (plane = 0; plane < outpamP->depth; ++plane) {
+            int rc;
+            rc = jas_image_readcmpt(jasperP, jasperCmptNo[plane], 0, row,
+                                    outpamP->width, 1,
+                                    matrix[plane]);
+            if (rc != 0)
+                pm_error("jas_image_readcmpt() of row %u plane %u "
+                         "failed.", 
+                         row, plane);
+            jasperRow[plane] = jas_matrix_getref(matrix[plane], 0, 0);
+        }
+        if (singleMaxval) 
+            copyRowSingleMaxval(jasperRow, tuplerow, outpamP);
+        else
+            copyRowAnyMaxval(jasperRow, tuplerow, outpamP, jasperMaxval);
+
+        pnm_writepamrow(outpamP, tuplerow);
+    }
+    pnm_freepamrow(tuplerow);
+
+    destroyMatrices(outpamP, matrix);
+
+    free(jasperRow);
+    free(jasperMaxval);
+}
+
+
+
+int
+main(int argc, char **argv)
+{
+    struct cmdlineInfo cmdline;
+    struct pam outpam;
+    int * jasperCmpt;  /* malloc'ed */
+       /* jaspercmpt[P] is the component number for use with the
+          Jasper library that corresponds to Plane P of the PAM.  
+       */
+    jas_image_t * jasperP;
+
+    pnm_init(&argc, argv);
+    
+    parseCommandLine(argc, argv, &cmdline);
+    
+    { 
+        int rc;
+        
+        rc = jas_init();
+        if ( rc != 0 )
+            pm_error("Failed to initialize Jasper library.  "
+                     "jas_init() returns rc %d", rc );
+    }
+    
+    jas_setdbglevel(cmdline.debuglevel);
+    
+    readJpc(cmdline.inputFilename, &jasperP);
+
+    outpam.file = stdout;
+    outpam.size = sizeof(outpam);
+    outpam.len  = PAM_STRUCT_SIZE(tuple_type);
+
+    computeOutputParm(jasperP, &outpam, &jasperCmpt);
+
+    pnm_writepaminit(&outpam);
+    
+    convertToPamPnm(&outpam, jasperP, jasperCmpt);
+    
+    free(jasperCmpt);
+	jas_image_destroy(jasperP);
+
+    pm_close(stdout);
+    
+    return 0;
+}
diff --git a/converter/other/jpeg2000/libjasper/Makefile b/converter/other/jpeg2000/libjasper/Makefile
new file mode 100644
index 00000000..73d263ec
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/Makefile
@@ -0,0 +1,28 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/other/jpeg2000/libjasper
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+SUBDIRS = base jp2 jpc
+
+# NOTE: The library source code uses 64 bit types, so you cannot
+# build it (compile will fail) if you don't have 64 bit types defined
+# by pm_config.h (normally by including <inttypes.h>)
+
+all: libjasper.a
+
+include $(SRCDIR)/$(SUBDIR)/Makefile.common
+
+# We cheat a bit here -- the real dependencies are all the .o files listed
+# in the part list, but since we don't know what those are, we just do a
+# 'make all' in each subdirectory to get them built.  That means it always
+# looks like dependencies were rebuilt and libjasper.a gets rebuilt every
+# time.
+libjasper.a: $(SUBDIRS:%=%/all) partlist 
+	ar rc $@ $(shell cat partlist)
+	$(RANLIB) $@
+
diff --git a/converter/other/jpeg2000/libjasper/Makefile.common b/converter/other/jpeg2000/libjasper/Makefile.common
new file mode 100644
index 00000000..71a11832
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/Makefile.common
@@ -0,0 +1,41 @@
+# -*-makefile-*-    <-- an Emacs control
+# This is common rules for the pnmtojpc subdirectories.
+#
+# Set the following variables before including this:
+#
+#  LIB_OBJECTS: List of object files from this directory that go into 
+#               libjasper.
+
+all: $(LIB_OBJECTS) partlist
+
+partlist: $(SUBDIRS:%=%/partlist)
+	cat /dev/null $(SUBDIRS:%=%/partlist) >$@
+	echo $(LIB_OBJECTS:%=$(CURDIR)/%) >>$@
+
+.PHONY: $(SUBDIRS:%=%/partlist)
+$(SUBDIRS:%=%/partlist): %/partlist: $(CURDIR)/%
+	$(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \
+	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
+
+INCLUDES = -I$(JASPERSRCDIR)/include -I$(JASPERSRCDIR)/importinc
+
+include $(SRCDIR)/Makefile.common
+
+DEFS = -DHAVE_LIBM=1 -DSTDC_HEADERS=1 -DHAVE_FCNTL_H=1 -DHAVE_LIMITS_H=1 -DHAVE_UNISTD_H=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STDDEF_H=1 -DEXCLUDE_BMP_SUPPORT -DEXCLUDE_RAS_SUPPORT -DEXCLUDE_MIF_SUPPORT -DEXCLUDE_JPG_SUPPORT -DEXCLUDE_PGX_SUPPORT -DEXCLUDE_PNM_SUPPORT
+
+$(LIB_OBJECTS):%.o:%.c
+	$(CC) -c $(CFLAGS) $(INCLUDES) $(DEFS) $(CADD) $<
+
+$(LIB_OBJECTS): $(JASPERSRCDIR)/importinc
+
+$(JASPERSRCDIR)/importinc:
+	$(MAKE) -C $(dir $@) $(notdir $@)
+
+clean: localclean
+
+.PHONY: localclean
+localclean:
+	rm -f partlist
+
+.PHONY: FORCE
+FORCE:
diff --git a/converter/other/jpeg2000/libjasper/README b/converter/other/jpeg2000/libjasper/README
new file mode 100644
index 00000000..ad3e019b
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/README
@@ -0,0 +1,17 @@
+The code in the directory 'libjasper' is taken from the Jasper package.
+In particular, from the 'src/libjasper' directory of Jasper Version
+1.600.0.
+
+The adaptation was done by Bryan Henderson on 2002.10.26.
+
+The adaptation involved:
+
+  - remove stuff for formats other than PNM.
+
+  - Replace build stuff (Jasper uses GNU Autoconf/Automake/Libtool).
+
+See <http://www.ece.uvic.ca/~mdadams/jasper/>.
+
+
+You cannot build this library without a 64 bit compiler (and 64 bit
+types such as int64_t defined).  It does a lot of 64 bit arithmetic.
diff --git a/converter/other/jpeg2000/libjasper/base/Makefile b/converter/other/jpeg2000/libjasper/base/Makefile
new file mode 100644
index 00000000..0ee65b5e
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/base/Makefile
@@ -0,0 +1,21 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../../../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/other/jpeg2000/libjasper/base
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+JASPERSRCDIR=$(SRCDIR)/$(SUBDIR)/..
+
+include $(BUILDDIR)/Makefile.config
+
+LIB_OBJECTS = jas_debug.o jas_getopt.o jas_image.o jas_init.o \
+	      jas_malloc.o jas_seq.o jas_stream.o jas_string.o \
+	      jas_tvp.o jas_version.o
+
+MERGE_OBJECTS =
+
+all: partlist $(LIB_OBJECTS)
+
+include $(JASPERSRCDIR)/Makefile.common
+
diff --git a/converter/other/jpeg2000/libjasper/base/jas_debug.c b/converter/other/jpeg2000/libjasper/base/jas_debug.c
new file mode 100644
index 00000000..4248178f
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/base/jas_debug.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "jasper/jas_types.h"
+#include "jasper/jas_debug.h"
+
+/******************************************************************************\
+* Local data.
+\******************************************************************************/
+
+static int jas_dbglevel = 0;
+/* The debug level. */
+
+/******************************************************************************\
+* Code for getting/setting the debug level.
+\******************************************************************************/
+
+/* Set the library debug level. */
+int jas_setdbglevel(int dbglevel)
+{
+	int olddbglevel;
+
+	/* Save the old debug level. */
+	olddbglevel = jas_dbglevel;
+
+	/* Change the debug level. */
+	jas_dbglevel = dbglevel;
+
+	/* Return the old debug level. */
+	return olddbglevel;
+}
+
+/* Get the library debug level. */
+int jas_getdbglevel()
+{
+	return jas_dbglevel;
+}
+
+/******************************************************************************\
+* Code.
+\******************************************************************************/
+
+/* Perform formatted output to standard error. */
+int jas_eprintf(const char *fmt, ...)
+{
+	int ret;
+	va_list ap;
+
+	va_start(ap, fmt);
+	ret = vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	return ret;
+}
+
+/* Dump memory to a stream. */
+int jas_memdump(FILE *out, void *data, size_t len)
+{
+	int i;
+	int j;
+	unsigned char *dp;
+	dp = data;
+	for (i = 0; i < len; i += 16) {
+		fprintf(out, "%04x:", i);
+		for (j = 0; j < 16; ++j) {
+			if (i + j < len) {
+				fprintf(out, " %02x", dp[i + j]);
+			}
+		}
+		fprintf(out, "\n");
+	}
+	return 0;
+}
diff --git a/converter/other/jpeg2000/libjasper/base/jas_getopt.c b/converter/other/jpeg2000/libjasper/base/jas_getopt.c
new file mode 100644
index 00000000..7f579b3e
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/base/jas_getopt.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 1999-2000, Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved. 
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Command Line Option Parsing Library
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "jasper/jas_getopt.h"
+#include "jasper/jas_math.h"
+
+/******************************************************************************\
+* Global data.
+\******************************************************************************/
+
+int jas_optind = 0;
+int jas_opterr = 1;
+char *jas_optarg = 0;
+
+/******************************************************************************\
+* Code.
+\******************************************************************************/
+
+static jas_opt_t *jas_optlookup(jas_opt_t *opts, char *name)
+{
+	jas_opt_t *opt;
+
+	for (opt = opts; opt->id >= 0 && opt->name; ++opt) {
+		if (!strcmp(opt->name, name)) {
+			return opt;
+		}
+	}
+	return 0;
+}
+
+int jas_getopt(int argc, char **argv, jas_opt_t *opts)
+{
+	char *cp;
+	int id;
+	int hasarg;
+	jas_opt_t *opt;
+	char *s;
+
+	if (!jas_optind) {
+		jas_optind = JAS_MIN(1, argc);
+	}
+	while (jas_optind < argc) {
+		s = cp = argv[jas_optind];
+		if (*cp == '-') {
+			/* We are processing an option. */
+			++jas_optind;
+			if (*++cp == '-') {
+				/* We are processing a long option. */
+				++cp;
+				if (*cp == '\0') {
+					/* This is the end of the options. */
+					return JAS_GETOPT_EOF;
+				}
+				if (!(opt = jas_optlookup(opts, cp))) {
+					if (jas_opterr) {
+						fprintf(stderr, "unknown long option %s\n", s);
+					}
+					return JAS_GETOPT_ERR;
+				}
+				hasarg = (opt->flags & JAS_OPT_HASARG) != 0;
+				id = opt->id;
+			} else {
+				/* We are processing a short option. */
+				if (strlen(cp) != 1 ||
+				  !(opt = jas_optlookup(opts, cp))) {
+					if (jas_opterr) {
+						fprintf(stderr, "unknown short option %s\n", s);
+					}
+					return JAS_GETOPT_ERR;
+				}
+				hasarg = (opt->flags & JAS_OPT_HASARG) != 0;
+				id = opt->id;
+			}
+			if (hasarg) {
+				/* The option has an argument. */
+				if (jas_optind >= argc) {
+					if (jas_opterr) {
+						fprintf(stderr, "missing argument for option %s\n", s);
+					}
+					return JAS_GETOPT_ERR;
+				}
+				jas_optarg = argv[jas_optind];
+				++jas_optind;
+			} else {
+				/* The option does not have an argument. */
+				jas_optarg = 0;
+			}
+			return id;
+		} else {
+			/* We are not processing an option. */
+			return JAS_GETOPT_EOF;
+		}
+	}
+	return JAS_GETOPT_EOF;
+}
diff --git a/converter/other/jpeg2000/libjasper/base/jas_image.c b/converter/other/jpeg2000/libjasper/base/jas_image.c
new file mode 100644
index 00000000..e6439fcd
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/base/jas_image.c
@@ -0,0 +1,968 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Image Library
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "jasper/jas_math.h"
+#include "jasper/jas_image.h"
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_string.h"
+
+/******************************************************************************\
+* Types.
+\******************************************************************************/
+
+/******************************************************************************\
+* Local prototypes.
+\******************************************************************************/
+
+static void jas_image_cmpt_destroy(jas_image_cmpt_t *cmpt);
+static jas_image_cmpt_t *jas_image_cmpt_create(uint_fast32_t tlx, uint_fast32_t tly,
+  uint_fast32_t hstep, uint_fast32_t vstep, uint_fast32_t width, uint_fast32_t
+  height, uint_fast16_t depth, bool sgnd, uint_fast32_t inmem);
+static void jas_image_setbbox(jas_image_t *image);
+static jas_image_cmpt_t *jas_image_cmpt_copy(jas_image_cmpt_t *cmpt);
+static jas_image_cmpt_t *jas_image_cmpt_create0();
+static int jas_image_growcmpts(jas_image_t *image, int maxcmpts);
+static uint_fast32_t inttobits(jas_seqent_t v, int prec, bool sgnd);
+static jas_seqent_t bitstoint(uint_fast32_t v, int prec, bool sgnd);
+
+/******************************************************************************\
+* Global data.
+\******************************************************************************/
+
+static int jas_image_numfmts = 0;
+static jas_image_fmtinfo_t jas_image_fmtinfos[JAS_IMAGE_MAXFMTS];
+
+/******************************************************************************\
+* Create and destroy operations.
+\******************************************************************************/
+
+jas_image_t *jas_image_create(uint_fast16_t numcmpts, jas_image_cmptparm_t *cmptparms,
+  int colorspace)
+{
+	jas_image_t *image;
+	uint_fast32_t rawsize;
+	uint_fast32_t inmem;
+	uint_fast16_t cmptno;
+	jas_image_cmptparm_t *cmptparm;
+
+	if (!(image = jas_image_create0())) {
+		return 0;
+	}
+
+	image->colorspace_ = colorspace;
+	image->maxcmpts_ = numcmpts;
+	image->inmem_ = true;
+
+	/* Allocate memory for the per-component information. */
+	if (!(image->cmpts_ = jas_malloc(image->maxcmpts_ *
+	  sizeof(jas_image_cmpt_t *)))) {
+		jas_image_destroy(image);
+		return 0;
+	}
+	/* Initialize in case of failure. */
+	for (cmptno = 0; cmptno < image->maxcmpts_; ++cmptno) {
+		image->cmpts_[cmptno] = 0;
+	}
+
+	/* Compute the approximate raw size of the image. */
+	rawsize = 0;
+	for (cmptno = 0, cmptparm = cmptparms; cmptno < numcmpts; ++cmptno,
+	  ++cmptparm) {
+		rawsize += cmptparm->width * cmptparm->height *
+		  (cmptparm->prec + 7) / 8;
+	}
+	/* Decide whether to buffer the image data in memory, based on the
+	  raw size of the image. */
+	inmem = (rawsize < JAS_IMAGE_INMEMTHRESH);
+
+	/* Create the individual image components. */
+	for (cmptno = 0, cmptparm = cmptparms; cmptno < numcmpts; ++cmptno,
+	  ++cmptparm) {
+		if (!(image->cmpts_[cmptno] = jas_image_cmpt_create(cmptparm->tlx,
+		  cmptparm->tly, cmptparm->hstep, cmptparm->vstep,
+		  cmptparm->width, cmptparm->height, cmptparm->prec,
+		  cmptparm->sgnd, inmem))) {
+			jas_image_destroy(image);
+			return 0;
+		}
+		++image->numcmpts_;
+	}
+
+	/* Determine the bounding box for all of the components on the
+	  reference grid (i.e., the image area) */
+	jas_image_setbbox(image);
+
+	return image;
+}
+
+jas_image_t *jas_image_create0()
+{
+	jas_image_t *image;
+
+	if (!(image = jas_malloc(sizeof(jas_image_t)))) {
+		return 0;
+	}
+
+	image->tlx_ = 0;
+	image->tly_ = 0;
+	image->brx_ = 0;
+	image->bry_ = 0;
+	image->colorspace_ = JAS_IMAGE_CS_UNKNOWN;
+	image->numcmpts_ = 0;
+	image->maxcmpts_ = 0;
+	image->cmpts_ = 0;
+	image->inmem_ = true;
+	image->iccp_ = 0;
+	image->iccplen_ = 0;
+
+	return image;
+}
+
+jas_image_t *jas_image_copy(jas_image_t *image)
+{
+	jas_image_t *newimage;
+	int cmptno;
+
+	newimage = jas_image_create0();
+	if (jas_image_growcmpts(newimage, image->numcmpts_)) {
+		goto error;
+	}
+	for (cmptno = 0; cmptno < image->numcmpts_; ++cmptno) {
+		if (!(newimage->cmpts_[cmptno] = jas_image_cmpt_copy(image->cmpts_[cmptno]))) {
+			goto error;
+		}
+		++newimage->numcmpts_;
+	}
+
+	jas_image_setbbox(newimage);
+
+	return newimage;
+error:
+	if (newimage) {
+		jas_image_destroy(newimage);
+	}
+	return 0;
+}
+
+static jas_image_cmpt_t *jas_image_cmpt_create0()
+{
+	jas_image_cmpt_t *cmpt;
+	if (!(cmpt = jas_malloc(sizeof(jas_image_cmpt_t)))) {
+		return 0;
+	}
+	memset(cmpt, 0, sizeof(jas_image_cmpt_t));
+	cmpt->type_ = JAS_IMAGE_CT_UNKNOWN;
+	return cmpt;
+}
+
+static jas_image_cmpt_t *jas_image_cmpt_copy(jas_image_cmpt_t *cmpt)
+{
+	jas_image_cmpt_t *newcmpt;
+
+	if (!(newcmpt = jas_image_cmpt_create0())) {
+		return 0;
+	}
+	newcmpt->tlx_ = cmpt->tlx_;
+	newcmpt->tly_ = cmpt->tly_;
+	newcmpt->hstep_ = cmpt->hstep_;
+	newcmpt->vstep_ = cmpt->vstep_;
+	newcmpt->width_ = cmpt->width_;
+	newcmpt->height_ = cmpt->height_;
+	newcmpt->prec_ = cmpt->prec_;
+	newcmpt->sgnd_ = cmpt->sgnd_;
+	newcmpt->cps_ = cmpt->cps_;
+	newcmpt->type_ = cmpt->type_;
+	if (!(newcmpt->stream_ = jas_stream_memopen(0, 0))) {
+		return 0;
+	}
+	if (jas_stream_seek(cmpt->stream_, 0, SEEK_SET)) {
+		return 0;
+	}
+	if (jas_stream_copy(newcmpt->stream_, cmpt->stream_, -1)) {
+		return 0;
+	}
+	if (jas_stream_seek(newcmpt->stream_, 0, SEEK_SET)) {
+		return 0;
+	}
+	return newcmpt;
+}
+
+void jas_image_destroy(jas_image_t *image)
+{
+	int i;
+
+	if (image->cmpts_) {
+		for (i = 0; i < image->numcmpts_; ++i) {
+			jas_image_cmpt_destroy(image->cmpts_[i]);
+			image->cmpts_[i] = 0;
+		}
+		jas_free(image->cmpts_);
+	}
+	jas_free(image);
+}
+
+static jas_image_cmpt_t *jas_image_cmpt_create(uint_fast32_t tlx, uint_fast32_t tly,
+  uint_fast32_t hstep, uint_fast32_t vstep, uint_fast32_t width, uint_fast32_t
+  height, uint_fast16_t depth, bool sgnd, uint_fast32_t inmem)
+{
+	jas_image_cmpt_t *cmpt;
+	long size;
+
+	if (!(cmpt = jas_malloc(sizeof(jas_image_cmpt_t)))) {
+		return 0;
+	}
+
+	cmpt->tlx_ = tlx;
+	cmpt->tly_ = tly;
+	cmpt->hstep_ = hstep;
+	cmpt->vstep_ = vstep;
+	cmpt->width_ = width;
+	cmpt->height_ = height;
+	cmpt->prec_ = depth;
+	cmpt->sgnd_ = sgnd;
+	cmpt->stream_ = 0;
+	cmpt->cps_ = (depth + 7) / 8;
+
+	size = cmpt->width_ * cmpt->height_ * cmpt->cps_;
+	cmpt->stream_ = (inmem) ? jas_stream_memopen(0, size) : jas_stream_tmpfile();
+	if (!cmpt->stream_) {
+		jas_image_cmpt_destroy(cmpt);
+		return 0;
+	}
+
+	/* Zero the component data.  This isn't necessary, but it is
+	convenient for debugging purposes. */
+	if (jas_stream_seek(cmpt->stream_, size - 1, SEEK_SET) < 0 ||
+	  jas_stream_putc(cmpt->stream_, 0) == EOF ||
+	  jas_stream_seek(cmpt->stream_, 0, SEEK_SET) < 0) {
+		jas_image_cmpt_destroy(cmpt);
+		return 0;
+	}
+
+	return cmpt;
+}
+
+static void jas_image_cmpt_destroy(jas_image_cmpt_t *cmpt)
+{
+	if (cmpt->stream_) {
+		jas_stream_close(cmpt->stream_);
+	}
+	jas_free(cmpt);
+}
+
+/******************************************************************************\
+* Load and save operations.
+\******************************************************************************/
+
+jas_image_t *jas_image_decode(jas_stream_t *in, int fmt, char *optstr)
+{
+	jas_image_fmtinfo_t *fmtinfo;
+
+	/* If possible, try to determine the format of the input data. */
+	if (fmt < 0) {
+		if ((fmt = jas_image_getfmt(in)) < 0) {
+			return 0;
+		}
+	}
+	if (!(fmtinfo = jas_image_lookupfmtbyid(fmt))) {
+		return 0;
+	}
+	return (fmtinfo->ops.decode) ? (*fmtinfo->ops.decode)(in, optstr) : 0;
+}
+
+int jas_image_encode(jas_image_t *image, jas_stream_t *out, int fmt, char *optstr)
+{
+	jas_image_fmtinfo_t *fmtinfo;
+	if (!(fmtinfo = jas_image_lookupfmtbyid(fmt))) {
+		return -1;
+	}
+	return (fmtinfo->ops.encode) ? (*fmtinfo->ops.encode)(image, out,
+	  optstr) : (-1);
+}
+
+/******************************************************************************\
+* Component read and write operations.
+\******************************************************************************/
+
+int jas_image_readcmpt(jas_image_t *image, uint_fast16_t cmptno, uint_fast32_t x, uint_fast32_t y, uint_fast32_t width,
+  uint_fast32_t height, jas_matrix_t *data)
+{
+	jas_image_cmpt_t *cmpt;
+	uint_fast32_t i;
+	uint_fast32_t j;
+	int k;
+	jas_seqent_t v;
+	int c;
+	jas_seqent_t *dr;
+	jas_seqent_t *d;
+	int drs;
+
+	if (cmptno >= image->numcmpts_) {
+		return -1;
+	}
+
+	cmpt = image->cmpts_[cmptno];
+	if (x >= cmpt->width_ || y >= cmpt->height_ ||
+	  x + width > cmpt->width_ ||
+	  y + height > cmpt->height_) {
+		return -1;
+	}
+
+	if (jas_matrix_numrows(data) != height || jas_matrix_numcols(data) != width) {
+		if (jas_matrix_resize(data, height, width)) {
+			return -1;
+		}
+	}
+
+	dr = jas_matrix_getref(data, 0, 0);
+	drs = jas_matrix_rowstep(data);
+	for (i = 0; i < height; ++i, dr += drs) {
+		d = dr;
+		if (jas_stream_seek(cmpt->stream_, (cmpt->width_ * (y + i) + x)
+		  * cmpt->cps_, SEEK_SET) < 0) {
+			return -1;
+		}
+		for (j = width; j > 0; --j, ++d) {
+			v = 0;
+			for (k = cmpt->cps_; k > 0; --k) {
+				if ((c = jas_stream_getc(cmpt->stream_)) == EOF) {
+					return -1;
+				}
+				v = (v << 8) | (c & 0xff);
+			}
+			*d = bitstoint(v, cmpt->prec_, cmpt->sgnd_);
+		}
+	}
+
+	return 0;
+}
+
+#if 0
+int_fast64_t jas_image_readcmpt1(jas_image_t *image, uint_fast16_t cmptno,
+  uint_fast32_t x, uint_fast32_t y)
+{
+	jas_image_cmpt_t *cmpt;
+	int k;
+	int c;
+	int_fast64_t v;
+	cmpt = image->cmpts_[cmptno];
+	if (jas_stream_seek(cmpt->stream_, (cmpt->width_ * y + x) * cmpt->cps_,
+	  SEEK_SET) < 0) {
+		goto error;
+	}
+	v = 0;
+	for (k = cmpt->cps_; k > 0; --k) {
+		if ((c = jas_stream_getc(cmpt->stream_)) == EOF) {
+			goto error;
+		}
+		v = (v << 8) | (c & 0xff);
+	}
+if (cmpt->sgnd_) {
+	abort();
+}
+
+	return v;
+
+error:
+	return 0;
+}
+#endif
+
+int jas_image_writecmpt(jas_image_t *image, uint_fast16_t cmptno, uint_fast32_t x, uint_fast32_t y, uint_fast32_t width,
+  uint_fast32_t height, jas_matrix_t *data)
+{
+	jas_image_cmpt_t *cmpt;
+	uint_fast32_t i;
+	uint_fast32_t j;
+	jas_seqent_t *d;
+	jas_seqent_t *dr;
+	int drs;
+	jas_seqent_t v;
+	int k;
+	int c;
+
+	if (cmptno >= image->numcmpts_) {
+		return -1;
+	}
+
+	cmpt = image->cmpts_[cmptno];
+	if (x >= cmpt->width_ || y >= cmpt->height_ ||
+	  x + width > cmpt->width_ ||
+	  y + height > cmpt->height_) {
+		return -1;
+	}
+
+	if (jas_matrix_numrows(data) != height || jas_matrix_numcols(data) != width) {
+		return -1;
+	}
+
+	dr = jas_matrix_getref(data, 0, 0);
+	drs = jas_matrix_rowstep(data);
+	for (i = 0; i < height; ++i, dr += drs) {
+		d = dr;
+		if (jas_stream_seek(cmpt->stream_, (cmpt->width_ * (y + i) + x)
+		  * cmpt->cps_, SEEK_SET) < 0) {
+			return -1;
+		}
+		for (j = width; j > 0; --j, ++d) {
+			v = inttobits(*d, cmpt->prec_, cmpt->sgnd_);
+			for (k = cmpt->cps_; k > 0; --k) {
+				c = (v >> (8 * (cmpt->cps_ - 1))) & 0xff;
+				if (jas_stream_putc(cmpt->stream_,
+				  (unsigned char) c) == EOF) {
+					return -1;
+				}
+				v <<= 8;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/******************************************************************************\
+* File format operations.
+\******************************************************************************/
+
+void jas_image_clearfmts()
+{
+	int i;
+	jas_image_fmtinfo_t *fmtinfo;
+	for (i = 0; i < jas_image_numfmts; ++i) {
+		fmtinfo = &jas_image_fmtinfos[i];
+		if (fmtinfo->name) {
+			jas_free(fmtinfo->name);
+			fmtinfo->name = 0;
+		}
+		if (fmtinfo->ext) {
+			jas_free(fmtinfo->ext);
+			fmtinfo->ext = 0;
+		}
+		if (fmtinfo->desc) {
+			jas_free(fmtinfo->desc);
+			fmtinfo->desc = 0;
+		}
+	}
+	jas_image_numfmts = 0;
+}
+
+int jas_image_addfmt(int id, const char *name, const char *ext, 
+  const char *desc,
+  jas_image_fmtops_t *ops)
+{
+	jas_image_fmtinfo_t *fmtinfo;
+	assert(id >= 0 && name && ext && ops);
+	if (jas_image_numfmts >= JAS_IMAGE_MAXFMTS) {
+		return -1;
+	}
+	fmtinfo = &jas_image_fmtinfos[jas_image_numfmts];
+	fmtinfo->id = id;
+	if (!(fmtinfo->name = jas_strdup(name))) {
+		return -1;
+	}
+	if (!(fmtinfo->ext = jas_strdup(ext))) {
+		jas_free(fmtinfo->name);
+		return -1;
+	}
+	if (!(fmtinfo->desc = jas_strdup(desc))) {
+		jas_free(fmtinfo->name);
+		jas_free(fmtinfo->ext);
+		return -1;
+	}
+	fmtinfo->ops = *ops;
+	++jas_image_numfmts;
+	return 0;
+}
+
+int jas_image_strtofmt(char *name)
+{
+	jas_image_fmtinfo_t *fmtinfo;
+	if (!(fmtinfo = jas_image_lookupfmtbyname(name))) {
+		return -1;
+	}
+	return fmtinfo->id;
+}
+
+char *jas_image_fmttostr(int fmt)
+{
+	jas_image_fmtinfo_t *fmtinfo;
+	if (!(fmtinfo = jas_image_lookupfmtbyid(fmt))) {
+		return 0;
+	}
+	return fmtinfo->name;
+}
+
+int jas_image_getfmt(jas_stream_t *in)
+{
+	jas_image_fmtinfo_t *fmtinfo;
+	int found;
+	int i;
+
+	/* Check for data in each of the supported formats. */
+	found = 0;
+	for (i = 0, fmtinfo = jas_image_fmtinfos; i < jas_image_numfmts; ++i,
+	  ++fmtinfo) {
+		if (fmtinfo->ops.validate) {
+			/* Is the input data valid for this format? */
+			if (!(*fmtinfo->ops.validate)(in)) {
+				found = 1;
+				break;
+			}
+		}
+	}
+	return found ? fmtinfo->id : (-1);
+}
+
+int jas_image_fmtfromname(char *name)
+{
+	int i;
+	char *ext;
+	jas_image_fmtinfo_t *fmtinfo;
+	/* Get the file name extension. */
+	if (!(ext = strrchr(name, '.'))) {
+		return -1;
+	}
+	++ext;
+	/* Try to find a format that uses this extension. */	
+	for (i = 0, fmtinfo = jas_image_fmtinfos; i < jas_image_numfmts; ++i,
+	  ++fmtinfo) {
+		/* Do we have a match? */
+		if (!strcmp(ext, fmtinfo->ext)) {
+			return fmtinfo->id;
+		}
+	}
+	return -1;
+}
+
+/******************************************************************************\
+* Miscellaneous operations.
+\******************************************************************************/
+
+uint_fast32_t jas_image_rawsize(jas_image_t *image)
+{
+	uint_fast32_t rawsize;
+	uint_fast32_t cmptno;
+	jas_image_cmpt_t *cmpt;
+
+	rawsize = 0;
+	for (cmptno = 0; cmptno < image->numcmpts_; ++cmptno) {
+		cmpt = image->cmpts_[cmptno];
+		rawsize += (cmpt->width_ * cmpt->height_ * cmpt->prec_ +
+		  7) / 8;
+	}
+	return rawsize;
+}
+
+void jas_image_delcmpt(jas_image_t *image, uint_fast16_t cmptno)
+{
+	if (cmptno >= image->numcmpts_) {
+		return;
+	}
+	jas_image_cmpt_destroy(image->cmpts_[cmptno]);
+	if (cmptno < image->numcmpts_) {
+		memmove(&image->cmpts_[cmptno], &image->cmpts_[cmptno + 1],
+		  (image->numcmpts_ - 1 - cmptno) * sizeof(jas_image_cmpt_t *));
+	}
+	--image->numcmpts_;
+
+	jas_image_setbbox(image);
+}
+
+int jas_image_addcmpt(jas_image_t *image, uint_fast16_t cmptno,
+  jas_image_cmptparm_t *cmptparm)
+{
+	jas_image_cmpt_t *newcmpt;
+	assert(cmptno <= image->numcmpts_);
+	if (image->numcmpts_ >= image->maxcmpts_) {
+		if (jas_image_growcmpts(image, image->maxcmpts_ + 128)) {
+			return -1;
+		}
+	}
+	if (!(newcmpt = jas_image_cmpt_create(cmptparm->tlx,
+	  cmptparm->tly, cmptparm->hstep, cmptparm->vstep,
+	  cmptparm->width, cmptparm->height, cmptparm->prec,
+	  cmptparm->sgnd, 1))) {
+		return -1;
+	}
+	if (cmptno < image->numcmpts_) {
+		memmove(&image->cmpts_[cmptno + 1], &image->cmpts_[cmptno],
+		  (image->numcmpts_ - cmptno) * sizeof(jas_image_cmpt_t *));
+	}
+	image->cmpts_[cmptno] = newcmpt;
+	++image->numcmpts_;
+
+	jas_image_setbbox(image);
+
+	return 0;
+}
+
+jas_image_fmtinfo_t *jas_image_lookupfmtbyid(int id)
+{
+	int i;
+	jas_image_fmtinfo_t *fmtinfo;
+
+	for (i = 0, fmtinfo = jas_image_fmtinfos; i < jas_image_numfmts; ++i, ++fmtinfo) {
+		if (fmtinfo->id == id) {
+			return fmtinfo;
+		}
+	}
+	return 0;
+}
+
+jas_image_fmtinfo_t *jas_image_lookupfmtbyname(const char *name)
+{
+	int i;
+	jas_image_fmtinfo_t *fmtinfo;
+
+	for (i = 0, fmtinfo = jas_image_fmtinfos; i < jas_image_numfmts; ++i, ++fmtinfo) {
+		if (!strcmp(fmtinfo->name, name)) {
+			return fmtinfo;
+		}
+	}
+	return 0;
+}
+
+
+
+
+
+static uint_fast32_t inttobits(jas_seqent_t v, int prec, bool sgnd)
+{
+	uint_fast32_t ret;
+	ret = ((sgnd && v < 0) ? ((1 << prec) + v) : v) & JAS_ONES(prec);
+	return ret;
+}
+
+static jas_seqent_t bitstoint(uint_fast32_t v, int prec, bool sgnd)
+{
+	jas_seqent_t ret;
+	v &= JAS_ONES(prec);
+	ret = (sgnd && (v & (1 << (prec - 1)))) ? (v - (1 << prec)) : v;
+	return ret;
+}
+
+static void jas_image_setbbox(jas_image_t *image)
+{
+	jas_image_cmpt_t *cmpt;
+	int cmptno;
+	int_fast32_t x;
+	int_fast32_t y;
+
+	if (image->numcmpts_ > 0) {
+		/* Determine the bounding box for all of the components on the
+		  reference grid (i.e., the image area) */
+		cmpt = image->cmpts_[0];
+		image->tlx_ = cmpt->tlx_;
+		image->tly_ = cmpt->tly_;
+		image->brx_ = cmpt->tlx_ + cmpt->hstep_ * (cmpt->width_ - 1) + 1;
+		image->bry_ = cmpt->tly_ + cmpt->vstep_ * (cmpt->height_ - 1) + 1;
+		for (cmptno = 1; cmptno < image->numcmpts_; ++cmptno) {
+			cmpt = image->cmpts_[cmptno];
+			if (image->tlx_ > cmpt->tlx_) {
+				image->tlx_ = cmpt->tlx_;
+			}
+			if (image->tly_ > cmpt->tly_) {
+				image->tly_ = cmpt->tly_;
+			}
+			x = cmpt->tlx_ + cmpt->hstep_ * (cmpt->width_ - 1) + 1;
+			if (image->brx_ < x) {
+				image->brx_ = x;
+			}
+			y = cmpt->tly_ + cmpt->vstep_ * (cmpt->height_ - 1) + 1;
+			if (image->bry_ < y) {
+				image->bry_ = y;
+			}
+		}
+	} else {
+		image->tlx_ = 0;
+		image->tly_ = 0;
+		image->brx_ = 0;
+		image->bry_ = 0;
+	}
+}
+
+static int jas_image_growcmpts(jas_image_t *image, int maxcmpts)
+{
+	jas_image_cmpt_t **newcmpts;
+	int cmptno;
+
+	newcmpts = (!image->cmpts_) ? jas_malloc(maxcmpts * sizeof(jas_image_cmpt_t *)) :
+	  jas_realloc(image->cmpts_, maxcmpts * sizeof(jas_image_cmpt_t *));
+	if (!newcmpts) {
+		return -1;
+	}
+	image->cmpts_ = newcmpts;
+	image->maxcmpts_ = maxcmpts;
+	for (cmptno = image->numcmpts_; cmptno < image->maxcmpts_; ++cmptno) {
+		image->cmpts_[cmptno] = 0;
+	}
+	return 0;
+}
+
+int jas_image_copycmpt(jas_image_t *dstimage, int dstcmptno, jas_image_t *srcimage,
+  int srccmptno)
+{
+	jas_image_cmpt_t *newcmpt;
+	if (dstimage->numcmpts_ >= dstimage->maxcmpts_) {
+		if (jas_image_growcmpts(dstimage, dstimage->maxcmpts_ + 128)) {
+			return -1;
+		}
+	}
+	if (!(newcmpt = jas_image_cmpt_copy(srcimage->cmpts_[srccmptno]))) {
+		return -1;
+	}
+	if (dstcmptno < dstimage->numcmpts_) {
+		memmove(&dstimage->cmpts_[dstcmptno + 1], &dstimage->cmpts_[dstcmptno],
+		  (dstimage->numcmpts_ - dstcmptno) * sizeof(jas_image_cmpt_t *));
+	}
+	dstimage->cmpts_[dstcmptno] = newcmpt;
+	++dstimage->numcmpts_;
+
+	jas_image_setbbox(dstimage);
+	return 0;
+}
+
+void jas_image_dump(jas_image_t *image, FILE *out)
+{
+	int cmptno;
+	jas_seq2d_t *data;
+	jas_image_cmpt_t *cmpt;
+	if (!(data = jas_seq2d_create(0, 0, 1, 1))) {
+		abort();
+	}
+	for (cmptno = 0; cmptno < image->numcmpts_; ++cmptno) {
+		cmpt = image->cmpts_[cmptno];
+		fprintf(out, "prec=%d sgnd=%d\n", cmpt->prec_, cmpt->sgnd_);
+		if (jas_image_readcmpt(image, cmptno, 0, 0, 1, 1, data)) {
+			abort();
+		}
+		fprintf(out, "tlsample %ld\n", (long) jas_seq2d_get(data, 0, 0));
+	}
+	jas_seq2d_destroy(data);
+}
+
+int jas_image_depalettize(jas_image_t *image, int cmptno, int numlutents,
+  int_fast32_t *lutents, int dtype, int newcmptno)
+{
+	jas_image_cmptparm_t cmptparms;
+	int_fast32_t v;
+	int i;
+	int j;
+	jas_image_cmpt_t *cmpt;
+
+	cmpt = image->cmpts_[cmptno];
+	cmptparms.tlx = cmpt->tlx_;
+	cmptparms.tly = cmpt->tly_;
+	cmptparms.hstep = cmpt->hstep_;
+	cmptparms.vstep = cmpt->vstep_;
+	cmptparms.width = cmpt->width_;
+	cmptparms.height = cmpt->height_;
+	cmptparms.prec = JAS_IMAGE_CDT_GETPREC(dtype);
+	cmptparms.sgnd = JAS_IMAGE_CDT_GETSGND(dtype);
+
+	if (jas_image_addcmpt(image, newcmptno, &cmptparms)) {
+		return -1;
+	}
+	if (newcmptno <= cmptno) {
+		++cmptno;
+		cmpt = image->cmpts_[cmptno];
+	}
+
+	for (j = 0; j < cmpt->height_; ++j) {
+		for (i = 0; i < cmpt->width_; ++i) {
+			v = jas_image_readcmptsample(image, cmptno, i, j);
+			if (v < 0) {
+				v = 0;
+			} else if (v >= numlutents) {
+				v = numlutents - 1;
+			}
+			jas_image_writecmptsample(image, newcmptno, i, j,
+			  lutents[v]);
+		}
+	}
+	return 0;
+}
+
+int jas_image_readcmptsample(jas_image_t *image, int cmptno, int x, int y)
+{
+	jas_image_cmpt_t *cmpt;
+	uint_fast32_t v;
+	int k;
+	int c;
+
+	cmpt = image->cmpts_[cmptno];
+
+	if (jas_stream_seek(cmpt->stream_, (cmpt->width_ * y + x) * cmpt->cps_,
+	  SEEK_SET) < 0) {
+		return -1;
+	}
+	v = 0;
+	for (k = cmpt->cps_; k > 0; --k) {
+		if ((c = jas_stream_getc(cmpt->stream_)) == EOF) {
+			return -1;
+		}
+		v = (v << 8) | (c & 0xff);
+	}
+	return bitstoint(v, cmpt->prec_, cmpt->sgnd_);
+}
+
+void jas_image_writecmptsample(jas_image_t *image, int cmptno, int x, int y,
+  int_fast32_t v)
+{
+	jas_image_cmpt_t *cmpt;
+	uint_fast32_t t;
+	int k;
+	int c;
+
+	cmpt = image->cmpts_[cmptno];
+
+	if (jas_stream_seek(cmpt->stream_, (cmpt->width_ * y + x) * cmpt->cps_,
+	  SEEK_SET) < 0) {
+		return;
+	}
+	t = inttobits(v, cmpt->prec_, cmpt->sgnd_);
+	for (k = cmpt->cps_; k > 0; --k) {
+		c = (t >> (8 * (cmpt->cps_ - 1))) & 0xff;
+		if (jas_stream_putc(cmpt->stream_, (unsigned char) c) == EOF) {
+			return;
+		}
+		t <<= 8;
+	}
+}
+
+int jas_image_getcmptbytype(jas_image_t *image, int ctype)
+{
+	int cmptno;
+
+	for (cmptno = 0; cmptno < image->numcmpts_; ++cmptno) {
+		if (image->cmpts_[cmptno]->type_ == ctype) {
+			return cmptno;
+		}
+	}
+    return -1;
+}
diff --git a/converter/other/jpeg2000/libjasper/base/jas_init.c b/converter/other/jpeg2000/libjasper/base/jas_init.c
new file mode 100644
index 00000000..c1114bf7
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/base/jas_init.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jasper/jas_types.h"
+#include "jasper/jas_image.h"
+#include "jasper/jas_init.h"
+
+/******************************************************************************\
+* Code.
+\******************************************************************************/
+
+/* Initialize the image format table. */
+int jas_init()
+{
+	jas_image_fmtops_t fmtops;
+	int fmtid;
+
+	fmtid = 0;
+
+#if !defined(EXCLUDE_MIF_SUPPORT)
+	fmtops.decode = mif_decode;
+	fmtops.encode = mif_encode;
+	fmtops.validate = mif_validate;
+	jas_image_addfmt(fmtid, "mif", "mif", "My Image Format (MIF)", &fmtops);
+	++fmtid;
+#endif
+
+#if !defined(EXCLUDE_PNM_SUPPORT)
+	fmtops.decode = pnm_decode;
+	fmtops.encode = pnm_encode;
+	fmtops.validate = pnm_validate;
+	jas_image_addfmt(fmtid, "pnm", "pnm", "Portable Graymap/Pixmap (PNM)",
+	  &fmtops);
+	jas_image_addfmt(fmtid, "pnm", "pgm", "Portable Graymap/Pixmap (PNM)",
+	  &fmtops);
+	jas_image_addfmt(fmtid, "pnm", "ppm", "Portable Graymap/Pixmap (PNM)",
+	  &fmtops);
+	++fmtid;
+#endif
+
+#if !defined(EXCLUDE_BMP_SUPPORT)
+	fmtops.decode = bmp_decode;
+	fmtops.encode = bmp_encode;
+	fmtops.validate = bmp_validate;
+	jas_image_addfmt(fmtid, "bmp", "bmp", "Microsoft Bitmap (BMP)", &fmtops);
+	++fmtid;
+#endif
+
+#if !defined(EXCLUDE_RAS_SUPPORT)
+	fmtops.decode = ras_decode;
+	fmtops.encode = ras_encode;
+	fmtops.validate = ras_validate;
+	jas_image_addfmt(fmtid, "ras", "ras", "Sun Rasterfile (RAS)", &fmtops);
+	++fmtid;
+#endif
+
+#if !defined(EXCLUDE_JP2_SUPPORT)
+	fmtops.decode = jp2_decode;
+	fmtops.encode = jp2_encode;
+	fmtops.validate = jp2_validate;
+	jas_image_addfmt(fmtid, "jp2", "jp2",
+	  "JPEG-2000 JP2 File Format Syntax (ISO/IEC 15444-1)", &fmtops);
+	++fmtid;
+	fmtops.decode = jpc_decode;
+	fmtops.encode = jpc_encode;
+	fmtops.validate = jpc_validate;
+	jas_image_addfmt(fmtid, "jpc", "jpc",
+	  "JPEG-2000 Code Stream Syntax (ISO/IEC 15444-1)", &fmtops);
+	++fmtid;
+#endif
+
+#if !defined(EXCLUDE_JPG_SUPPORT)
+	fmtops.decode = jpg_decode;
+	fmtops.encode = jpg_encode;
+	fmtops.validate = jpg_validate;
+	jas_image_addfmt(fmtid, "jpg", "jpg", "JPEG (ISO/IEC 10918-1)", &fmtops);
+	++fmtid;
+#endif
+
+#if !defined(EXCLUDE_PGX_SUPPORT)
+	fmtops.decode = pgx_decode;
+	fmtops.encode = pgx_encode;
+	fmtops.validate = pgx_validate;
+	jas_image_addfmt(fmtid, "pgx", "pgx", "JPEG-2000 VM Format (PGX)", &fmtops);
+	++fmtid;
+#endif
+
+	return 0;
+}
diff --git a/converter/other/jpeg2000/libjasper/base/jas_malloc.c b/converter/other/jpeg2000/libjasper/base/jas_malloc.c
new file mode 100644
index 00000000..43c4d3cd
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/base/jas_malloc.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Memory Allocator
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdio.h>
+
+/* We need the prototype for memset. */
+#include <string.h>
+
+#include "jasper/jas_malloc.h"
+
+/******************************************************************************\
+* Code.
+\******************************************************************************/
+
+#if defined(DEBUG_MEMALLOC)
+#include "../../../local/src/memalloc.c"
+#endif
+
+#if !defined(DEBUG_MEMALLOC)
+
+void *jas_malloc(size_t size)
+{
+	return malloc(size);
+}
+
+void jas_free(void *ptr)
+{
+	free(ptr);
+}
+
+void *jas_realloc(void *ptr, size_t size)
+{
+	return realloc(ptr, size);
+}
+
+void *jas_calloc(size_t nmemb, size_t size)
+{
+	void *ptr;
+	size_t n;
+	n = nmemb * size;
+	if (!(ptr = jas_malloc(n * sizeof(char)))) {
+		return 0;
+	}
+	memset(ptr, 0, n);
+	return ptr;
+}
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/base/jas_seq.c b/converter/other/jpeg2000/libjasper/base/jas_seq.c
new file mode 100644
index 00000000..b8e3c94b
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/base/jas_seq.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Sequence/Matrix Library
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdlib.h>
+#include <assert.h>
+#include <math.h>
+
+#include "jasper/jas_seq.h"
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_math.h"
+
+/******************************************************************************\
+* Constructors and destructors.
+\******************************************************************************/
+
+jas_matrix_t *jas_seq2d_create(int xstart, int ystart, int xend, int yend)
+{
+	jas_matrix_t *matrix;
+	assert(xstart <= xend && ystart <= yend);
+	if (!(matrix = jas_matrix_create(yend - ystart, xend - xstart))) {
+		return 0;
+	}
+	matrix->xstart_ = xstart;
+	matrix->ystart_ = ystart;
+	matrix->xend_ = xend;
+	matrix->yend_ = yend;
+	return matrix;
+}
+
+jas_matrix_t *jas_matrix_create(int numrows, int numcols)
+{
+	jas_matrix_t *matrix;
+	int i;
+
+	if (!(matrix = jas_malloc(sizeof(jas_matrix_t)))) {
+		return 0;
+	}
+	matrix->flags_ = 0;
+	matrix->numrows_ = numrows;
+	matrix->numcols_ = numcols;
+	matrix->rows_ = 0;
+	matrix->maxrows_ = numrows;
+	matrix->data_ = 0;
+	matrix->datasize_ = numrows * numcols;
+
+	if (matrix->maxrows_ > 0) {
+		if (!(matrix->rows_ = jas_malloc(matrix->maxrows_ *
+		  sizeof(jas_seqent_t *)))) {
+			jas_matrix_destroy(matrix);
+			return 0;
+		}
+	}
+
+	if (matrix->datasize_ > 0) {
+		if (!(matrix->data_ = jas_malloc(matrix->datasize_ *
+		  sizeof(jas_seqent_t)))) {
+			jas_matrix_destroy(matrix);
+			return 0;
+		}
+	}
+
+	for (i = 0; i < numrows; ++i) {
+		matrix->rows_[i] = &matrix->data_[i * matrix->numcols_];
+	}
+
+	for (i = 0; i < matrix->datasize_; ++i) {
+		matrix->data_[i] = 0;
+	}
+
+	matrix->xstart_ = 0;
+	matrix->ystart_ = 0;
+	matrix->xend_ = matrix->numcols_;
+	matrix->yend_ = matrix->numrows_;
+
+	return matrix;
+}
+
+void jas_matrix_destroy(jas_matrix_t *matrix)
+{
+	if (matrix->data_) {
+		assert(!(matrix->flags_ & JAS_MATRIX_REF));
+		jas_free(matrix->data_);
+		matrix->data_ = 0;
+	}
+	if (matrix->rows_) {
+		jas_free(matrix->rows_);
+		matrix->rows_ = 0;
+	}
+	jas_free(matrix);
+}
+
+jas_seq2d_t *jas_seq2d_copy(jas_seq2d_t *x)
+{
+	jas_matrix_t *y;
+	int i;
+	int j;
+	y = jas_seq2d_create(jas_seq2d_xstart(x), jas_seq2d_ystart(x), jas_seq2d_xend(x),
+	  jas_seq2d_yend(x));
+	assert(y);
+	for (i = 0; i < x->numrows_; ++i) {
+		for (j = 0; j < x->numcols_; ++j) {
+			*jas_matrix_getref(y, i, j) = jas_matrix_get(x, i, j);
+		}
+	}
+	return y;
+}
+
+jas_matrix_t *jas_matrix_copy(jas_matrix_t *x)
+{
+	jas_matrix_t *y;
+	int i;
+	int j;
+	y = jas_matrix_create(x->numrows_, x->numcols_);
+	for (i = 0; i < x->numrows_; ++i) {
+		for (j = 0; j < x->numcols_; ++j) {
+			*jas_matrix_getref(y, i, j) = jas_matrix_get(x, i, j);
+		}
+	}
+	return y;
+}
+
+/******************************************************************************\
+* Bind operations.
+\******************************************************************************/
+
+void jas_seq2d_bindsub(jas_matrix_t *s, jas_matrix_t *s1, int xstart, int ystart,
+  int xend, int yend)
+{
+	jas_matrix_bindsub(s, s1, ystart - s1->ystart_, xstart - s1->xstart_,
+	  yend - s1->ystart_ - 1, xend - s1->xstart_ - 1);
+}
+
+void jas_matrix_bindsub(jas_matrix_t *mat0, jas_matrix_t *mat1, int r0, int c0,
+  int r1, int c1)
+{
+	int i;
+
+	if (mat0->data_) {
+		if (!(mat0->flags_ & JAS_MATRIX_REF)) {
+			jas_free(mat0->data_);
+		}
+		mat0->data_ = 0;
+		mat0->datasize_ = 0;
+	}
+	if (mat0->rows_) {
+		jas_free(mat0->rows_);
+		mat0->rows_ = 0;
+	}
+	mat0->flags_ |= JAS_MATRIX_REF;
+	mat0->numrows_ = r1 - r0 + 1;
+	mat0->numcols_ = c1 - c0 + 1;
+	mat0->maxrows_ = mat0->numrows_;
+	mat0->rows_ = jas_malloc(mat0->maxrows_ * sizeof(jas_seqent_t *));
+	for (i = 0; i < mat0->numrows_; ++i) {
+		mat0->rows_[i] = mat1->rows_[r0 + i] + c0;
+	}
+
+	mat0->xstart_ = mat1->xstart_ + c0;
+	mat0->ystart_ = mat1->ystart_ + r0;
+	mat0->xend_ = mat0->xstart_ + mat0->numcols_;
+	mat0->yend_ = mat0->ystart_ + mat0->numrows_;
+}
+
+/******************************************************************************\
+* Arithmetic operations.
+\******************************************************************************/
+
+int jas_matrix_cmp(jas_matrix_t *mat0, jas_matrix_t *mat1)
+{
+	int i;
+	int j;
+
+	if (mat0->numrows_ != mat1->numrows_ || mat0->numcols_ !=
+	  mat1->numcols_) {
+		return 1;
+	}
+	for (i = 0; i < mat0->numrows_; i++) {
+		for (j = 0; j < mat0->numcols_; j++) {
+			if (jas_matrix_get(mat0, i, j) != jas_matrix_get(mat1, i, j)) {
+				return 1;
+			}
+		}
+	}
+	return 0;
+}
+
+void jas_matrix_divpow2(jas_matrix_t *matrix, int n)
+{
+	int i;
+	int j;
+	jas_seqent_t *rowstart;
+	int rowstep;
+	jas_seqent_t *data;
+
+	rowstep = jas_matrix_rowstep(matrix);
+	for (i = matrix->numrows_, rowstart = matrix->rows_[0]; i > 0; --i,
+	  rowstart += rowstep) {
+		for (j = matrix->numcols_, data = rowstart; j > 0; --j,
+		  ++data) {
+			*data = (*data >= 0) ? ((*data) >> n) :
+			  (-((-(*data)) >> n));
+		}
+	}
+}
+
+void jas_matrix_clip(jas_matrix_t *matrix, jas_seqent_t minval, jas_seqent_t maxval)
+{
+	int i;
+	int j;
+	jas_seqent_t v;
+	jas_seqent_t *rowstart;
+	jas_seqent_t *data;
+	int rowstep;
+
+	rowstep = jas_matrix_rowstep(matrix);
+	for (i = matrix->numrows_, rowstart = matrix->rows_[0]; i > 0; --i,
+	  rowstart += rowstep) {
+		data = rowstart;
+		for (j = matrix->numcols_, data = rowstart; j > 0; --j,
+		  ++data) {
+			v = *data;
+			if (v < minval) {
+				*data = minval;
+			} else if (v > maxval) {
+				*data = maxval;
+			}
+		}
+	}
+}
+
+void jas_matrix_asr(jas_matrix_t *matrix, int n)
+{
+	int i;
+	int j;
+	jas_seqent_t *rowstart;
+	int rowstep;
+	jas_seqent_t *data;
+
+	assert(n >= 0);
+	rowstep = jas_matrix_rowstep(matrix);
+	for (i = matrix->numrows_, rowstart = matrix->rows_[0]; i > 0; --i,
+	  rowstart += rowstep) {
+		for (j = matrix->numcols_, data = rowstart; j > 0; --j,
+		  ++data) {
+			*data >>= n;
+		}
+	}
+}
+
+void jas_matrix_asl(jas_matrix_t *matrix, int n)
+{
+	int i;
+	int j;
+	jas_seqent_t *rowstart;
+	int rowstep;
+	jas_seqent_t *data;
+
+	rowstep = jas_matrix_rowstep(matrix);
+	for (i = matrix->numrows_, rowstart = matrix->rows_[0]; i > 0; --i,
+	  rowstart += rowstep) {
+		for (j = matrix->numcols_, data = rowstart; j > 0; --j,
+		  ++data) {
+			*data <<= n;
+		}
+	}
+}
+
+/******************************************************************************\
+* Code.
+\******************************************************************************/
+
+int jas_matrix_resize(jas_matrix_t *matrix, int numrows, int numcols)
+{
+	int size;
+	int i;
+
+	size = numrows * numcols;
+	if (size > matrix->datasize_ || numrows > matrix->maxrows_) {
+		return -1;
+	}
+
+	matrix->numrows_ = numrows;
+	matrix->numcols_ = numcols;
+
+	for (i = 0; i < numrows; ++i) {
+		matrix->rows_[i] = &matrix->data_[numcols * i];
+	}
+
+	return 0;
+}
+
+int jas_matrix_output(jas_matrix_t *matrix, FILE *out)
+{
+	int i;
+	int j;
+	jas_seqent_t x;
+
+	fprintf(out, "%d %d\n", jas_matrix_numrows(matrix), jas_matrix_numcols(matrix));
+	for (i = 0; i < jas_matrix_numrows(matrix); ++i) {
+		for (j = 0; j < jas_matrix_numcols(matrix); ++j) {
+			x = jas_matrix_get(matrix, i, j);
+			fprintf(out, "%ld", JAS_CAST(long, x));
+			if (j < jas_matrix_numcols(matrix) - 1) {
+				fprintf(out, " ");
+			}
+		}
+		fprintf(out, "\n");
+	}
+	return 0;
+}
+
+void jas_matrix_setall(jas_matrix_t *matrix, jas_seqent_t val)
+{
+	int i;
+	int j;
+	jas_seqent_t *rowstart;
+	int rowstep;
+	jas_seqent_t *data;
+
+	rowstep = jas_matrix_rowstep(matrix);
+	for (i = matrix->numrows_, rowstart = matrix->rows_[0]; i > 0; --i,
+	  rowstart += rowstep) {
+		for (j = matrix->numcols_, data = rowstart; j > 0; --j,
+		  ++data) {
+			*data = val;
+		}
+	}
+}
+
+jas_matrix_t *jas_matrix_input(FILE *in)
+{
+	jas_matrix_t *matrix;
+	int i;
+	int j;
+	long x;
+	int numrows;
+	int numcols;
+
+	if (fscanf(in, "%d %d", &numrows, &numcols) != 2)
+		return 0;
+	if (!(matrix = jas_matrix_create(numrows, numcols)))
+		return 0;
+
+	/* Get matrix data. */
+	for (i = 0; i < jas_matrix_numrows(matrix); i++) {
+		for (j = 0; j < jas_matrix_numcols(matrix); j++) {
+			if (fscanf(in, "%ld", &x) != 1) {
+				jas_matrix_destroy(matrix);
+				return 0;
+			}
+			jas_matrix_set(matrix, i, j, JAS_CAST(jas_seqent_t, x));
+		}
+	}
+
+	return matrix;
+}
diff --git a/converter/other/jpeg2000/libjasper/base/jas_stream.c b/converter/other/jpeg2000/libjasper/base/jas_stream.c
new file mode 100644
index 00000000..f450aabf
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/base/jas_stream.c
@@ -0,0 +1,1204 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+#define _SVID_SOURCE
+    /* Make sure P_tmpdir is defined in GNU libc 2.0.7 (_XOPEN_SOURCE 500
+       does it in other libc's).  pm_config.h defines TMPDIR as P_tmpdir
+       in some environments.
+    */
+#define _XOPEN_SOURCE 500    /* Make sure P_tmpdir is defined */
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <ctype.h>
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#if defined(WIN32) || defined(HAVE_IO_H)
+#include <io.h>
+#endif
+
+#include "jasper/jas_types.h"
+#include "jasper/jas_stream.h"
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_math.h"
+
+/****************************************************************************\
+* Local function prototypes.
+\****************************************************************************/
+
+static int jas_strtoopenmode(const char *s);
+static void jas_stream_destroy(jas_stream_t *stream);
+static jas_stream_t *jas_stream_create(void);
+static void jas_stream_initbuf(jas_stream_t *stream, int bufmode, char *buf,
+  int bufsize);
+
+static int mem_read(jas_stream_obj_t *obj, char *buf, int cnt);
+static int mem_write(jas_stream_obj_t *obj, char *buf, int cnt);
+static long mem_seek(jas_stream_obj_t *obj, long offset, int origin);
+static int mem_close(jas_stream_obj_t *obj);
+
+static int sfile_read(jas_stream_obj_t *obj, char *buf, int cnt);
+static int sfile_write(jas_stream_obj_t *obj, char *buf, int cnt);
+static long sfile_seek(jas_stream_obj_t *obj, long offset, int origin);
+static int sfile_close(jas_stream_obj_t *obj);
+
+static int file_read(jas_stream_obj_t *obj, char *buf, int cnt);
+static int file_write(jas_stream_obj_t *obj, char *buf, int cnt);
+static long file_seek(jas_stream_obj_t *obj, long offset, int origin);
+static int file_close(jas_stream_obj_t *obj);
+
+/******************************************************************************\
+* Local data.
+\******************************************************************************/
+
+static jas_stream_ops_t jas_stream_fileops = {
+	file_read,
+	file_write,
+	file_seek,
+	file_close
+};
+
+static jas_stream_ops_t jas_stream_sfileops = {
+	sfile_read,
+	sfile_write,
+	sfile_seek,
+	sfile_close
+};
+
+static jas_stream_ops_t jas_stream_memops = {
+	mem_read,
+	mem_write,
+	mem_seek,
+	mem_close
+};
+
+/******************************************************************************\
+* Code for opening and closing streams.
+\******************************************************************************/
+
+static jas_stream_t *jas_stream_create()
+{
+	jas_stream_t *stream;
+
+	if (!(stream = jas_malloc(sizeof(jas_stream_t)))) {
+		return 0;
+	}
+	stream->openmode_ = 0;
+	stream->bufmode_ = 0;
+	stream->flags_ = 0;
+	stream->bufbase_ = 0;
+	stream->bufstart_ = 0;
+	stream->bufsize_ = 0;
+	stream->ptr_ = 0;
+	stream->cnt_ = 0;
+	stream->ops_ = 0;
+	stream->obj_ = 0;
+	stream->rwcnt_ = 0;
+	stream->rwlimit_ = -1;
+
+	return stream;
+}
+
+jas_stream_t *jas_stream_memopen(char *buf, int bufsize)
+{
+	jas_stream_t *stream;
+	jas_stream_memobj_t *obj;
+
+	if (!(stream = jas_stream_create())) {
+		return 0;
+	}
+
+	/* A stream associated with a memory buffer is always opened
+	for both reading and writing in binary mode. */
+	stream->openmode_ = JAS_STREAM_READ | JAS_STREAM_WRITE | JAS_STREAM_BINARY;
+
+	/* Since the stream data is already resident in memory, buffering
+	is not necessary. */
+	/* But... It still may be faster to use buffering anyways. */
+	jas_stream_initbuf(stream, JAS_STREAM_FULLBUF, 0, 0);
+
+	/* Select the operations for a memory stream. */
+	stream->ops_ = &jas_stream_memops;
+
+	/* Allocate memory for the underlying memory stream object. */
+	if (!(obj = jas_malloc(sizeof(jas_stream_memobj_t)))) {
+		jas_stream_destroy(stream);
+		return 0;
+	}
+	stream->obj_ = (void *) obj;
+
+	/* Initialize a few important members of the memory stream object. */
+	obj->myalloc_ = 0;
+	obj->buf_ = 0;
+
+	/* If the buffer size specified is nonpositive, then the buffer
+	is allocated internally and automatically grown as needed. */
+	if (bufsize <= 0) {
+		obj->bufsize_ = 1024;
+		obj->growable_ = 1;
+	} else {
+		obj->bufsize_ = bufsize;
+		obj->growable_ = 0;
+	}
+	if (buf) {
+		obj->buf_ = (unsigned char *) buf;
+	} else {
+		obj->buf_ = jas_malloc(obj->bufsize_ * sizeof(char));
+		obj->myalloc_ = 1;
+	}
+	if (!obj->buf_) {
+		jas_stream_close(stream);
+		return 0;
+	}
+
+	if (bufsize > 0 && buf) {
+		/* If a buffer was supplied by the caller and its length is positive,
+		  make the associated buffer data appear in the stream initially. */
+		obj->len_ = bufsize;
+	} else {
+		/* The stream is initially empty. */
+		obj->len_ = 0;
+	}
+	obj->pos_ = 0;
+	
+	return stream;
+}
+
+jas_stream_t *jas_stream_fopen(const char *filename, const char *mode)
+{
+	jas_stream_t *stream;
+	int *obj;
+	int openflags;
+
+	/* Allocate a stream object. */
+	if (!(stream = jas_stream_create())) {
+		return 0;
+	}
+
+	/* Parse the mode string. */
+	stream->openmode_ = jas_strtoopenmode(mode);
+
+	/* Determine the correct flags to use for opening the file. */
+	if ((stream->openmode_ & JAS_STREAM_READ) &&
+	  (stream->openmode_ & JAS_STREAM_WRITE)) {
+		openflags = O_RDWR;
+	} else if (stream->openmode_ & JAS_STREAM_READ) {
+		openflags = O_RDONLY;
+	} else if (stream->openmode_ & JAS_STREAM_WRITE) {
+		openflags = O_WRONLY;
+	} else {
+		openflags = 0;
+	}
+	if (stream->openmode_ & JAS_STREAM_APPEND) {
+		openflags |= O_APPEND;
+	}
+	if (stream->openmode_ & JAS_STREAM_BINARY) {
+		openflags |= O_BINARY;
+	}
+	if (stream->openmode_ & JAS_STREAM_CREATE) {
+		openflags |= O_CREAT | O_TRUNC;
+	}
+
+	/* Allocate space for the underlying file stream object. */
+	if (!(obj = jas_malloc(sizeof(int)))) {
+		jas_stream_destroy(stream);
+		return 0;
+	}
+	stream->obj_ = (void *) obj;
+
+	/* Select the operations for a file stream object. */
+	stream->ops_ = &jas_stream_fileops;
+
+	/* Open the underlying file. */
+	if ((*obj = open(filename, openflags, JAS_STREAM_PERMS)) < 0) {
+		jas_stream_destroy(stream);
+		return 0;
+	}
+
+	/* By default, use full buffering for this type of stream. */
+	jas_stream_initbuf(stream, JAS_STREAM_FULLBUF, 0, 0);
+
+	return stream;
+}
+
+jas_stream_t *jas_stream_freopen(const char *path, const char *mode, FILE *fp)
+{
+	jas_stream_t *stream;
+	int openflags;
+
+	/* Allocate a stream object. */
+	if (!(stream = jas_stream_create())) {
+		return 0;
+	}
+
+	/* Parse the mode string. */
+	stream->openmode_ = jas_strtoopenmode(mode);
+
+	/* Determine the correct flags to use for opening the file. */
+	if ((stream->openmode_ & JAS_STREAM_READ) &&
+	  (stream->openmode_ & JAS_STREAM_WRITE)) {
+		openflags = O_RDWR;
+	} else if (stream->openmode_ & JAS_STREAM_READ) {
+		openflags = O_RDONLY;
+	} else if (stream->openmode_ & JAS_STREAM_WRITE) {
+		openflags = O_WRONLY;
+	} else {
+		openflags = 0;
+	}
+	if (stream->openmode_ & JAS_STREAM_APPEND) {
+		openflags |= O_APPEND;
+	}
+	if (stream->openmode_ & JAS_STREAM_BINARY) {
+		openflags |= O_BINARY;
+	}
+	if (stream->openmode_ & JAS_STREAM_CREATE) {
+		openflags |= O_CREAT | O_TRUNC;
+	}
+
+	stream->obj_ = JAS_CAST(void *, fp);
+
+	/* Select the operations for a file stream object. */
+	stream->ops_ = &jas_stream_sfileops;
+
+	/* By default, use full buffering for this type of stream. */
+	jas_stream_initbuf(stream, JAS_STREAM_FULLBUF, 0, 0);
+
+	return stream;
+}
+
+
+static int
+tmpfilex(void) {
+/*----------------------------------------------------------------------------
+   This is a copy of pm_tmpfile() without the libnetpbm dependencies
+   and returning a file descriptor instead of stream.
+-----------------------------------------------------------------------------*/
+    const char libname[] = "jasper";
+
+    int fd;
+    char buf[FILENAME_MAX];
+    const char * tbuf;
+    unsigned int fnamelen;
+
+    fnamelen = strlen(libname) + 10; /* "/" + "_XXXXXX\0" */
+
+    tbuf = getenv("TMPDIR");
+
+    if ((tbuf != NULL) && (strlen(tbuf) > FILENAME_MAX - fnamelen))
+        /* length of TMPDIR value too big for buf */
+        tbuf = NULL;
+
+    buf[FILENAME_MAX - fnamelen -1] = 0;
+
+    if ((tbuf == NULL) || (strlen(tbuf) == 0))
+        /* environment variable not suitable to construct file name.
+           Use default.
+        */
+        strncpy(buf, TMPDIR, sizeof(buf) - fnamelen);
+    else
+        strncpy(buf, tbuf, sizeof(buf) - fnamelen);
+
+    if (buf[strlen(buf) - 1] != '/')
+        strcat (buf, "/");
+    
+    strcat(buf, libname);
+    strcat(buf, "_XXXXXX");
+
+    fd = mkstemp(buf);
+
+    if (fd >= 0)
+        unlink(buf);
+
+    return fd;
+}
+
+jas_stream_t *jas_stream_tmpfile()
+{
+	jas_stream_t *stream;
+	int *obj;
+
+	if (!(stream = jas_stream_create())) {
+		return 0;
+	}
+
+	/* A temporary file stream is always opened for both reading and
+	writing in binary mode. */
+	stream->openmode_ = JAS_STREAM_READ | JAS_STREAM_WRITE | JAS_STREAM_BINARY;
+
+	/* Allocate memory for the underlying temporary file object. */
+	if (!(obj = jas_malloc(sizeof(int)))) {
+		jas_stream_destroy(stream);
+		return 0;
+	}
+	stream->obj_ = obj;
+    
+    /* This is a Netpbm enhancement.  Original Jasper library uses
+       tmpnam(), which is unsafe.
+    */
+    if ((*obj = tmpfilex()) < 0) {
+		jas_stream_destroy(stream);
+		return 0;
+    }
+	/* Use full buffering. */
+	jas_stream_initbuf(stream, JAS_STREAM_FULLBUF, 0, 0);
+
+	stream->ops_ = &jas_stream_fileops;
+
+	return stream;
+}
+
+jas_stream_t *jas_stream_fdopen(int fd, const char *mode)
+{
+	jas_stream_t *stream;
+	int *obj;
+
+	/* Allocate a stream object. */
+	if (!(stream = jas_stream_create())) {
+		return 0;
+	}
+
+	/* Parse the mode string. */
+	stream->openmode_ = jas_strtoopenmode(mode);
+
+#if defined(WIN32)
+	/* Argh!!!  Someone ought to banish text mode (i.e., O_TEXT) to the
+	  greatest depths of purgatory! */
+	/* Ensure that the file descriptor is in binary mode, if the caller
+	  has specified the binary mode flag.  Arguably, the caller ought to
+	  take care of this, but text mode is a ugly wart anyways, so we save
+	  the caller some grief by handling this within the stream library. */
+	/* This ugliness is mainly for the benefit of those who run the
+	  JasPer software under Windows from shells that insist on opening
+	  files in text mode.  For example, in the Cygwin environment,
+	  shells often open files in text mode when I/O redirection is
+	  used.  Grr... */
+	if (stream->openmode_ & JAS_STREAM_BINARY) {
+		setmode(fd, O_BINARY);
+	}
+#endif
+
+	/* Allocate space for the underlying file stream object. */
+	if (!(obj = jas_malloc(sizeof(int)))) {
+		jas_stream_destroy(stream);
+		return 0;
+	}
+	stream->obj_ = (void *) obj;
+	*obj = fd;
+
+	/* By default, use full buffering for this type of stream. */
+	jas_stream_initbuf(stream, JAS_STREAM_FULLBUF, 0, 0);
+
+	/* Select the operations for a file stream object. */
+	stream->ops_ = &jas_stream_fileops;
+
+/* Do not close the underlying file descriptor when the stream is closed. */
+	stream->openmode_ |= JAS_STREAM_NOCLOSE;
+
+	return stream;
+}
+
+static void jas_stream_destroy(jas_stream_t *stream)
+{
+	/* If the memory for the buffer was allocated with malloc, free
+	this memory. */
+	if ((stream->bufmode_ & JAS_STREAM_FREEBUF) && stream->bufbase_) {
+		jas_free(stream->bufbase_);
+		stream->bufbase_ = 0;
+	}
+	jas_free(stream);
+}
+
+int jas_stream_close(jas_stream_t *stream)
+{
+	/* Flush buffer if necessary. */
+	jas_stream_flush(stream);
+
+	/* Close the underlying stream object. */
+	if (!(stream->openmode_ & JAS_STREAM_NOCLOSE)) {
+		(*stream->ops_->close_)(stream->obj_);
+	}
+
+	jas_stream_destroy(stream);
+
+	return 0;
+}
+
+/******************************************************************************\
+* Code for reading and writing streams.
+\******************************************************************************/
+
+int jas_stream_getc_func(jas_stream_t *stream)
+{
+	assert(stream->ptr_ - stream->bufbase_ <= stream->bufsize_ +
+	  JAS_STREAM_MAXPUTBACK);
+	return jas_stream_getc_macro(stream);
+}
+
+int jas_stream_putc_func(jas_stream_t *stream, int c)
+{
+	assert(stream->ptr_ - stream->bufstart_ <= stream->bufsize_);
+	return jas_stream_putc_macro(stream, c);
+}
+
+int jas_stream_ungetc(jas_stream_t *stream, int c)
+{
+	if (!stream->ptr_ || stream->ptr_ == stream->bufbase_) {
+		return -1;
+	}
+
+	/* Reset the EOF indicator (since we now have at least one character
+	  to read). */
+	stream->flags_ &= ~JAS_STREAM_EOF;
+
+	--stream->rwcnt_;
+	--stream->ptr_;
+	++stream->cnt_;
+	*stream->ptr_ = c;
+	return 0;
+}
+
+int jas_stream_read(jas_stream_t *stream, void *buf, int cnt)
+{
+	int n;
+	int c;
+	char *bufptr;
+
+	bufptr = buf;
+
+	n = 0;
+	while (n < cnt) {
+		if ((c = jas_stream_getc(stream)) == EOF) {
+			return n;
+		}
+		*bufptr++ = c;
+		++n;
+	}
+
+	return n;
+}
+
+int jas_stream_write(jas_stream_t *stream, const void *buf, int cnt)
+{
+	int n;
+	const char *bufptr;
+
+	bufptr = buf;
+
+	n = 0;
+	while (n < cnt) {
+		if (jas_stream_putc(stream, *bufptr) == EOF) {
+			return n;
+		}
+		++bufptr;
+		++n;
+	}
+
+	return n;
+}
+
+/* Note: This function uses a fixed size buffer.  Therefore, it cannot
+  handle invocations that will produce more output than can be held
+  by the buffer. */
+int jas_stream_printf(jas_stream_t *stream, const char *fmt, ...)
+{
+	va_list ap;
+	char buf[4096];
+	int ret;
+
+	va_start(ap, fmt);
+	ret = vsprintf(buf, fmt, ap);
+	jas_stream_puts(stream, buf);
+	va_end(ap);
+	return ret;
+}
+
+int jas_stream_puts(jas_stream_t *stream, const char *s)
+{
+	while (*s != '\0') {
+		if (jas_stream_putc_macro(stream, *s) == EOF) {
+			return -1;
+		}
+		++s;
+	}
+	return 0;
+}
+
+char *jas_stream_gets(jas_stream_t *stream, char *buf, int bufsize)
+{
+	int c;
+	char *bufptr;
+	assert(bufsize > 0);
+
+	bufptr = buf;
+	while (bufsize > 1) {
+		if ((c = jas_stream_getc(stream)) == EOF) {
+			break;
+		}
+		*bufptr++ = c;
+		--bufsize;
+		if (c == '\n') {
+			break;
+		}
+	}
+	*bufptr = '\0';
+	return buf;
+}
+
+int jas_stream_gobble(jas_stream_t *stream, int n)
+{
+	int m;
+	m = n;
+	for (m = n; m > 0; --m) {
+		if (jas_stream_getc(stream) == EOF) {
+			return n - m;
+		}
+	}
+	return n;
+}
+
+/******************************************************************************\
+* Code for getting and setting the stream position.
+\******************************************************************************/
+
+int jas_stream_isseekable(jas_stream_t *stream)
+{
+	if (stream->ops_ == &jas_stream_memops) {
+		return 1;
+	} else if (stream->ops_ == &jas_stream_fileops) {
+		if ((*stream->ops_->seek_)(stream->obj_, 0, SEEK_CUR) < 0) {
+			return 0;
+		}
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+int jas_stream_rewind(jas_stream_t *stream)
+{
+	return jas_stream_seek(stream, 0, SEEK_SET);
+}
+
+long jas_stream_seek(jas_stream_t *stream, long offset, int origin)
+{
+	long newpos;
+
+	/* The buffer cannot be in use for both reading and writing. */
+	assert(!((stream->bufmode_ & JAS_STREAM_RDBUF) && (stream->bufmode_ &
+	  JAS_STREAM_WRBUF)));
+
+	/* Reset the EOF indicator (since we may not be at the EOF anymore). */
+	stream->flags_ &= ~JAS_STREAM_EOF;
+
+	if (stream->bufmode_ & JAS_STREAM_RDBUF) {
+		if (origin == SEEK_CUR) {
+			offset -= stream->cnt_;
+		}
+	} else if (stream->bufmode_ & JAS_STREAM_WRBUF) {
+		if (jas_stream_flush(stream)) {
+			return -1;
+		}
+	}
+	stream->cnt_ = 0;
+	stream->ptr_ = stream->bufstart_;
+	stream->bufmode_ &= ~(JAS_STREAM_RDBUF | JAS_STREAM_WRBUF);
+
+	if ((newpos = (*stream->ops_->seek_)(stream->obj_, offset, origin))
+	  < 0) {
+		return -1;
+	}
+
+	return newpos;
+}
+
+long jas_stream_tell(jas_stream_t *stream)
+{
+	int adjust;
+	int offset;
+
+	if (stream->bufmode_ & JAS_STREAM_RDBUF) {
+		adjust = -stream->cnt_;
+	} else if (stream->bufmode_ & JAS_STREAM_WRBUF) {
+		adjust = stream->ptr_ - stream->bufstart_;
+	} else {
+		adjust = 0;
+	}
+
+	if ((offset = (*stream->ops_->seek_)(stream->obj_, 0, SEEK_CUR)) < 0) {
+		return -1;
+	}
+
+	return offset + adjust;
+}
+
+/******************************************************************************\
+* Buffer initialization code.
+\******************************************************************************/
+
+static void jas_stream_initbuf(jas_stream_t *stream, int bufmode, char *buf,
+  int bufsize)
+{
+	/* If this function is being called, the buffer should not have been
+	  initialized yet. */
+	assert(!stream->bufbase_);
+
+	if (bufmode != JAS_STREAM_UNBUF) {
+		/* The full- or line-buffered mode is being employed. */
+		if (!buf) {
+			/* The caller has not specified a buffer to employ, so allocate
+			  one. */
+			if ((stream->bufbase_ = jas_malloc(JAS_STREAM_BUFSIZE +
+			  JAS_STREAM_MAXPUTBACK))) {
+				stream->bufmode_ |= JAS_STREAM_FREEBUF;
+				stream->bufsize_ = JAS_STREAM_BUFSIZE;
+			} else {
+				/* The buffer allocation has failed.  Resort to unbuffered
+				  operation. */
+				stream->bufbase_ = stream->tinybuf_;
+				stream->bufsize_ = 1;
+			}
+		} else {
+			/* The caller has specified a buffer to employ. */
+			/* The buffer must be large enough to accommodate maximum
+			  putback. */
+			assert(bufsize > JAS_STREAM_MAXPUTBACK);
+			stream->bufbase_ = JAS_CAST(unsigned char *, buf);
+			stream->bufsize_ = bufsize - JAS_STREAM_MAXPUTBACK;
+		}
+	} else {
+		/* The unbuffered mode is being employed. */
+		/* A buffer should not have been supplied by the caller. */
+		assert(!buf);
+		/* Use a trivial one-character buffer. */
+		stream->bufbase_ = stream->tinybuf_;
+		stream->bufsize_ = 1;
+	}
+	stream->bufstart_ = &stream->bufbase_[JAS_STREAM_MAXPUTBACK];
+	stream->ptr_ = stream->bufstart_;
+	stream->cnt_ = 0;
+	stream->bufmode_ |= bufmode & JAS_STREAM_BUFMODEMASK;
+}
+
+/******************************************************************************\
+* Buffer filling and flushing code.
+\******************************************************************************/
+
+int jas_stream_flush(jas_stream_t *stream)
+{
+	if (stream->bufmode_ & JAS_STREAM_RDBUF) {
+		return 0;
+	}
+	return jas_stream_flushbuf(stream, EOF);
+}
+
+int jas_stream_fillbuf(jas_stream_t *stream, int getflag)
+{
+	int c;
+
+	/* The stream must not be in an error or EOF state. */
+	if ((stream->flags_ & (JAS_STREAM_ERRMASK)) != 0) {
+		return EOF;
+	}
+
+	/* The stream must be open for reading. */
+	if ((stream->openmode_ & JAS_STREAM_READ) == 0) {
+		return EOF;
+	}
+
+	/* Make a half-hearted attempt to confirm that the buffer is not
+	currently being used for writing.  This check is not intended
+	to be foolproof! */
+	assert((stream->bufmode_ & JAS_STREAM_WRBUF) == 0);
+
+	assert(stream->ptr_ - stream->bufstart_ <= stream->bufsize_);
+
+	/* Mark the buffer as being used for reading. */
+	stream->bufmode_ |= JAS_STREAM_RDBUF;
+
+	/* Read new data into the buffer. */
+	stream->ptr_ = stream->bufstart_;
+	if ((stream->cnt_ = (*stream->ops_->read_)(stream->obj_,
+	  (char *) stream->bufstart_, stream->bufsize_)) <= 0) {
+		if (stream->cnt_ < 0) {
+			stream->flags_ |= JAS_STREAM_ERR;
+		} else {
+			stream->flags_ |= JAS_STREAM_EOF;
+		}
+		stream->cnt_ = 0;
+		return EOF;
+	}
+
+	assert(stream->cnt_ > 0);
+	/* Get or peek at the first character in the buffer. */
+	c = (getflag) ? jas_stream_getc2(stream) : (*stream->ptr_);
+
+	return c;
+}
+
+int jas_stream_flushbuf(jas_stream_t *stream, int c)
+{
+	int len;
+	int n;
+
+	/* The stream should not be in an error or EOF state. */
+	if ((stream->flags_ & (JAS_STREAM_ERRMASK)) != 0) {
+		return EOF;
+	}
+
+	/* The stream must be open for writing. */
+	if ((stream->openmode_ & (JAS_STREAM_WRITE | JAS_STREAM_APPEND)) == 0) {
+		return EOF;
+	}
+
+	/* The buffer should not currently be in use for reading. */
+	assert(!(stream->bufmode_ & JAS_STREAM_RDBUF));
+
+	/* Note: Do not use the quantity stream->cnt to determine the number
+	of characters in the buffer!  Depending on how this function was
+	called, the stream->cnt value may be "off-by-one". */
+	len = stream->ptr_ - stream->bufstart_;
+	if (len > 0) {
+		n = (*stream->ops_->write_)(stream->obj_, (char *)
+		  stream->bufstart_, len);
+		if (n != len) {
+			stream->flags_ |= JAS_STREAM_ERR;
+			return EOF;
+		}
+	}
+	stream->cnt_ = stream->bufsize_;
+	stream->ptr_ = stream->bufstart_;
+
+	stream->bufmode_ |= JAS_STREAM_WRBUF;
+
+	if (c != EOF) {
+		assert(stream->cnt_ > 0);
+		jas_stream_putc2(stream, c);
+	}
+
+	return 0;
+}
+
+/******************************************************************************\
+* Miscellaneous code.
+\******************************************************************************/
+
+static int jas_strtoopenmode(const char *s)
+{
+	int openmode = 0;
+	while (*s != '\0') {
+		switch (*s) {
+		case 'r':
+			openmode |= JAS_STREAM_READ;
+			break;
+		case 'w':
+			openmode |= JAS_STREAM_WRITE | JAS_STREAM_CREATE;
+			break;
+		case 'b':
+			openmode |= JAS_STREAM_BINARY;
+			break;
+		case 'a':
+			openmode |= JAS_STREAM_APPEND;
+			break;
+		case '+':
+			openmode |= JAS_STREAM_READ | JAS_STREAM_WRITE;
+			break;
+		default:
+			break;
+		}
+		++s;
+	}
+	return openmode;
+}
+
+int jas_stream_copy(jas_stream_t *out, jas_stream_t *in, int n)
+{
+	int all;
+	int c;
+	int m;
+
+	all = (n < 0) ? 1 : 0;
+
+	m = n;
+	while (all || m > 0) {
+		if ((c = jas_stream_getc_macro(in)) == EOF) {
+			/* The next character of input could not be read. */
+			/* Return with an error if an I/O error occured
+			  (not including EOF) or if an explicit copy count
+			  was specified. */
+			return (!all || jas_stream_error(in)) ? (-1) : 0;
+		}
+		if (jas_stream_putc_macro(out, c) == EOF) {
+			return -1;
+		}
+		--m;
+	}
+	return 0;
+}
+
+long jas_stream_setrwcount(jas_stream_t *stream, long rwcnt)
+{
+	int old;
+
+	old = stream->rwcnt_;
+	stream->rwcnt_ = rwcnt;
+	return old;
+}
+
+int jas_stream_display(jas_stream_t *stream, FILE *fp, int n)
+{
+	unsigned char buf[16];
+	int i;
+	int j;
+	int m;
+	int c;
+	int display;
+	int cnt;
+
+	cnt = n - (n % 16);
+	display = 1;
+
+	for (i = 0; i < n; i += 16) {
+		if (n > 16 && i > 0) {
+			display = (i >= cnt) ? 1 : 0;
+		}
+		if (display) {
+			fprintf(fp, "%08x:", i);
+		}
+		m = JAS_MIN(n - i, 16);
+		for (j = 0; j < m; ++j) {
+			if ((c = jas_stream_getc(stream)) == EOF) {
+				abort();
+				return -1;
+			}
+			buf[j] = c;
+		}
+		if (display) {
+			for (j = 0; j < m; ++j) {
+				fprintf(fp, " %02x", buf[j]);
+			}
+			fputc(' ', fp);
+			for (; j < 16; ++j) {
+				fprintf(fp, "   ");
+			}
+			for (j = 0; j < m; ++j) {
+				if (isprint(buf[j])) {
+					fputc(buf[j], fp);
+				} else {
+					fputc(' ', fp);
+				}
+			}
+			fprintf(fp, "\n");
+		}
+
+
+	}
+	return 0;
+}
+
+long jas_stream_length(jas_stream_t *stream)
+{
+	long oldpos;
+	long pos;
+	if ((oldpos = jas_stream_tell(stream)) < 0) {
+		return -1;
+	}
+	if (jas_stream_seek(stream, 0, SEEK_END) < 0) {
+		return -1;
+	}
+	if ((pos = jas_stream_tell(stream)) < 0) {
+		return -1;
+	}
+	if (jas_stream_seek(stream, oldpos, SEEK_SET) < 0) {
+		return -1;
+	}
+	return pos;
+}
+
+/******************************************************************************\
+* Memory stream object.
+\******************************************************************************/
+
+static int mem_read(jas_stream_obj_t *obj, char *buf, int cnt)
+{
+	int n;
+	jas_stream_memobj_t *m = (jas_stream_memobj_t *)obj;
+	n = m->len_ - m->pos_;
+	cnt = JAS_MIN(n, cnt);
+	memcpy(buf, &m->buf_[m->pos_], cnt);
+	m->pos_ += cnt;
+	return cnt;
+}
+
+static int mem_resize(jas_stream_memobj_t *m, int bufsize)
+{
+	unsigned char *buf;
+
+	assert(m->buf_);
+	if (!(buf = jas_realloc(m->buf_, bufsize * sizeof(unsigned char)))) {
+		return -1;
+	}
+	m->buf_ = buf;
+	m->bufsize_ = bufsize;
+	return 0;
+}
+
+static int mem_write(jas_stream_obj_t *obj, char *buf, int cnt)
+{
+	int n;
+	int ret;
+	jas_stream_memobj_t *m = (jas_stream_memobj_t *)obj;
+	long newbufsize;
+	long newpos;
+
+	newpos = m->pos_ + cnt;
+	if (newpos > m->bufsize_ && m->growable_) {
+		newbufsize = m->bufsize_;
+		while (newbufsize < newpos) {
+			newbufsize <<= 1;
+			assert(newbufsize >= 0);
+		}
+		if (mem_resize(m, newbufsize)) {
+			return -1;
+		}
+	}
+	if (m->pos_ > m->len_) {
+		/* The current position is beyond the end of the file, so
+		  pad the file to the current position with zeros. */
+		n = JAS_MIN(m->pos_, m->bufsize_) - m->len_;
+		if (n > 0) {
+			memset(&m->buf_[m->len_], 0, n);
+			m->len_ += n;
+		}
+		if (m->pos_ != m->len_) {
+			/* The buffer is not big enough. */
+			return 0;
+		}
+	}
+	n = m->bufsize_ - m->pos_;
+	ret = JAS_MIN(n, cnt);
+	if (ret > 0) {
+		memcpy(&m->buf_[m->pos_], buf, ret);
+		m->pos_ += ret;
+	}
+	if (m->pos_ > m->len_) {
+		m->len_ = m->pos_;
+	}
+assert(ret == cnt);
+	return ret;
+}
+
+static long mem_seek(jas_stream_obj_t *obj, long offset, int origin)
+{
+	jas_stream_memobj_t *m = (jas_stream_memobj_t *)obj;
+	long newpos;
+
+	switch (origin) {
+	case SEEK_SET:
+		newpos = offset;
+		break;
+	case SEEK_END:
+		newpos = m->len_ - offset;
+		break;
+	case SEEK_CUR:
+		newpos = m->pos_ + offset;
+		break;
+	default:
+		abort();
+		break;
+	}
+	if (newpos < 0) {
+		return -1;
+	}
+	m->pos_ = newpos;
+
+	return m->pos_;
+}
+
+static int mem_close(jas_stream_obj_t *obj)
+{
+	jas_stream_memobj_t *m = (jas_stream_memobj_t *)obj;
+	if (m->myalloc_ && m->buf_) {
+		jas_free(m->buf_);
+		m->buf_ = 0;
+	}
+	jas_free(obj);
+	return 0;
+}
+
+/******************************************************************************\
+* File stream object.
+\******************************************************************************/
+
+static int file_read(jas_stream_obj_t *obj, char *buf, int cnt)
+{
+	int fd;
+	fd = *((int *)obj);
+	return read(fd, buf, cnt);
+}
+
+static int file_write(jas_stream_obj_t *obj, char *buf, int cnt)
+{
+	int fd;
+	fd = *((int *)obj);
+	return write(fd, buf, cnt);
+}
+
+static long file_seek(jas_stream_obj_t *obj, long offset, int origin)
+{
+	int fd;
+	fd = *((int *)obj);
+	return lseek(fd, offset, origin);
+}
+
+static int file_close(jas_stream_obj_t *obj)
+{
+	int fd;
+	fd = *((int *)obj);
+	jas_free(obj);
+	return close(fd);
+}
+
+/******************************************************************************\
+* Stdio file stream object.
+\******************************************************************************/
+
+static int sfile_read(jas_stream_obj_t *obj, char *buf, int cnt)
+{
+	FILE *fp;
+	fp = JAS_CAST(FILE *, obj);
+	return fread(buf, 1, cnt, fp);
+}
+
+static int sfile_write(jas_stream_obj_t *obj, char *buf, int cnt)
+{
+	FILE *fp;
+	fp = JAS_CAST(FILE *, obj);
+	return fwrite(buf, 1, cnt, fp);
+}
+
+static long sfile_seek(jas_stream_obj_t *obj, long offset, int origin)
+{
+	FILE *fp;
+	fp = JAS_CAST(FILE *, obj);
+	return fseek(fp, offset, origin);
+}
+
+static int sfile_close(jas_stream_obj_t *obj)
+{
+	FILE *fp;
+	fp = JAS_CAST(FILE *, obj);
+	return fclose(fp);
+}
diff --git a/converter/other/jpeg2000/libjasper/base/jas_string.c b/converter/other/jpeg2000/libjasper/base/jas_string.c
new file mode 100644
index 00000000..945731a1
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/base/jas_string.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * String Library
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes
+\******************************************************************************/
+
+#include <string.h>
+
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_string.h"
+
+/******************************************************************************\
+* Miscellaneous Functions
+\******************************************************************************/
+
+/* This function is equivalent to the popular but non-standard (and
+  not-always-available) strdup function. */
+
+char *jas_strdup(const char *s)
+{
+	int n;
+	char *p;
+	n = strlen(s) + 1;
+	if (!(p = jas_malloc(n * sizeof(char)))) {
+		return 0;
+	}
+	strcpy(p, s);
+	return p;
+}
diff --git a/converter/other/jpeg2000/libjasper/base/jas_tvp.c b/converter/other/jpeg2000/libjasper/base/jas_tvp.c
new file mode 100644
index 00000000..aab12703
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/base/jas_tvp.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Tag-Value Parser Library
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <assert.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_string.h"
+#include "jasper/jas_tvp.h"
+
+/******************************************************************************\
+* Macros.
+\******************************************************************************/
+
+/* Is the specified character valid for a tag name? */
+#define	JAS_TVP_ISTAG(x) \
+	(isalpha(x) || (x) == '_' || isdigit(x))
+
+/******************************************************************************\
+* Code for creating and destroying a tag-value parser.
+\******************************************************************************/
+
+jas_tvparser_t *jas_tvparser_create(const char *s)
+{
+	jas_tvparser_t *tvp;
+	if (!(tvp = jas_malloc(sizeof(jas_tvparser_t)))) {
+		return 0;
+	}
+	if (!(tvp->buf = jas_strdup(s))) {
+		jas_tvparser_destroy(tvp);
+		return 0;
+	}
+	tvp->pos = tvp->buf;
+	tvp->tag = 0;
+	tvp->val = 0;
+	return tvp;
+}
+
+void jas_tvparser_destroy(jas_tvparser_t *tvp)
+{
+	if (tvp->buf) {
+		jas_free(tvp->buf);
+	}
+	jas_free(tvp);
+}
+
+/******************************************************************************\
+* Main parsing code.
+\******************************************************************************/
+
+/* Get the next tag-value pair. */
+int jas_tvparser_next(jas_tvparser_t *tvp)
+{
+	char *p;
+	char *tag;
+	const char *val;
+
+	/* Skip any leading whitespace. */
+	p = tvp->pos;
+	while (*p != '\0' && isspace(*p)) {
+		++p;
+	}
+
+	/* Has the end of the input data been reached? */
+	if (*p == '\0') {
+		/* No more tags are present. */
+		tvp->pos = p;
+		return 1;
+	}
+
+	/* Does the tag name begin with a valid character? */
+	if (!JAS_TVP_ISTAG(*p)) {
+		return -1;
+	}
+
+	/* Remember where the tag name begins. */
+	tag = p;
+
+	/* Find the end of the tag name. */
+	while (*p != '\0' && JAS_TVP_ISTAG(*p)) {
+		++p;
+	}
+
+	/* Has the end of the input data been reached? */
+	if (*p == '\0') {
+		/* The value field is empty. */
+		tvp->tag = tag;
+		tvp->val = "";
+		tvp->pos = p;
+		return 0;
+	}
+
+	/* Is a value field not present? */
+	if (*p != '=') {
+		if (*p != '\0' && !isspace(*p)) {
+			return -1;
+		}
+		*p++ = '\0';
+		tvp->tag = tag;
+		tvp->val = "";
+		tvp->pos = p;
+		return 0;
+	}
+
+	*p++ = '\0';
+
+	val = p;
+	while (*p != '\0' && !isspace(*p)) {
+		++p;
+	}
+
+	if (*p != '\0') {
+		*p++ = '\0';
+	}
+
+	tvp->pos = p;
+	tvp->tag = tag;
+	tvp->val = val;
+
+	return 0;
+}
+
+/******************************************************************************\
+* Code for querying the current tag/value.
+\******************************************************************************/
+
+/* Get the current tag. */
+char *jas_tvparser_gettag(jas_tvparser_t *tvp)
+{
+	return tvp->tag;
+}
+
+/* Get the current value. */
+const char *jas_tvparser_getval(jas_tvparser_t *tvp)
+{
+	return tvp->val;
+}
+
+/******************************************************************************\
+* Miscellaneous code.
+\******************************************************************************/
+
+/* Lookup a tag by name. */
+jas_taginfo_t *jas_taginfos_lookup(jas_taginfo_t *taginfos, const char *name)
+{
+	jas_taginfo_t *taginfo;
+	taginfo = taginfos;
+	while (taginfo->id >= 0) {
+		if (!strcmp(taginfo->name, name)) {
+			return taginfo;
+		}
+		++taginfo;
+	}
+	return 0;
+}
+
+/* This function is simply for convenience. */
+/* One can avoid testing for the special case of a null pointer, by
+  using this function.   This function never returns a null pointer.  */
+jas_taginfo_t *jas_taginfo_nonull(jas_taginfo_t *taginfo)
+{
+	static jas_taginfo_t invalidtaginfo = {
+		-1, 0
+	};
+	
+	return taginfo ? taginfo : &invalidtaginfo;
+}
diff --git a/converter/other/jpeg2000/libjasper/base/jas_version.c b/converter/other/jpeg2000/libjasper/base/jas_version.c
new file mode 100644
index 00000000..5608c4d4
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/base/jas_version.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+#include "jasper/jas_version.h"
+
+const char *jas_getversion()
+{
+	return JAS_VERSION;
+}
diff --git a/converter/other/jpeg2000/libjasper/base/partlist b/converter/other/jpeg2000/libjasper/base/partlist
new file mode 100644
index 00000000..cd244275
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/base/partlist
@@ -0,0 +1 @@
+/mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_debug.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_getopt.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_image.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_init.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_malloc.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_seq.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_stream.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_string.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_tvp.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_version.o
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_debug.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_debug.h
new file mode 100644
index 00000000..6c597c59
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_debug.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Debugging-Related Code
+ *
+ * $Id$
+ */
+
+#ifndef JAS_DEBUG_H
+#define JAS_DEBUG_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdio.h>
+
+#include "jasper/jas_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************\
+* Macros and functions.
+\******************************************************************************/
+
+/* Output debugging information to standard error provided that the debug
+  level is set sufficiently high. */
+#if defined(DEBUG)
+#define	JAS_DBGLOG(n, x) \
+	((jas_getdbglevel() >= (n)) ? (jas_eprintf x) : 0)
+#else
+#define	JAS_DBGLOG(n, x)
+#endif
+
+/* Get the library debug level. */
+int jas_getdbglevel(void);
+
+/* Set the library debug level. */
+int jas_setdbglevel(int dbglevel);
+
+/* Perform formatted output to standard error. */
+int jas_eprintf(const char *fmt, ...);
+
+/* Dump memory to a stream. */
+int jas_memdump(FILE *out, void *data, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_fix.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_fix.h
new file mode 100644
index 00000000..9599eb1c
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_fix.h
@@ -0,0 +1,406 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Fixed-Point Number Class
+ *
+ * $Id$
+ */
+
+#ifndef JAS_FIX_H
+#define JAS_FIX_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <jasper/jas_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************\
+* Constants.
+\******************************************************************************/
+
+/* The representation of the value zero. */
+#define	JAS_FIX_ZERO(fix_t, fracbits) \
+	JAS_CAST(fix_t, 0)
+
+/* The representation of the value one. */
+#define	JAS_FIX_ONE(fix_t, fracbits) \
+	(JAS_CAST(fix_t, 1) << (fracbits))
+
+/* The representation of the value one half. */
+#define	JAS_FIX_HALF(fix_t, fracbits) \
+	(JAS_CAST(fix_t, 1) << ((fracbits) - 1))
+
+/******************************************************************************\
+* Conversion operations.
+\******************************************************************************/
+
+/* Convert an int to a fixed-point number. */
+#define JAS_INTTOFIX(fix_t, fracbits, x) \
+	JAS_CAST(fix_t, (x) << (fracbits))
+
+/* Convert a fixed-point number to an int. */
+#define JAS_FIXTOINT(fix_t, fracbits, x) \
+	JAS_CAST(int, (x) >> (fracbits))
+
+/* Convert a fixed-point number to a double. */
+#define JAS_FIXTODBL(fix_t, fracbits, x) \
+	(JAS_CAST(double, x) / (JAS_CAST(fix_t, 1) << (fracbits)))
+
+/* Convert a double to a fixed-point number. */
+#define JAS_DBLTOFIX(fix_t, fracbits, x) \
+	JAS_CAST(fix_t, ((x) * JAS_CAST(double, JAS_CAST(fix_t, 1) << (fracbits))))
+
+/******************************************************************************\
+* Basic arithmetic operations.
+* All other arithmetic operations are synthesized from these basic operations.
+* There are three macros for each type of arithmetic operation.
+* One macro always performs overflow/underflow checking, one never performs
+* overflow/underflow checking, and one is generic with its behavior
+* depending on compile-time flags.
+* Only the generic macros should be invoked directly by application code.
+\******************************************************************************/
+
+/* Calculate the sum of two fixed-point numbers. */
+#if !defined(DEBUG_OVERFLOW)
+#define JAS_FIX_ADD			JAS_FIX_ADD_FAST
+#else
+#define JAS_FIX_ADD			JAS_FIX_ADD_OFLOW
+#endif
+
+/* Calculate the sum of two fixed-point numbers without overflow checking. */
+#define	JAS_FIX_ADD_FAST(fix_t, fracbits, x, y)	((x) + (y))
+
+/* Calculate the sum of two fixed-point numbers with overflow checking. */
+#define	JAS_FIX_ADD_OFLOW(fix_t, fracbits, x, y) \
+	((x) >= 0) ? \
+	  (((y) >= 0) ? ((x) + (y) >= 0 || JAS_FIX_OFLOW(), (x) + (y)) : \
+	  ((x) + (y))) : \
+	  (((y) >= 0) ? ((x) + (y)) : ((x) + (y) < 0 || JAS_FIX_OFLOW(), \
+	  (x) + (y)))
+
+/* Calculate the product of two fixed-point numbers. */
+#if !defined(DEBUG_OVERFLOW)
+#define JAS_FIX_MUL			JAS_FIX_MUL_FAST
+#else
+#define JAS_FIX_MUL			JAS_FIX_MUL_OFLOW
+#endif
+
+/* Calculate the product of two fixed-point numbers without overflow
+  checking. */
+#define	JAS_FIX_MUL_FAST(fix_t, fracbits, bigfix_t, x, y) \
+	JAS_CAST(fix_t, (JAS_CAST(bigfix_t, x) * JAS_CAST(bigfix_t, y)) >> \
+	  (fracbits))
+
+/* Calculate the product of two fixed-point numbers with overflow
+  checking. */
+#define JAS_FIX_MUL_OFLOW(fix_t, fracbits, bigfix_t, x, y) \
+	((JAS_CAST(bigfix_t, x) * JAS_CAST(bigfix_t, y) >> (fracbits)) == \
+	  JAS_CAST(fix_t, (JAS_CAST(bigfix_t, x) * JAS_CAST(bigfix_t, y) >> \
+	  (fracbits))) ? \
+	  JAS_CAST(fix_t, (JAS_CAST(bigfix_t, x) * JAS_CAST(bigfix_t, y) >> \
+	  (fracbits))) : JAS_FIX_OFLOW())
+
+/* Calculate the product of a fixed-point number and an int. */
+#if !defined(DEBUG_OVERFLOW)
+#define	JAS_FIX_MULBYINT	JAS_FIX_MULBYINT_FAST
+#else
+#define	JAS_FIX_MULBYINT	JAS_FIX_MULBYINT_OFLOW
+#endif
+
+/* Calculate the product of a fixed-point number and an int without overflow
+  checking. */
+#define	JAS_FIX_MULBYINT_FAST(fix_t, fracbits, x, y) \
+	JAS_CAST(fix_t, ((x) * (y)))
+
+/* Calculate the product of a fixed-point number and an int with overflow
+  checking. */
+#define	JAS_FIX_MULBYINT_OFLOW(fix_t, fracbits, x, y) \
+	JAS_FIX_MULBYINT_FAST(fix_t, fracbits, x, y)
+
+/* Calculate the quotient of two fixed-point numbers. */
+#if !defined(DEBUG_OVERFLOW)
+#define JAS_FIX_DIV			JAS_FIX_DIV_FAST
+#else
+#define JAS_FIX_DIV			JAS_FIX_DIV_UFLOW
+#endif
+
+/* Calculate the quotient of two fixed-point numbers without underflow
+  checking. */
+#define	JAS_FIX_DIV_FAST(fix_t, fracbits, bigfix_t, x, y) \
+	JAS_CAST(fix_t, (JAS_CAST(bigfix_t, x) << (fracbits)) / (y))
+
+/* Calculate the quotient of two fixed-point numbers with underflow
+  checking. */
+#define JAS_FIX_DIV_UFLOW(fix_t, fracbits, bigfix_t, x, y) \
+	JAS_FIX_DIV_FAST(fix_t, fracbits, bigfix_t, x, y)
+
+/* Negate a fixed-point number. */
+#if !defined(DEBUG_OVERFLOW)
+#define	JAS_FIX_NEG			JAS_FIX_NEG_FAST
+#else
+#define	JAS_FIX_NEG			JAS_FIX_NEG_OFLOW
+#endif
+
+/* Negate a fixed-point number without overflow checking. */
+#define	JAS_FIX_NEG_FAST(fix_t, fracbits, x) \
+	(-(x))
+
+/* Negate a fixed-point number with overflow checking. */
+/* Yes, overflow is actually possible for two's complement representations,
+  although highly unlikely to occur. */
+#define	JAS_FIX_NEG_OFLOW(fix_t, fracbits, x) \
+	(((x) < 0) ? (-(x) > 0 || JAS_FIX_OFLOW(), -(x)) : (-(x)))
+
+/* Perform an arithmetic shift left of a fixed-point number. */
+#if !defined(DEBUG_OVERFLOW)
+#define	JAS_FIX_ASL			JAS_FIX_ASL_FAST
+#else
+#define	JAS_FIX_ASL			JAS_FIX_ASL_OFLOW
+#endif
+
+/* Perform an arithmetic shift left of a fixed-point number without overflow
+  checking. */
+#define	JAS_FIX_ASL_FAST(fix_t, fracbits, x, n) \
+	((x) << (n))
+
+/* Perform an arithmetic shift left of a fixed-point number with overflow
+  checking. */
+#define	JAS_FIX_ASL_OFLOW(fix_t, fracbits, x, n) \
+	((((x) << (n)) >> (n)) == (x) || JAS_FIX_OFLOW(), (x) << (n))
+
+/* Perform an arithmetic shift right of a fixed-point number. */
+#if !defined(DEBUG_OVERFLOW)
+#define	JAS_FIX_ASR			JAS_FIX_ASR_FAST
+#else
+#define	JAS_FIX_ASR			JAS_FIX_ASR_UFLOW
+#endif
+
+/* Perform an arithmetic shift right of a fixed-point number without underflow
+  checking. */
+#define	JAS_FIX_ASR_FAST(fix_t, fracbits, x, n) \
+	((x) >> (n))
+
+/* Perform an arithmetic shift right of a fixed-point number with underflow
+  checking. */
+#define	JAS_FIX_ASR_UFLOW(fix_t, fracbits, x, n) \
+	JAS_FIX_ASR_FAST(fix_t, fracbits, x, n)
+
+/******************************************************************************\
+* Other basic arithmetic operations.
+\******************************************************************************/
+
+/* Calculate the difference between two fixed-point numbers. */
+#define JAS_FIX_SUB(fix_t, fracbits, x, y) \
+	JAS_FIX_ADD(fix_t, fracbits, x, JAS_FIX_NEG(fix_t, fracbits, y))
+
+/* Add one fixed-point number to another. */
+#define JAS_FIX_PLUSEQ(fix_t, fracbits, x, y) \
+	((x) = JAS_FIX_ADD(fix_t, fracbits, x, y))
+
+/* Subtract one fixed-point number from another. */
+#define JAS_FIX_MINUSEQ(fix_t, fracbits, x, y) \
+	((x) = JAS_FIX_SUB(fix_t, fracbits, x, y))
+
+/* Multiply one fixed-point number by another. */
+#define	JAS_FIX_MULEQ(fix_t, fracbits, bigfix_t, x, y) \
+	((x) = JAS_FIX_MUL(fix_t, fracbits, bigfix_t, x, y))
+
+/******************************************************************************\
+* Miscellaneous operations.
+\******************************************************************************/
+
+/* Calculate the absolute value of a fixed-point number. */
+#define	JAS_FIX_ABS(fix_t, fracbits, x) \
+	(((x) >= 0) ? (x) : (JAS_FIX_NEG(fix_t, fracbits, x)))
+
+/* Is a fixed-point number an integer? */
+#define	JAS_FIX_ISINT(fix_t, fracbits, x) \
+	(JAS_FIX_FLOOR(fix_t, fracbits, x) == (x))
+
+/* Get the sign of a fixed-point number. */
+#define JAS_FIX_SGN(fix_t, fracbits, x) \
+	((x) >= 0 ? 1 : (-1))
+
+/******************************************************************************\
+* Relational operations.
+\******************************************************************************/
+
+/* Compare two fixed-point numbers. */
+#define JAS_FIX_CMP(fix_t, fracbits, x, y) \
+	((x) > (y) ? 1 : (((x) == (y)) ? 0 : (-1)))
+
+/* Less than. */
+#define	JAS_FIX_LT(fix_t, fracbits, x, y) \
+	((x) < (y))
+
+/* Less than or equal. */
+#define	JAS_FIX_LTE(fix_t, fracbits, x, y) \
+	((x) <= (y))
+
+/* Greater than. */
+#define	JAS_FIX_GT(fix_t, fracbits, x, y) \
+	((x) > (y))
+
+/* Greater than or equal. */
+#define	JAS_FIX_GTE(fix_t, fracbits, x, y) \
+	((x) >= (y))
+
+/******************************************************************************\
+* Rounding functions.
+\******************************************************************************/
+
+/* Round a fixed-point number to the nearest integer. */
+#define	JAS_FIX_ROUND(fix_t, fracbits, x) \
+	(((x) < 0) ? JAS_FIX_FLOOR(fix_t, fracbits, JAS_FIX_ADD(fix_t, fracbits, \
+	  (x), JAS_FIX_HALF(fix_t, fracbits))) : \
+	  JAS_FIX_NEG(fix_t, fracbits, JAS_FIX_FLOOR(fix_t, fracbits, \
+	  JAS_FIX_ADD(fix_t, fracbits, (-(x)), JAS_FIX_HALF(fix_t, fracbits)))))
+
+/* Round a fixed-point number to the nearest integer in the direction of
+  negative infinity (i.e., the floor function). */
+#define	JAS_FIX_FLOOR(fix_t, fracbits, x) \
+	((x) & (~((JAS_CAST(fix_t, 1) << (fracbits)) - 1)))
+
+/* Round a fixed-point number to the nearest integer in the direction
+  of zero. */
+#define JAS_FIX_TRUNC(fix_t, fracbits, x) \
+	(((x) >= 0) ? JAS_FIX_FLOOR(fix_t, fracbits, x) : \
+	  JAS_FIX_CEIL(fix_t, fracbits, x))
+
+/******************************************************************************\
+* The below macros are for internal library use only.  Do not invoke them
+* directly in application code.
+\******************************************************************************/
+
+/* Handle overflow. */
+#define	JAS_FIX_OFLOW() \
+	fprintf(stderr, "overflow error: file %s, line %d\n", __FILE__, __LINE__)
+
+/* Handle underflow. */
+#define	JAS_FIX_UFLOW() \
+	fprintf(stderr, "underflow error: file %s, line %d\n", __FILE__, __LINE__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_getopt.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_getopt.h
new file mode 100644
index 00000000..4f272dc9
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_getopt.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Command Line Option Parsing Code
+ *
+ * $Id$
+ */
+
+#ifndef JAS_GETOPT_H
+#define JAS_GETOPT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************\
+* Constants.
+\******************************************************************************/
+
+#define	JAS_GETOPT_EOF	(-1)
+#define	JAS_GETOPT_ERR	'?'
+
+/* option flags. */
+#define	JAS_OPT_HASARG	0x01	/* option has argument */
+
+/******************************************************************************\
+* Types.
+\******************************************************************************/
+
+/* Command line option type. */
+typedef struct {
+
+	int id;
+	/* The unique identifier for this option. */
+
+	char *name;
+	/* The name of this option. */
+
+	int flags;
+	/* option flags. */
+
+} jas_opt_t;
+
+/******************************************************************************\
+* External data.
+\******************************************************************************/
+
+/* The current option index. */
+extern int jas_optind;
+
+/* The current option argument. */
+extern char *jas_optarg;
+
+/* The debug level. */
+extern int jas_opterr;
+
+/******************************************************************************\
+* Prototypes.
+\******************************************************************************/
+
+/* Get the next option. */
+int jas_getopt(int argc, char **argv, jas_opt_t *opts);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_image.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_image.h
new file mode 100644
index 00000000..d6456f7c
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_image.h
@@ -0,0 +1,593 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Image Class
+ *
+ * $Id$
+ */
+
+#ifndef JAS_IMAGE_H
+#define JAS_IMAGE_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "pm_c_util.h"
+#include <jasper/jas_stream.h>
+#include <jasper/jas_seq.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************\
+* Constants.
+\******************************************************************************/
+
+/*
+ * Miscellaneous constants.
+ */
+
+/* The threshold at which image data is no longer stored in memory. */
+#define JAS_IMAGE_INMEMTHRESH	(16 * 1024 * 1024)
+
+/*
+ * Color models.
+ */
+
+#define JAS_IMAGE_CS_UNKNOWN	0	/* Unknown */
+#define	JAS_IMAGE_CS_GRAY	1	/* Standard Gray */
+#define	JAS_IMAGE_CS_RGB	2	/* Standard RGB */
+#define	JAS_IMAGE_CS_YCBCR	3	/* Standard YCC */
+#if 0
+#define	JAS_IMAGE_CM_ICC	4	/* ICC Profile */
+#endif
+/*
+ * Component types
+ */
+
+#define	JAS_IMAGE_CT_UNKNOWN	0x10000
+#define	JAS_IMAGE_CT_COLOR(n)	((n) & 0x7fff)
+#define	JAS_IMAGE_CT_OPACITY	0x08000
+
+#define	JAS_IMAGE_CT_RGB_R	0
+#define	JAS_IMAGE_CT_RGB_G	1
+#define	JAS_IMAGE_CT_RGB_B	2
+
+#define	JAS_IMAGE_CT_YCBCR_Y	0
+#define	JAS_IMAGE_CT_YCBCR_CB	1
+#define	JAS_IMAGE_CT_YCBCR_CR	2
+
+#define	JAS_IMAGE_CT_GRAY_Y	0
+
+/******************************************************************************\
+* Image class and supporting classes.
+\******************************************************************************/
+
+/* Image component class. */
+
+typedef struct {
+
+	uint_fast32_t tlx_;
+	/* The x-coordinate of the top-left corner of the component. */
+
+	uint_fast32_t tly_;
+	/* The y-coordinate of the top-left corner of the component. */
+
+	uint_fast32_t hstep_;
+	/* The horizontal sampling period in units of the reference grid. */
+
+	uint_fast32_t vstep_;
+	/* The vertical sampling period in units of the reference grid. */
+
+	uint_fast32_t width_;
+	/* The component width in samples. */
+
+	uint_fast32_t height_;
+	/* The component height in samples. */
+
+	uint_fast16_t prec_;
+	/* The precision of the sample data (i.e., the number of bits per
+	sample).  If the samples are signed values, this quantity
+	includes the sign bit. */
+
+	uint_fast8_t sgnd_;
+	/* The signedness of the sample data. */
+
+	jas_stream_t *stream_;
+	/* The stream containing the component data. */
+
+	int cps_;
+	/* The number of characters per sample in the stream. */
+
+	uint_fast32_t type_;
+	/* The type of component (e.g., opacity, red, green, blue, luma). */
+
+} jas_image_cmpt_t;
+
+/* Image class. */
+
+typedef struct {
+
+	uint_fast32_t tlx_;
+	/* The x-coordinate of the top-left corner of the image bounding box. */
+
+	uint_fast32_t tly_;
+	/* The y-coordinate of the top-left corner of the image bounding box. */
+
+	uint_fast32_t brx_;
+	/* The x-coordinate of the bottom-right corner of the image bounding
+	  box (plus one). */
+
+	uint_fast32_t bry_;
+	/* The y-coordinate of the bottom-right corner of the image bounding
+	  box (plus one). */
+
+	uint_fast16_t numcmpts_;
+	/* The number of components. */
+
+	uint_fast16_t maxcmpts_;
+	/* The maximum number of components that this image can have (i.e., the
+	  allocated size of the components array). */
+
+	jas_image_cmpt_t **cmpts_;
+	/* Per-component information. */
+
+	int colorspace_;
+	/* The color space used (e.g., RGB, YCbCr, gray).  This field is only
+	of particular relevance in the case of a multi-component image. */
+
+	unsigned char *iccp_;
+	/* ICC profile information. */
+
+	int iccplen_;
+
+	bool inmem_;
+
+} jas_image_t;
+
+/* Component parameters class. */
+/* This data type exists solely/mainly for the purposes of the
+  jas_image_create function. */
+
+typedef struct {
+
+	uint_fast32_t tlx;
+	/* The x-coordinate of the top-left corner of the component. */
+
+	uint_fast32_t tly;
+	/* The y-coordinate of the top-left corner of the component. */
+
+	uint_fast32_t hstep;
+	/* The horizontal sampling period in units of the reference grid. */
+
+	uint_fast32_t vstep;
+	/* The vertical sampling period in units of the reference grid. */
+
+	uint_fast32_t width;
+	/* The width of the component in samples. */
+
+	uint_fast32_t height;
+	/* The height of the component in samples. */
+
+	uint_fast16_t prec;
+	/* The precision of the component sample data. */
+
+	bool sgnd;
+	/* The signedness of the component sample data. */
+
+} jas_image_cmptparm_t;
+
+/******************************************************************************\
+* File format related classes.
+\******************************************************************************/
+
+#define	JAS_IMAGE_MAXFMTS	32
+/* The maximum number of image data formats supported. */
+
+/* Image format-dependent operations. */
+
+typedef struct {
+
+	jas_image_t *(*decode)(jas_stream_t *in, char *opts);
+	/* Decode image data from a stream. */
+
+	int (*encode)(jas_image_t *image, jas_stream_t *out, char *opts);
+	/* Encode image data to a stream. */
+
+	int (*validate)(jas_stream_t *in);
+	/* Determine if stream data is in a particular format. */
+
+} jas_image_fmtops_t;
+
+/* Image format information. */
+
+typedef struct {
+
+	int id;
+	/* The ID for this format. */
+
+	char *name;
+	/* The name by which this format is identified. */
+
+	char *ext;
+	/* The file name extension associated with this format. */
+
+	char *desc;
+	/* A brief description of the format. */
+
+	jas_image_fmtops_t ops;
+	/* The operations for this format. */
+
+} jas_image_fmtinfo_t;
+
+/******************************************************************************\
+* Image operations.
+\******************************************************************************/
+
+/* Create an image. */
+jas_image_t *jas_image_create(uint_fast16_t numcmpts,
+  jas_image_cmptparm_t *cmptparms, int colorspace);
+
+/* Create an "empty" image. */
+jas_image_t *jas_image_create0(void);
+
+/* Clone an image. */
+jas_image_t *jas_image_copy(jas_image_t *image);
+
+/* Deallocate any resources associated with an image. */
+void jas_image_destroy(jas_image_t *image);
+
+/* Get the width of the image in units of the image reference grid. */
+#define jas_image_width(image) \
+	((image)->brx_ - (image)->tlx_)
+
+/* Get the height of the image in units of the image reference grid. */
+#define	jas_image_height(image) \
+	((image)->bry_ - (image)->tly_)
+
+/* Get the x-coordinate of the top-left corner of the image bounding box
+  on the reference grid. */
+#define jas_image_tlx(image) \
+	((image)->tlx_)
+
+/* Get the y-coordinate of the top-left corner of the image bounding box
+  on the reference grid. */
+#define jas_image_tly(image) \
+	((image)->tly_)
+
+/* Get the x-coordinate of the bottom-right corner of the image bounding box
+  on the reference grid (plus one). */
+#define jas_image_brx(image) \
+	((image)->brx_)
+
+/* Get the y-coordinate of the bottom-right corner of the image bounding box
+  on the reference grid (plus one). */
+#define jas_image_bry(image) \
+	((image)->bry_)
+
+/* Get the number of image components. */
+#define	jas_image_numcmpts(image) \
+	((image)->numcmpts_)
+
+/* Get the color model used by the image. */
+#define	jas_image_colorspace(image) \
+	((image)->colorspace_)
+
+/* Set the color model for an image. */
+#define jas_image_setcolorspace(image, colorspace) \
+	((image)->colorspace_ = (colorspace))
+
+#define jas_image_cmpttype(image, cmptno) \
+	((image)->cmpts_[(cmptno)]->type_)
+#define jas_image_setcmpttype(image, cmptno, type) \
+	((image)->cmpts_[(cmptno)]->type_ = (type))
+
+/* Get the width of a component. */
+#define	jas_image_cmptwidth(image, cmptno) \
+	((image)->cmpts_[cmptno]->width_)
+
+/* Get the height of a component. */
+#define	jas_image_cmptheight(image, cmptno) \
+	((image)->cmpts_[cmptno]->height_)
+
+/* Get the signedness of the sample data for a component. */
+#define	jas_image_cmptsgnd(image, cmptno) \
+	((image)->cmpts_[cmptno]->sgnd_)
+
+/* Get the precision of the sample data for a component. */
+#define	jas_image_cmptprec(image, cmptno) \
+	((image)->cmpts_[cmptno]->prec_)
+
+/* Get the horizontal subsampling factor for a component. */
+#define	jas_image_cmpthstep(image, cmptno) \
+	((image)->cmpts_[cmptno]->hstep_)
+
+/* Get the vertical subsampling factor for a component. */
+#define	jas_image_cmptvstep(image, cmptno) \
+	((image)->cmpts_[cmptno]->vstep_)
+
+/* Get the x-coordinate of the top-left corner of a component. */
+#define	jas_image_cmpttlx(image, cmptno) \
+	((image)->cmpts_[cmptno]->tlx_)
+
+/* Get the y-coordinate of the top-left corner of a component. */
+#define	jas_image_cmpttly(image, cmptno) \
+	((image)->cmpts_[cmptno]->tly_)
+
+/* Get the x-coordinate of the bottom-right corner of a component
+  (plus "one"). */
+#define	jas_image_cmptbrx(image, cmptno) \
+	((image)->cmpts_[cmptno]->tlx_ + (image)->cmpts_[cmptno]->width_ * \
+	  (image)->cmpts_[cmptno]->hstep_)
+
+/* Get the y-coordinate of the bottom-right corner of a component
+  (plus "one"). */
+#define	jas_image_cmptbry(image, cmptno) \
+	((image)->cmpts_[cmptno]->tly_ + (image)->cmpts_[cmptno]->height_ * \
+	  (image)->cmpts_[cmptno]->vstep_)
+
+/* Get the raw size of an image (i.e., the nominal size of the image without
+  any compression. */
+uint_fast32_t jas_image_rawsize(jas_image_t *image);
+
+/* Create an image from a stream in some specified format. */
+jas_image_t *jas_image_decode(jas_stream_t *in, int fmt, char *optstr);
+
+/* Write an image to a stream in a specified format. */
+int jas_image_encode(jas_image_t *image, jas_stream_t *out, int fmt,
+  char *optstr);
+
+/* Read a rectangular region of an image component. */
+/* The position and size of the rectangular region to be read is specified
+relative to the component's coordinate system. */
+int jas_image_readcmpt(jas_image_t *image, uint_fast16_t cmptno,
+  uint_fast32_t x, uint_fast32_t y, uint_fast32_t width, uint_fast32_t height,
+  jas_matrix_t *data);
+
+/* Write a rectangular region of an image component. */
+int jas_image_writecmpt(jas_image_t *image, uint_fast16_t cmptno,
+  uint_fast32_t x, uint_fast32_t y, uint_fast32_t width, uint_fast32_t height,
+  jas_matrix_t *data);
+
+/* Delete a component from an image. */
+void jas_image_delcmpt(jas_image_t *image, uint_fast16_t cmptno);
+
+/* Add a component to an image. */
+int jas_image_addcmpt(jas_image_t *image, uint_fast16_t cmptno,
+  jas_image_cmptparm_t *cmptparm);
+
+/* Copy a component from one image to another. */
+int jas_image_copycmpt(jas_image_t *dstimage, int dstcmptno,
+  jas_image_t *srcimage, int srccmptno);
+
+#if 0
+int_fast64_t jas_image_readcmpt1(jas_image_t *image, uint_fast16_t cmptno,
+  uint_fast32_t x, uint_fast32_t y);
+#endif
+
+#define	JAS_IMAGE_CDT_GETSGND(dtype) (((dtype) >> 7) & 1)
+#define	JAS_IMAGE_CDT_SETSGND(dtype) (((dtype) & 1) << 7)
+#define	JAS_IMAGE_CDT_GETPREC(dtype) ((dtype) & 0x7f)
+#define	JAS_IMAGE_CDT_SETPREC(dtype) ((dtype) & 0x7f)
+
+#define	jas_image_cmptdtype(image, cmptno) \
+	(JAS_IMAGE_CDT_SETSGND((image)->cmpts_[cmptno]->sgnd_) | JAS_IMAGE_CDT_SETPREC((image)->cmpts_[cmptno]->prec_))
+
+void jas_image_dump(jas_image_t *image, FILE *out);
+
+int jas_image_depalettize(jas_image_t *image, int cmptno, int numlutents,
+  int_fast32_t *lutents, int dtype, int newcmptno);
+
+int jas_image_readcmptsample(jas_image_t *image, int cmptno, int x, int y);
+void jas_image_writecmptsample(jas_image_t *image, int cmptno, int x, int y,
+  int_fast32_t v);
+
+int jas_image_getcmptbytype(jas_image_t *image, int ctype);
+
+/******************************************************************************\
+* Image format-related operations.
+\******************************************************************************/
+
+/* Clear the table of image formats. */
+void jas_image_clearfmts(void);
+
+/* Add entry to table of image formats. */
+int jas_image_addfmt(int id, const char *name, const char *ext, 
+  const char *desc,
+  jas_image_fmtops_t *ops);
+
+/* Get the ID for the image format with the specified name. */
+int jas_image_strtofmt(char *s);
+
+/* Get the name of the image format with the specified ID. */
+char *jas_image_fmttostr(int fmt);
+
+/* Lookup image format information by the format ID. */
+jas_image_fmtinfo_t *jas_image_lookupfmtbyid(int id);
+
+/* Lookup image format information by the format name. */
+jas_image_fmtinfo_t *jas_image_lookupfmtbyname(const char *name);
+
+/* Guess the format of an image file based on its name. */
+int jas_image_fmtfromname(char *filename);
+
+/* Get the format of image data in a stream. */
+int jas_image_getfmt(jas_stream_t *in);
+
+/******************************************************************************\
+* Image format-dependent operations.
+\******************************************************************************/
+
+#if !defined(EXCLUDE_JPG_SUPPORT)
+/* Format-dependent operations for JPG support. */
+jas_image_t *jpg_decode(jas_stream_t *in, char *optstr);
+int jpg_encode(jas_image_t *image, jas_stream_t *out, char *optstr);
+int jpg_validate(jas_stream_t *in);
+#endif
+
+#if !defined(EXCLUDE_MIF_SUPPORT)
+/* Format-dependent operations for MIF support. */
+jas_image_t *mif_decode(jas_stream_t *in, char *optstr);
+int mif_encode(jas_image_t *image, jas_stream_t *out, char *optstr);
+int mif_validate(jas_stream_t *in);
+#endif
+
+#if !defined(EXCLUDE_PNM_SUPPORT)
+/* Format-dependent operations for PNM support. */
+jas_image_t *pnm_decode(jas_stream_t *in, char *optstr);
+int pnm_encode(jas_image_t *image, jas_stream_t *out, char *optstr);
+int pnm_validate(jas_stream_t *in);
+#endif
+
+#if !defined(EXCLUDE_RAS_SUPPORT)
+/* Format-dependent operations for Sun Rasterfile support. */
+jas_image_t *ras_decode(jas_stream_t *in, char *optstr);
+int ras_encode(jas_image_t *image, jas_stream_t *out, char *optstr);
+int ras_validate(jas_stream_t *in);
+#endif
+
+#if !defined(EXCLUDE_BMP_SUPPORT)
+/* Format-dependent operations for BMP support. */
+jas_image_t *bmp_decode(jas_stream_t *in, char *optstr);
+int bmp_encode(jas_image_t *image, jas_stream_t *out, char *optstr);
+int bmp_validate(jas_stream_t *in);
+#endif
+
+#if !defined(EXCLUDE_JP2_SUPPORT)
+/* Format-dependent operations for JP2 support. */
+jas_image_t *jp2_decode(jas_stream_t *in, char *optstr);
+int jp2_encode(jas_image_t *image, jas_stream_t *out, char *optstr);
+int jp2_validate(jas_stream_t *in);
+#endif
+
+#if !defined(EXCLUDE_JPC_SUPPORT)
+/* Format-dependent operations for JPEG-2000 code stream support. */
+jas_image_t *jpc_decode(jas_stream_t *in, char *optstr);
+int jpc_encode(jas_image_t *image, jas_stream_t *out, char *optstr);
+int jpc_validate(jas_stream_t *in);
+#endif
+
+#if !defined(EXCLUDE_PGX_SUPPORT)
+/* Format-dependent operations for PGX support. */
+jas_image_t *pgx_decode(jas_stream_t *in, char *optstr);
+int pgx_encode(jas_image_t *image, jas_stream_t *out, char *optstr);
+int pgx_validate(jas_stream_t *in);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_init.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_init.h
new file mode 100644
index 00000000..4ebeaa87
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_init.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+#ifndef JAS_INIT_H
+#define JAS_INIT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************\
+* Functions.
+\******************************************************************************/
+
+int jas_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_malloc.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_malloc.h
new file mode 100644
index 00000000..fe5b6172
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_malloc.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Memory Allocator
+ *
+ * $Id$
+ */
+
+#ifndef JAS_MALLOC_H
+#define JAS_MALLOC_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************\
+* Hack follows...
+\******************************************************************************/
+
+#if defined(DEBUG_MEMALLOC)
+/* This is somewhat of a hack, but it's a useful hack. :-) */
+/* Use my own custom memory allocator for debugging. */
+#include "../../../../local/src/memalloc.h"
+#define jas_malloc	MEMALLOC
+#define	jas_free	MEMFREE
+#define	jas_realloc	MEMREALLOC
+#define	jas_calloc	MEMCALLOC
+#endif
+
+/******************************************************************************\
+* Functions.
+\******************************************************************************/
+
+#if !defined(DEBUG_MEMALLOC)
+
+/* Allocate memory. */
+void *jas_malloc(size_t size);
+
+/* Free memory. */
+void jas_free(void *ptr);
+
+/* Resize a block of allocated memory. */
+void *jas_realloc(void *ptr, size_t size);
+
+/* Allocate a block of memory and initialize the contents to zero. */
+void *jas_calloc(size_t nmemb, size_t size);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_math.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_math.h
new file mode 100644
index 00000000..a43051e5
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_math.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Math-Related Code
+ *
+ * $Id$
+ */
+
+#ifndef	JAS_MATH_H
+#define	JAS_MATH_H
+
+/******************************************************************************\
+* Includes
+\******************************************************************************/
+
+#include	<assert.h>
+#include	<stdio.h>
+#include	<string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************\
+* Macros
+\******************************************************************************/
+
+/* Compute the absolute value. */
+#define	JAS_ABS(x) \
+	(((x) >= 0) ? (x) : (-(x)))
+
+/* Compute the minimum of two values. */
+#define	JAS_MIN(x, y) \
+	(((x) < (y)) ? (x) : (y))
+
+/* Compute the maximum of two values. */
+#define	JAS_MAX(x, y) \
+	(((x) > (y)) ? (x) : (y))
+
+/* Compute the remainder from division (where division is defined such
+  that the remainder is always nonnegative). */
+#define	JAS_MOD(x, y) \
+	(((x) < 0) ? (((-x) % (y)) ? ((y) - ((-(x)) % (y))) : (0)) : ((x) % (y)))
+
+/* Compute the integer with the specified number of least significant bits
+  set to one. */
+#define	JAS_ONES(n) \
+  ((1 << (n)) - 1)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_seq.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_seq.h
new file mode 100644
index 00000000..4599f50c
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_seq.h
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Sequence/Matrix Library
+ *
+ * $Id$
+ */
+
+#ifndef JAS_SEQ_H
+#define JAS_SEQ_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <jasper/jas_stream.h>
+#include <jasper/jas_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************\
+* Constants.
+\******************************************************************************/
+
+/* This matrix is a reference to another matrix. */
+#define JAS_MATRIX_REF	0x0001
+
+/******************************************************************************\
+* Types.
+\******************************************************************************/
+
+/* An element in a sequence. */
+typedef int_fast32_t jas_seqent_t;
+
+/* An element in a matrix. */
+typedef int_fast32_t jas_matent_t;
+
+/* Matrix. */
+
+typedef struct {
+
+	/* Additional state information. */
+	int flags_;
+
+	/* The starting horizontal index. */
+	int_fast32_t xstart_;
+
+	/* The starting vertical index. */
+	int_fast32_t ystart_;
+
+	/* The ending horizontal index. */
+	int_fast32_t xend_;
+
+	/* The ending vertical index. */
+	int_fast32_t yend_;
+
+	/* The number of rows in the matrix. */
+	int_fast32_t numrows_;
+
+	/* The number of columns in the matrix. */
+	int_fast32_t numcols_;
+
+	/* Pointers to the start of each row. */
+	jas_seqent_t **rows_;
+
+	/* The allocated size of the rows array. */
+	int_fast32_t maxrows_;
+
+	/* The matrix data buffer. */
+	jas_seqent_t *data_;
+
+	/* The allocated size of the data array. */
+	int_fast32_t datasize_;
+
+} jas_matrix_t;
+
+typedef jas_matrix_t jas_seq2d_t;
+typedef jas_matrix_t jas_seq_t;
+
+/******************************************************************************\
+* Functions/macros for matrix class.
+\******************************************************************************/
+
+/* Get the number of rows. */
+#define jas_matrix_numrows(matrix) \
+	((matrix)->numrows_)
+
+/* Get the number of columns. */
+#define jas_matrix_numcols(matrix) \
+	((matrix)->numcols_)
+
+/* Get a matrix element. */
+#define jas_matrix_get(matrix, i, j) \
+	((matrix)->rows_[i][j])
+
+/* Set a matrix element. */
+#define jas_matrix_set(matrix, i, j, v) \
+	((matrix)->rows_[i][j] = (v))
+
+/* Get an element from a matrix that is known to be a row or column vector. */
+#define jas_matrix_getv(matrix, i) \
+	(((matrix)->numrows_ == 1) ? ((matrix)->rows_[0][i]) : \
+	  ((matrix)->rows_[i][0]))
+
+/* Set an element in a matrix that is known to be a row or column vector. */
+#define jas_matrix_setv(matrix, i, v) \
+	(((matrix)->numrows_ == 1) ? ((matrix)->rows_[0][i] = (v)) : \
+	  ((matrix)->rows_[i][0] = (v)))
+
+/* Get the address of an element in a matrix. */
+#define	jas_matrix_getref(matrix, i, j) \
+	(&(matrix)->rows_[i][j])
+
+#define	jas_matrix_getvref(matrix, i) \
+	(((matrix)->numrows_ > 1) ? jas_matrix_getref(matrix, i, 0) : jas_matrix_getref(matrix, 0, i))
+
+#define jas_matrix_length(matrix) \
+	(max((matrix)->numrows_, (matrix)->numcols_))
+
+/* Create a matrix with the specified dimensions. */
+jas_matrix_t *jas_matrix_create(int numrows, int numcols);
+
+/* Destroy a matrix. */
+void jas_matrix_destroy(jas_matrix_t *matrix);
+
+jas_matrix_t *jas_matrix_copy(jas_matrix_t *x);
+
+/* Resize a matrix.  The previous contents of the matrix are lost. */
+int jas_matrix_resize(jas_matrix_t *matrix, int numrows, int numcols);
+
+int jas_matrix_output(jas_matrix_t *matrix, FILE *out);
+
+jas_matrix_t *jas_matrix_input(FILE *in);
+
+/* Create a matrix that references part of another matrix. */
+void jas_matrix_bindsub(jas_matrix_t *mat0, jas_matrix_t *mat1, int r0, int c0,
+  int r1, int c1);
+
+/* Create a matrix that is a reference to a row of another matrix. */
+#define jas_matrix_bindrow(mat0, mat1, r) \
+  (jas_matrix_bindsub((mat0), (mat1), (r), 0, (r), (mat1)->numcols_ - 1))
+
+/* Create a matrix that is a reference to a column of another matrix. */
+#define jas_matrix_bindcol(mat0, mat1, c) \
+  (jas_matrix_bindsub((mat0), (mat1), 0, (c), (mat1)->numrows_ - 1, (c)))
+
+/* Clip the values of matrix elements to the specified range. */
+void jas_matrix_clip(jas_matrix_t *matrix, jas_seqent_t minval,
+  jas_seqent_t maxval);
+
+/* Arithmetic shift left of all elements in a matrix. */
+void jas_matrix_asl(jas_matrix_t *matrix, int n);
+
+/* Arithmetic shift right of all elements in a matrix. */
+void jas_matrix_asr(jas_matrix_t *matrix, int n);
+
+/* Almost-but-not-quite arithmetic shift right of all elements in a matrix. */
+void jas_matrix_divpow2(jas_matrix_t *matrix, int n);
+
+/* Set all elements of a matrix to the specified value. */
+void jas_matrix_setall(jas_matrix_t *matrix, jas_seqent_t val);
+
+/* The spacing between rows of a matrix. */
+#define	jas_matrix_rowstep(matrix) \
+	(((matrix)->numrows_ > 1) ? ((matrix)->rows_[1] - (matrix)->rows_[0]) : (0))
+
+/* The spacing between columns of a matrix. */
+#define	jas_matrix_step(matrix) \
+	(((matrix)->numrows_ > 1) ? (jas_matrix_rowstep(matrix)) : (1))
+
+/* Compare two matrices for equality. */
+int jas_matrix_cmp(jas_matrix_t *mat0, jas_matrix_t *mat1);
+
+/******************************************************************************\
+* Functions/macros for 2-D sequence class.
+\******************************************************************************/
+
+jas_seq2d_t *jas_seq2d_copy(jas_seq2d_t *x);
+
+jas_matrix_t *jas_seq2d_create(int xstart, int ystart, int xend, int yend);
+
+#define	jas_seq2d_destroy(s) \
+	jas_matrix_destroy(s)
+
+#define	jas_seq2d_xstart(s) \
+	((s)->xstart_)
+#define	jas_seq2d_ystart(s) \
+	((s)->ystart_)
+#define	jas_seq2d_xend(s) \
+	((s)->xend_)
+#define	jas_seq2d_yend(s) \
+	((s)->yend_)
+#define	jas_seq2d_getref(s, x, y) \
+	(jas_matrix_getref(s, (y) - (s)->ystart_, (x) - (s)->xstart_))
+#define	jas_seq2d_get(s, x, y) \
+	(jas_matrix_get(s, (y) - (s)->ystart_, (x) - (s)->xstart_))
+#define	jas_seq2d_rowstep(s) \
+	jas_matrix_rowstep(s)
+#define	jas_seq2d_width(s) \
+	((s)->xend_ - (s)->xstart_)
+#define	jas_seq2d_height(s) \
+	((s)->yend_ - (s)->ystart_)
+#define	jas_seq2d_setshift(s, x, y) \
+	((s)->xstart_ = (x), (s)->ystart_ = (y), \
+	  (s)->xend_ = (s)->xstart_ + (s)->numcols_, \
+	  (s)->yend_ = (s)->ystart_ + (s)->numrows_)
+
+void jas_seq2d_bindsub(jas_matrix_t *s, jas_matrix_t *s1, int xstart,
+  int ystart, int xend, int yend);
+
+/******************************************************************************\
+* Functions/macros for 1-D sequence class.
+\******************************************************************************/
+
+#define	jas_seq_create(start, end) \
+	(jas_seq2d_create(start, 0, end, 1))
+
+#define	jas_seq_destroy(seq) \
+	(jas_seq2d_destroy(seq))
+
+#define jas_seq_set(seq, i, v) \
+	((seq)->rows_[0][(i) - (seq)->xstart_] = (v))
+#define	jas_seq_getref(seq, i) \
+	(&(seq)->rows_[0][(i) - (seq)->xstart_])
+#define	jas_seq_get(seq, i) \
+	((seq)->rows_[0][(i) - (seq)->xstart_])
+#define	jas_seq_start(seq) \
+	((seq)->xstart_)
+#define	jas_seq_end(seq) \
+	((seq)->xend_)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_stream.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_stream.h
new file mode 100644
index 00000000..1062fe1d
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_stream.h
@@ -0,0 +1,498 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * I/O Stream Class
+ *
+ * $Id$
+ */
+
+#ifndef JAS_STREAM_H
+#define JAS_STREAM_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <jasper/jas_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************\
+* Constants.
+\******************************************************************************/
+
+/* On most UNIX systems, we probably need to define O_BINARY ourselves. */
+#ifndef O_BINARY
+#define O_BINARY	0
+#endif
+
+/*
+ * Stream open flags.
+ */
+
+/* The stream was opened for reading. */
+#define JAS_STREAM_READ	0x0001
+/* The stream was opened for writing. */
+#define JAS_STREAM_WRITE	0x0002
+/* The stream was opened for appending. */
+#define JAS_STREAM_APPEND	0x0004
+/* The stream was opened in binary mode. */
+#define JAS_STREAM_BINARY	0x0008
+/* The stream should be created/truncated. */
+#define JAS_STREAM_CREATE	0x0010
+
+#define JAS_STREAM_NOCLOSE	0x0020
+
+/*
+ * Stream buffering flags.
+ */
+
+/* The stream is unbuffered. */
+#define JAS_STREAM_UNBUF	0x0000
+/* The stream is line buffered. */
+#define JAS_STREAM_LINEBUF	0x0001
+/* The stream is fully buffered. */
+#define JAS_STREAM_FULLBUF	0x0002
+/* The buffering mode mask. */
+#define	JAS_STREAM_BUFMODEMASK	0x000f
+
+/* The memory associated with the buffer needs to be deallocated when the
+  stream is destroyed. */
+#define JAS_STREAM_FREEBUF	0x0008
+/* The buffer is currently being used for reading. */
+#define JAS_STREAM_RDBUF	0x0010
+/* The buffer is currently being used for writing. */
+#define JAS_STREAM_WRBUF	0x0020
+
+/*
+ * Stream error flags.
+ */
+
+/* The end-of-file has been encountered (on reading). */
+#define JAS_STREAM_EOF	0x0001
+/* An I/O error has been encountered on the stream. */
+#define JAS_STREAM_ERR	0x0002
+/* The read/write limit has been exceeded. */
+#define	JAS_STREAM_RWLIMIT	0x0004
+/* The error mask. */
+#define JAS_STREAM_ERRMASK \
+	(JAS_STREAM_EOF | JAS_STREAM_ERR | JAS_STREAM_RWLIMIT)
+
+/*
+ * Other miscellaneous constants.
+ */
+
+/* The default buffer size (for fully-buffered operation). */
+#define JAS_STREAM_BUFSIZE	8192
+/* The default permission mask for file creation. */
+#define JAS_STREAM_PERMS	0666
+
+/* The maximum number of characters that can always be put back on a stream. */
+#define	JAS_STREAM_MAXPUTBACK	16
+
+/******************************************************************************\
+* Types.
+\******************************************************************************/
+
+/*
+ * Generic file object.
+ */
+
+typedef void jas_stream_obj_t;
+
+/*
+ * Generic file object operations.
+ */
+
+typedef struct {
+
+	/* Read characters from a file object. */
+	int (*read_)(jas_stream_obj_t *obj, char *buf, int cnt);
+
+	/* Write characters to a file object. */
+	int (*write_)(jas_stream_obj_t *obj, char *buf, int cnt);
+
+	/* Set the position for a file object. */
+	long (*seek_)(jas_stream_obj_t *obj, long offset, int origin);
+
+	/* Close a file object. */
+	int (*close_)(jas_stream_obj_t *obj);
+
+} jas_stream_ops_t;
+
+/*
+ * Stream object.
+ */
+
+typedef struct {
+
+	/* The mode in which the stream was opened. */
+	int openmode_;
+
+	/* The buffering mode. */
+	int bufmode_;
+
+	/* The stream status. */
+	int flags_;
+
+	/* The start of the buffer area to use for reading/writing. */
+	unsigned char *bufbase_;
+
+	/* The start of the buffer area excluding the extra initial space for
+	  character putback. */
+	unsigned char *bufstart_;
+
+	/* The buffer size. */
+	int bufsize_;
+
+	/* The current position in the buffer. */
+	unsigned char *ptr_;
+
+	/* The number of characters that must be read/written before
+	the buffer needs to be filled/flushed. */
+	int cnt_;
+
+	/* A trivial buffer to be used for unbuffered operation. */
+	unsigned char tinybuf_[JAS_STREAM_MAXPUTBACK + 1];
+
+	/* The operations for the underlying stream file object. */
+	jas_stream_ops_t *ops_;
+
+	/* The underlying stream file object. */
+	jas_stream_obj_t *obj_;
+
+	/* The number of characters read/written. */
+	long rwcnt_;
+
+	/* The maximum number of characters that may be read/written. */
+	long rwlimit_;
+
+} jas_stream_t;
+
+/*
+ * Regular file object.
+ */
+
+/* Note: This is simply a file descriptor. */
+typedef int jas_stream_fileobj_t;
+
+/*
+ * Memory file object.
+ */
+
+typedef struct {
+
+	/* The data associated with this file. */
+	unsigned char *buf_;
+
+	/* The allocated size of the buffer for holding file data. */
+	int bufsize_;
+
+	/* The length of the file. */
+	int_fast32_t len_;
+
+	/* The seek position. */
+	int_fast32_t pos_;
+
+	/* Is the buffer growable? */
+	int growable_;
+
+	/* Was the buffer allocated internally? */
+	int myalloc_;
+
+} jas_stream_memobj_t;
+
+/******************************************************************************\
+* Macros/functions for opening and closing streams.
+\******************************************************************************/
+
+/* Open a file as a stream. */
+jas_stream_t *jas_stream_fopen(const char *filename, const char *mode);
+
+/* Open a memory buffer as a stream. */
+jas_stream_t *jas_stream_memopen(char *buf, int bufsize);
+
+/* Open a file descriptor as a stream. */
+jas_stream_t *jas_stream_fdopen(int fd, const char *mode);
+
+/* Open a stdio stream as a stream. */
+jas_stream_t *jas_stream_freopen(const char *path, const char *mode, FILE *fp);
+
+/* Open a temporary file as a stream. */
+jas_stream_t *jas_stream_tmpfile(void);
+
+/* Close a stream. */
+int jas_stream_close(jas_stream_t *stream);
+
+/******************************************************************************\
+* Macros/functions for getting/setting the stream state.
+\******************************************************************************/
+
+/* Get the EOF indicator for a stream. */
+#define jas_stream_eof(stream) \
+	(((stream)->flags_ & JAS_STREAM_EOF) != 0)
+
+/* Get the error indicator for a stream. */
+#define jas_stream_error(stream) \
+	(((stream)->flags_ & JAS_STREAM_ERR) != 0)
+
+/* Clear the error indicator for a stream. */
+#define jas_stream_clearerr(stream) \
+	((stream)->flags_ &= ~(JAS_STREAM_ERR | JAS_STREAM_EOF))
+
+/* Get the read/write limit for a stream. */
+#define	jas_stream_getrwlimit(stream) \
+	(((const jas_stream_t *)(stream))->rwlimit_)
+
+/* Set the read/write limit for a stream. */
+int jas_stream_setrwlimit(jas_stream_t *stream, long rwlimit);
+
+/* Get the read/write count for a stream. */
+#define	jas_stream_getrwcount(stream) \
+	(((const jas_stream_t *)(stream))->rwcnt_)
+
+/* Set the read/write count for a stream. */
+long jas_stream_setrwcount(jas_stream_t *stream, long rwcnt);
+
+/******************************************************************************\
+* Macros/functions for I/O.
+\******************************************************************************/
+
+/* Read a character from a stream. */
+#if defined(DEBUG)
+#define	jas_stream_getc(stream)	jas_stream_getc_func(stream)
+#else
+#define jas_stream_getc(stream)	jas_stream_getc_macro(stream)
+#endif
+
+/* Write a character to a stream. */
+#if defined(DEBUG)
+#define jas_stream_putc(stream, c)	jas_stream_putc_func(stream, c)
+#else
+#define jas_stream_putc(stream, c)	jas_stream_putc_macro(stream, c)
+#endif
+
+/* Read characters from a stream into a buffer. */
+int jas_stream_read(jas_stream_t *stream, void *buf, int cnt);
+
+/* Write characters from a buffer to a stream. */
+int jas_stream_write(jas_stream_t *stream, const void *buf, int cnt);
+
+/* Write formatted output to a stream. */
+int jas_stream_printf(jas_stream_t *stream, const char *fmt, ...);
+
+/* Write a string to a stream. */
+int jas_stream_puts(jas_stream_t *stream, const char *s);
+
+/* Read a line of input from a stream. */
+char *jas_stream_gets(jas_stream_t *stream, char *buf, int bufsize);
+
+/* Look at the next character to be read from a stream without actually
+  removing it from the stream. */
+#define	jas_stream_peekc(stream) \
+	(((stream)->cnt_ <= 0) ? jas_stream_fillbuf(stream, 0) : \
+	  ((int)(*(stream)->ptr_)))
+
+/* Put a character back on a stream. */
+int jas_stream_ungetc(jas_stream_t *stream, int c);
+
+/******************************************************************************\
+* Macros/functions for getting/setting the stream position.
+\******************************************************************************/
+
+/* Is it possible to seek on this stream? */
+int jas_stream_isseekable(jas_stream_t *stream);
+
+/* Set the current position within the stream. */
+long jas_stream_seek(jas_stream_t *stream, long offset, int origin);
+
+/* Get the current position within the stream. */
+long jas_stream_tell(jas_stream_t *stream);
+
+/* Seek to the beginning of a stream. */
+int jas_stream_rewind(jas_stream_t *stream);
+
+/******************************************************************************\
+* Macros/functions for flushing.
+\******************************************************************************/
+
+/* Flush any pending output to a stream. */
+int jas_stream_flush(jas_stream_t *stream);
+
+/******************************************************************************\
+* Miscellaneous macros/functions.
+\******************************************************************************/
+
+/* Copy data from one stream to another. */
+int jas_stream_copy(jas_stream_t *dst, jas_stream_t *src, int n);
+
+/* Display stream contents (for debugging purposes). */
+int jas_stream_display(jas_stream_t *stream, FILE *fp, int n);
+
+/* Consume (i.e., discard) characters from stream. */
+int jas_stream_gobble(jas_stream_t *stream, int n);
+
+/* Get the size of the file associated with the specified stream.
+  The specified stream must be seekable. */
+long jas_stream_length(jas_stream_t *stream);
+
+/******************************************************************************\
+* Internal functions.
+\******************************************************************************/
+
+/* The following functions are for internal use only!  If you call them
+directly, you will die a horrible, miserable, and painful death! */
+
+/* Read a character from a stream. */
+#define jas_stream_getc_macro(stream) \
+	((!((stream)->flags_ & (JAS_STREAM_ERR | JAS_STREAM_EOF | \
+	  JAS_STREAM_RWLIMIT))) ? \
+	  (((stream)->rwlimit_ >= 0 && (stream)->rwcnt_ >= (stream)->rwlimit_) ? \
+	  (stream->flags_ |= JAS_STREAM_RWLIMIT, EOF) : \
+	  jas_stream_getc2(stream)) : EOF)
+#define jas_stream_getc2(stream) \
+	((--(stream)->cnt_ < 0) ? jas_stream_fillbuf(stream, 1) : \
+	  (++(stream)->rwcnt_, (int)(*(stream)->ptr_++)))
+
+/* Write a character to a stream. */
+#define jas_stream_putc_macro(stream, c) \
+	((!((stream)->flags_ & (JAS_STREAM_ERR | JAS_STREAM_EOF | \
+	  JAS_STREAM_RWLIMIT))) ? \
+	  (((stream)->rwlimit_ >= 0 && (stream)->rwcnt_ >= (stream)->rwlimit_) ? \
+	  (stream->flags_ |= JAS_STREAM_RWLIMIT, EOF) : \
+	  jas_stream_putc2(stream, c)) : EOF)
+#define jas_stream_putc2(stream, c) \
+	(((stream)->bufmode_ |= JAS_STREAM_WRBUF, --(stream)->cnt_ < 0) ? \
+	  jas_stream_flushbuf((stream), (unsigned char)(c)) : \
+	  (++(stream)->rwcnt_, (int)(*(stream)->ptr_++ = (c))))
+
+/* These prototypes need to be here for the sake of the stream_getc and
+stream_putc macros. */
+int jas_stream_fillbuf(jas_stream_t *stream, int getflag);
+int jas_stream_flushbuf(jas_stream_t *stream, int c);
+int jas_stream_getc_func(jas_stream_t *stream);
+int jas_stream_putc_func(jas_stream_t *stream, int c);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_string.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_string.h
new file mode 100644
index 00000000..55b611ba
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_string.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * String Library
+ *
+ * $Id$
+ */
+
+#ifndef	JAS_STRING_H
+#define	JAS_STRING_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************\
+* Functions.
+\******************************************************************************/
+
+/* Copy a string (a la strdup). */
+char *jas_strdup(const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_tvp.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_tvp.h
new file mode 100644
index 00000000..9ac50dbd
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_tvp.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Tag/Value Parser
+ *
+ * $Id$
+ */
+
+#ifndef JAS_TVP_H
+#define JAS_TVP_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************\
+* Types.
+\******************************************************************************/
+
+/* Tag information type. */
+
+typedef struct {
+
+	int id;
+	/* The ID for the tag. */
+
+	const char *name;
+	/* The name of the tag. */
+
+} jas_taginfo_t;
+
+/* Tag-value parser type. */
+
+typedef struct {
+
+	char *buf;
+	/* The parsing buffer. */
+
+	char *tag;
+	/* The current tag name. */
+
+	const char *val;
+	/* The current value. */
+
+	char *pos;
+	/* The current position in the parsing buffer. */
+
+} jas_tvparser_t;
+
+/******************************************************************************\
+* Tag information functions.
+\******************************************************************************/
+
+/* Lookup a tag by name. */
+jas_taginfo_t *jas_taginfos_lookup(jas_taginfo_t *taginfos, const char *name);
+
+/* This function returns a pointer to the specified taginfo object if it
+  exists (i.e., the pointer is nonnull); otherwise, a pointer to a dummy
+  object is returned.  This is useful in some situations to avoid checking
+  for a null pointer. */
+jas_taginfo_t *jas_taginfo_nonull(jas_taginfo_t *taginfo);
+
+/******************************************************************************\
+* Tag-value parser functions.
+\******************************************************************************/
+
+/* Create a tag-value parser for the specified string. */
+jas_tvparser_t *jas_tvparser_create(const char *s);
+
+/* Destroy a tag-value parser. */
+void jas_tvparser_destroy(jas_tvparser_t *tvparser);
+
+/* Get the next tag-value pair. */
+int jas_tvparser_next(jas_tvparser_t *tvparser);
+
+/* Get the tag name for the current tag-value pair. */
+char *jas_tvparser_gettag(jas_tvparser_t *tvparser);
+
+/* Get the value for the current tag-value pair. */
+const char *jas_tvparser_getval(jas_tvparser_t *tvparser);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h
new file mode 100644
index 00000000..4d3a4988
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h
@@ -0,0 +1,14 @@
+/* In the original Jasper library, this contains definitions of the int_fast32,
+   etc. types and bool.
+
+   In Netpbm, we do that with pm_config.h, and the original Jasper
+   method doesn't work.
+*/
+#include "pm_config.h"
+
+
+/* The below macro is intended to be used for type casts.  By using this
+  macro, type casts can be easily located in the source code with
+  tools like "grep". */
+#define	JAS_CAST(t, e) \
+	((t) (e))
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig b/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig
new file mode 100644
index 00000000..10c1152d
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Primitive Types
+ *
+ * $Id$
+ */
+
+#ifndef JAS_TYPES_H
+#define JAS_TYPES_H
+
+#if defined(HAVE_STDLIB_H)
+#include <stdlib.h>
+#endif
+#if defined(HAVE_STDDEF_H)
+#include <stddef.h>
+#endif
+#if defined(HAVE_SYS_TYPES_H)
+#include <sys/types.h>
+#endif
+
+#if defined(HAVE_STDBOOL_H)
+/*
+ * The C language implementation does correctly provide the standard header
+ * file "stdbool.h".
+ */
+#include <stdbool.h>
+#else
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The C language implementation does not provide the standard header file
+ * "stdbool.h" as required by ISO/IEC 9899:1999.  Try to compensate for this
+ * braindamage below.
+ */
+#if !defined(bool)
+#define	bool	int
+#endif
+#if !defined(true)
+#define true	1
+#endif
+#if !defined(false)
+#define	false	0
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+/* pm_config.h defines the FAST integer types if possible (typically by
+   including <inttypes.h>.  If not, the following try to take care
+   of it.
+*/
+/**********/
+#if !defined(INT_FAST8_MAX)
+typedef signed char int_fast8_t;
+#define INT_FAST8_MIN	(-127)
+#define INT_FAST8_MAX	128
+#endif
+/**********/
+#if !defined(UINT_FAST8_MAX)
+typedef unsigned char uint_fast8_t;
+#define UINT_FAST8_MAX	255
+#endif
+/**********/
+#if !defined(INT_FAST16_MAX)
+typedef short int_fast16_t;
+#define INT_FAST16_MIN	SHRT_MIN
+#define INT_FAST16_MAX	SHRT_MAX
+#endif
+/**********/
+#if !defined(UINT_FAST16_MAX)
+typedef unsigned short uint_fast16_t;
+#define UINT_FAST16_MAX	USHRT_MAX
+#endif
+/**********/
+#if !defined(INT_FAST32_MAX)
+typedef int int_fast32_t;
+#define INT_FAST32_MIN	INT_MIN
+#define INT_FAST32_MAX	INT_MAX
+#endif
+/**********/
+#if !defined(UINT_FAST32_MAX)
+typedef unsigned int uint_fast32_t;
+#define UINT_FAST32_MAX	UINT_MAX
+#endif
+/**********/
+#if !defined(INT_FAST64_MAX)
+typedef int int_fast64_t;
+#define INT_FAST64_MIN	LLONG_MIN
+#define INT_FAST64_MAX	LLONG_MAX
+#endif
+/**********/
+#if !defined(UINT_FAST64_MAX)
+typedef unsigned int uint_fast64_t;
+#define UINT_FAST64_MAX	ULLONG_MAX
+#endif
+/**********/
+#endif
+
+/* The below macro is intended to be used for type casts.  By using this
+  macro, type casts can be easily located in the source code with
+  tools like "grep". */
+#define	JAS_CAST(t, e) \
+	((t) (e))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_version.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_version.h
new file mode 100644
index 00000000..2178257c
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_version.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef JAS_VERSION_H
+#define JAS_VERSION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************************************************************\
+* Constants and types.
+\******************************************************************************/
+
+#if !defined(JAS_VERSION)
+/* The version information below should match that specified in
+  the "configure.in" file! */
+#define	JAS_VERSION		"1.600.0"
+#endif
+
+#define	JAS_COPYRIGHT \
+	"Copyright (c) 1999-2000 Image Power, Inc. and the University of\n" \
+	"  British Columbia.\n" \
+	"Copyright (c) 2001-2002 Michael David Adams.\n" \
+	"All rights reserved.\n"
+
+#define	JAS_NOTES \
+	"For more information about this software, please visit the following\n" \
+	"web sites/pages:\n" \
+	"    http://www.ece.ubc.ca/~mdadams/jasper\n" \
+	"    http://www.jpeg.org/software\n" \
+	"To be added to the (moderated) JasPer software announcements\n" \
+	"mailing list, send an email to:\n" \
+	"    jasper-announce-subscribe@yahoogroups.com\n" \
+	"To be added to the (unmoderated) JasPer software discussion\n" \
+	"mailing list, send an email to:\n" \
+	"    jasper-discussion-subscribe@yahoogroups.com\n" \
+	"Please send any bug reports to:\n" \
+	"    mdadams@ieee.org\n"
+
+/******************************************************************************\
+* Functions.
+\******************************************************************************/
+
+const char *jas_getversion(void);
+/* Get the version information for the JasPer library. */
+/* Note:  Since libjasper can be built as a shared library, the version
+  returned by this function may not necessarily correspond to JAS_VERSION. */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jasper.h b/converter/other/jpeg2000/libjasper/include/jasper/jasper.h
new file mode 100644
index 00000000..b003acfa
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jasper.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+#ifndef JAS_JASPER_H
+#define JAS_JASPER_H
+
+#include <jasper/jas_types.h>
+#include <jasper/jas_version.h>
+
+#include <jasper/jas_init.h>
+#include <jasper/jas_fix.h>
+#include <jasper/jas_debug.h>
+#include <jasper/jas_getopt.h>
+#include <jasper/jas_image.h>
+#include <jasper/jas_math.h>
+#include <jasper/jas_malloc.h>
+#include <jasper/jas_seq.h>
+#include <jasper/jas_stream.h>
+#include <jasper/jas_string.h>
+#include <jasper/jas_tvp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jp2/Makefile b/converter/other/jpeg2000/libjasper/jp2/Makefile
new file mode 100644
index 00000000..254b7f56
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jp2/Makefile
@@ -0,0 +1,19 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../../../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/other/jpeg2000/libjasper/jp2
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+JASPERSRCDIR=$(SRCDIR)/$(SUBDIR)/..
+
+include $(BUILDDIR)/Makefile.config
+
+LIB_OBJECTS = jp2_cod.o jp2_dec.o jp2_enc.o
+
+MERGE_OBJECTS =
+
+all: partlist $(LIB_OBJECTS)
+
+include $(JASPERSRCDIR)/Makefile.common
+
diff --git a/converter/other/jpeg2000/libjasper/jp2/jp2_cod.c b/converter/other/jpeg2000/libjasper/jp2/jp2_cod.c
new file mode 100644
index 00000000..4769c408
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jp2/jp2_cod.c
@@ -0,0 +1,928 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * JP2 Library
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "pm_c_util.h"
+
+#include "jasper/jas_stream.h"
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_debug.h"
+
+#include "jp2_cod.h"
+
+/******************************************************************************\
+* Function prototypes.
+\******************************************************************************/
+
+#define	ONES(n)	((1 << (n)) - 1)
+
+jp2_boxinfo_t *jp2_boxinfolookup(int type);
+
+static int jp2_getuint8(jas_stream_t *in, uint_fast8_t *val);
+static int jp2_getuint16(jas_stream_t *in, uint_fast16_t *val);
+static int jp2_getuint32(jas_stream_t *in, uint_fast32_t *val);
+static int jp2_getuint64(jas_stream_t *in, uint_fast64_t *val);
+static int jp2_putuint8(jas_stream_t *out, uint_fast8_t val);
+static int jp2_putuint16(jas_stream_t *out, uint_fast16_t val);
+static int jp2_putuint32(jas_stream_t *out, uint_fast32_t val);
+
+static int jp2_getint(jas_stream_t *in, int s, int n, int_fast32_t *val);
+
+jp2_box_t *jp2_box_get(jas_stream_t *in);
+void jp2_box_dump(jp2_box_t *box, FILE *out);
+
+static int jp2_jp_getdata(jp2_box_t *box, jas_stream_t *in);
+static int jp2_jp_putdata(jp2_box_t *box, jas_stream_t *out);
+static int jp2_ftyp_getdata(jp2_box_t *box, jas_stream_t *in);
+static int jp2_ftyp_putdata(jp2_box_t *box, jas_stream_t *out);
+static int jp2_ihdr_getdata(jp2_box_t *box, jas_stream_t *in);
+static int jp2_ihdr_putdata(jp2_box_t *box, jas_stream_t *out);
+static void jp2_bpcc_destroy(jp2_box_t *box);
+static int jp2_bpcc_getdata(jp2_box_t *box, jas_stream_t *in);
+static int jp2_bpcc_putdata(jp2_box_t *box, jas_stream_t *out);
+static int jp2_colr_getdata(jp2_box_t *box, jas_stream_t *in);
+static int jp2_colr_putdata(jp2_box_t *box, jas_stream_t *out);
+static void jp2_colr_dumpdata(jp2_box_t *box, FILE *out);
+static void jp2_colr_destroy(jp2_box_t *box);
+static void jp2_cdef_destroy(jp2_box_t *box);
+static int jp2_cdef_getdata(jp2_box_t *box, jas_stream_t *in);
+static int jp2_cdef_putdata(jp2_box_t *box, jas_stream_t *out);
+static void jp2_cdef_dumpdata(jp2_box_t *box, FILE *out);
+static void jp2_cmap_destroy(jp2_box_t *box);
+static int jp2_cmap_getdata(jp2_box_t *box, jas_stream_t *in);
+static int jp2_cmap_putdata(jp2_box_t *box, jas_stream_t *out);
+static void jp2_cmap_dumpdata(jp2_box_t *box, FILE *out);
+static void jp2_pclr_destroy(jp2_box_t *box);
+static int jp2_pclr_getdata(jp2_box_t *box, jas_stream_t *in);
+static int jp2_pclr_putdata(jp2_box_t *box, jas_stream_t *out);
+static void jp2_pclr_dumpdata(jp2_box_t *box, FILE *out);
+
+/******************************************************************************\
+* Local data.
+\******************************************************************************/
+
+jp2_boxinfo_t jp2_boxinfos[] = {
+	{JP2_BOX_JP, "JP", 0,
+	  {0, 0, jp2_jp_getdata, jp2_jp_putdata, 0}},
+	{JP2_BOX_FTYP, "FTYP", 0,
+	  {0, 0, jp2_ftyp_getdata, jp2_ftyp_putdata, 0}},
+	{JP2_BOX_JP2H, "JP2H", JP2_BOX_SUPER,
+	  {0, 0, 0, 0, 0}},
+	{JP2_BOX_IHDR, "IHDR", 0,
+	  {0, 0, jp2_ihdr_getdata, jp2_ihdr_putdata, 0}},
+	{JP2_BOX_BPCC, "BPCC", 0,
+	  {0, jp2_bpcc_destroy, jp2_bpcc_getdata, jp2_bpcc_putdata, 0}},
+	{JP2_BOX_COLR, "COLR", 0,
+	  {0, jp2_colr_destroy, jp2_colr_getdata, jp2_colr_putdata, jp2_colr_dumpdata}},
+	{JP2_BOX_PCLR, "PCLR", 0,
+	  {0, jp2_pclr_destroy, jp2_pclr_getdata, jp2_pclr_putdata, jp2_pclr_dumpdata}},
+	{JP2_BOX_CMAP, "CMAP", 0,
+	  {0, jp2_cmap_destroy, jp2_cmap_getdata, jp2_cmap_putdata, jp2_cmap_dumpdata}},
+	{JP2_BOX_CDEF, "CDEF", 0,
+	  {0, jp2_cdef_destroy, jp2_cdef_getdata, jp2_cdef_putdata, jp2_cdef_dumpdata}},
+	{JP2_BOX_RES, "RES", JP2_BOX_SUPER,
+	  {0, 0, 0, 0, 0}},
+	{JP2_BOX_RESC, "RESC", 0,
+	  {0, 0, 0, 0, 0}},
+	{JP2_BOX_RESD, "RESD", 0,
+	  {0, 0, 0, 0, 0}},
+	{JP2_BOX_JP2C, "JP2C", JP2_BOX_NODATA,
+	  {0, 0, 0, 0, 0}},
+	{JP2_BOX_JP2I, "JP2I", 0,
+	  {0, 0, 0, 0, 0}},
+	{JP2_BOX_XML, "XML", 0,
+	  {0, 0, 0, 0, 0}},
+	{JP2_BOX_UUID, "UUID", 0,
+	  {0, 0, 0, 0, 0}},
+	{JP2_BOX_UINF, "UINF", JP2_BOX_SUPER,
+	  {0, 0, 0, 0, 0}},
+	{JP2_BOX_ULST, "ULST", 0,
+	  {0, 0, 0, 0, 0}},
+	{JP2_BOX_URL, "URL", 0,
+	  {0, 0, 0, 0, 0}},
+	{0, 0, 0, {0, 0, 0, 0, 0}},
+};
+
+jp2_boxinfo_t jp2_boxinfo_unk = {
+	0, "Unknown", 0, {0, 0, 0, 0}
+};
+
+/******************************************************************************\
+* Box constructor.
+\******************************************************************************/
+
+jp2_box_t *jp2_box_create(int type)
+{
+	jp2_box_t *box;
+	jp2_boxinfo_t *boxinfo;
+
+	if (!(box = jas_malloc(sizeof(jp2_box_t)))) {
+		return 0;
+	}
+	memset(box, 0, sizeof(jp2_box_t));
+	box->type = type;
+	box->len = 0;
+	if (!(boxinfo = jp2_boxinfolookup(type))) {
+		return 0;
+	}
+	box->info = boxinfo;
+	box->ops = &boxinfo->ops;
+	return box;
+}
+
+/******************************************************************************\
+* Box destructor.
+\******************************************************************************/
+
+void jp2_box_destroy(jp2_box_t *box)
+{
+	if (box->ops->destroy) {
+		(*box->ops->destroy)(box);
+	}
+	jas_free(box);
+}
+
+static void jp2_bpcc_destroy(jp2_box_t *box)
+{
+	jp2_bpcc_t *bpcc = &box->data.bpcc;
+	if (bpcc->bpcs) {
+		jas_free(bpcc->bpcs);
+		bpcc->bpcs = 0;
+	}
+}
+
+static void jp2_cdef_destroy(jp2_box_t *box)
+{
+	jp2_cdef_t *cdef = &box->data.cdef;
+	if (cdef->ents) {
+		jas_free(cdef->ents);
+		cdef->ents = 0;
+	}
+}
+
+/******************************************************************************\
+* Box input.
+\******************************************************************************/
+
+jp2_box_t *jp2_box_get(jas_stream_t *in)
+{
+	jp2_box_t *box;
+	jp2_boxinfo_t *boxinfo;
+	jas_stream_t *tmpstream;
+	uint_fast32_t len;
+    uint_fast64_t extlen;
+	bool dataflag;
+
+	box = 0;
+	tmpstream = 0;
+
+	if (!(box = jas_malloc(sizeof(jp2_box_t)))) {
+		goto error;
+	}
+	box->ops = &jp2_boxinfo_unk.ops;
+	if (jp2_getuint32(in, &len) || jp2_getuint32(in, &box->type)) {
+		goto error;
+	}
+	boxinfo = jp2_boxinfolookup(box->type);
+	box->info = boxinfo;
+	box->ops = &boxinfo->ops;
+	box->len = len;
+	if (box->len == 1) {
+		if (jp2_getuint64(in, &extlen)) {
+			goto error;
+		}
+		box->len = extlen;
+	}
+	if (box->len != 0 && box->len < 8) {
+		goto error;
+	}
+
+	dataflag = !(box->info->flags & (JP2_BOX_SUPER | JP2_BOX_NODATA));
+
+	if (dataflag) {
+		if (!(tmpstream = jas_stream_memopen(0, 0))) {
+			goto error;
+		}
+		if (jas_stream_copy(tmpstream, in, box->len - JP2_BOX_HDRLEN)) {
+			goto error;
+		}
+		jas_stream_rewind(tmpstream);
+
+		if (box->ops->getdata) {
+			if ((*box->ops->getdata)(box, tmpstream)) {
+				goto error;
+			}
+		}
+		jas_stream_close(tmpstream);
+	}
+
+	jp2_box_dump(box, stderr);
+
+	return box;
+	abort();
+
+error:
+	if (box) {
+		jp2_box_destroy(box);
+	}
+	if (tmpstream) {
+		jas_stream_close(tmpstream);
+	}
+	return 0;
+}
+
+void jp2_box_dump(jp2_box_t *box, FILE *out)
+{
+	jp2_boxinfo_t *boxinfo;
+	boxinfo = jp2_boxinfolookup(box->type);
+	assert(boxinfo);
+
+	fprintf(out, "JP2 box: ");
+	fprintf(out, "type=%c%s%c (0x%08x); length=%d\n", '"', boxinfo->name,
+	  '"', box->type, box->len);
+	if (box->ops->dumpdata) {
+		(*box->ops->dumpdata)(box, out);
+	}
+}
+
+static int jp2_jp_getdata(jp2_box_t *box, jas_stream_t *in)
+{
+	jp2_jp_t *jp = &box->data.jp;
+	if (jp2_getuint32(in, &jp->magic)) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jp2_ftyp_getdata(jp2_box_t *box, jas_stream_t *in)
+{
+	jp2_ftyp_t *ftyp = &box->data.ftyp;
+	int i;
+	if (jp2_getuint32(in, &ftyp->majver) || jp2_getuint32(in, &ftyp->minver)) {
+		return -1;
+	}
+	ftyp->numcompatcodes = ((box->len - JP2_BOX_HDRLEN) - 8) / 4;
+	if (ftyp->numcompatcodes > JP2_FTYP_MAXCOMPATCODES) {
+		return -1;
+	}
+	for (i = 0; i < ftyp->numcompatcodes; ++i) {
+		if (jp2_getuint32(in, &ftyp->compatcodes[i])) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int jp2_ihdr_getdata(jp2_box_t *box, jas_stream_t *in)
+{
+	jp2_ihdr_t *ihdr = &box->data.ihdr;
+	if (jp2_getuint32(in, &ihdr->height) || jp2_getuint32(in, &ihdr->width) ||
+	  jp2_getuint16(in, &ihdr->numcmpts) || jp2_getuint8(in, &ihdr->bpc) ||
+	  jp2_getuint8(in, &ihdr->comptype) || jp2_getuint8(in, &ihdr->csunk) ||
+	  jp2_getuint8(in, &ihdr->ipr)) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jp2_bpcc_getdata(jp2_box_t *box, jas_stream_t *in)
+{
+	jp2_bpcc_t *bpcc = &box->data.bpcc;
+	int i;
+	bpcc->numcmpts = box->len - JP2_BOX_HDRLEN;
+	if (!(bpcc->bpcs = jas_malloc(bpcc->numcmpts * sizeof(uint_fast8_t)))) {
+		return -1;
+	}
+	for (i = 0; i < bpcc->numcmpts; ++i) {
+		if (jp2_getuint8(in, &bpcc->bpcs[i])) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static void jp2_colr_dumpdata(jp2_box_t *box, FILE *out)
+{
+	jp2_colr_t *colr = &box->data.colr;
+	fprintf(out, "method=%d; pri=%d; approx=%d\n", (int)colr->method, (int)colr->pri, (int)colr->approx);
+	switch (colr->method) {
+	case JP2_COLR_ENUM:
+		fprintf(out, "csid=%d\n", (int)colr->csid);
+		break;
+	case JP2_COLR_ICC:
+		jas_memdump(out, colr->iccp, colr->iccplen);
+		break;
+	}
+}
+
+static int jp2_colr_getdata(jp2_box_t *box, jas_stream_t *in)
+{
+	jp2_colr_t *colr = &box->data.colr;
+	colr->csid = 0;
+	colr->iccp = 0;
+	colr->iccplen = 0;
+
+	if (jp2_getuint8(in, &colr->method) || jp2_getuint8(in, &colr->pri) ||
+	  jp2_getuint8(in, &colr->approx)) {
+		return -1;
+	}
+	switch (colr->method) {
+	case JP2_COLR_ENUM:
+		if (jp2_getuint32(in, &colr->csid)) {
+			return -1;
+		}
+		break;
+	case JP2_COLR_ICC:
+		colr->iccplen = box->len - JP2_BOX_HDRLEN - 3;
+		if (!(colr->iccp = jas_malloc(colr->iccplen * sizeof(uint_fast8_t)))) {
+			return -1;
+		}
+		if (jas_stream_read(in, colr->iccp, colr->iccplen) != colr->iccplen) {
+			return -1;
+		}
+		break;
+	}
+	return 0;
+}
+
+static void jp2_cdef_dumpdata(jp2_box_t *box, FILE *out)
+{
+	jp2_cdef_t *cdef = &box->data.cdef;
+	int i;
+	for (i = 0; i < cdef->numchans; ++i) {
+		fprintf(out, "channo=%d; type=%d; assoc=%d\n",
+		  cdef->ents[i].channo, cdef->ents[i].type, cdef->ents[i].assoc);
+	}
+}
+
+static void jp2_colr_destroy(jp2_box_t *box)
+{
+	jp2_colr_t *colr = &box->data.colr;
+	if (colr->iccp) {
+		free(colr->iccp);
+	}
+}
+
+static int jp2_cdef_getdata(jp2_box_t *box, jas_stream_t *in)
+{
+	jp2_cdef_t *cdef = &box->data.cdef;
+	jp2_cdefchan_t *chan;
+	int channo;
+	if (jp2_getuint16(in, &cdef->numchans)) {
+		return -1;
+	}
+	if (!(cdef->ents = jas_malloc(cdef->numchans * sizeof(jp2_cdefchan_t)))) {
+		return -1;
+	}
+	for (channo = 0; channo < cdef->numchans; ++channo) {
+		chan = &cdef->ents[channo];
+		if (jp2_getuint16(in, &chan->channo) || jp2_getuint16(in, &chan->type) ||
+		  jp2_getuint16(in, &chan->assoc)) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* Box output.
+\******************************************************************************/
+
+int jp2_box_put(jp2_box_t *box, jas_stream_t *out)
+{
+	jas_stream_t *tmpstream;
+	bool dataflag;
+
+	tmpstream = 0;
+
+	dataflag = !(box->info->flags & (JP2_BOX_SUPER | JP2_BOX_NODATA));
+
+	if (dataflag) {
+		tmpstream = jas_stream_memopen(0, 0);
+		if (box->ops->putdata) {
+			if ((*box->ops->putdata)(box, tmpstream)) {
+				goto error;
+			}
+		}
+		box->len = jas_stream_tell(tmpstream) + JP2_BOX_HDRLEN;
+		jas_stream_rewind(tmpstream);
+	}
+    /* There was code here in official Jasper to handle 64 bit lengths,
+       but it was based on determining whether box->len fits in 32 bits.
+       But box->len is a 32 bit data type, so it fits in 32 bits by
+       definition.  Either this encoder is incapable of making boxes with
+       lengths that don't fit in 32 bits, or it passes invalid values of
+       box->len to us.  We assume the former because it's easier.
+    */
+	if (jp2_putuint32(out, box->len)) {
+		goto error;
+	}
+	if (jp2_putuint32(out, box->type)) {
+		goto error;
+	}
+
+	if (dataflag) {
+		if (jas_stream_copy(out, tmpstream, box->len - JP2_BOX_HDRLEN)) {
+			goto error;
+		}
+		jas_stream_close(tmpstream);
+	}
+
+	return 0;
+	abort();
+
+error:
+
+	if (tmpstream) {
+		jas_stream_close(tmpstream);
+	}
+	return -1;
+}
+
+static int jp2_jp_putdata(jp2_box_t *box, jas_stream_t *out)
+{
+	jp2_jp_t *jp = &box->data.jp;
+	if (jp2_putuint32(out, jp->magic)) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jp2_ftyp_putdata(jp2_box_t *box, jas_stream_t *out)
+{
+	jp2_ftyp_t *ftyp = &box->data.ftyp;
+	int i;
+	if (jp2_putuint32(out, ftyp->majver) || jp2_putuint32(out, ftyp->minver)) {
+		return -1;
+	}
+	for (i = 0; i < ftyp->numcompatcodes; ++i) {
+		if (jp2_putuint32(out, ftyp->compatcodes[i])) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int jp2_ihdr_putdata(jp2_box_t *box, jas_stream_t *out)
+{
+	jp2_ihdr_t *ihdr = &box->data.ihdr;
+	if (jp2_putuint32(out, ihdr->height) || jp2_putuint32(out, ihdr->width) ||
+	  jp2_putuint16(out, ihdr->numcmpts) || jp2_putuint8(out, ihdr->bpc) ||
+	  jp2_putuint8(out, ihdr->comptype) || jp2_putuint8(out, ihdr->csunk) ||
+	  jp2_putuint8(out, ihdr->ipr)) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jp2_bpcc_putdata(jp2_box_t *box, jas_stream_t *out)
+{
+	jp2_bpcc_t *bpcc = &box->data.bpcc;
+	int i;
+	for (i = 0; i < bpcc->numcmpts; ++i) {
+		if (jp2_putuint8(out, bpcc->bpcs[i])) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int jp2_colr_putdata(jp2_box_t *box, jas_stream_t *out)
+{
+	jp2_colr_t *colr = &box->data.colr;
+	if (jp2_putuint8(out, colr->method) || jp2_putuint8(out, colr->pri) ||
+	  jp2_putuint8(out, colr->approx)) {
+		return -1;
+	}
+	switch (colr->method) {
+	case JP2_COLR_ENUM:
+		if (jp2_putuint32(out, colr->csid)) {
+			return -1;
+		}
+		break;
+	case JP2_COLR_ICC:
+		/* XXX - not implemented */
+		abort();
+		break;
+	}
+	return 0;
+}
+
+static int jp2_cdef_putdata(jp2_box_t *box, jas_stream_t *out)
+{
+	jp2_cdef_t *cdef = &box->data.cdef;
+	int i;
+	jp2_cdefchan_t *ent;
+
+	if (jp2_putuint16(out, cdef->numchans)) {
+		return -1;
+	}
+
+	for (i = 0; i < cdef->numchans; ++i) {
+		ent = &cdef->ents[i];
+		if (jp2_putuint16(out, ent->channo) ||
+		  jp2_putuint16(out, ent->type) ||
+		  jp2_putuint16(out, ent->assoc)) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* Input operations for primitive types.
+\******************************************************************************/
+
+static int jp2_getuint8(jas_stream_t *in, uint_fast8_t *val)
+{
+	int c;
+	if ((c = jas_stream_getc(in)) == EOF) {
+		return -1;
+	}
+	if (val) {
+		*val = c;
+	}
+	return 0;
+}
+
+static int jp2_getuint16(jas_stream_t *in, uint_fast16_t *val)
+{
+	uint_fast16_t v;
+	int c;
+	if ((c = jas_stream_getc(in)) == EOF) {
+		return -1;
+	}
+	v = c;
+	if ((c = jas_stream_getc(in)) == EOF) {
+		return -1;
+	}
+	v = (v << 8) | c;
+	if (val) {
+		*val = v;
+	}
+	return 0;
+}
+
+static int jp2_getuint32(jas_stream_t *in, uint_fast32_t *val)
+{
+	uint_fast32_t v;
+	int c;
+	if ((c = jas_stream_getc(in)) == EOF) {
+		return -1;
+	}
+	v = c;
+	if ((c = jas_stream_getc(in)) == EOF) {
+		return -1;
+	}
+	v = (v << 8) | c;
+	if ((c = jas_stream_getc(in)) == EOF) {
+		return -1;
+	}
+	v = (v << 8) | c;
+	if ((c = jas_stream_getc(in)) == EOF) {
+		return -1;
+	}
+	v = (v << 8) | c;
+	if (val) {
+		*val = v;
+	}
+	return 0;
+}
+
+static int jp2_getuint64(jas_stream_t *in, uint_fast64_t *val)
+{
+	abort();
+}
+
+/******************************************************************************\
+* Output operations for primitive types.
+\******************************************************************************/
+
+static int jp2_putuint8(jas_stream_t *out, uint_fast8_t val)
+{
+	if (jas_stream_putc(out, val & 0xff) == EOF) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jp2_putuint16(jas_stream_t *out, uint_fast16_t val)
+{
+	if (jas_stream_putc(out, (val >> 8) & 0xff) == EOF ||
+	  jas_stream_putc(out, val & 0xff) == EOF) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jp2_putuint32(jas_stream_t *out, uint_fast32_t val)
+{
+	if (jas_stream_putc(out, (val >> 24) & 0xff) == EOF ||
+	  jas_stream_putc(out, (val >> 16) & 0xff) == EOF ||
+	  jas_stream_putc(out, (val >> 8) & 0xff) == EOF ||
+	  jas_stream_putc(out, val & 0xff) == EOF) {
+		return -1;
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* Miscellaneous code.
+\******************************************************************************/
+
+jp2_boxinfo_t *jp2_boxinfolookup(int type)
+{
+	jp2_boxinfo_t *boxinfo;
+	for (boxinfo = jp2_boxinfos; boxinfo->name; ++boxinfo) {
+		if (boxinfo->type == type) {
+			return boxinfo;
+		}
+	}
+	return &jp2_boxinfo_unk;
+}
+
+
+
+
+
+static void jp2_cmap_destroy(jp2_box_t *box)
+{
+	jp2_cmap_t *cmap = &box->data.cmap;
+	if (cmap->ents) {
+		jas_free(cmap->ents);
+	}
+}
+
+static int jp2_cmap_getdata(jp2_box_t *box, jas_stream_t *in)
+{
+	jp2_cmap_t *cmap = &box->data.cmap;
+	jp2_cmapent_t *ent;
+	int i;
+
+	cmap->numchans = (box->len - JP2_BOX_HDRLEN) / 4;
+	if (!(cmap->ents = jas_malloc(cmap->numchans * sizeof(jp2_cmapent_t)))) {
+		return -1;
+	}
+	for (i = 0; i < cmap->numchans; ++i) {
+		ent = &cmap->ents[i];
+		if (jp2_getuint16(in, &ent->cmptno) ||
+		  jp2_getuint8(in, &ent->map) ||
+		  jp2_getuint8(in, &ent->pcol)) {
+			return -1;
+		}
+	}
+	
+	return 0;
+}
+
+static int jp2_cmap_putdata(jp2_box_t *box, jas_stream_t *out)
+{
+	return -1;
+}
+
+static void jp2_cmap_dumpdata(jp2_box_t *box, FILE *out)
+{
+	jp2_cmap_t *cmap = &box->data.cmap;
+	int i;
+	jp2_cmapent_t *ent;
+	fprintf(stderr, "numchans = %d\n", (int) cmap->numchans);
+	for (i = 0; i < cmap->numchans; ++i) {
+		ent = &cmap->ents[i];
+		fprintf(stderr, "cmptno=%d; map=%d; pcol=%d\n",
+		  (int) ent->cmptno, (int) ent->map, (int) ent->pcol);
+	}
+}
+
+static void jp2_pclr_destroy(jp2_box_t *box)
+{
+	jp2_pclr_t *pclr = &box->data.pclr;
+	if (pclr->lutdata) {
+		jas_free(pclr->lutdata);
+	}
+}
+
+static int jp2_pclr_getdata(jp2_box_t *box, jas_stream_t *in)
+{
+	jp2_pclr_t *pclr = &box->data.pclr;
+	int lutsize;
+	int i;
+	int j;
+	int_fast32_t x;
+
+	pclr->lutdata = 0;
+
+	if (jp2_getuint16(in, &pclr->numlutents) ||
+	  jp2_getuint8(in, &pclr->numchans)) {
+		return -1;
+	}
+	lutsize = pclr->numlutents * pclr->numchans;
+	if (!(pclr->lutdata = jas_malloc(lutsize * sizeof(int_fast32_t)))) {
+		return -1;
+	}
+	if (!(pclr->bpc = jas_malloc(pclr->numchans * sizeof(uint_fast8_t)))) {
+		return -1;
+	}
+	for (i = 0; i < pclr->numchans; ++i) {
+		if (jp2_getuint8(in, &pclr->bpc[i])) {
+			return -1;
+		}
+	}
+	for (i = 0; i < pclr->numlutents; ++i) {
+		for (j = 0; j < pclr->numchans; ++j) {
+			if (jp2_getint(in, (pclr->bpc[j] & 0x80) != 0,
+			  (pclr->bpc[j] & 0x7f) + 1, &x)) {
+				return -1;
+			}
+			pclr->lutdata[i * pclr->numchans + j] = x;
+		}
+	}
+	return 0;
+}
+
+static int jp2_pclr_putdata(jp2_box_t *box, jas_stream_t *out)
+{
+    /* This code from official Jasper must be part of unfinished work.
+       It generates an unused variable warning.
+	jp2_pclr_t *pclr = &box->data.pclr;
+    */
+	return -1;
+}
+
+static void jp2_pclr_dumpdata(jp2_box_t *box, FILE *out)
+{
+	jp2_pclr_t *pclr = &box->data.pclr;
+	int i;
+	int j;
+	fprintf(out, "numents=%d; numchans=%d\n", (int) pclr->numlutents,
+	  (int) pclr->numchans);
+	for (i = 0; i < pclr->numlutents; ++i) {
+		for (j = 0; j < pclr->numchans; ++j) {
+			fprintf(out, "LUT[%d][%d]=%d\n", i, j, pclr->lutdata[i * pclr->numchans + j]);
+		}
+	}
+}
+
+static int jp2_getint(jas_stream_t *in, int s, int n, int_fast32_t *val)
+{
+	int c;
+	int i;
+	uint_fast32_t v;
+	int m;
+
+	m = (n + 7) / 8;
+
+	v = 0;
+	for (i = 0; i < m; ++i) {
+		if ((c = jas_stream_getc(in)) == EOF) {
+			return -1;
+		}
+		v = (v << 8) | c;
+	}
+	v &= ONES(n);
+	if (s) {
+		int sb;
+		sb = v & (1 << (8 * m - 1));
+		*val = ((~v) + 1) & ONES(8 * m);
+		if (sb) {
+			*val = -*val;
+		}
+	} else {
+		*val = v;
+	}
+
+	return 0;
+}
+
+jp2_cdefchan_t *jp2_cdef_lookup(jp2_cdef_t *cdef, int channo)
+{
+	int i;
+	jp2_cdefchan_t *cdefent;
+	for (i = 0; i < cdef->numchans; ++i) {
+		cdefent = &cdef->ents[i];
+		if (cdefent->channo == channo) {
+			return cdefent;
+		}
+	}
+	return 0;
+}
diff --git a/converter/other/jpeg2000/libjasper/jp2/jp2_cod.h b/converter/other/jpeg2000/libjasper/jp2/jp2_cod.h
new file mode 100644
index 00000000..96b48d12
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jp2/jp2_cod.h
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * JP2 Library
+ *
+ * $Id$
+ */
+
+#ifndef JP2_COD_H
+#define JP2_COD_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jasper/jas_types.h"
+
+/******************************************************************************\
+* Macros.
+\******************************************************************************/
+
+#define	JP2_SPTOBPC(s, p) \
+	((((p) - 1) & 0x7f) | (((s) & 1) << 7))
+
+/******************************************************************************\
+* Box class.
+\******************************************************************************/
+
+#define	JP2_BOX_HDRLEN	8
+
+/* Box types. */
+#define	JP2_BOX_JP		0x6a502020	/* Signature */
+#define JP2_BOX_FTYP	0x66747970	/* File Type */
+#define	JP2_BOX_JP2H	0x6a703268	/* JP2 Header */
+#define	JP2_BOX_IHDR	0x69686472	/* Image Header */
+#define	JP2_BOX_BPCC	0x62706363	/* Bits Per Component */
+#define	JP2_BOX_COLR	0x636f6c72	/* Color Specification */
+#define	JP2_BOX_PCLR	0x70636c72	/* Palette */
+#define	JP2_BOX_CMAP	0x636d6170	/* Component Mapping */
+#define	JP2_BOX_CDEF	0x63646566	/* Channel Definition */
+#define	JP2_BOX_RES		0x72657320	/* Resolution */
+#define	JP2_BOX_RESC	0x72657363	/* Capture Resolution */
+#define	JP2_BOX_RESD	0x72657364	/* Default Display Resolution */
+#define	JP2_BOX_JP2C	0x6a703263	/* Contiguous Code Stream */
+#define	JP2_BOX_JP2I	0x6a703269	/* Intellectual Property */
+#define	JP2_BOX_XML		0x786d6c20	/* XML */
+#define	JP2_BOX_UUID	0x75756964	/* UUID */
+#define	JP2_BOX_UINF	0x75696e66	/* UUID Info */
+#define	JP2_BOX_ULST	0x75637374	/* UUID List */
+#define	JP2_BOX_URL		0x75726c20	/* URL */
+
+#define	JP2_BOX_SUPER	0x01
+#define	JP2_BOX_NODATA	0x02
+
+/* JP box data. */
+
+#define	JP2_JP_MAGIC	0x0d0a870a
+#define	JP2_JP_LEN		12
+
+typedef struct {
+	uint_fast32_t magic;
+} jp2_jp_t;
+
+/* FTYP box data. */
+
+#define	JP2_FTYP_MAXCOMPATCODES	32
+#define	JP2_FTYP_MAJVER		0x6a703220
+#define	JP2_FTYP_MINVER		0
+#define	JP2_FTYP_COMPATCODE		JP2_FTYP_MAJVER
+
+typedef struct {
+	uint_fast32_t majver;
+	uint_fast32_t minver;
+	uint_fast32_t numcompatcodes;
+	uint_fast32_t compatcodes[JP2_FTYP_MAXCOMPATCODES];
+} jp2_ftyp_t;
+
+/* IHDR box data. */
+
+#define	JP2_IHDR_COMPTYPE	7
+#define	JP2_IHDR_BPCNULL	255
+
+typedef struct {
+	uint_fast32_t width;
+	uint_fast32_t height;
+	uint_fast16_t numcmpts;
+	uint_fast8_t bpc;
+	uint_fast8_t comptype;
+	uint_fast8_t csunk;
+	uint_fast8_t ipr;
+} jp2_ihdr_t;
+
+/* BPCC box data. */
+
+typedef struct {
+	uint_fast16_t numcmpts;
+	uint_fast8_t *bpcs;
+} jp2_bpcc_t;
+
+/* COLR box data. */
+
+#define	JP2_COLR_ENUM	1
+#define	JP2_COLR_ICC	2
+#define	JP2_COLR_PRI	0
+
+#define	JP2_COLR_SRGB	16
+#define	JP2_COLR_SGRAY	17
+#define	JP2_COLR_SYCC	18
+
+typedef struct {
+	uint_fast8_t method;
+	uint_fast8_t pri;
+	uint_fast8_t approx;
+	uint_fast32_t csid;
+	uint_fast8_t *iccp;
+	int iccplen;
+	/* XXX - Someday we ought to add ICC profile data here. */
+} jp2_colr_t;
+
+/* PCLR box data. */
+
+typedef struct {
+	uint_fast16_t numlutents;
+	uint_fast8_t numchans;
+	int_fast32_t *lutdata;
+	uint_fast8_t *bpc;
+} jp2_pclr_t;
+
+/* CDEF box per-channel data. */
+
+#define JP2_CDEF_RGB_R	1
+#define JP2_CDEF_RGB_G	2
+#define JP2_CDEF_RGB_B	3
+
+#define JP2_CDEF_YCBCR_Y	1
+#define JP2_CDEF_YCBCR_CB	2
+#define JP2_CDEF_YCBCR_CR	3
+
+#define	JP2_CDEF_GRAY_Y	1
+
+#define	JP2_CDEF_TYPE_COLOR	0
+#define	JP2_CDEF_TYPE_OPACITY	1
+#define	JP2_CDEF_TYPE_UNSPEC	65535
+#define	JP2_CDEF_ASOC_ALL	0
+#define	JP2_CDEF_ASOC_NONE	65535
+
+typedef struct {
+	uint_fast16_t channo;
+	uint_fast16_t type;
+	uint_fast16_t assoc;
+} jp2_cdefchan_t;
+
+/* CDEF box data. */
+
+typedef struct {
+	uint_fast16_t numchans;
+	jp2_cdefchan_t *ents;
+} jp2_cdef_t;
+
+typedef struct {
+	uint_fast16_t cmptno;
+	uint_fast8_t map;
+	uint_fast8_t pcol;
+} jp2_cmapent_t;
+
+typedef struct {
+	uint_fast16_t numchans;
+	jp2_cmapent_t *ents;
+} jp2_cmap_t;
+
+#define	JP2_CMAP_DIRECT		0
+#define	JP2_CMAP_PALETTE	1
+
+/* Generic box. */
+
+struct jp2_boxops_s;
+typedef struct {
+
+	struct jp2_boxops_s *ops;
+	struct jp2_boxinfo_s *info;
+
+	uint_fast32_t type;
+	uint_fast32_t len;
+
+	union {
+		jp2_jp_t jp;
+		jp2_ftyp_t ftyp;
+		jp2_ihdr_t ihdr;
+		jp2_bpcc_t bpcc;
+		jp2_colr_t colr;
+		jp2_pclr_t pclr;
+		jp2_cdef_t cdef;
+		jp2_cmap_t cmap;
+	} data;
+
+} jp2_box_t;
+
+typedef struct jp2_boxops_s {
+	void (*init)(jp2_box_t *box);
+	void (*destroy)(jp2_box_t *box);
+	int (*getdata)(jp2_box_t *box, jas_stream_t *in);
+	int (*putdata)(jp2_box_t *box, jas_stream_t *out);
+	void (*dumpdata)(jp2_box_t *box, FILE *out);
+} jp2_boxops_t;
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+typedef struct jp2_boxinfo_s {
+	int type;
+	const char *name;
+	int flags;
+	jp2_boxops_t ops;
+} jp2_boxinfo_t;
+
+/******************************************************************************\
+* Box class.
+\******************************************************************************/
+
+jp2_box_t *jp2_box_create(int type);
+void jp2_box_destroy(jp2_box_t *box);
+jp2_box_t *jp2_box_get(jas_stream_t *in);
+int jp2_box_put(jp2_box_t *box, jas_stream_t *out);
+
+#define JP2_DTYPETOBPC(dtype) \
+  ((JAS_IMAGE_CDT_GETSGND(dtype) << 7) | (JAS_IMAGE_CDT_GETPREC(dtype) - 1))
+#define	JP2_BPCTODTYPE(bpc) \
+  (JAS_IMAGE_CDT_SETSGND(bpc >> 7) | JAS_IMAGE_CDT_SETPREC((bpc & 0x7f) + 1))
+
+#define ICC_CS_RGB	0x52474220
+#define ICC_CS_YCBCR	0x59436272
+#define ICC_CS_GRAY	0x47524159
+
+jp2_cdefchan_t *jp2_cdef_lookup(jp2_cdef_t *cdef, int channo);
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jp2/jp2_dec.c b/converter/other/jpeg2000/libjasper/jp2/jp2_dec.c
new file mode 100644
index 00000000..3cce9278
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jp2/jp2_dec.c
@@ -0,0 +1,649 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * JP2 Library
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jasper/jas_image.h"
+#include "jasper/jas_stream.h"
+#include "jasper/jas_math.h"
+#include "jasper/jas_debug.h"
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_version.h"
+
+#include "jp2_cod.h"
+#include "jp2_dec.h"
+
+#define	JP2_VALIDATELEN	(JAS_MIN(JP2_JP_LEN + 16, JAS_STREAM_MAXPUTBACK))
+
+static jp2_dec_t *jp2_dec_create(void);
+static void jp2_dec_destroy(jp2_dec_t *dec);
+static int jp2_getcs(jp2_colr_t *colr);
+static int jp2_getct(int colorspace, int type, int assoc);
+static int fromiccpcs(int cs);
+
+/******************************************************************************\
+* Functions.
+\******************************************************************************/
+
+jas_image_t *jp2_decode(jas_stream_t *in, char *optstr)
+{
+	jp2_box_t *box;
+	int found;
+	jas_image_t *image;
+	jp2_dec_t *dec;
+	bool samedtype;
+	int dtype;
+	int i;
+	jp2_cmap_t *cmapd;
+	jp2_pclr_t *pclrd;
+	jp2_cdef_t *cdefd;
+	int channo;
+	int newcmptno;
+	int_fast32_t *lutents;
+	jp2_cmapent_t *cmapent;
+	unsigned char *iccp;
+	int cs;
+
+	dec = 0;
+	box = 0;
+	image = 0;
+
+	if (!(dec = jp2_dec_create())) {
+		goto error;
+	}
+
+	/* Get the first box.  This should be a JP box. */
+	if (!(box = jp2_box_get(in))) {
+		jas_eprintf("error: cannot get box\n");
+		goto error;
+	}
+	if (box->type != JP2_BOX_JP) {
+		jas_eprintf("error: expecting signature box\n");
+		goto error;
+	}
+	if (box->data.jp.magic != JP2_JP_MAGIC) {
+		jas_eprintf("incorrect magic number\n");
+		goto error;
+	}
+	jp2_box_destroy(box);
+	box = 0;
+
+	/* Get the second box.  This should be a FTYP box. */
+	if (!(box = jp2_box_get(in))) {
+		goto error;
+	}
+	if (box->type != JP2_BOX_FTYP) {
+		jas_eprintf("expecting file type box\n");
+		goto error;
+	}
+	jp2_box_destroy(box);
+	box = 0;
+
+	/* Get more boxes... */
+	found = 0;
+	while ((box = jp2_box_get(in))) {
+		if (jas_getdbglevel() >= 1) {
+			fprintf(stderr, "box type %s\n", box->info->name);
+		}
+		switch (box->type) {
+		case JP2_BOX_JP2C:
+			found = 1;
+			break;
+		case JP2_BOX_IHDR:
+			if (!dec->ihdr) {
+				dec->ihdr = box;
+				box = 0;
+			}
+			break;
+		case JP2_BOX_BPCC:
+			if (!dec->bpcc) {
+				dec->bpcc = box;
+				box = 0;
+			}
+			break;
+		case JP2_BOX_CDEF:
+			if (!dec->cdef) {
+				dec->cdef = box;
+				box = 0;
+			}
+			break;
+		case JP2_BOX_PCLR:
+			if (!dec->pclr) {
+				dec->pclr = box;
+				box = 0;
+			}
+			break;
+		case JP2_BOX_CMAP:
+			if (!dec->cmap) {
+				dec->cmap = box;
+				box = 0;
+			}
+			break;
+		case JP2_BOX_COLR:
+			if (!dec->colr) {
+				dec->colr = box;
+				box = 0;
+			}
+			break;
+		}
+		if (box) {
+			jp2_box_destroy(box);
+			box = 0;
+		}
+		if (found) {
+			break;
+		}
+	}
+
+	if (!found) {
+		jas_eprintf("error: no code stream found\n");
+		goto error;
+	}
+
+	if (!(dec->image = jpc_decode(in, optstr))) {
+		jas_eprintf("error: cannot decode code stream\n");
+		goto error;
+	}
+
+	/* An IHDR box must be present. */
+	if (!dec->ihdr) {
+		jas_eprintf("error: missing IHDR box\n");
+		goto error;
+	}
+
+	/* Does the number of components indicated in the IHDR box match
+	  the value specified in the code stream? */
+	if (dec->ihdr->data.ihdr.numcmpts != jas_image_numcmpts(dec->image)) {
+		jas_eprintf("warning: number of components mismatch\n");
+	}
+
+	/* At least one component must be present. */
+	if (!jas_image_numcmpts(dec->image)) {
+		jas_eprintf("error: no components\n");
+		goto error;
+	}
+
+	/* Determine if all components have the same data type. */
+	samedtype = true;
+	dtype = jas_image_cmptdtype(dec->image, 0);
+	for (i = 1; i < jas_image_numcmpts(dec->image); ++i) {
+		if (jas_image_cmptdtype(dec->image, i) != dtype) {
+			samedtype = false;
+			break;
+		}
+	}
+
+	/* Is the component data type indicated in the IHDR box consistent
+	  with the data in the code stream? */
+	if ((samedtype && dec->ihdr->data.ihdr.bpc != JP2_DTYPETOBPC(dtype)) ||
+	  (!samedtype && dec->ihdr->data.ihdr.bpc != JP2_IHDR_BPCNULL)) {
+		jas_eprintf("warning: component data type mismatch\n");
+	}
+
+	/* Is the compression type supported? */
+	if (dec->ihdr->data.ihdr.comptype != JP2_IHDR_COMPTYPE) {
+		jas_eprintf("error: unsupported compression type\n");
+		goto error;
+	}
+
+	if (dec->bpcc) {
+		/* Is the number of components indicated in the BPCC box
+		  consistent with the code stream data? */
+		if (dec->bpcc->data.bpcc.numcmpts != jas_image_numcmpts(
+		  dec->image)) {
+			jas_eprintf("warning: number of components mismatch\n");
+		}
+		/* Is the component data type information indicated in the BPCC
+		  box consistent with the code stream data? */
+		if (!samedtype) {
+			for (i = 0; i < jas_image_numcmpts(dec->image); ++i) {
+				if (jas_image_cmptdtype(dec->image, i) != JP2_BPCTODTYPE(dec->bpcc->data.bpcc.bpcs[i])) {
+					jas_eprintf("warning: component data type mismatch\n");
+				}
+			}
+		} else {
+			jas_eprintf("warning: superfluous BPCC box\n");
+		}
+	}
+
+	/* A COLR box must be present. */
+	if (!dec->colr) {
+		jas_eprintf("error: no COLR box\n");
+		goto error;
+	}
+
+	switch (dec->colr->data.colr.method) {
+	case JP2_COLR_ENUM:
+		jas_image_setcolorspace(dec->image, jp2_getcs(&dec->colr->data.colr));
+		break;
+	case JP2_COLR_ICC:
+		if (dec->colr->data.colr.iccplen < 128) {
+			abort();
+		}
+		iccp = dec->colr->data.colr.iccp;
+		cs = (iccp[16] << 24) | (iccp[17] << 16) | (iccp[18] << 8) |
+		  iccp[19];
+		jas_eprintf("ICC Profile CS %08x\n", cs);
+		jas_image_setcolorspace(dec->image, fromiccpcs(cs));
+		break;
+	}
+
+	/* If a CMAP box is present, a PCLR box must also be present. */
+	if (dec->cmap && !dec->pclr) {
+		jas_eprintf("warning: missing PCLR box or superfluous CMAP box\n");
+		jp2_box_destroy(dec->cmap);
+		dec->cmap = 0;
+	}
+
+	/* If a CMAP box is not present, a PCLR box must not be present. */
+	if (!dec->cmap && dec->pclr) {
+		jas_eprintf("warning: missing CMAP box or superfluous PCLR box\n");
+		jp2_box_destroy(dec->pclr);
+		dec->pclr = 0;
+	}
+
+	/* Determine the number of channels (which is essentially the number
+	  of components after any palette mappings have been applied). */
+	dec->numchans = dec->cmap ? dec->cmap->data.cmap.numchans : jas_image_numcmpts(dec->image);
+
+	/* Perform a basic sanity check on the CMAP box if present. */
+	if (dec->cmap) {
+		for (i = 0; i < dec->numchans; ++i) {
+			/* Is the component number reasonable? */
+			if (dec->cmap->data.cmap.ents[i].cmptno >= jas_image_numcmpts(dec->image)) {
+				jas_eprintf("error: invalid component number in CMAP box\n");
+				goto error;
+			}
+			/* Is the LUT index reasonable? */
+			if (dec->cmap->data.cmap.ents[i].pcol >= dec->pclr->data.pclr.numchans) {
+				jas_eprintf("error: invalid CMAP LUT index\n");
+				goto error;
+			}
+		}
+	}
+
+	/* Allocate space for the channel-number to component-number LUT. */
+	if (!(dec->chantocmptlut = jas_malloc(dec->numchans * sizeof(uint_fast16_t)))) {
+		jas_eprintf("error: no memory\n");
+		goto error;
+	}
+
+	if (!dec->cmap) {
+		for (i = 0; i < dec->numchans; ++i) {
+			dec->chantocmptlut[i] = i;
+		}
+	} else {
+		cmapd = &dec->cmap->data.cmap;
+		pclrd = &dec->pclr->data.pclr;
+		cdefd = &dec->cdef->data.cdef;
+		for (channo = 0; channo < cmapd->numchans; ++channo) {
+			cmapent = &cmapd->ents[channo];
+			if (cmapent->map == JP2_CMAP_DIRECT) {
+				dec->chantocmptlut[channo] = channo;
+			} else if (cmapent->map == JP2_CMAP_PALETTE) {
+				lutents = jas_malloc(pclrd->numlutents * sizeof(int_fast32_t));
+				for (i = 0; i < pclrd->numlutents; ++i) {
+					lutents[i] = pclrd->lutdata[cmapent->pcol + i * pclrd->numchans];
+				}
+				newcmptno = jas_image_numcmpts(dec->image);
+				jas_image_depalettize(dec->image, cmapent->cmptno, pclrd->numlutents, lutents, JP2_BPCTODTYPE(pclrd->bpc[cmapent->pcol]), newcmptno);
+				dec->chantocmptlut[channo] = newcmptno;
+				jas_free(lutents);
+#if 0
+				if (dec->cdef) {
+					cdefent = jp2_cdef_lookup(cdefd, channo);
+					if (!cdefent) {
+						abort();
+					}
+				jas_image_setcmpttype(dec->image, newcmptno, jp2_getct(jas_image_colorspace(dec->image), cdefent->type, cdefent->assoc));
+				} else {
+				jas_image_setcmpttype(dec->image, newcmptno, jp2_getct(jas_image_colorspace(dec->image), 0, channo + 1));
+				}
+#endif
+			}
+		}
+	}
+
+	/* Mark all components as being of unknown type. */
+
+	for (i = 0; i < jas_image_numcmpts(dec->image); ++i) {
+		jas_image_setcmpttype(dec->image, i, JAS_IMAGE_CT_UNKNOWN);
+	}
+
+	/* Determine the type of each component. */
+	if (dec->cdef) {
+		for (i = 0; i < dec->numchans; ++i) {
+			jas_image_setcmpttype(dec->image,
+			  dec->chantocmptlut[dec->cdef->data.cdef.ents[i].channo],
+			  jp2_getct(jas_image_colorspace(dec->image),
+			  dec->cdef->data.cdef.ents[i].type, dec->cdef->data.cdef.ents[i].assoc));
+		}
+	} else {
+		for (i = 0; i < dec->numchans; ++i) {
+			jas_image_setcmpttype(dec->image, dec->chantocmptlut[i],
+			  jp2_getct(jas_image_colorspace(dec->image), 0, i + 1));
+		}
+	}
+
+	/* Delete any components that are not of interest. */
+	for (i = jas_image_numcmpts(dec->image) - 1; i >= 0; --i) {
+		if (jas_image_cmpttype(dec->image, i) == JAS_IMAGE_CT_UNKNOWN) {
+			jas_image_delcmpt(dec->image, i);
+		}
+	}
+
+	/* Ensure that some components survived. */
+	if (!jas_image_numcmpts(dec->image)) {
+		jas_eprintf("error: no components\n");
+		goto error;
+	}
+fprintf(stderr, "no of components is %d\n", jas_image_numcmpts(dec->image));
+
+	/* Prevent the image from being destroyed later. */
+	image = dec->image;
+	dec->image = 0;
+
+	jp2_dec_destroy(dec);
+
+	return image;
+
+error:
+	if (box) {
+		jp2_box_destroy(box);
+	}
+	if (dec) {
+		jp2_dec_destroy(dec);
+	}
+	return 0;
+}
+
+int jp2_validate(jas_stream_t *in)
+{
+	char buf[JP2_VALIDATELEN];
+	int i;
+	int n;
+
+	assert(JAS_STREAM_MAXPUTBACK >= JP2_VALIDATELEN);
+
+	/* Read the validation data (i.e., the data used for detecting
+	  the format). */
+	if ((n = jas_stream_read(in, buf, JP2_VALIDATELEN)) < 0) {
+		return -1;
+	}
+
+	/* Put the validation data back onto the stream, so that the
+	  stream position will not be changed. */
+	for (i = n - 1; i >= 0; --i) {
+		if (jas_stream_ungetc(in, buf[i]) == EOF) {
+			return -1;
+		}
+	}
+
+	/* Did we read enough data? */
+	if (n < JP2_VALIDATELEN) {
+		return -1;
+	}
+
+	/* Is the box type correct? */
+	if (((buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]) !=
+	  JP2_BOX_JP)
+	{
+		return -1;
+	}
+
+	return 0;
+}
+
+static jp2_dec_t *jp2_dec_create(void)
+{
+	jp2_dec_t *dec;
+
+	if (!(dec = jas_malloc(sizeof(jp2_dec_t)))) {
+		return 0;
+	}
+	dec->ihdr = 0;
+	dec->bpcc = 0;
+	dec->cdef = 0;
+	dec->pclr = 0;
+	dec->image = 0;
+	dec->chantocmptlut = 0;
+	dec->cmap = 0;
+	dec->colr = 0;
+	return dec;
+}
+
+static void jp2_dec_destroy(jp2_dec_t *dec)
+{
+	if (dec->ihdr) {
+		jp2_box_destroy(dec->ihdr);
+	}
+	if (dec->bpcc) {
+		jp2_box_destroy(dec->bpcc);
+	}
+	if (dec->cdef) {
+		jp2_box_destroy(dec->cdef);
+	}
+	if (dec->pclr) {
+		jp2_box_destroy(dec->pclr);
+	}
+	if (dec->image) {
+		jas_image_destroy(dec->image);
+	}
+	if (dec->cmap) {
+		jp2_box_destroy(dec->cmap);
+	}
+	if (dec->colr) {
+		jp2_box_destroy(dec->colr);
+	}
+	if (dec->chantocmptlut) {
+		jas_free(dec->chantocmptlut);
+	}
+	jas_free(dec);
+}
+
+
+
+
+
+
+static int jp2_getct(int colorspace, int type, int assoc)
+{
+	if (type == 1 && assoc == 0) {
+		return JAS_IMAGE_CT_OPACITY;
+	}
+	if (type == 0 && assoc >= 1 && assoc <= 65534) {
+		switch (colorspace) {
+		case JAS_IMAGE_CS_RGB:
+			switch (assoc) {
+			case JP2_CDEF_RGB_R:
+				return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_R);
+				break;
+			case JP2_CDEF_RGB_G:
+				return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_G);
+				break;
+			case JP2_CDEF_RGB_B:
+				return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_B);
+				break;
+			}
+			break;
+		case JAS_IMAGE_CS_YCBCR:
+			switch (assoc) {
+			case JP2_CDEF_YCBCR_Y:
+				return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_YCBCR_Y);
+				break;
+			case JP2_CDEF_YCBCR_CB:
+				return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_YCBCR_CB);
+				break;
+			case JP2_CDEF_YCBCR_CR:
+				return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_YCBCR_CR);
+				break;
+			}
+			break;
+		case JAS_IMAGE_CS_GRAY:
+			switch (assoc) {
+			case JP2_CDEF_GRAY_Y:
+				return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_GRAY_Y);
+				break;
+			}
+			break;
+#if 0
+		case JAS_IMAGE_CS_ICC:
+#endif
+		default:
+			return JAS_IMAGE_CT_COLOR(assoc - 1);
+			break;
+		}
+	}
+	return JAS_IMAGE_CT_UNKNOWN;
+}
+
+static int jp2_getcs(jp2_colr_t *colr)
+{
+	if (colr->method == JP2_COLR_ENUM) {
+		switch (colr->csid) {
+		case JP2_COLR_SRGB:
+			return JAS_IMAGE_CS_RGB;
+			break;
+		case JP2_COLR_SYCC:
+			return JAS_IMAGE_CS_YCBCR;
+			break;
+		case JP2_COLR_SGRAY:
+			return JAS_IMAGE_CS_GRAY;
+			break;
+		}
+	}
+	return JAS_IMAGE_CS_UNKNOWN;
+}
+
+static int fromiccpcs(int cs)
+{
+	switch (cs) {
+	case ICC_CS_RGB:
+		return JAS_IMAGE_CS_RGB;
+		break;
+	case ICC_CS_YCBCR:
+		return JAS_IMAGE_CS_YCBCR;
+		break;
+	case ICC_CS_GRAY:
+		return JAS_IMAGE_CS_GRAY;
+		break;
+	}
+	return JAS_IMAGE_CS_UNKNOWN;
+}
diff --git a/converter/other/jpeg2000/libjasper/jp2/jp2_dec.h b/converter/other/jpeg2000/libjasper/jp2/jp2_dec.h
new file mode 100644
index 00000000..300be107
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jp2/jp2_dec.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+#ifndef JP2_DEC_H
+#define JP2_DEC_H
+
+#include "jasper/jas_image.h"
+#include "jasper/jas_stream.h"
+#include "jp2_cod.h"
+
+typedef struct {
+
+	jp2_box_t *pclr;
+	jp2_box_t *cdef;
+	jp2_box_t *ihdr;
+	jp2_box_t *bpcc;
+	jp2_box_t *cmap;
+	jp2_box_t *colr;
+	jas_image_t *image;
+	uint_fast16_t numchans;
+	uint_fast16_t *chantocmptlut;
+
+} jp2_dec_t;
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jp2/jp2_enc.c b/converter/other/jpeg2000/libjasper/jp2/jp2_enc.c
new file mode 100644
index 00000000..12538248
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jp2/jp2_enc.c
@@ -0,0 +1,414 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * JP2 Library
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_image.h"
+#include "jasper/jas_stream.h"
+
+#include "jp2_cod.h"
+
+static uint_fast32_t jp2_gettypeasoc(int colorspace, int ctype);
+
+/******************************************************************************\
+* Functions.
+\******************************************************************************/
+
+int jp2_encode(jas_image_t *image, jas_stream_t *out, char *optstr)
+{
+	jp2_box_t *box;
+	jp2_ftyp_t *ftyp;
+	jp2_ihdr_t *ihdr;
+	jas_stream_t *tmpstream;
+	int allcmptssame;
+	jp2_bpcc_t *bpcc;
+	long len;
+	uint_fast16_t cmptno;
+	jp2_colr_t *colr;
+	char buf[4096];
+	uint_fast32_t overhead;
+	jp2_cdefchan_t *cdefchanent;
+	jp2_cdef_t *cdef;
+	int i;
+	uint_fast32_t typeasoc;
+
+	box = 0;
+	tmpstream = 0;
+
+	/* Output the signature box. */
+
+	if (!(box = jp2_box_create(JP2_BOX_JP))) {
+		goto error;
+	}
+	box->data.jp.magic = JP2_JP_MAGIC;
+	if (jp2_box_put(box, out)) {
+		goto error;
+	}
+	jp2_box_destroy(box);
+	box = 0;
+
+	/* Output the file type box. */
+
+	if (!(box = jp2_box_create(JP2_BOX_FTYP))) {
+		goto error;
+	}
+	ftyp = &box->data.ftyp;
+	ftyp->majver = JP2_FTYP_MAJVER;
+	ftyp->minver = JP2_FTYP_MINVER;
+	ftyp->numcompatcodes = 1;
+	ftyp->compatcodes[0] = JP2_FTYP_COMPATCODE;
+	if (jp2_box_put(box, out)) {
+		goto error;
+	}
+	jp2_box_destroy(box);
+	box = 0;
+
+	/*
+	 * Generate the data portion of the JP2 header box.
+	 * We cannot simply output the header for this box
+	 * since we do not yet know the correct value for the length
+	 * field.
+	 */
+
+	if (!(tmpstream = jas_stream_memopen(0, 0))) {
+		goto error;
+	}
+
+	/* Generate image header box. */
+
+	if (!(box = jp2_box_create(JP2_BOX_IHDR))) {
+		goto error;
+	}
+	ihdr = &box->data.ihdr;
+	ihdr->width = jas_image_width(image);
+	ihdr->height = jas_image_height(image);
+	ihdr->numcmpts = jas_image_numcmpts(image);
+	allcmptssame = 0;
+	ihdr->bpc = allcmptssame ? JP2_SPTOBPC(jas_image_cmptsgnd(image, 0),
+	  jas_image_cmptprec(image, 0)) : JP2_IHDR_BPCNULL;
+	ihdr->comptype = JP2_IHDR_COMPTYPE;
+	ihdr->csunk = 0;
+	ihdr->ipr = 0;
+	if (jp2_box_put(box, tmpstream)) {
+		goto error;
+	}
+	jp2_box_destroy(box);
+	box = 0;
+
+	/* Generate bits per component box. */
+
+	if (!allcmptssame) {
+		if (!(box = jp2_box_create(JP2_BOX_BPCC))) {
+			goto error;
+		}
+		bpcc = &box->data.bpcc;
+		bpcc->numcmpts = jas_image_numcmpts(image);
+		if (!(bpcc->bpcs = jas_malloc(bpcc->numcmpts *
+		  sizeof(uint_fast8_t)))) {
+			goto error;
+		}
+		for (cmptno = 0; cmptno < bpcc->numcmpts; ++cmptno) {
+			bpcc->bpcs[cmptno] = JP2_SPTOBPC(jas_image_cmptsgnd(image,
+			  cmptno), jas_image_cmptprec(image, cmptno));
+		}
+		if (jp2_box_put(box, tmpstream)) {
+			goto error;
+		}
+		jp2_box_destroy(box);
+		box = 0;
+	}
+
+	/* Generate color specification box. */
+
+	if (!(box = jp2_box_create(JP2_BOX_COLR))) {
+		goto error;
+	}
+	colr = &box->data.colr;
+	colr->method = JP2_COLR_ENUM;
+	colr->pri = JP2_COLR_PRI;
+	colr->approx = 0;
+	colr->csid = (jas_image_colorspace(image) == JAS_IMAGE_CS_RGB) ? JP2_COLR_SRGB :
+	  JP2_COLR_SGRAY;
+	if (jp2_box_put(box, tmpstream)) {
+		goto error;
+	}
+	jp2_box_destroy(box);
+	box = 0;
+
+	if (!(jas_image_colorspace(image) == JAS_IMAGE_CS_RGB &&
+	  jas_image_numcmpts(image) == 3 &&
+	  jas_image_getcmptbytype(image, 0) ==
+	  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_R) &&
+	  jas_image_getcmptbytype(image, 1) ==
+	  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_G) &&
+	  jas_image_getcmptbytype(image, 2) ==
+	  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_B)) &&
+	  !(jas_image_colorspace(image) == JAS_IMAGE_CS_YCBCR &&
+	  jas_image_numcmpts(image) != 3 &&
+	  jas_image_getcmptbytype(image, 0) ==
+	  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_YCBCR_Y) &&
+	  jas_image_getcmptbytype(image, 1) ==
+	  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_YCBCR_CB) &&
+	  jas_image_getcmptbytype(image, 2) ==
+	  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_YCBCR_CR)) &&
+	  !(jas_image_colorspace(image) == JAS_IMAGE_CS_GRAY &&
+	  jas_image_numcmpts(image) == 1 &&
+	  jas_image_getcmptbytype(image, 0) ==
+	  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_GRAY_Y))) {
+
+		if (!(box = jp2_box_create(JP2_BOX_CDEF))) {
+			goto error;
+		}
+		cdef = &box->data.cdef;
+		cdef->numchans = jas_image_numcmpts(image);
+		cdef->ents = jas_malloc(cdef->numchans * sizeof(jp2_cdefchan_t));
+		for (i = 0; i < jas_image_numcmpts(image); ++i) {
+			cdefchanent = &cdef->ents[i];
+			cdefchanent->channo = i;
+			typeasoc = jp2_gettypeasoc(jas_image_colorspace(image), jas_image_cmpttype(image, i));
+			cdefchanent->type = typeasoc >> 16;
+			cdefchanent->assoc = typeasoc & 0x7fff;
+		}
+		jp2_box_destroy(box);
+		box = 0;
+	}
+
+	/* Determine the total length of the JP2 header box. */
+
+	len = jas_stream_tell(tmpstream);
+	jas_stream_rewind(tmpstream);
+
+	/*
+	 * Output the JP2 header box and all of the boxes which it contains.
+	 */
+
+	if (!(box = jp2_box_create(JP2_BOX_JP2H))) {
+		goto error;
+	}
+	box->len = len + JP2_BOX_HDRLEN;
+	if (jp2_box_put(box, out)) {
+		goto error;
+	}
+	jp2_box_destroy(box);
+	box = 0;
+
+	if (jas_stream_copy(out, tmpstream, len)) {
+		goto error;
+	}
+
+	jas_stream_close(tmpstream);
+	tmpstream = 0;
+
+	/*
+	 * Output the contiguous code stream box.
+	 */
+
+	if (!(box = jp2_box_create(JP2_BOX_JP2C))) {
+		goto error;
+	}
+	box->len = 0;
+	if (jp2_box_put(box, out)) {
+		goto error;
+	}
+	jp2_box_destroy(box);
+	box = 0;
+
+	/* Output the JPEG-2000 code stream. */
+
+	overhead = jas_stream_getrwcount(out);
+	sprintf(buf, "%s\n_jp2overhead=%lu\n", (optstr ? optstr : ""),
+	  (unsigned long) overhead);
+
+	if (jpc_encode(image, out, buf)) {
+		goto error;
+	}
+
+	return 0;
+	abort();
+
+error:
+
+	if (box) {
+		jp2_box_destroy(box);
+	}
+	if (tmpstream) {
+		jas_stream_close(tmpstream);
+	}
+	return -1;
+}
+
+
+static uint_fast32_t jp2_gettypeasoc(int colorspace, int ctype)
+{
+	int type;
+	int asoc;
+
+	if (ctype & JAS_IMAGE_CT_OPACITY) {
+		type = JP2_CDEF_TYPE_OPACITY;
+		asoc = JP2_CDEF_ASOC_ALL;
+		goto done;
+	}
+
+	type = JP2_CDEF_TYPE_UNSPEC;
+	asoc = JP2_CDEF_ASOC_NONE;
+	switch (colorspace) {
+	case JAS_IMAGE_CS_RGB:
+		switch (JAS_IMAGE_CT_COLOR(ctype)) {
+		case JAS_IMAGE_CT_RGB_R:
+			type = JP2_CDEF_TYPE_COLOR;
+			asoc = JP2_CDEF_RGB_R;
+			break;
+		case JAS_IMAGE_CT_RGB_G:
+			type = JP2_CDEF_TYPE_COLOR;
+			asoc = JP2_CDEF_RGB_G;
+			break;
+		case JAS_IMAGE_CT_RGB_B:
+			type = JP2_CDEF_TYPE_COLOR;
+			asoc = JP2_CDEF_RGB_B;
+			break;
+		}
+		break;
+	case JAS_IMAGE_CS_YCBCR:
+		switch (JAS_IMAGE_CT_COLOR(ctype)) {
+		case JAS_IMAGE_CT_YCBCR_Y:
+			type = JP2_CDEF_TYPE_COLOR;
+			asoc = JP2_CDEF_YCBCR_Y;
+			break;
+		case JAS_IMAGE_CT_YCBCR_CB:
+			type = JP2_CDEF_TYPE_COLOR;
+			asoc = JP2_CDEF_YCBCR_CB;
+			break;
+		case JAS_IMAGE_CT_YCBCR_CR:
+			type = JP2_CDEF_TYPE_COLOR;
+			asoc = JP2_CDEF_YCBCR_CR;
+			break;
+		}
+		break;
+	case JAS_IMAGE_CS_GRAY:
+		type = JP2_CDEF_TYPE_COLOR;
+		asoc = JP2_CDEF_GRAY_Y;
+		break;
+	}
+
+done:
+	return (type << 16) | asoc;
+}
+
diff --git a/converter/other/jpeg2000/libjasper/jp2/partlist b/converter/other/jpeg2000/libjasper/jp2/partlist
new file mode 100644
index 00000000..26ec4c8a
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jp2/partlist
@@ -0,0 +1 @@
+/mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jp2/jp2_cod.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jp2/jp2_dec.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jp2/jp2_enc.o
diff --git a/converter/other/jpeg2000/libjasper/jpc/Makefile b/converter/other/jpeg2000/libjasper/jpc/Makefile
new file mode 100644
index 00000000..e176bd48
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/Makefile
@@ -0,0 +1,22 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../../../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/other/jpeg2000/libjasper/jpc
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+JASPERSRCDIR=$(SRCDIR)/$(SUBDIR)/..
+
+include $(BUILDDIR)/Makefile.config
+
+LIB_OBJECTS = jpc_bs.o jpc_cs.o jpc_dec.o jpc_enc.o \
+	jpc_math.o jpc_mct.o jpc_mqcod.o jpc_mqdec.o jpc_mqenc.o \
+	jpc_qmfb.o jpc_tagtree.o jpc_t1cod.o jpc_t1dec.o jpc_t1enc.o \
+	jpc_tsfb.o jpc_t2cod.o jpc_t2dec.o jpc_t2enc.o jpc_util.o
+
+MERGE_OBJECTS =
+
+all: partlist $(LIB_OBJECTS)
+
+include $(JASPERSRCDIR)/Makefile.common
+
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_bs.c b/converter/other/jpeg2000/libjasper/jpc/jpc_bs.c
new file mode 100644
index 00000000..54f0a819
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_bs.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (c) 1999-2000, Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Bit Stream Class
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_math.h"
+#include "jasper/jas_debug.h"
+
+#include "jpc_bs.h"
+
+/******************************************************************************\
+* Code for opening and closing bit streams.
+\******************************************************************************/
+
+/* Allocate a new bit stream. */
+static jpc_bitstream_t *jpc_bitstream_alloc()
+{
+	jpc_bitstream_t *bitstream;
+
+	/* Allocate memory for the new bit stream object. */
+	if (!(bitstream = jas_malloc(sizeof(jpc_bitstream_t)))) {
+		return 0;
+	}
+	/* Initialize all of the data members. */
+	bitstream->stream_ = 0;
+	bitstream->cnt_ = 0;
+	bitstream->flags_ = 0;
+	bitstream->openmode_ = 0;
+
+	return bitstream;
+}
+
+/* Open a bit stream from a stream. */
+jpc_bitstream_t *jpc_bitstream_sopen(jas_stream_t *stream, const char *mode)
+{
+	jpc_bitstream_t *bitstream;
+
+	/* Ensure that the open mode is valid. */
+	assert(!strcmp(mode, "r") || !strcmp(mode, "w") || !strcmp(mode, "r+")
+	  || !strcmp(mode, "w+"));
+
+	if (!(bitstream = jpc_bitstream_alloc())) {
+		return 0;
+	}
+
+	/* By default, do not close the underlying (character) stream, upon
+	  the close of the bit stream. */
+	bitstream->flags_ = JPC_BITSTREAM_NOCLOSE;
+
+	bitstream->stream_ = stream;
+	bitstream->openmode_ = (mode[0] == 'w') ? JPC_BITSTREAM_WRITE :
+	  JPC_BITSTREAM_READ;
+
+	/* Mark the data buffer as empty. */
+	bitstream->cnt_ = (bitstream->openmode_ == JPC_BITSTREAM_READ) ? 0 : 8;
+	bitstream->buf_ = 0;
+
+	return bitstream;
+}
+
+/* Close a bit stream. */
+int jpc_bitstream_close(jpc_bitstream_t *bitstream)
+{
+	int ret = 0;
+
+	/* Align to the next byte boundary while considering the effects of
+	  bit stuffing. */
+	if (jpc_bitstream_align(bitstream)) {
+		ret = -1;
+	}
+
+	/* If necessary, close the underlying (character) stream. */
+	if (!(bitstream->flags_ & JPC_BITSTREAM_NOCLOSE) && bitstream->stream_) {
+		if (jas_stream_close(bitstream->stream_)) {
+			ret = -1;
+		}
+		bitstream->stream_ = 0;
+	}
+
+	jas_free(bitstream);
+	return ret;
+}
+
+/******************************************************************************\
+* Code for reading/writing from/to bit streams.
+\******************************************************************************/
+
+/* Get a bit from a bit stream. */
+int jpc_bitstream_getbit_func(jpc_bitstream_t *bitstream)
+{
+	int ret;
+	JAS_DBGLOG(1000, ("jpc_bitstream_getbit_func(%p)\n", bitstream));
+	ret = jpc_bitstream_getbit_macro(bitstream);
+	JAS_DBGLOG(1000, ("jpc_bitstream_getbit_func -> %d\n", ret));
+	return ret;
+}
+
+/* Put a bit to a bit stream. */
+int jpc_bitstream_putbit_func(jpc_bitstream_t *bitstream, int b)
+{
+	int ret;
+	JAS_DBGLOG(1000, ("jpc_bitstream_putbit_func(%p, %d)\n", bitstream, b));
+	ret = jpc_bitstream_putbit_macro(bitstream, b);
+	JAS_DBGLOG(1000, ("jpc_bitstream_putbit_func() -> %d\n", ret));
+	return ret;
+}
+
+/* Get one or more bits from a bit stream. */
+long jpc_bitstream_getbits(jpc_bitstream_t *bitstream, int n)
+{
+	long v;
+	int u;
+
+	/* We can reliably get at most 31 bits since ISO/IEC 9899 only
+	  guarantees that a long can represent values up to 2^31-1. */
+	assert(n >= 0 && n < 32);
+
+	/* Get the number of bits requested from the specified bit stream. */
+	v = 0;
+	while (--n >= 0) {
+		if ((u = jpc_bitstream_getbit(bitstream)) < 0) {
+			return -1;
+		}
+		v = (v << 1) | u;
+	}
+	return v;
+}
+
+/* Put one or more bits to a bit stream. */
+int jpc_bitstream_putbits(jpc_bitstream_t *bitstream, int n, long v)
+{
+	int m;
+
+	/* We can reliably put at most 31 bits since ISO/IEC 9899 only
+	  guarantees that a long can represent values up to 2^31-1. */
+	assert(n >= 0 && n < 32);
+	/* Ensure that only the bits to be output are nonzero. */
+	assert(!(v & (~JAS_ONES(n))));
+
+	/* Put the desired number of bits to the specified bit stream. */
+	m = n - 1;
+	while (--n >= 0) {
+		if (jpc_bitstream_putbit(bitstream, (v >> m) & 1) == EOF) {
+			return EOF;
+		}
+		v <<= 1;
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* Code for buffer filling and flushing.
+\******************************************************************************/
+
+/* Fill the buffer for a bit stream. */
+int jpc_bitstream_fillbuf(jpc_bitstream_t *bitstream)
+{
+	int c;
+	/* Note: The count has already been decremented by the caller. */
+	assert(bitstream->openmode_ & JPC_BITSTREAM_READ);
+	assert(bitstream->cnt_ <= 0);
+
+	if (bitstream->flags_ & JPC_BITSTREAM_ERR) {
+		bitstream->cnt_ = 0;
+		return -1;
+	}
+
+	if (bitstream->flags_ & JPC_BITSTREAM_EOF) {
+		bitstream->buf_ = 0x7f;
+		bitstream->cnt_ = 7;
+		return 1;
+	}
+
+	bitstream->buf_ = (bitstream->buf_ << 8) & 0xffff;
+	if ((c = jas_stream_getc((bitstream)->stream_)) == EOF) {
+		bitstream->flags_ |= JPC_BITSTREAM_EOF;
+		return 1;
+	}
+	bitstream->cnt_ = (bitstream->buf_ == 0xff00) ? 6 : 7;
+	bitstream->buf_ |= c & ((1 << (bitstream->cnt_ + 1)) - 1);
+	return (bitstream->buf_ >> bitstream->cnt_) & 1;
+}
+
+
+/******************************************************************************\
+* Code related to flushing.
+\******************************************************************************/
+
+/* Does the bit stream need to be aligned to a byte boundary (considering
+  the effects of bit stuffing)? */
+int jpc_bitstream_needalign(jpc_bitstream_t *bitstream)
+{
+	if (bitstream->openmode_ & JPC_BITSTREAM_READ) {
+		/* The bit stream is open for reading. */
+		/* If there are any bits buffered for reading, or the
+		  previous byte forced a stuffed bit, alignment is
+		  required. */
+		if ((bitstream->cnt_ < 8 && bitstream->cnt_ > 0) ||
+		  ((bitstream->buf_ >> 8) & 0xff) == 0xff) {
+			return 1;
+		}
+	} else if (bitstream->openmode_ & JPC_BITSTREAM_WRITE) {
+		/* The bit stream is open for writing. */
+		/* If there are any bits buffered for writing, or the
+		  previous byte forced a stuffed bit, alignment is
+		  required. */
+		if ((bitstream->cnt_ < 8 && bitstream->cnt_ >= 0) ||
+		  ((bitstream->buf_ >> 8) & 0xff) == 0xff) {
+			return 1;
+		}
+	} else {
+		/* This should not happen.  Famous last words, eh? :-) */
+		assert(0);
+		return -1;
+	}
+	return 0;
+}
+
+/* How many additional bytes would be output if we align the bit stream? */
+int jpc_bitstream_pending(jpc_bitstream_t *bitstream)
+{
+	if (bitstream->openmode_ & JPC_BITSTREAM_WRITE) {
+		/* The bit stream is being used for writing. */
+#if 1
+		/* XXX - Is this really correct?  Check someday... */
+		if (bitstream->cnt_ < 8) {
+			return 1;
+		}
+#else
+		if (bitstream->cnt_ < 8) {
+			if (((bitstream->buf_ >> 8) & 0xff) == 0xff) {
+				return 2;
+			}
+			return 1;
+		}
+#endif
+		return 0;
+	} else {
+		/* This operation should not be invoked on a bit stream that
+		  is being used for reading. */
+		return -1;
+	}
+}
+
+/* Align the bit stream to a byte boundary. */
+int jpc_bitstream_align(jpc_bitstream_t *bitstream)
+{
+	int ret;
+	if (bitstream->openmode_ & JPC_BITSTREAM_READ) {
+		ret = jpc_bitstream_inalign(bitstream, 0, 0);
+	} else if (bitstream->openmode_ & JPC_BITSTREAM_WRITE) {
+		ret = jpc_bitstream_outalign(bitstream, 0);
+	}
+	return ret;
+}
+
+/* Align a bit stream in the input case. */
+int jpc_bitstream_inalign(jpc_bitstream_t *bitstream, int fillmask,
+  int filldata)
+{
+	int n;
+	int v;
+	int u;
+	int numfill;
+	int m;
+
+	numfill = 7;
+	m = 0;
+	v = 0;
+	if (bitstream->cnt_ > 0) {
+		n = bitstream->cnt_;
+	} else if (!bitstream->cnt_) {
+		n = ((bitstream->buf_ & 0xff) == 0xff) ? 7 : 0;
+	} else {
+		n = 0;
+	}
+	if (n > 0) {
+		if ((u = jpc_bitstream_getbits(bitstream, n)) < 0) {
+			return -1;
+		}
+		m += n;
+		v = (v << n) | u;
+	}
+	if ((bitstream->buf_ & 0xff) == 0xff) {
+		if ((u = jpc_bitstream_getbits(bitstream, 7)) < 0) {
+			return -1;
+		}
+		v = (v << 7) | u;
+		m += 7;
+	}
+	if (m > numfill) {
+		v >>= m - numfill;
+	} else {
+		filldata >>= numfill - m;
+		fillmask >>= numfill - m;
+	}
+	if (((~(v ^ filldata)) & fillmask) != fillmask) {
+		/* The actual fill pattern does not match the expected one. */
+		return 1;
+	}
+
+	return 0;
+}
+
+/* Align a bit stream in the output case. */
+int jpc_bitstream_outalign(jpc_bitstream_t *bitstream, int filldata)
+{
+	int n;
+	int v;
+
+	/* Ensure that this bit stream is open for writing. */
+	assert(bitstream->openmode_ & JPC_BITSTREAM_WRITE);
+
+	/* Ensure that the first bit of fill data is zero. */
+	/* Note: The first bit of fill data must be zero.  If this were not
+	  the case, the fill data itself could cause further bit stuffing to
+	  be required (which would cause numerous complications). */
+	assert(!(filldata & (~0x3f)));
+
+	if (!bitstream->cnt_) {
+		if ((bitstream->buf_ & 0xff) == 0xff) {
+			n = 7;
+			v = filldata;
+		} else {
+			n = 0;
+			v = 0;
+		}
+	} else if (bitstream->cnt_ > 0 && bitstream->cnt_ < 8) {
+		n = bitstream->cnt_;
+		v = filldata >> (7 - n);
+	} else {
+		n = 0;
+		v = 0;
+		return 0;
+	}
+
+	/* Write the appropriate fill data to the bit stream. */
+	if (n > 0) {
+		if (jpc_bitstream_putbits(bitstream, n, v)) {
+			return -1;
+		}
+	}
+	if (bitstream->cnt_ < 8) {
+		assert(bitstream->cnt_ >= 0 && bitstream->cnt_ < 8);
+		assert((bitstream->buf_ & 0xff) != 0xff);
+		/* Force the pending byte of output to be written to the
+		  underlying (character) stream. */
+		if (jas_stream_putc(bitstream->stream_, bitstream->buf_ & 0xff) == EOF) {
+			return -1;
+		}
+		bitstream->cnt_ = 8;
+		bitstream->buf_ = (bitstream->buf_ << 8) & 0xffff;
+	}
+
+	return 0;
+}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_bs.h b/converter/other/jpeg2000/libjasper/jpc/jpc_bs.h
new file mode 100644
index 00000000..f515972b
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_bs.h
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Bit Stream Class
+ *
+ * $Id$
+ */
+
+#ifndef JPC_BS_H
+#define JPC_BS_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdio.h>
+
+#include "jasper/jas_types.h"
+#include "jasper/jas_stream.h"
+
+/******************************************************************************\
+* Constants.
+\******************************************************************************/
+
+/*
+ * Bit stream open mode flags.
+ */
+
+/* Bit stream open for reading. */
+#define	JPC_BITSTREAM_READ	0x01
+/* Bit stream open for writing. */
+#define	JPC_BITSTREAM_WRITE	0x02
+
+/*
+ * Bit stream flags.
+ */
+
+/* Do not close underlying character stream. */
+#define	JPC_BITSTREAM_NOCLOSE	0x01
+/* End of file has been reached while reading. */
+#define	JPC_BITSTREAM_EOF	0x02
+/* An I/O error has occured. */
+#define	JPC_BITSTREAM_ERR	0x04
+
+/******************************************************************************\
+* Types.
+\******************************************************************************/
+
+/* Bit stream class. */
+
+typedef struct {
+
+	/* Some miscellaneous flags. */
+	int flags_;
+
+	/* The input/output buffer. */
+	uint_fast16_t buf_;
+
+	/* The number of bits remaining in the byte being read/written. */
+	int cnt_;
+
+	/* The underlying stream associated with this bit stream. */
+	jas_stream_t *stream_;
+
+	/* The mode in which this bit stream was opened. */
+	int openmode_;
+
+} jpc_bitstream_t;
+
+/******************************************************************************\
+* Functions/macros for opening and closing bit streams..
+\******************************************************************************/
+
+/* Open a stream as a bit stream. */
+jpc_bitstream_t *jpc_bitstream_sopen(jas_stream_t *stream, const char *mode);
+
+/* Close a bit stream. */
+int jpc_bitstream_close(jpc_bitstream_t *bitstream);
+
+/******************************************************************************\
+* Functions/macros for reading from and writing to bit streams..
+\******************************************************************************/
+
+/* Read a bit from a bit stream. */
+#if defined(DEBUG)
+#define	jpc_bitstream_getbit(bitstream) \
+	jpc_bitstream_getbit_func(bitstream)
+#else
+#define jpc_bitstream_getbit(bitstream) \
+	jpc_bitstream_getbit_macro(bitstream)
+#endif
+
+/* Write a bit to a bit stream. */
+#if defined(DEBUG)
+#define	jpc_bitstream_putbit(bitstream, v) \
+	jpc_bitstream_putbit_func(bitstream, v)
+#else
+#define	jpc_bitstream_putbit(bitstream, v) \
+	jpc_bitstream_putbit_macro(bitstream, v)
+#endif
+
+/* Read one or more bits from a bit stream. */
+long jpc_bitstream_getbits(jpc_bitstream_t *bitstream, int n);
+
+/* Write one or more bits to a bit stream. */
+int jpc_bitstream_putbits(jpc_bitstream_t *bitstream, int n, long v);
+
+/******************************************************************************\
+* Functions/macros for flushing and aligning bit streams.
+\******************************************************************************/
+
+/* Align the current position within the bit stream to the next byte
+  boundary. */
+int jpc_bitstream_align(jpc_bitstream_t *bitstream);
+
+/* Align the current position in the bit stream with the next byte boundary,
+  ensuring that certain bits consumed in the process match a particular
+  pattern. */
+int jpc_bitstream_inalign(jpc_bitstream_t *bitstream, int fillmask,
+  int filldata);
+
+/* Align the current position in the bit stream with the next byte boundary,
+  writing bits from the specified pattern (if necessary) in the process. */
+int jpc_bitstream_outalign(jpc_bitstream_t *bitstream, int filldata);
+
+/* Check if a bit stream needs alignment. */
+int jpc_bitstream_needalign(jpc_bitstream_t *bitstream);
+
+/* How many additional bytes would be output if the bit stream was aligned? */
+int jpc_bitstream_pending(jpc_bitstream_t *bitstream);
+
+/******************************************************************************\
+* Functions/macros for querying state information for bit streams.
+\******************************************************************************/
+
+/* Has EOF been encountered on a bit stream? */
+#define jpc_bitstream_eof(bitstream) \
+	((bitstream)->flags_ & JPC_BITSTREAM_EOF)
+
+/******************************************************************************\
+* Internals.
+\******************************************************************************/
+
+/* DO NOT DIRECTLY INVOKE ANY OF THE MACROS OR FUNCTIONS BELOW.  THEY ARE
+  FOR INTERNAL USE ONLY. */
+
+int jpc_bitstream_getbit_func(jpc_bitstream_t *bitstream);
+
+int jpc_bitstream_putbit_func(jpc_bitstream_t *bitstream, int v);
+
+int jpc_bitstream_fillbuf(jpc_bitstream_t *bitstream);
+
+#define	jpc_bitstream_getbit_macro(bitstream) \
+	(assert((bitstream)->openmode_ & JPC_BITSTREAM_READ), \
+	  (--(bitstream)->cnt_ >= 0) ? \
+	  (((bitstream)->buf_ >> (bitstream)->cnt_) & 1) : \
+	  jpc_bitstream_fillbuf(bitstream))
+
+#define jpc_bitstream_putbit_macro(bitstream, bit) \
+	(assert((bitstream)->openmode_ & JPC_BITSTREAM_WRITE), \
+	  (--(bitstream)->cnt_ < 0) ? \
+	  ((bitstream)->buf_ = ((bitstream)->buf_ << 8) & 0xffff, \
+	  (bitstream)->cnt_ = ((bitstream)->buf_ == 0xff00) ? 6 : 7, \
+	  (bitstream)->buf_ |= ((bit) & 1) << (bitstream)->cnt_, \
+	  (jas_stream_putc((bitstream)->stream_, (bitstream)->buf_ >> 8) == EOF) \
+	  ? (EOF) : ((bit) & 1)) : \
+	  ((bitstream)->buf_ |= ((bit) & 1) << (bitstream)->cnt_, \
+	  (bit) & 1))
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_cod.h b/converter/other/jpeg2000/libjasper/jpc/jpc_cod.h
new file mode 100644
index 00000000..9b3ddbee
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_cod.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef JPC_COD_H
+#define JPC_COD_H
+
+/******************************************************************************\
+* Constants.
+\******************************************************************************/
+
+/* The nominal word size used by this implementation. */
+#define	JPC_PREC	32
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_cs.c b/converter/other/jpeg2000/libjasper/jpc/jpc_cs.c
new file mode 100644
index 00000000..63cf8ba7
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_cs.c
@@ -0,0 +1,1614 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * JPEG-2000 Code Stream Library
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdlib.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_debug.h"
+
+#include "jpc_cs.h"
+
+/******************************************************************************\
+* Types.
+\******************************************************************************/
+
+/* Marker segment table entry. */
+typedef struct {
+	int id;
+	const char *name;
+	jpc_msops_t ops;
+} jpc_mstabent_t;
+
+/******************************************************************************\
+* Local prototypes.
+\******************************************************************************/
+
+static jpc_mstabent_t *jpc_mstab_lookup(int id);
+
+static int jpc_poc_dumpparms(jpc_ms_t *ms, FILE *out);
+static int jpc_poc_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out);
+static int jpc_poc_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in);
+static void jpc_poc_destroyparms(jpc_ms_t *ms);
+
+static int jpc_unk_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in);
+static int jpc_sot_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in);
+static int jpc_siz_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in);
+static int jpc_cod_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in);
+static int jpc_coc_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in);
+static int jpc_qcd_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in);
+static int jpc_qcc_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in);
+static int jpc_rgn_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in);
+static int jpc_sop_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in);
+static int jpc_ppm_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in);
+static int jpc_ppt_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in);
+static int jpc_crg_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in);
+static int jpc_com_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in);
+
+static int jpc_sot_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out);
+static int jpc_siz_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out);
+static int jpc_cod_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out);
+static int jpc_coc_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out);
+static int jpc_qcd_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out);
+static int jpc_qcc_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out);
+static int jpc_rgn_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out);
+static int jpc_unk_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out);
+static int jpc_sop_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out);
+static int jpc_ppm_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out);
+static int jpc_ppt_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out);
+static int jpc_crg_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out);
+static int jpc_com_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out);
+
+static int jpc_sot_dumpparms(jpc_ms_t *ms, FILE *out);
+static int jpc_siz_dumpparms(jpc_ms_t *ms, FILE *out);
+static int jpc_cod_dumpparms(jpc_ms_t *ms, FILE *out);
+static int jpc_coc_dumpparms(jpc_ms_t *ms, FILE *out);
+static int jpc_qcd_dumpparms(jpc_ms_t *ms, FILE *out);
+static int jpc_qcc_dumpparms(jpc_ms_t *ms, FILE *out);
+static int jpc_rgn_dumpparms(jpc_ms_t *ms, FILE *out);
+static int jpc_unk_dumpparms(jpc_ms_t *ms, FILE *out);
+static int jpc_sop_dumpparms(jpc_ms_t *ms, FILE *out);
+static int jpc_ppm_dumpparms(jpc_ms_t *ms, FILE *out);
+static int jpc_ppt_dumpparms(jpc_ms_t *ms, FILE *out);
+static int jpc_crg_dumpparms(jpc_ms_t *ms, FILE *out);
+static int jpc_com_dumpparms(jpc_ms_t *ms, FILE *out);
+
+static void jpc_siz_destroyparms(jpc_ms_t *ms);
+static void jpc_qcd_destroyparms(jpc_ms_t *ms);
+static void jpc_qcc_destroyparms(jpc_ms_t *ms);
+static void jpc_cod_destroyparms(jpc_ms_t *ms);
+static void jpc_coc_destroyparms(jpc_ms_t *ms);
+static void jpc_unk_destroyparms(jpc_ms_t *ms);
+static void jpc_ppm_destroyparms(jpc_ms_t *ms);
+static void jpc_ppt_destroyparms(jpc_ms_t *ms);
+static void jpc_crg_destroyparms(jpc_ms_t *ms);
+static void jpc_com_destroyparms(jpc_ms_t *ms);
+
+static void jpc_qcx_destroycompparms(jpc_qcxcp_t *compparms);
+static int jpc_qcx_getcompparms(jpc_qcxcp_t *compparms, jpc_cstate_t *cstate,
+  jas_stream_t *in, uint_fast16_t len);
+static int jpc_qcx_putcompparms(jpc_qcxcp_t *compparms, jpc_cstate_t *cstate,
+  jas_stream_t *out);
+static void jpc_cox_destroycompparms(jpc_coxcp_t *compparms);
+static int jpc_cox_getcompparms(jpc_ms_t *ms, jpc_cstate_t *cstate,
+  jas_stream_t *in, int prtflag, jpc_coxcp_t *compparms);
+static int jpc_cox_putcompparms(jpc_ms_t *ms, jpc_cstate_t *cstate,
+  jas_stream_t *out, int prtflag, jpc_coxcp_t *compparms);
+
+/******************************************************************************\
+* Global data.
+\******************************************************************************/
+
+static jpc_mstabent_t jpc_mstab[] = {
+	{JPC_MS_SOC, "SOC", {0, 0, 0, 0}},
+	{JPC_MS_SOT, "SOT", {0, jpc_sot_getparms, jpc_sot_putparms,
+	  jpc_sot_dumpparms}},
+	{JPC_MS_SOD, "SOD", {0, 0, 0, 0}},
+	{JPC_MS_EOC, "EOC", {0, 0, 0, 0}},
+	{JPC_MS_SIZ, "SIZ", {jpc_siz_destroyparms, jpc_siz_getparms,
+	  jpc_siz_putparms, jpc_siz_dumpparms}},
+	{JPC_MS_COD, "COD", {jpc_cod_destroyparms, jpc_cod_getparms,
+	  jpc_cod_putparms, jpc_cod_dumpparms}},
+	{JPC_MS_COC, "COC", {jpc_coc_destroyparms, jpc_coc_getparms,
+	  jpc_coc_putparms, jpc_coc_dumpparms}},
+	{JPC_MS_RGN, "RGN", {0, jpc_rgn_getparms, jpc_rgn_putparms,
+	  jpc_rgn_dumpparms}},
+	{JPC_MS_QCD, "QCD", {jpc_qcd_destroyparms, jpc_qcd_getparms,
+	  jpc_qcd_putparms, jpc_qcd_dumpparms}},
+	{JPC_MS_QCC, "QCC", {jpc_qcc_destroyparms, jpc_qcc_getparms,
+	  jpc_qcc_putparms, jpc_qcc_dumpparms}},
+	{JPC_MS_POC, "POC", {jpc_poc_destroyparms, jpc_poc_getparms,
+	  jpc_poc_putparms, jpc_poc_dumpparms}},
+	{JPC_MS_TLM, "TLM", {0, jpc_unk_getparms, jpc_unk_putparms, 0}},
+	{JPC_MS_PLM, "PLM", {0, jpc_unk_getparms, jpc_unk_putparms, 0}},
+	{JPC_MS_PPM, "PPM", {jpc_ppm_destroyparms, jpc_ppm_getparms,
+	  jpc_ppm_putparms, jpc_ppm_dumpparms}},
+	{JPC_MS_PPT, "PPT", {jpc_ppt_destroyparms, jpc_ppt_getparms,
+	  jpc_ppt_putparms, jpc_ppt_dumpparms}},
+	{JPC_MS_SOP, "SOP", {0, jpc_sop_getparms, jpc_sop_putparms,
+	  jpc_sop_dumpparms}},
+	{JPC_MS_EPH, "EPH", {0, 0, 0, 0}},
+	{JPC_MS_CRG, "CRG", {0, jpc_crg_getparms, jpc_crg_putparms,
+	  jpc_crg_dumpparms}},
+	{JPC_MS_COM, "COM", {jpc_com_destroyparms, jpc_com_getparms,
+	  jpc_com_putparms, jpc_com_dumpparms}},
+	{-1, "UNKNOWN",  {jpc_unk_destroyparms, jpc_unk_getparms,
+	  jpc_unk_putparms, jpc_unk_dumpparms}}
+};
+
+/******************************************************************************\
+* Code stream manipulation functions.
+\******************************************************************************/
+
+/* Create a code stream state object. */
+jpc_cstate_t *jpc_cstate_create()
+{
+	jpc_cstate_t *cstate;
+	if (!(cstate = jas_malloc(sizeof(jpc_cstate_t)))) {
+		return 0;
+	}
+	cstate->numcomps = 0;
+	return cstate;
+}
+
+/* Destroy a code stream state object. */
+void jpc_cstate_destroy(jpc_cstate_t *cstate)
+{
+	jas_free(cstate);
+}
+
+/* Read a marker segment from a stream. */
+jpc_ms_t *jpc_getms(jas_stream_t *in, jpc_cstate_t *cstate)
+{
+	jpc_ms_t *ms;
+	jpc_mstabent_t *mstabent;
+	jas_stream_t *tmpstream;
+
+	if (!(ms = jpc_ms_create(0))) {
+		return 0;
+	}
+
+	/* Get the marker type. */
+	if (jpc_getuint16(in, &ms->id) || ms->id < JPC_MS_MIN ||
+	  ms->id > JPC_MS_MAX) {
+		jpc_ms_destroy(ms);
+		return 0;
+	}
+
+	mstabent = jpc_mstab_lookup(ms->id);
+	ms->ops = &mstabent->ops;
+
+	/* Get the marker segment length and parameters if present. */
+	/* Note: It is tacitly assumed that a marker segment cannot have
+	  parameters unless it has a length field.  That is, there cannot
+	  be a parameters field without a length field and vice versa. */
+	if (JPC_MS_HASPARMS(ms->id)) {
+		/* Get the length of the marker segment. */
+		if (jpc_getuint16(in, &ms->len) || ms->len < 3) {
+			jpc_ms_destroy(ms);
+			return 0;
+		}
+		/* Calculate the length of the marker segment parameters. */
+		ms->len -= 2;
+		/* Create and prepare a temporary memory stream from which to
+		  read the marker segment parameters. */
+		/* Note: This approach provides a simple way of ensuring that
+		  we never read beyond the end of the marker segment (even if
+		  the marker segment length is errantly set too small). */
+		if (!(tmpstream = jas_stream_memopen(0, 0))) {
+			jpc_ms_destroy(ms);
+			return 0;
+		}
+		if (jas_stream_copy(tmpstream, in, ms->len) ||
+		  jas_stream_seek(tmpstream, 0, SEEK_SET) < 0) {
+			jas_stream_close(tmpstream);
+			jpc_ms_destroy(ms);
+			return 0;
+		}
+		/* Get the marker segment parameters. */
+		if ((*ms->ops->getparms)(ms, cstate, tmpstream)) {
+			ms->ops = 0;
+			jpc_ms_destroy(ms);
+			jas_stream_close(tmpstream);
+			return 0;
+		}
+
+		if (jas_getdbglevel() > 0) {
+			jpc_ms_dump(ms, stderr);
+		}
+
+		if (jas_stream_tell(tmpstream) != ms->len) {
+			fprintf(stderr,
+			  "warning: trailing garbage in marker segment (%ld bytes)\n",
+			  ms->len - jas_stream_tell(tmpstream));
+		}
+
+		/* Close the temporary stream. */
+		jas_stream_close(tmpstream);
+
+	} else {
+		/* There are no marker segment parameters. */
+		ms->len = 0;
+
+		if (jas_getdbglevel() > 0) {
+			jpc_ms_dump(ms, stderr);
+		}
+	}
+
+	/* Update the code stream state information based on the type of
+	  marker segment read. */
+	/* Note: This is a bit of a hack, but I'm not going to define another
+	  type of virtual function for this one special case. */
+	if (ms->id == JPC_MS_SIZ) {
+		cstate->numcomps = ms->parms.siz.numcomps;
+	}
+
+	return ms;
+}
+
+/* Write a marker segment to a stream. */
+int jpc_putms(jas_stream_t *out, jpc_cstate_t *cstate, jpc_ms_t *ms)
+{
+	jas_stream_t *tmpstream;
+	int len;
+
+	/* Output the marker segment type. */
+	if (jpc_putuint16(out, ms->id)) {
+		return -1;
+	}
+
+	/* Output the marker segment length and parameters if necessary. */
+	if (ms->ops->putparms) {
+		/* Create a temporary stream in which to buffer the
+		  parameter data. */
+		if (!(tmpstream = jas_stream_memopen(0, 0))) {
+			return -1;
+		}
+		if ((*ms->ops->putparms)(ms, cstate, tmpstream)) {
+			jas_stream_close(tmpstream);
+			return -1;
+		}
+		/* Get the number of bytes of parameter data written. */
+		if ((len = jas_stream_tell(tmpstream)) < 0) {
+			jas_stream_close(tmpstream);
+			return -1;
+		}
+		ms->len = len;
+		/* Write the marker segment length and parameter data to
+		  the output stream. */
+		if (jas_stream_seek(tmpstream, 0, SEEK_SET) < 0 ||
+		  jpc_putuint16(out, ms->len + 2) ||
+		  jas_stream_copy(out, tmpstream, ms->len) < 0) {
+			jas_stream_close(tmpstream);
+			return -1;
+		}
+		/* Close the temporary stream. */
+		jas_stream_close(tmpstream);
+	}
+
+	/* This is a bit of a hack, but I'm not going to define another
+	  type of virtual function for this one special case. */
+	if (ms->id == JPC_MS_SIZ) {
+		cstate->numcomps = ms->parms.siz.numcomps;
+	}
+
+	if (jas_getdbglevel() > 0) {
+		jpc_ms_dump(ms, stderr);
+	}
+
+	return 0;
+}
+
+/******************************************************************************\
+* Marker segment operations.
+\******************************************************************************/
+
+/* Create a marker segment of the specified type. */
+jpc_ms_t *jpc_ms_create(int type)
+{
+	jpc_ms_t *ms;
+	jpc_mstabent_t *mstabent;
+
+	if (!(ms = jas_malloc(sizeof(jpc_ms_t)))) {
+		return 0;
+	}
+	ms->id = type;
+	ms->len = 0;
+	mstabent = jpc_mstab_lookup(ms->id);
+	ms->ops = &mstabent->ops;
+	memset(&ms->parms, 0, sizeof(jpc_msparms_t));
+	return ms;
+}
+
+/* Destroy a marker segment. */
+void jpc_ms_destroy(jpc_ms_t *ms)
+{
+	if (ms->ops && ms->ops->destroyparms) {
+		(*ms->ops->destroyparms)(ms);
+	}
+	jas_free(ms);
+}
+
+/* Dump a marker segment to a stream for debugging. */
+void jpc_ms_dump(jpc_ms_t *ms, FILE *out)
+{
+	jpc_mstabent_t *mstabent;
+	mstabent = jpc_mstab_lookup(ms->id);
+	fprintf(out, "type = 0x%04x (%s);", ms->id, mstabent->name);
+	if (JPC_MS_HASPARMS(ms->id)) {
+		fprintf(out, " len = %d;", ms->len + 2);
+		if (ms->ops->dumpparms) {
+			(*ms->ops->dumpparms)(ms, out);
+		} else {
+			fprintf(out, "\n");
+		}
+	} else {
+		fprintf(out, "\n");
+	}
+}
+
+/******************************************************************************\
+* SOT marker segment operations.
+\******************************************************************************/
+
+static int jpc_sot_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
+{
+	jpc_sot_t *sot = &ms->parms.sot;
+	if (jpc_getuint16(in, &sot->tileno) ||
+	  jpc_getuint32(in, &sot->len) ||
+	  jpc_getuint8(in, &sot->partno) ||
+	  jpc_getuint8(in, &sot->numparts)) {
+		return -1;
+	}
+	if (jas_stream_eof(in)) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_sot_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
+{
+	jpc_sot_t *sot = &ms->parms.sot;
+	if (jpc_putuint16(out, sot->tileno) ||
+	  jpc_putuint32(out, sot->len) ||
+	  jpc_putuint8(out, sot->partno) ||
+	  jpc_putuint8(out, sot->numparts)) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_sot_dumpparms(jpc_ms_t *ms, FILE *out)
+{
+	jpc_sot_t *sot = &ms->parms.sot;
+	fprintf(out, "tileno = %d; len = %d; partno = %d; numparts = %d\n",
+	  sot->tileno, sot->len, sot->partno, sot->numparts);
+	return 0;
+}
+
+/******************************************************************************\
+* SIZ marker segment operations.
+\******************************************************************************/
+
+static void jpc_siz_destroyparms(jpc_ms_t *ms)
+{
+	jpc_siz_t *siz = &ms->parms.siz;
+	if (siz->comps) {
+		jas_free(siz->comps);
+	}
+}
+
+static int jpc_siz_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
+{
+	jpc_siz_t *siz = &ms->parms.siz;
+	int i;
+	uint_fast8_t tmp;
+	if (jpc_getuint16(in, &siz->caps) ||
+	  jpc_getuint32(in, &siz->width) ||
+	  jpc_getuint32(in, &siz->height) ||
+	  jpc_getuint32(in, &siz->xoff) ||
+	  jpc_getuint32(in, &siz->yoff) ||
+	  jpc_getuint32(in, &siz->tilewidth) ||
+	  jpc_getuint32(in, &siz->tileheight) ||
+	  jpc_getuint32(in, &siz->tilexoff) ||
+	  jpc_getuint32(in, &siz->tileyoff) ||
+	  jpc_getuint16(in, &siz->numcomps)) {
+		return -1;
+	}
+	if (!siz->width || !siz->height || !siz->tilewidth ||
+	  !siz->tileheight || !siz->numcomps) {
+		return -1;
+	}
+	if (!(siz->comps = jas_malloc(siz->numcomps * sizeof(jpc_sizcomp_t)))) {
+		return -1;
+	}
+	for (i = 0; i < siz->numcomps; ++i) {
+		if (jpc_getuint8(in, &tmp) ||
+		  jpc_getuint8(in, &siz->comps[i].hsamp) ||
+		  jpc_getuint8(in, &siz->comps[i].vsamp)) {
+			jas_free(siz->comps);
+			return -1;
+		}
+		siz->comps[i].sgnd = (tmp >> 7) & 1;
+		siz->comps[i].prec = (tmp & 0x7f) + 1;
+	}
+	if (jas_stream_eof(in)) {
+		jas_free(siz->comps);
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_siz_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
+{
+	jpc_siz_t *siz = &ms->parms.siz;
+	int i;
+	assert(siz->width && siz->height && siz->tilewidth &&
+	  siz->tileheight && siz->numcomps);
+	if (jpc_putuint16(out, siz->caps) ||
+	  jpc_putuint32(out, siz->width) ||
+	  jpc_putuint32(out, siz->height) ||
+	  jpc_putuint32(out, siz->xoff) ||
+	  jpc_putuint32(out, siz->yoff) ||
+	  jpc_putuint32(out, siz->tilewidth) ||
+	  jpc_putuint32(out, siz->tileheight) ||
+	  jpc_putuint32(out, siz->tilexoff) ||
+	  jpc_putuint32(out, siz->tileyoff) ||
+	  jpc_putuint16(out, siz->numcomps)) {
+		return -1;
+	}
+	for (i = 0; i < siz->numcomps; ++i) {
+		if (jpc_putuint8(out, ((siz->comps[i].sgnd & 1) << 7) |
+		  ((siz->comps[i].prec - 1) & 0x7f)) ||
+		  jpc_putuint8(out, siz->comps[i].hsamp) ||
+		  jpc_putuint8(out, siz->comps[i].vsamp)) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int jpc_siz_dumpparms(jpc_ms_t *ms, FILE *out)
+{
+	jpc_siz_t *siz = &ms->parms.siz;
+	int i;
+	fprintf(out, "caps = 0x%02x;\n", siz->caps);
+	fprintf(out, "width = %d; height = %d; xoff = %d; yoff = %d;\n",
+	  siz->width, siz->height, siz->xoff, siz->yoff);
+	fprintf(out, "tilewidth = %d; tileheight = %d; tilexoff = %d; "
+	  "tileyoff = %d;\n", siz->tilewidth, siz->tileheight, siz->tilexoff,
+	  siz->tileyoff);
+	for (i = 0; i < siz->numcomps; ++i) {
+		fprintf(out, "prec[%d] = %d; sgnd[%d] = %d; hsamp[%d] = %d; "
+		  "vsamp[%d] = %d\n", i, siz->comps[i].prec, i,
+		  siz->comps[i].sgnd, i, siz->comps[i].hsamp, i,
+		  siz->comps[i].vsamp);
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* COD marker segment operations.
+\******************************************************************************/
+
+static void jpc_cod_destroyparms(jpc_ms_t *ms)
+{
+	jpc_cod_t *cod = &ms->parms.cod;
+	jpc_cox_destroycompparms(&cod->compparms);
+}
+
+static int jpc_cod_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
+{
+	jpc_cod_t *cod = &ms->parms.cod;
+	if (jpc_getuint8(in, &cod->csty)) {
+		return -1;
+	}
+	if (jpc_getuint8(in, &cod->prg) ||
+	  jpc_getuint16(in, &cod->numlyrs) ||
+	  jpc_getuint8(in, &cod->mctrans)) {
+		return -1;
+	}
+	if (jpc_cox_getcompparms(ms, cstate, in,
+	  (cod->csty & JPC_COX_PRT) != 0, &cod->compparms)) {
+		return -1;
+	}
+	if (jas_stream_eof(in)) {
+		jpc_cod_destroyparms(ms);
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_cod_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
+{
+	jpc_cod_t *cod = &ms->parms.cod;
+	assert(cod->numlyrs > 0 && cod->compparms.numdlvls <= 32);
+	assert(cod->compparms.numdlvls == cod->compparms.numrlvls - 1);
+	if (jpc_putuint8(out, cod->compparms.csty) ||
+	  jpc_putuint8(out, cod->prg) ||
+	  jpc_putuint16(out, cod->numlyrs) ||
+	  jpc_putuint8(out, cod->mctrans)) {
+		return -1;
+	}
+	if (jpc_cox_putcompparms(ms, cstate, out,
+	  (cod->csty & JPC_COX_PRT) != 0, &cod->compparms)) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_cod_dumpparms(jpc_ms_t *ms, FILE *out)
+{
+	jpc_cod_t *cod = &ms->parms.cod;
+	int i;
+	fprintf(out, "csty = 0x%02x;\n", cod->compparms.csty);
+	fprintf(out, "numdlvls = %d; qmfbid = %d; mctrans = %d\n",
+	  cod->compparms.numdlvls, cod->compparms.qmfbid, cod->mctrans);
+	fprintf(out, "prg = %d; numlyrs = %d;\n",
+	  cod->prg, cod->numlyrs);
+	fprintf(out, "cblkwidthval = %d; cblkheightval = %d; "
+	  "cblksty = 0x%02x;\n", cod->compparms.cblkwidthval, cod->compparms.cblkheightval,
+	  cod->compparms.cblksty);
+	if (cod->csty & JPC_COX_PRT) {
+		for (i = 0; i < cod->compparms.numrlvls; ++i) {
+			fprintf(stderr, "prcwidth[%d] = %d, prcheight[%d] = %d\n",
+			  i, cod->compparms.rlvls[i].parwidthval,
+			  i, cod->compparms.rlvls[i].parheightval);
+		}
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* COC marker segment operations.
+\******************************************************************************/
+
+static void jpc_coc_destroyparms(jpc_ms_t *ms)
+{
+	jpc_coc_t *coc = &ms->parms.coc;
+	jpc_cox_destroycompparms(&coc->compparms);
+}
+
+static int jpc_coc_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
+{
+	jpc_coc_t *coc = &ms->parms.coc;
+	uint_fast8_t tmp;
+	if (cstate->numcomps <= 256) {
+		if (jpc_getuint8(in, &tmp)) {
+			return -1;
+		}
+		coc->compno = tmp;
+	} else {
+		if (jpc_getuint16(in, &coc->compno)) {
+			return -1;
+		}
+	}
+	if (jpc_getuint8(in, &coc->compparms.csty)) {
+		return -1;
+	}
+	if (jpc_cox_getcompparms(ms, cstate, in,
+	  (coc->compparms.csty & JPC_COX_PRT) != 0, &coc->compparms)) {
+		return -1;
+	}
+	if (jas_stream_eof(in)) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_coc_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
+{
+	jpc_coc_t *coc = &ms->parms.coc;
+	assert(coc->compparms.numdlvls <= 32);
+	if (cstate->numcomps <= 256) {
+		if (jpc_putuint8(out, coc->compno)) {
+			return -1;
+		}
+	} else {
+		if (jpc_putuint16(out, coc->compno)) {
+			return -1;
+		}
+	}
+	if (jpc_putuint8(out, coc->compparms.csty)) {
+		return -1;
+	}
+	if (jpc_cox_putcompparms(ms, cstate, out,
+	  (coc->compparms.csty & JPC_COX_PRT) != 0, &coc->compparms)) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_coc_dumpparms(jpc_ms_t *ms, FILE *out)
+{
+	jpc_coc_t *coc = &ms->parms.coc;
+	fprintf(out, "compno = %d; csty = 0x%02x; numdlvls = %d;\n",
+	  coc->compno, coc->compparms.csty, coc->compparms.numdlvls);
+	fprintf(out, "cblkwidthval = %d; cblkheightval = %d; "
+	  "cblksty = 0x%02x; qmfbid = %d;\n", coc->compparms.cblkwidthval,
+	  coc->compparms.cblkheightval, coc->compparms.cblksty, coc->compparms.qmfbid);
+	return 0;
+}
+/******************************************************************************\
+* COD/COC marker segment operation helper functions.
+\******************************************************************************/
+
+static void jpc_cox_destroycompparms(jpc_coxcp_t *compparms)
+{
+}
+
+static int jpc_cox_getcompparms(jpc_ms_t *ms, jpc_cstate_t *cstate,
+  jas_stream_t *in, int prtflag, jpc_coxcp_t *compparms)
+{
+	uint_fast8_t tmp;
+	int i;
+	if (jpc_getuint8(in, &compparms->numdlvls) ||
+	  jpc_getuint8(in, &compparms->cblkwidthval) ||
+	  jpc_getuint8(in, &compparms->cblkheightval) ||
+	  jpc_getuint8(in, &compparms->cblksty) ||
+	  jpc_getuint8(in, &compparms->qmfbid)) {
+		return -1;
+	}
+	compparms->numrlvls = compparms->numdlvls + 1;
+	if (prtflag) {
+		for (i = 0; i < compparms->numrlvls; ++i) {
+			if (jpc_getuint8(in, &tmp)) {
+				jpc_cox_destroycompparms(compparms);
+				return -1;
+			}
+			compparms->rlvls[i].parwidthval = tmp & 0xf;
+			compparms->rlvls[i].parheightval = (tmp >> 4) & 0xf;
+		}
+/* Sigh.  This bit should be in the same field in both COC and COD mrk segs. */
+compparms->csty |= JPC_COX_PRT;
+	} else {
+	}
+	if (jas_stream_eof(in)) {
+		jpc_cox_destroycompparms(compparms);
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_cox_putcompparms(jpc_ms_t *ms, jpc_cstate_t *cstate,
+  jas_stream_t *out, int prtflag, jpc_coxcp_t *compparms)
+{
+	int i;
+	assert(compparms->numdlvls <= 32);
+	if (jpc_putuint8(out, compparms->numdlvls) ||
+	  jpc_putuint8(out, compparms->cblkwidthval) ||
+	  jpc_putuint8(out, compparms->cblkheightval) ||
+	  jpc_putuint8(out, compparms->cblksty) ||
+	  jpc_putuint8(out, compparms->qmfbid)) {
+		return -1;
+	}
+	if (prtflag) {
+		for (i = 0; i < compparms->numrlvls; ++i) {
+			if (jpc_putuint8(out,
+			  ((compparms->rlvls[i].parheightval & 0xf) << 4) |
+			  (compparms->rlvls[i].parwidthval & 0xf))) {
+				return -1;
+			}
+		}
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* RGN marker segment operations.
+\******************************************************************************/
+
+static int jpc_rgn_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
+{
+	jpc_rgn_t *rgn = &ms->parms.rgn;
+	uint_fast8_t tmp;
+	if (cstate->numcomps <= 256) {
+		if (jpc_getuint8(in, &tmp)) {
+			return -1;
+		}
+		rgn->compno = tmp;
+	} else {
+		if (jpc_getuint16(in, &rgn->compno)) {
+			return -1;
+		}
+	}
+	if (jpc_getuint8(in, &rgn->roisty) ||
+	  jpc_getuint8(in, &rgn->roishift)) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_rgn_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
+{
+	jpc_rgn_t *rgn = &ms->parms.rgn;
+	if (cstate->numcomps <= 256) {
+		if (jpc_putuint8(out, rgn->compno)) {
+			return -1;
+		}
+	} else {
+		if (jpc_putuint16(out, rgn->compno)) {
+			return -1;
+		}
+	}
+	if (jpc_putuint8(out, rgn->roisty) ||
+	  jpc_putuint8(out, rgn->roishift)) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_rgn_dumpparms(jpc_ms_t *ms, FILE *out)
+{
+	jpc_rgn_t *rgn = &ms->parms.rgn;
+	fprintf(out, "compno = %d; roisty = %d; roishift = %d\n",
+	  rgn->compno, rgn->roisty, rgn->roishift);
+	return 0;
+}
+
+/******************************************************************************\
+* QCD marker segment operations.
+\******************************************************************************/
+
+static void jpc_qcd_destroyparms(jpc_ms_t *ms)
+{
+	jpc_qcd_t *qcd = &ms->parms.qcd;
+	jpc_qcx_destroycompparms(&qcd->compparms);
+}
+
+static int jpc_qcd_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
+{
+	jpc_qcxcp_t *compparms = &ms->parms.qcd.compparms;
+	return jpc_qcx_getcompparms(compparms, cstate, in, ms->len);
+}
+
+static int jpc_qcd_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
+{
+	jpc_qcxcp_t *compparms = &ms->parms.qcd.compparms;
+	return jpc_qcx_putcompparms(compparms, cstate, out);
+}
+
+static int jpc_qcd_dumpparms(jpc_ms_t *ms, FILE *out)
+{
+	jpc_qcd_t *qcd = &ms->parms.qcd;
+	int i;
+	fprintf(out, "qntsty = %d; numguard = %d; numstepsizes = %d\n",
+	  (int) qcd->compparms.qntsty, qcd->compparms.numguard, qcd->compparms.numstepsizes);
+	for (i = 0; i < qcd->compparms.numstepsizes; ++i) {
+		fprintf(out, "expn[%d] = 0x%04x; mant[%d] = 0x%04x;\n",
+		  i, (unsigned) JPC_QCX_GETEXPN(qcd->compparms.stepsizes[i]),
+		  i, (unsigned) JPC_QCX_GETMANT(qcd->compparms.stepsizes[i]));
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* QCC marker segment operations.
+\******************************************************************************/
+
+static void jpc_qcc_destroyparms(jpc_ms_t *ms)
+{
+	jpc_qcc_t *qcc = &ms->parms.qcc;
+	jpc_qcx_destroycompparms(&qcc->compparms);
+}
+
+static int jpc_qcc_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
+{
+	jpc_qcc_t *qcc = &ms->parms.qcc;
+	uint_fast8_t tmp;
+	int len;
+	len = ms->len;
+	if (cstate->numcomps <= 256) {
+		jpc_getuint8(in, &tmp);
+		qcc->compno = tmp;
+		--len;
+	} else {
+		jpc_getuint16(in, &qcc->compno);
+		len -= 2;
+	}
+	if (jpc_qcx_getcompparms(&qcc->compparms, cstate, in, len)) {
+		return -1;
+	}
+	if (jas_stream_eof(in)) {
+		jpc_qcc_destroyparms(ms);
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_qcc_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
+{
+	jpc_qcc_t *qcc = &ms->parms.qcc;
+	if (cstate->numcomps <= 256) {
+		jpc_putuint8(out, qcc->compno);
+	} else {
+		jpc_putuint16(out, qcc->compno);
+	}
+	if (jpc_qcx_putcompparms(&qcc->compparms, cstate, out)) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_qcc_dumpparms(jpc_ms_t *ms, FILE *out)
+{
+	jpc_qcc_t *qcc = &ms->parms.qcc;
+	int i;
+	fprintf(out, "compno = %d; qntsty = %d; numguard = %d; "
+	  "numstepsizes = %d\n", qcc->compno, qcc->compparms.qntsty, qcc->compparms.numguard,
+	  qcc->compparms.numstepsizes);
+	for (i = 0; i < qcc->compparms.numstepsizes; ++i) {
+		fprintf(out, "expn[%d] = 0x%04x; mant[%d] = 0x%04x;\n",
+		  i, (unsigned) JPC_QCX_GETEXPN(qcc->compparms.stepsizes[i]),
+		  i, (unsigned) JPC_QCX_GETMANT(qcc->compparms.stepsizes[i]));
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* QCD/QCC marker segment helper functions.
+\******************************************************************************/
+
+static void jpc_qcx_destroycompparms(jpc_qcxcp_t *compparms)
+{
+	if (compparms->stepsizes) {
+		jas_free(compparms->stepsizes);
+	}
+}
+
+static int jpc_qcx_getcompparms(jpc_qcxcp_t *compparms, jpc_cstate_t *cstate,
+  jas_stream_t *in, uint_fast16_t len)
+{
+	uint_fast8_t tmp;
+	int n;
+	int i;
+	n = 0;
+	jpc_getuint8(in, &tmp);
+	++n;
+	compparms->qntsty = tmp & 0x1f;
+	compparms->numguard = (tmp >> 5) & 7;
+	switch (compparms->qntsty) {
+	case JPC_QCX_SIQNT:
+		compparms->numstepsizes = 1;
+		break;
+	case JPC_QCX_NOQNT:
+		compparms->numstepsizes = (len - n);
+		break;
+	case JPC_QCX_SEQNT:
+		/* XXX - this is a hack */
+		compparms->numstepsizes = (len - n) / 2;
+		break;
+	}
+if (compparms->numstepsizes > 0) {
+	compparms->stepsizes = jas_malloc(compparms->numstepsizes *
+	  sizeof(uint_fast32_t));
+	assert(compparms->stepsizes);
+	for (i = 0; i < compparms->numstepsizes; ++i) {
+		if (compparms->qntsty == JPC_QCX_NOQNT) {
+			jpc_getuint8(in, &tmp);
+			compparms->stepsizes[i] = JPC_QCX_EXPN(tmp >> 3);
+		} else {
+			jpc_getuint16(in, &compparms->stepsizes[i]);
+		}
+	}
+} else {
+	compparms->stepsizes = 0;
+}
+	if (jas_stream_error(in) || jas_stream_eof(in)) {
+		jpc_qcx_destroycompparms(compparms);
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_qcx_putcompparms(jpc_qcxcp_t *compparms, jpc_cstate_t *cstate,
+  jas_stream_t *out)
+{
+	int i;
+	jpc_putuint8(out, ((compparms->numguard & 7) << 5) | compparms->qntsty);
+	for (i = 0; i < compparms->numstepsizes; ++i) {
+		if (compparms->qntsty == JPC_QCX_NOQNT) {
+			jpc_putuint8(out, JPC_QCX_GETEXPN(
+			  compparms->stepsizes[i]) << 3);
+		} else {
+			jpc_putuint16(out, compparms->stepsizes[i]);
+		}
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* SOP marker segment operations.
+\******************************************************************************/
+
+static int jpc_sop_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
+{
+	jpc_sop_t *sop = &ms->parms.sop;
+	if (jpc_getuint16(in, &sop->seqno)) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_sop_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
+{
+	jpc_sop_t *sop = &ms->parms.sop;
+	if (jpc_putuint16(out, sop->seqno)) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_sop_dumpparms(jpc_ms_t *ms, FILE *out)
+{
+	jpc_sop_t *sop = &ms->parms.sop;
+	fprintf(out, "seqno = %d;\n", sop->seqno);
+	return 0;
+}
+
+/******************************************************************************\
+* PPM marker segment operations.
+\******************************************************************************/
+
+static void jpc_ppm_destroyparms(jpc_ms_t *ms)
+{
+	jpc_ppm_t *ppm = &ms->parms.ppm;
+	if (ppm->data) {
+		jas_free(ppm->data);
+	}
+}
+
+static int jpc_ppm_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
+{
+	jpc_ppm_t *ppm = &ms->parms.ppm;
+
+	ppm->data = 0;
+
+	if (ms->len < 1) {
+		goto error;
+	}
+	if (jpc_getuint8(in, &ppm->ind)) {
+		goto error;
+	}
+
+	ppm->len = ms->len - 1;
+	if (ppm->len > 0) {
+		if (!(ppm->data = jas_malloc(ppm->len * sizeof(unsigned char)))) {
+			goto error;
+		}
+		if (jas_stream_read(in, ppm->data, ppm->len) != ppm->len) {
+			goto error;
+		}
+	} else {
+		ppm->data = 0;
+	}
+	return 0;
+
+error:
+	jpc_ppm_destroyparms(ms);
+	return -1;
+}
+
+static int jpc_ppm_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
+{
+	jpc_ppm_t *ppm = &ms->parms.ppm;
+	if (jas_stream_write(out, (char *) ppm->data, ppm->len) != ppm->len) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_ppm_dumpparms(jpc_ms_t *ms, FILE *out)
+{
+	jpc_ppm_t *ppm = &ms->parms.ppm;
+	fprintf(out, "ind=%d; len = %d;\n", ppm->ind, ppm->len);
+	if (ppm->len > 0) {
+		fprintf(out, "data =\n");
+		jas_memdump(out, ppm->data, ppm->len);
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* PPT marker segment operations.
+\******************************************************************************/
+
+static void jpc_ppt_destroyparms(jpc_ms_t *ms)
+{
+	jpc_ppt_t *ppt = &ms->parms.ppt;
+	if (ppt->data) {
+		jas_free(ppt->data);
+	}
+}
+
+static int jpc_ppt_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
+{
+	jpc_ppt_t *ppt = &ms->parms.ppt;
+	ppt->data = 0;
+
+	if (ms->len < 1) {
+		goto error;
+	}
+	if (jpc_getuint8(in, &ppt->ind)) {
+		goto error;
+	}
+	ppt->len = ms->len - 1;
+	if (ppt->len > 0) {
+		if (!(ppt->data = jas_malloc(ppt->len * sizeof(unsigned char)))) {
+			goto error;
+		}
+		if (jas_stream_read(in, (char *) ppt->data, ppt->len) != ppt->len) {
+			goto error;
+		}
+	} else {
+		ppt->data = 0;
+	}
+	return 0;
+
+error:
+	jpc_ppt_destroyparms(ms);
+	return -1;
+}
+
+static int jpc_ppt_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
+{
+	jpc_ppt_t *ppt = &ms->parms.ppt;
+	if (jpc_putuint8(out, ppt->ind)) {
+		return -1;
+	}
+	if (jas_stream_write(out, (char *) ppt->data, ppt->len) != ppt->len) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_ppt_dumpparms(jpc_ms_t *ms, FILE *out)
+{
+	jpc_ppt_t *ppt = &ms->parms.ppt;
+	fprintf(out, "ind=%d; len = %d;\n", ppt->ind, ppt->len);
+	if (ppt->len > 0) {
+		fprintf(out, "data =\n");
+		jas_memdump(out, ppt->data, ppt->len);
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* POC marker segment operations.
+\******************************************************************************/
+
+static void jpc_poc_destroyparms(jpc_ms_t *ms)
+{
+	jpc_poc_t *poc = &ms->parms.poc;
+	if (poc->pchgs) {
+		jas_free(poc->pchgs);
+	}
+}
+
+static int jpc_poc_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
+{
+	jpc_poc_t *poc = &ms->parms.poc;
+	jpc_pocpchg_t *pchg;
+	int pchgno;
+	uint_fast8_t tmp;
+	poc->numpchgs = (cstate->numcomps > 256) ? (ms->len / 9) :
+	  (ms->len / 7);
+	if (!(poc->pchgs = jas_malloc(poc->numpchgs * sizeof(jpc_pocpchg_t)))) {
+		goto error;
+	}
+	for (pchgno = 0, pchg = poc->pchgs; pchgno < poc->numpchgs; ++pchgno,
+	  ++pchg) {
+		if (jpc_getuint8(in, &pchg->rlvlnostart)) {
+			goto error;
+		}
+		if (cstate->numcomps > 256) {
+			if (jpc_getuint16(in, &pchg->compnostart)) {
+				goto error;
+			}
+		} else {
+			if (jpc_getuint8(in, &tmp)) {
+				goto error;
+			};
+			pchg->compnostart = tmp;
+		}
+		if (jpc_getuint16(in, &pchg->lyrnoend) ||
+		  jpc_getuint8(in, &pchg->rlvlnoend)) {
+			goto error;
+		}
+		if (cstate->numcomps > 256) {
+			if (jpc_getuint16(in, &pchg->compnoend)) {
+				goto error;
+			}
+		} else {
+			if (jpc_getuint8(in, &tmp)) {
+				goto error;
+			}
+			pchg->compnoend = tmp;
+		}
+		if (jpc_getuint8(in, &pchg->prgord)) {
+			goto error;
+		}
+		if (pchg->rlvlnostart > pchg->rlvlnoend ||
+		  pchg->compnostart > pchg->compnoend) {
+			goto error;
+		}
+	}
+	return 0;
+
+error:
+	jpc_poc_destroyparms(ms);
+	return -1;
+}
+
+static int jpc_poc_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
+{
+	jpc_poc_t *poc = &ms->parms.poc;
+	jpc_pocpchg_t *pchg;
+	int pchgno;
+	for (pchgno = 0, pchg = poc->pchgs; pchgno < poc->numpchgs; ++pchgno,
+	  ++pchg) {
+		if (jpc_putuint8(out, pchg->rlvlnostart) ||
+		  ((cstate->numcomps > 256) ?
+		  jpc_putuint16(out, pchg->compnostart) :
+		  jpc_putuint8(out, pchg->compnostart)) ||
+		  jpc_putuint16(out, pchg->lyrnoend) ||
+		  jpc_putuint8(out, pchg->rlvlnoend) ||
+		  ((cstate->numcomps > 256) ?
+		  jpc_putuint16(out, pchg->compnoend) :
+		  jpc_putuint8(out, pchg->compnoend)) ||
+		  jpc_putuint8(out, pchg->prgord)) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int jpc_poc_dumpparms(jpc_ms_t *ms, FILE *out)
+{
+	jpc_poc_t *poc = &ms->parms.poc;
+	jpc_pocpchg_t *pchg;
+	int pchgno;
+	for (pchgno = 0, pchg = poc->pchgs; pchgno < poc->numpchgs;
+	  ++pchgno, ++pchg) {
+		fprintf(out, "po[%d] = %d; ", pchgno, pchg->prgord);
+		fprintf(out, "cs[%d] = %d; ce[%d] = %d; ",
+		  pchgno, pchg->compnostart, pchgno, pchg->compnoend);
+		fprintf(out, "rs[%d] = %d; re[%d] = %d; ",
+		  pchgno, pchg->rlvlnostart, pchgno, pchg->rlvlnoend);
+		fprintf(out, "le[%d] = %d\n", pchgno, pchg->lyrnoend);
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* CRG marker segment operations.
+\******************************************************************************/
+
+static void jpc_crg_destroyparms(jpc_ms_t *ms)
+{
+	jpc_crg_t *crg = &ms->parms.crg;
+	if (crg->comps) {
+		jas_free(crg->comps);
+	}
+}
+
+static int jpc_crg_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
+{
+	jpc_crg_t *crg = &ms->parms.crg;
+	jpc_crgcomp_t *comp;
+	uint_fast16_t compno;
+	crg->numcomps = cstate->numcomps;
+	if (!(crg->comps = jas_malloc(cstate->numcomps * sizeof(uint_fast16_t)))) {
+		return -1;
+	}
+	for (compno = 0, comp = crg->comps; compno < cstate->numcomps;
+	  ++compno, ++comp) {
+		if (jpc_getuint16(in, &comp->hoff) ||
+		  jpc_getuint16(in, &comp->voff)) {
+			jpc_crg_destroyparms(ms);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int jpc_crg_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
+{
+	jpc_crg_t *crg = &ms->parms.crg;
+	int compno;
+	jpc_crgcomp_t *comp;
+	for (compno = 0, comp = crg->comps; compno < crg->numcomps; ++compno,
+	  ++comp) {
+		if (jpc_putuint16(out, comp->hoff) ||
+		  jpc_putuint16(out, comp->voff)) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int jpc_crg_dumpparms(jpc_ms_t *ms, FILE *out)
+{
+	jpc_crg_t *crg = &ms->parms.crg;
+	int compno;
+	jpc_crgcomp_t *comp;
+	for (compno = 0, comp = crg->comps; compno < crg->numcomps; ++compno,
+	  ++comp) {
+		fprintf(out, "hoff[%d] = %d; voff[%d] = %d\n", compno,
+		  comp->hoff, compno, comp->voff);
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* Operations for COM marker segment.
+\******************************************************************************/
+
+static void jpc_com_destroyparms(jpc_ms_t *ms)
+{
+	jpc_com_t *com = &ms->parms.com;
+	if (com->data) {
+		jas_free(com->data);
+	}
+}
+
+static int jpc_com_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
+{
+	jpc_com_t *com = &ms->parms.com;
+	if (jpc_getuint16(in, &com->regid)) {
+		return -1;
+	}
+	com->len = ms->len - 2;
+	if (com->len > 0) {
+		if (!(com->data = jas_malloc(com->len))) {
+			return -1;
+		}
+		if (jas_stream_read(in, com->data, com->len) != com->len) {
+			return -1;
+		}
+	} else {
+		com->data = 0;
+	}
+	return 0;
+}
+
+static int jpc_com_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
+{
+	jpc_com_t *com = &ms->parms.com;
+	if (jpc_putuint16(out, com->regid)) {
+		return -1;
+	}
+	if (jas_stream_write(out, com->data, com->len) != com->len) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_com_dumpparms(jpc_ms_t *ms, FILE *out)
+{
+	jpc_com_t *com = &ms->parms.com;
+	int i;
+	int printable;
+	fprintf(out, "regid = %d;\n", com->regid);
+	printable = 1;
+	for (i = 0; i < com->len; ++i) {
+		if (!isprint(com->data[i])) {
+			printable = 0;
+			break;
+		}
+	}
+	if (printable) {
+		fprintf(out, "data = ");
+		fwrite(com->data, sizeof(char), com->len, out);
+		fprintf(out, "\n");
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* Operations for unknown types of marker segments.
+\******************************************************************************/
+
+static void jpc_unk_destroyparms(jpc_ms_t *ms)
+{
+	jpc_unk_t *unk = &ms->parms.unk;
+	if (unk->data) {
+		jas_free(unk->data);
+	}
+}
+
+static int jpc_unk_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
+{
+	jpc_unk_t *unk = &ms->parms.unk;
+
+	if (ms->len > 0) {
+		if (!(unk->data = jas_malloc(ms->len * sizeof(unsigned char)))) {
+			return -1;
+		}
+		if (jas_stream_read(in, (char *) unk->data, ms->len) != ms->len) {
+			jas_free(unk->data);
+			return -1;
+		}
+		unk->len = ms->len;
+	} else {
+		unk->data = 0;
+		unk->len = 0;
+	}
+	return 0;
+}
+
+static int jpc_unk_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
+{
+	/* If this function is called, we are trying to write an unsupported
+	  type of marker segment.  Return with an error indication.  */
+	return -1;
+}
+
+static int jpc_unk_dumpparms(jpc_ms_t *ms, FILE *out)
+{
+	int i;
+	jpc_unk_t *unk = &ms->parms.unk;
+	for (i = 0; i < unk->len; ++i) {
+		fprintf(out, "%02x ", unk->data[i]);
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* Primitive I/O operations.
+\******************************************************************************/
+
+int jpc_getuint8(jas_stream_t *in, uint_fast8_t *val)
+{
+	int c;
+	if ((c = jas_stream_getc(in)) == EOF) {
+		return -1;
+	}
+	if (val) {
+		*val = c;
+	}
+	return 0;
+}
+
+int jpc_putuint8(jas_stream_t *out, uint_fast8_t val)
+{
+	if (jas_stream_putc(out, val & 0xff) == EOF) {
+		return -1;
+	}
+	return 0;
+}
+
+int jpc_getuint16(jas_stream_t *in, uint_fast16_t *val)
+{
+	uint_fast16_t v;
+	int c;
+	if ((c = jas_stream_getc(in)) == EOF) {
+		return -1;
+	}
+	v = c;
+	if ((c = jas_stream_getc(in)) == EOF) {
+		return -1;
+	}
+	v = (v << 8) | c;
+	if (val) {
+		*val = v;
+	}
+	return 0;
+}
+
+int jpc_putuint16(jas_stream_t *out, uint_fast16_t val)
+{
+	if (jas_stream_putc(out, (val >> 8) & 0xff) == EOF ||
+	  jas_stream_putc(out, val & 0xff) == EOF) {
+		return -1;
+	}
+	return 0;
+}
+
+int jpc_getuint32(jas_stream_t *in, uint_fast32_t *val)
+{
+	uint_fast32_t v;
+	int c;
+	if ((c = jas_stream_getc(in)) == EOF) {
+		return -1;
+	}
+	v = c;
+	if ((c = jas_stream_getc(in)) == EOF) {
+		return -1;
+	}
+	v = (v << 8) | c;
+	if ((c = jas_stream_getc(in)) == EOF) {
+		return -1;
+	}
+	v = (v << 8) | c;
+	if ((c = jas_stream_getc(in)) == EOF) {
+		return -1;
+	}
+	v = (v << 8) | c;
+	if (val) {
+		*val = v;
+	}
+	return 0;
+}
+
+int jpc_putuint32(jas_stream_t *out, uint_fast32_t val)
+{
+	if (jas_stream_putc(out, (val >> 24) & 0xff) == EOF ||
+	  jas_stream_putc(out, (val >> 16) & 0xff) == EOF ||
+	  jas_stream_putc(out, (val >> 8) & 0xff) == EOF ||
+	  jas_stream_putc(out, val & 0xff) == EOF) {
+		return -1;
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* Miscellany
+\******************************************************************************/
+
+static jpc_mstabent_t *jpc_mstab_lookup(int id)
+{
+	jpc_mstabent_t *mstabent;
+	for (mstabent = jpc_mstab;; ++mstabent) {
+		if (mstabent->id == id || mstabent->id < 0) {
+			return mstabent;
+		}
+	}
+	assert(0);
+	return 0;
+}
+
+int jpc_validate(jas_stream_t *in)
+{
+	int n;
+	int i;
+	unsigned char buf[2];
+
+	assert(JAS_STREAM_MAXPUTBACK >= 2);
+
+	if ((n = jas_stream_read(in, (char *) buf, 2)) < 0) {
+		return -1;
+	}
+	for (i = n - 1; i >= 0; --i) {
+		if (jas_stream_ungetc(in, buf[i]) == EOF) {
+			return -1;
+		}
+	}
+	if (n < 2) {
+		return -1;
+	}
+	if (buf[0] == (JPC_MS_SOC >> 8) && buf[1] == (JPC_MS_SOC & 0xff)) {
+		return 0;
+	}
+	return -1;
+}
+
+int jpc_getdata(jas_stream_t *in, jas_stream_t *out, long len)
+{
+	return jas_stream_copy(out, in, len);
+}
+
+int jpc_putdata(jas_stream_t *out, jas_stream_t *in, long len)
+{
+	return jas_stream_copy(out, in, len);
+}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_cs.h b/converter/other/jpeg2000/libjasper/jpc/jpc_cs.h
new file mode 100644
index 00000000..07a046d1
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_cs.h
@@ -0,0 +1,812 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * JPEG-2000 Code Stream Library
+ *
+ * $Id$
+ */
+
+#ifndef JPC_CS_H
+#define JPC_CS_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jasper/jas_image.h"
+#include "jasper/jas_stream.h"
+
+#include "jpc_cod.h"
+
+/******************************************************************************\
+* Constants and Types.
+\******************************************************************************/
+
+/* The maximum number of resolution levels. */
+#define	JPC_MAXRLVLS	33
+
+/* The maximum number of bands. */
+#define	JPC_MAXBANDS	(3 * JPC_MAXRLVLS + 1)
+
+/* The maximum number of layers. */
+#define JPC_MAXLYRS	16384
+
+/**************************************\
+* Code stream.
+\**************************************/
+
+/*
+ * Code stream states.
+ */
+
+/* Initial. */
+#define	JPC_CS_INIT	0
+/* Main header. */
+#define	JPC_CS_MHDR	1
+/* Tile-part header. */
+#define	JPC_CS_THDR	2
+/* Main trailer. */
+#define	JPC_CS_MTLR	3
+/* Tile-part data. */
+#define	JPC_CS_TDATA	4
+
+/*
+ * Unfortunately, the code stream syntax was not designed in such a way that
+ * any given marker segment can be correctly decoded without additional state
+ * derived from previously decoded marker segments.
+ * For example, a RGN/COC/QCC marker segment cannot be decoded unless the
+ * number of components is known.
+ */
+
+/*
+ * Code stream state information.
+ */
+
+typedef struct {
+
+	/* The number of components. */
+	int numcomps;
+
+} jpc_cstate_t;
+
+/**************************************\
+* SOT marker segment parameters.
+\**************************************/
+
+typedef struct {
+
+	/* The tile number. */
+	uint_fast16_t tileno;
+
+	/* The combined length of the marker segment and its auxilary data
+	  (i.e., packet data). */
+	uint_fast32_t len;
+
+	/* The tile-part instance. */
+	uint_fast8_t partno;
+
+	/* The number of tile-parts. */
+	uint_fast8_t numparts;
+
+} jpc_sot_t;
+
+/**************************************\
+* SIZ marker segment parameters.
+\**************************************/
+
+/* Per component information. */
+
+typedef struct {
+
+	/* The precision of the samples. */
+	uint_fast8_t prec;
+
+	/* The signedness of the samples. */
+	uint_fast8_t sgnd;
+
+	/* The horizontal separation of samples with respect to the reference
+	  grid. */
+	uint_fast8_t hsamp;
+
+	/* The vertical separation of samples with respect to the reference
+	  grid. */
+	uint_fast8_t vsamp;
+
+} jpc_sizcomp_t;
+
+/* SIZ marker segment parameters. */
+
+typedef struct {
+
+	/* The code stream capabilities. */
+	uint_fast16_t caps;
+
+	/* The width of the image in units of the reference grid. */
+	uint_fast32_t width;
+
+	/* The height of the image in units of the reference grid. */
+	uint_fast32_t height;
+
+	/* The horizontal offset from the origin of the reference grid to the
+	  left side of the image area. */
+	uint_fast32_t xoff;
+
+	/* The vertical offset from the origin of the reference grid to the
+	  top side of the image area. */
+	uint_fast32_t yoff;
+
+	/* The nominal width of a tile in units of the reference grid. */
+	uint_fast32_t tilewidth;
+
+	/* The nominal height of a tile in units of the reference grid. */
+	uint_fast32_t tileheight;
+
+	/* The horizontal offset from the origin of the reference grid to the
+	  left side of the first tile. */
+	uint_fast32_t tilexoff;
+
+	/* The vertical offset from the origin of the reference grid to the
+	  top side of the first tile. */
+	uint_fast32_t tileyoff;
+
+	/* The number of components. */
+	uint_fast16_t numcomps;
+
+	/* The per-component information. */
+	jpc_sizcomp_t *comps;
+
+} jpc_siz_t;
+
+/**************************************\
+* COD marker segment parameters.
+\**************************************/
+
+/*
+ * Coding style constants.
+ */
+
+/* Precincts may be used. */
+#define	JPC_COX_PRT	0x01
+/* SOP marker segments may be used. */
+#define	JPC_COD_SOP	0x02
+/* EPH marker segments may be used. */
+#define	JPC_COD_EPH	0x04
+
+/*
+ * Progression order constants.
+ */
+
+/* Layer-resolution-component-precinct progressive
+  (i.e., progressive by fidelity). */
+#define	JPC_COD_LRCPPRG	0
+/* Resolution-layer-component-precinct progressive
+  (i.e., progressive by resolution). */
+#define	JPC_COD_RLCPPRG	1
+/* Resolution-precinct-component-layer progressive. */
+#define	JPC_COD_RPCLPRG	2
+/* Precinct-component-resolution-layer progressive. */
+#define	JPC_COD_PCRLPRG	3
+/* Component-position-resolution-layer progressive. */
+#define	JPC_COD_CPRLPRG	4
+
+/*
+ * Code block style constants.
+ */
+
+#define	JPC_COX_LAZY	0x01 /* Selective arithmetic coding bypass. */
+#define	JPC_COX_RESET	0x02 /* Reset context probabilities. */
+#define	JPC_COX_TERMALL	0x04 /* Terminate all coding passes. */
+#define	JPC_COX_VSC		0x08 /* Vertical stripe causal context formation. */
+#define	JPC_COX_PTERM	0x10 /* Predictable termination. */
+#define	JPC_COX_SEGSYM	0x20 /* Use segmentation symbols. */
+
+/* Transform constants. */
+#define	JPC_COX_INS	0x00 /* Irreversible 9/7. */
+#define	JPC_COX_RFT	0x01 /* Reversible 5/3. */
+
+/* Multicomponent transform constants. */
+#define	JPC_COD_NOMCT	0x00 /* No multicomponent transform. */
+#define	JPC_COD_MCT		0x01 /* Multicomponent transform. */
+
+/* Get the code block size value from the code block size exponent. */
+#define	JPC_COX_CBLKSIZEEXPN(x)		((x) - 2)
+/* Get the code block size exponent from the code block size value. */
+#define	JPC_COX_GETCBLKSIZEEXPN(x)	((x) + 2)
+
+/* Per resolution-level information. */
+
+typedef struct {
+
+	/* The packet partition width. */
+	uint_fast8_t parwidthval;
+
+	/* The packet partition height. */
+	uint_fast8_t parheightval;
+
+} jpc_coxrlvl_t;
+
+/* Per component information. */
+
+typedef struct {
+
+	/* The coding style. */
+	uint_fast8_t csty;
+
+	/* The number of decomposition levels. */
+	uint_fast8_t numdlvls;
+
+	/* The nominal code block width specifier. */
+	uint_fast8_t cblkwidthval;
+
+	/* The nominal code block height specifier. */
+	uint_fast8_t cblkheightval;
+
+	/* The style of coding passes. */
+	uint_fast8_t cblksty;
+
+	/* The QMFB employed. */
+	uint_fast8_t qmfbid;
+
+	/* The number of resolution levels. */
+	int numrlvls;
+
+	/* The per-resolution-level information. */
+	jpc_coxrlvl_t rlvls[JPC_MAXRLVLS];
+
+} jpc_coxcp_t;
+
+/* COD marker segment parameters. */
+
+typedef struct {
+
+	/* The general coding style. */
+	uint_fast8_t csty;
+
+	/* The progression order. */
+	uint_fast8_t prg;
+
+	/* The number of layers. */
+	uint_fast16_t numlyrs;
+
+	/* The multicomponent transform. */
+	uint_fast8_t mctrans;
+
+	/* Component-related parameters. */
+	jpc_coxcp_t compparms;
+
+} jpc_cod_t;
+
+/* COC marker segment parameters. */
+
+typedef struct {
+
+	/* The component number. */
+	uint_fast16_t compno;
+
+	/* Component-related parameters. */
+	jpc_coxcp_t compparms;
+
+} jpc_coc_t;
+
+/**************************************\
+* RGN marker segment parameters.
+\**************************************/
+
+/* The maxshift ROI style. */
+#define	JPC_RGN_MAXSHIFT	0x00
+
+typedef struct {
+
+	/* The component to which the marker applies. */
+	uint_fast16_t compno;
+
+	/* The ROI style. */
+	uint_fast8_t roisty;
+
+	/* The ROI shift value. */
+	uint_fast8_t roishift;
+
+} jpc_rgn_t;
+
+/**************************************\
+* QCD/QCC marker segment parameters.
+\**************************************/
+
+/*
+ * Quantization style constants.
+ */
+
+#define	JPC_QCX_NOQNT	0 /* No quantization. */
+#define	JPC_QCX_SIQNT	1 /* Scalar quantization, implicit. */
+#define	JPC_QCX_SEQNT	2 /* Scalar quantization, explicit. */
+
+/*
+ * Stepsize manipulation macros.
+ */
+
+#define	JPC_QCX_GETEXPN(x)	((x) >> 11)
+#define	JPC_QCX_GETMANT(x)	((x) & 0x07ff)
+#define	JPC_QCX_EXPN(x)		(assert(!((x) & (~0x1f))), (((x) & 0x1f) << 11))
+#define	JPC_QCX_MANT(x)		(assert(!((x) & (~0x7ff))), ((x) & 0x7ff))
+
+/* Per component information. */
+
+typedef struct {
+
+	/* The quantization style. */
+	uint_fast8_t qntsty;
+
+	/* The number of step sizes. */
+	int numstepsizes;
+
+	/* The step sizes. */
+	uint_fast16_t *stepsizes;
+
+	/* The number of guard bits. */
+	uint_fast8_t numguard;
+
+} jpc_qcxcp_t;
+
+/* QCC marker segment parameters. */
+
+typedef struct {
+
+	/* The component associated with this marker segment. */
+	uint_fast16_t compno;
+
+	/* The parameters. */
+	jpc_qcxcp_t compparms;
+
+} jpc_qcc_t;
+
+/* QCD marker segment parameters. */
+
+typedef struct {
+
+	/* The parameters. */
+	jpc_qcxcp_t compparms;
+
+} jpc_qcd_t;
+
+/**************************************\
+* POD marker segment parameters.
+\**************************************/
+
+typedef struct {
+
+	/* The progression order. */
+	uint_fast8_t prgord;
+
+	/* The lower bound (inclusive) on the resolution level for the
+	  progression order volume. */
+	uint_fast8_t rlvlnostart;
+
+	/* The upper bound (exclusive) on the resolution level for the
+	  progression order volume. */
+	uint_fast8_t rlvlnoend;
+
+	/* The lower bound (inclusive) on the component for the progression
+	  order volume. */
+	uint_fast16_t compnostart;
+
+	/* The upper bound (exclusive) on the component for the progression
+	  order volume. */
+	uint_fast16_t compnoend;
+
+	/* The upper bound (exclusive) on the layer for the progression
+	  order volume. */
+	uint_fast16_t lyrnoend;
+
+} jpc_pocpchg_t;
+
+/* An alias for the above type. */
+typedef jpc_pocpchg_t jpc_pchg_t;
+
+/* POC marker segment parameters. */
+
+typedef struct {
+
+	/* The number of progression order changes. */
+	int numpchgs;
+
+	/* The per-progression-order-change information. */
+	jpc_pocpchg_t *pchgs;
+
+} jpc_poc_t;
+
+/**************************************\
+* PPM/PPT marker segment parameters.
+\**************************************/
+
+/* PPM marker segment parameters. */
+
+typedef struct {
+
+	/* The index. */
+	uint_fast8_t ind;
+
+	/* The length. */
+	uint_fast16_t len;
+
+	/* The data. */
+	unsigned char *data;
+
+} jpc_ppm_t;
+
+/* PPT marker segment parameters. */
+
+typedef struct {
+
+	/* The index. */
+	uint_fast8_t ind;
+
+	/* The length. */
+	uint_fast32_t len;
+
+	/* The data. */
+	unsigned char *data;
+
+} jpc_ppt_t;
+
+/**************************************\
+* COM marker segment parameters.
+\**************************************/
+
+/*
+ * Registration IDs.
+ */
+
+#define	JPC_COM_BIN		0x00
+#define	JPC_COM_LATIN	0x01
+
+typedef struct {
+
+	/* The registration ID. */
+	uint_fast16_t regid;
+
+	/* The length of the data in bytes. */
+	uint_fast16_t len;
+
+	/* The data. */
+	unsigned char *data;
+
+} jpc_com_t;
+
+/**************************************\
+* SOP marker segment parameters.
+\**************************************/
+
+typedef struct {
+
+	/* The sequence number. */
+	uint_fast16_t seqno;
+
+} jpc_sop_t;
+
+/**************************************\
+* CRG marker segment parameters.
+\**************************************/
+
+/* Per component information. */
+
+typedef struct {
+
+	/* The horizontal offset. */
+	uint_fast16_t hoff;
+
+	/* The vertical offset. */
+	uint_fast16_t voff;
+
+} jpc_crgcomp_t;
+
+typedef struct {
+
+	/* The number of components. */
+	int numcomps;
+
+	/* Per component information. */
+	jpc_crgcomp_t *comps;
+
+} jpc_crg_t;
+
+/**************************************\
+* Marker segment parameters for unknown marker type.
+\**************************************/
+
+typedef struct {
+
+	/* The data. */
+	unsigned char *data;
+
+	/* The length. */
+	uint_fast16_t len;
+
+} jpc_unk_t;
+
+/**************************************\
+* Generic marker segment parameters.
+\**************************************/
+
+typedef union {
+	int soc;	/* unused */
+	jpc_sot_t sot;
+	int sod;	/* unused */
+	int eoc;	/* unused */
+	jpc_siz_t siz;
+	jpc_cod_t cod;
+	jpc_coc_t coc;
+	jpc_rgn_t rgn;
+	jpc_qcd_t qcd;
+	jpc_qcc_t qcc;
+	jpc_poc_t poc;
+	/* jpc_plm_t plm; */
+	/* jpc_plt_t plt; */
+	jpc_ppm_t ppm;
+	jpc_ppt_t ppt;
+	jpc_sop_t sop;
+	int eph;	/* unused */
+	jpc_com_t com;
+	jpc_crg_t crg;
+	jpc_unk_t unk;
+} jpc_msparms_t;
+
+/**************************************\
+* Marker segment.
+\**************************************/
+
+/* Marker segment IDs. */
+
+/* The smallest valid marker value. */
+#define	JPC_MS_MIN	0xff00
+
+/* The largest valid marker value. */
+#define	JPC_MS_MAX	0xffff
+
+/* The minimum marker value that cannot occur within packet data. */
+#define	JPC_MS_INMIN	0xff80
+/* The maximum marker value that cannot occur within packet data. */
+#define	JPC_MS_INMAX	0xffff
+
+/* Delimiting marker segments. */
+#define	JPC_MS_SOC	0xff4f /* Start of code stream (SOC). */
+#define	JPC_MS_SOT	0xff90 /* Start of tile-part (SOT). */
+#define	JPC_MS_SOD	0xff93 /* Start of data (SOD). */
+#define	JPC_MS_EOC	0xffd9 /* End of code stream (EOC). */
+
+/* Fixed information marker segments. */
+#define	JPC_MS_SIZ	0xff51 /* Image and tile size (SIZ). */
+
+/* Functional marker segments. */
+#define	JPC_MS_COD	0xff52 /* Coding style default (COD). */
+#define JPC_MS_COC	0xff53 /* Coding style component (COC). */
+#define	JPC_MS_RGN	0xff5e /* Region of interest (RGN). */
+#define JPC_MS_QCD	0xff5c /* Quantization default (QCD). */
+#define JPC_MS_QCC	0xff5d /* Quantization component (QCC). */
+#define JPC_MS_POC	0xff5f /* Progression order default (POC). */
+
+/* Pointer marker segments. */
+#define	JPC_MS_TLM	0xff55 /* Tile-part lengths, main header (TLM). */
+#define	JPC_MS_PLM	0xff57 /* Packet length, main header (PLM). */
+#define	JPC_MS_PLT	0xff58 /* Packet length, tile-part header (PLT). */
+#define	JPC_MS_PPM	0xff60 /* Packed packet headers, main header (PPM). */
+#define	JPC_MS_PPT	0xff61 /* Packet packet headers, tile-part header (PPT). */
+
+/* In bit stream marker segments. */
+#define	JPC_MS_SOP	0xff91	/* Start of packet (SOP). */
+#define	JPC_MS_EPH	0xff92	/* End of packet header (EPH). */
+
+/* Informational marker segments. */
+#define	JPC_MS_CRG	0xff63 /* Component registration (CRG). */
+#define JPC_MS_COM	0xff64 /* Comment (COM). */
+
+/* Forward declaration. */
+struct jpc_msops_s;
+
+/* Generic marker segment class. */
+
+typedef struct {
+
+	/* The type of marker segment. */
+	uint_fast16_t id;
+
+	/* The length of the marker segment. */
+	uint_fast16_t len;
+
+	/* The starting offset within the stream. */
+	uint_fast32_t off;
+
+	/* The parameters of the marker segment. */
+	jpc_msparms_t parms;
+
+	/* The marker segment operations. */
+	struct jpc_msops_s *ops;
+
+} jpc_ms_t;
+
+/* Marker segment operations (which depend on the marker segment type). */
+
+typedef struct jpc_msops_s {
+
+	/* Destroy the marker segment parameters. */
+	void (*destroyparms)(jpc_ms_t *ms);
+
+	/* Get the marker segment parameters from a stream. */
+	int (*getparms)(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in);
+
+	/* Put the marker segment parameters to a stream. */
+	int (*putparms)(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out);
+
+	/* Dump the marker segment parameters (for debugging). */
+	int (*dumpparms)(jpc_ms_t *ms, FILE *out);
+
+} jpc_msops_t;
+
+/******************************************************************************\
+* Macros/Functions.
+\******************************************************************************/
+
+/* Create a code-stream state object. */
+jpc_cstate_t *jpc_cstate_create(void);
+
+/* Destroy a code-stream state object. */
+void jpc_cstate_destroy(jpc_cstate_t *cstate);
+
+/* Create a marker segment. */
+jpc_ms_t *jpc_ms_create(int type);
+
+/* Destroy a marker segment. */
+void jpc_ms_destroy(jpc_ms_t *ms);
+
+/* Does a marker segment have parameters? */
+#define	JPC_MS_HASPARMS(x) \
+	(!((x) == JPC_MS_SOC || (x) == JPC_MS_SOD || (x) == JPC_MS_EOC || \
+	  (x) == JPC_MS_EPH || ((x) >= 0xff30 && (x) <= 0xff3f)))
+
+/* Get the marker segment type. */
+#define	jpc_ms_gettype(ms) \
+	((ms)->id)
+
+/* Read a marker segment from a stream. */
+jpc_ms_t *jpc_getms(jas_stream_t *in, jpc_cstate_t *cstate);
+
+/* Write a marker segment to a stream. */
+int jpc_putms(jas_stream_t *out, jpc_cstate_t *cstate, jpc_ms_t *ms);
+
+/* Copy code stream data from one stream to another. */
+int jpc_getdata(jas_stream_t *in, jas_stream_t *out, long n);
+
+/* Copy code stream data from one stream to another. */
+int jpc_putdata(jas_stream_t *out, jas_stream_t *in, long n);
+
+/* Dump a marker segment (for debugging). */
+void jpc_ms_dump(jpc_ms_t *ms, FILE *out);
+
+/* Read a 8-bit unsigned integer from a stream. */
+int jpc_getuint8(jas_stream_t *in, uint_fast8_t *val);
+
+/* Read a 16-bit unsigned integer from a stream. */
+int jpc_getuint16(jas_stream_t *in, uint_fast16_t *val);
+
+/* Read a 32-bit unsigned integer from a stream. */
+int jpc_getuint32(jas_stream_t *in, uint_fast32_t *val);
+
+/* Write a 8-bit unsigned integer to a stream. */
+int jpc_putuint8(jas_stream_t *out, uint_fast8_t val);
+
+/* Write a 16-bit unsigned integer to a stream. */
+int jpc_putuint16(jas_stream_t *out, uint_fast16_t val);
+
+/* Write a 32-bit unsigned integer to a stream. */
+int jpc_putuint32(jas_stream_t *out, uint_fast32_t val);
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_dec.c b/converter/other/jpeg2000/libjasper/jpc/jpc_dec.c
new file mode 100644
index 00000000..42980225
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_dec.c
@@ -0,0 +1,2334 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "jasper/jas_types.h"
+#include "jasper/jas_math.h"
+#include "jasper/jas_tvp.h"
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_debug.h"
+
+#include "jpc_fix.h"
+#include "jpc_dec.h"
+#include "jpc_cs.h"
+#include "jpc_mct.h"
+#include "jpc_t2dec.h"
+#include "jpc_t1dec.h"
+#include "jpc_math.h"
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+#define	JPC_MHSOC	0x0001
+  /* In the main header, expecting a SOC marker segment. */
+#define	JPC_MHSIZ	0x0002
+  /* In the main header, expecting a SIZ marker segment. */
+#define	JPC_MH		0x0004
+  /* In the main header, expecting "other" marker segments. */
+#define	JPC_TPHSOT	0x0008
+  /* In a tile-part header, expecting a SOT marker segment. */
+#define	JPC_TPH		0x0010
+  /* In a tile-part header, expecting "other" marker segments. */
+#define	JPC_MT		0x0020
+  /* In the main trailer. */
+
+typedef struct {
+
+	uint_fast16_t id;
+	/* The marker segment type. */
+
+	int validstates;
+	/* The states in which this type of marker segment can be
+	  validly encountered. */
+
+	int (*action)(jpc_dec_t *dec, jpc_ms_t *ms);
+	/* The action to take upon encountering this type of marker segment. */
+
+} jpc_dec_mstabent_t;
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+/* COD/COC parameters have been specified. */
+#define	JPC_CSET	0x0001
+/* QCD/QCC parameters have been specified. */
+#define	JPC_QSET	0x0002
+/* COD/COC parameters set from a COC marker segment. */
+#define	JPC_COC	0x0004
+/* QCD/QCC parameters set from a QCC marker segment. */
+#define	JPC_QCC	0x0008
+
+/******************************************************************************\
+* Local function prototypes.
+\******************************************************************************/
+
+static int jpc_dec_dump(jpc_dec_t *dec, FILE *out);
+
+jpc_ppxstab_t *jpc_ppxstab_create(void);
+void jpc_ppxstab_destroy(jpc_ppxstab_t *tab);
+int jpc_ppxstab_grow(jpc_ppxstab_t *tab, int maxents);
+int jpc_ppxstab_insert(jpc_ppxstab_t *tab, jpc_ppxstabent_t *ent);
+jpc_streamlist_t *jpc_ppmstabtostreams(jpc_ppxstab_t *tab);
+int jpc_pptstabwrite(jas_stream_t *out, jpc_ppxstab_t *tab);
+jpc_ppxstabent_t *jpc_ppxstabent_create(void);
+void jpc_ppxstabent_destroy(jpc_ppxstabent_t *ent);
+
+int jpc_streamlist_numstreams(jpc_streamlist_t *streamlist);
+jpc_streamlist_t *jpc_streamlist_create(void);
+int jpc_streamlist_insert(jpc_streamlist_t *streamlist, int streamno,
+  jas_stream_t *stream);
+jas_stream_t *jpc_streamlist_remove(jpc_streamlist_t *streamlist, int streamno);
+void jpc_streamlist_destroy(jpc_streamlist_t *streamlist);
+jas_stream_t *jpc_streamlist_get(jpc_streamlist_t *streamlist, int streamno);
+
+static void jpc_dec_cp_resetflags(jpc_dec_cp_t *cp);
+static jpc_dec_cp_t *jpc_dec_cp_create(uint_fast16_t numcomps);
+static int jpc_dec_cp_isvalid(jpc_dec_cp_t *cp);
+static jpc_dec_cp_t *jpc_dec_cp_copy(jpc_dec_cp_t *cp);
+static int jpc_dec_cp_setfromcod(jpc_dec_cp_t *cp, jpc_cod_t *cod);
+static int jpc_dec_cp_setfromcoc(jpc_dec_cp_t *cp, jpc_coc_t *coc);
+static int jpc_dec_cp_setfromcox(jpc_dec_cp_t *cp, jpc_dec_ccp_t *ccp,
+  jpc_coxcp_t *compparms, int flags);
+static int jpc_dec_cp_setfromqcd(jpc_dec_cp_t *cp, jpc_qcd_t *qcd);
+static int jpc_dec_cp_setfromqcc(jpc_dec_cp_t *cp, jpc_qcc_t *qcc);
+static int jpc_dec_cp_setfromqcx(jpc_dec_cp_t *cp, jpc_dec_ccp_t *ccp,
+  jpc_qcxcp_t *compparms, int flags);
+static int jpc_dec_cp_setfromrgn(jpc_dec_cp_t *cp, jpc_rgn_t *rgn);
+static int jpc_dec_cp_prepare(jpc_dec_cp_t *cp);
+static void jpc_dec_cp_destroy(jpc_dec_cp_t *cp);
+static int jpc_dec_cp_setfrompoc(jpc_dec_cp_t *cp, jpc_poc_t *poc, int reset);
+static int jpc_pi_addpchgfrompoc(jpc_pi_t *pi, jpc_poc_t *poc);
+
+static int jpc_dec_decode(jpc_dec_t *dec);
+static jpc_dec_t *jpc_dec_create(jpc_dec_importopts_t *impopts, jas_stream_t *in);
+static void jpc_dec_destroy(jpc_dec_t *dec);
+static void jpc_dequantize(jas_matrix_t *x, jpc_fix_t absstepsize);
+static void jpc_undo_roi(jas_matrix_t *x, int roishift, int bgshift, int numbps);
+static jpc_fix_t jpc_calcabsstepsize(int stepsize, int numbits);
+static int jpc_dec_tiledecode(jpc_dec_t *dec, jpc_dec_tile_t *tile);
+static int jpc_dec_tileinit(jpc_dec_t *dec, jpc_dec_tile_t *tile);
+static int jpc_dec_tilefini(jpc_dec_t *dec, jpc_dec_tile_t *tile);
+static int jpc_dec_process_soc(jpc_dec_t *dec, jpc_ms_t *ms);
+static int jpc_dec_process_sot(jpc_dec_t *dec, jpc_ms_t *ms);
+static int jpc_dec_process_sod(jpc_dec_t *dec, jpc_ms_t *ms);
+static int jpc_dec_process_eoc(jpc_dec_t *dec, jpc_ms_t *ms);
+static int jpc_dec_process_siz(jpc_dec_t *dec, jpc_ms_t *ms);
+static int jpc_dec_process_cod(jpc_dec_t *dec, jpc_ms_t *ms);
+static int jpc_dec_process_coc(jpc_dec_t *dec, jpc_ms_t *ms);
+static int jpc_dec_process_rgn(jpc_dec_t *dec, jpc_ms_t *ms);
+static int jpc_dec_process_qcd(jpc_dec_t *dec, jpc_ms_t *ms);
+static int jpc_dec_process_qcc(jpc_dec_t *dec, jpc_ms_t *ms);
+static int jpc_dec_process_poc(jpc_dec_t *dec, jpc_ms_t *ms);
+static int jpc_dec_process_ppm(jpc_dec_t *dec, jpc_ms_t *ms);
+static int jpc_dec_process_ppt(jpc_dec_t *dec, jpc_ms_t *ms);
+static int jpc_dec_process_com(jpc_dec_t *dec, jpc_ms_t *ms);
+static int jpc_dec_process_unk(jpc_dec_t *dec, jpc_ms_t *ms);
+static int jpc_dec_process_crg(jpc_dec_t *dec, jpc_ms_t *ms);
+static int jpc_dec_parseopts(char *optstr, jpc_dec_importopts_t *opts);
+
+/******************************************************************************\
+* Global data.
+\******************************************************************************/
+
+jpc_dec_mstabent_t jpc_dec_mstab[] = {
+	{JPC_MS_SOC, JPC_MHSOC, jpc_dec_process_soc},
+	{JPC_MS_SOT, JPC_MH | JPC_TPHSOT, jpc_dec_process_sot},
+	{JPC_MS_SOD, JPC_TPH, jpc_dec_process_sod},
+	{JPC_MS_EOC, JPC_TPHSOT, jpc_dec_process_eoc},
+	{JPC_MS_SIZ, JPC_MHSIZ, jpc_dec_process_siz},
+	{JPC_MS_COD, JPC_MH | JPC_TPH, jpc_dec_process_cod},
+	{JPC_MS_COC, JPC_MH | JPC_TPH, jpc_dec_process_coc},
+	{JPC_MS_RGN, JPC_MH | JPC_TPH, jpc_dec_process_rgn},
+	{JPC_MS_QCD, JPC_MH | JPC_TPH, jpc_dec_process_qcd},
+	{JPC_MS_QCC, JPC_MH | JPC_TPH, jpc_dec_process_qcc},
+	{JPC_MS_POC, JPC_MH | JPC_TPH, jpc_dec_process_poc},
+	{JPC_MS_TLM, JPC_MH, 0},
+	{JPC_MS_PLM, JPC_MH, 0},
+	{JPC_MS_PLT, JPC_TPH, 0},
+	{JPC_MS_PPM, JPC_MH, jpc_dec_process_ppm},
+	{JPC_MS_PPT, JPC_TPH, jpc_dec_process_ppt},
+	{JPC_MS_SOP, 0, 0},
+	{JPC_MS_CRG, JPC_MH, jpc_dec_process_crg},
+	{JPC_MS_COM, JPC_MH | JPC_TPH, jpc_dec_process_com},
+	{0, JPC_MH | JPC_TPH, jpc_dec_process_unk}
+};
+
+/******************************************************************************\
+* The main entry point for the JPEG-2000 decoder.
+\******************************************************************************/
+
+jas_image_t *jpc_decode(jas_stream_t *in, char *optstr)
+{
+	jpc_dec_importopts_t opts;
+	jpc_dec_t *dec;
+	jas_image_t *image;
+
+	dec = 0;
+
+	if (jpc_dec_parseopts(optstr, &opts)) {
+		goto error;
+	}
+
+	jpc_initluts();
+
+	if (!(dec = jpc_dec_create(&opts, in))) {
+		goto error;
+	}
+
+	/* Do most of the work. */
+	if (jpc_dec_decode(dec)) {
+		goto error;
+	}
+
+	if (jas_image_numcmpts(dec->image) >= 3) {
+		jas_image_setcolorspace(dec->image, JAS_IMAGE_CS_RGB);
+		jas_image_setcmpttype(dec->image, 0,
+		  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_R));
+		jas_image_setcmpttype(dec->image, 1,
+		  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_G));
+		jas_image_setcmpttype(dec->image, 2,
+		  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_B));
+	} else {
+		jas_image_setcolorspace(dec->image, JAS_IMAGE_CS_GRAY);
+		jas_image_setcmpttype(dec->image, 0,
+		  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_GRAY_Y));
+	}
+
+	/* Save the return value. */
+	image = dec->image;
+
+	/* Stop the image from being discarded. */
+	dec->image = 0;
+
+	/* Destroy decoder. */
+	jpc_dec_destroy(dec);
+
+	return image;
+
+error:
+	if (dec) {
+		jpc_dec_destroy(dec);
+	}
+	return 0;
+}
+
+typedef enum {
+	OPT_MAXLYRS,
+	OPT_MAXPKTS,
+	OPT_DEBUG
+} optid_t;
+
+jas_taginfo_t decopts[] = {
+	{OPT_MAXLYRS, "maxlyrs"},
+	{OPT_MAXPKTS, "maxpkts"},
+	{OPT_DEBUG, "debug"},
+	{-1, 0}
+};
+
+static int jpc_dec_parseopts(char *optstr, jpc_dec_importopts_t *opts)
+{
+	jas_tvparser_t *tvp;
+
+	opts->debug = 0;
+	opts->maxlyrs = JPC_MAXLYRS;
+	opts->maxpkts = -1;
+
+	if (!(tvp = jas_tvparser_create(optstr ? optstr : ""))) {
+		return -1;
+	}
+
+	while (!jas_tvparser_next(tvp)) {
+		switch (jas_taginfo_nonull(jas_taginfos_lookup(decopts,
+		  jas_tvparser_gettag(tvp)))->id) {
+		case OPT_MAXLYRS:
+			opts->maxlyrs = atoi(jas_tvparser_getval(tvp));
+			break;
+		case OPT_DEBUG:
+			opts->debug = atoi(jas_tvparser_getval(tvp));
+			break;
+		case OPT_MAXPKTS:
+			opts->maxpkts = atoi(jas_tvparser_getval(tvp));
+			break;
+		default:
+			fprintf(stderr, "warning: ignoring invalid option %s\n",
+			  jas_tvparser_gettag(tvp));
+			break;
+		}
+	}
+
+	jas_tvparser_destroy(tvp);
+
+	return 0;
+}
+
+/******************************************************************************\
+* Code for table-driven code stream decoder.
+\******************************************************************************/
+
+static jpc_dec_mstabent_t *jpc_dec_mstab_lookup(uint_fast16_t id)
+{
+	jpc_dec_mstabent_t *mstabent;
+	for (mstabent = jpc_dec_mstab; mstabent->id != 0; ++mstabent) {
+		if (mstabent->id == id) {
+			break;
+		}
+	}
+	return mstabent;
+}
+
+static int jpc_dec_decode(jpc_dec_t *dec)
+{
+	jpc_ms_t *ms;
+	jpc_dec_mstabent_t *mstabent;
+	int ret;
+	jpc_cstate_t *cstate;
+
+	if (!(cstate = jpc_cstate_create())) {
+		return -1;
+	}
+	dec->cstate = cstate;
+
+	/* Initially, we should expect to encounter a SOC marker segment. */
+	dec->state = JPC_MHSOC;
+
+	for (;;) {
+
+		/* Get the next marker segment in the code stream. */
+		if (!(ms = jpc_getms(dec->in, cstate))) {
+			fprintf(stderr, "cannot get marker segment\n");
+			return -1;
+		}
+
+		mstabent = jpc_dec_mstab_lookup(ms->id);
+		assert(mstabent);
+
+		/* Ensure that this type of marker segment is permitted
+		  at this point in the code stream. */
+		if (!(dec->state & mstabent->validstates)) {
+			fprintf(stderr, "unexpected marker segment type\n");
+			jpc_ms_destroy(ms);
+			return -1;
+		}
+
+		/* Process the marker segment. */
+		if (mstabent->action) {
+			ret = (*mstabent->action)(dec, ms);
+		} else {
+			/* No explicit action is required. */
+			ret = 0;
+		}
+
+		/* Destroy the marker segment. */
+		jpc_ms_destroy(ms);
+
+		if (ret < 0) {
+			return -1;
+		} else if (ret > 0) {
+			break;
+		}
+
+	}
+
+	return 0;
+}
+
+static int jpc_dec_process_crg(jpc_dec_t *dec, jpc_ms_t *ms)
+{
+	uint_fast16_t cmptno;
+	jpc_dec_cmpt_t *cmpt;
+	jpc_crg_t *crg;
+
+	crg = &ms->parms.crg;
+	for (cmptno = 0, cmpt = dec->cmpts; cmptno < dec->numcomps; ++cmptno,
+	  ++cmpt) {
+		/* Ignore the information in the CRG marker segment for now.
+		  This information serves no useful purpose for decoding anyhow.
+		  Some other parts of the code need to be changed if these lines
+		  are uncommented.
+		cmpt->hsubstep = crg->comps[cmptno].hoff;
+		cmpt->vsubstep = crg->comps[cmptno].voff;
+		*/
+	}
+	return 0;
+}
+
+static int jpc_dec_process_soc(jpc_dec_t *dec, jpc_ms_t *ms)
+{
+	/* We should expect to encounter a SIZ marker segment next. */
+	dec->state = JPC_MHSIZ;
+
+	return 0;
+}
+
+static int jpc_dec_process_sot(jpc_dec_t *dec, jpc_ms_t *ms)
+{
+	jpc_dec_tile_t *tile;
+	jpc_sot_t *sot = &ms->parms.sot;
+	jas_image_cmptparm_t *compinfos;
+	jas_image_cmptparm_t *compinfo;
+	jpc_dec_cmpt_t *cmpt;
+	uint_fast16_t cmptno;
+
+	if (dec->state == JPC_MH) {
+
+		compinfos = jas_malloc(dec->numcomps * sizeof(jas_image_cmptparm_t));
+		assert(compinfos);
+		for (cmptno = 0, cmpt = dec->cmpts, compinfo = compinfos;
+		  cmptno < dec->numcomps; ++cmptno, ++cmpt, ++compinfo) {
+			compinfo->tlx = 0;
+			compinfo->tly = 0;
+			compinfo->prec = cmpt->prec;
+			compinfo->sgnd = cmpt->sgnd;
+			compinfo->width = cmpt->width;
+			compinfo->height = cmpt->height;
+			compinfo->hstep = cmpt->hstep;
+			compinfo->vstep = cmpt->vstep;
+		}
+
+		if (!(dec->image = jas_image_create(dec->numcomps, compinfos,
+		  JAS_IMAGE_CS_UNKNOWN))) {
+			return -1;
+		}
+		jas_free(compinfos);
+
+		/* Is the packet header information stored in PPM marker segments in
+		  the main header? */
+		if (dec->ppmstab) {
+			/* Convert the PPM marker segment data into a collection of streams
+			  (one stream per tile-part). */
+			if (!(dec->pkthdrstreams = jpc_ppmstabtostreams(dec->ppmstab))) {
+				abort();
+			}
+			jpc_ppxstab_destroy(dec->ppmstab);
+			dec->ppmstab = 0;
+		}
+	}
+
+	if (sot->len > 0) {
+		dec->curtileendoff = jas_stream_getrwcount(dec->in) - ms->len -
+		  4 + sot->len;
+	} else {
+		dec->curtileendoff = 0;
+	}
+
+	if (sot->tileno > dec->numtiles) {
+		fprintf(stderr, "invalid tile number in SOT marker segment\n");
+		return -1;
+	}
+	/* Set the current tile. */
+	dec->curtile = &dec->tiles[sot->tileno];
+	tile = dec->curtile;
+	/* Ensure that this is the expected part number. */
+	if (sot->partno != tile->partno) {
+		return -1;
+	}
+	if (tile->numparts > 0 && sot->partno >= tile->numparts) {
+		return -1;
+	}
+	if (!tile->numparts && sot->numparts > 0) {
+		tile->numparts = sot->numparts;
+	}
+
+	tile->pptstab = 0;
+
+	switch (tile->state) {
+	case JPC_TILE_INIT:
+		/* This is the first tile-part for this tile. */
+		tile->state = JPC_TILE_ACTIVE;
+		assert(!tile->cp);
+		if (!(tile->cp = jpc_dec_cp_copy(dec->cp))) {
+			return -1;
+		}
+		jpc_dec_cp_resetflags(dec->cp);
+		break;
+	default:
+		if (sot->numparts == sot->partno - 1) {
+			tile->state = JPC_TILE_ACTIVELAST;
+		}
+		break;
+	}
+
+	/* Note: We do not increment the expected tile-part number until
+	  all processing for this tile-part is complete. */
+
+	/* We should expect to encounter other tile-part header marker
+	  segments next. */
+	dec->state = JPC_TPH;
+
+	return 0;
+}
+
+static int jpc_dec_process_sod(jpc_dec_t *dec, jpc_ms_t *ms)
+{
+	jpc_dec_tile_t *tile;
+	int pos;
+
+	if (!(tile = dec->curtile)) {
+		return -1;
+	}
+
+	if (!tile->partno) {
+		if (!jpc_dec_cp_isvalid(tile->cp)) {
+			return -1;
+		}
+		jpc_dec_cp_prepare(tile->cp);
+		if (jpc_dec_tileinit(dec, tile)) {
+			return -1;
+		}
+	}
+
+	/* Are packet headers stored in the main header or tile-part header? */
+	if (dec->pkthdrstreams) {
+		/* Get the stream containing the packet header data for this
+		  tile-part. */
+		if (!(tile->pkthdrstream = jpc_streamlist_remove(dec->pkthdrstreams, 0))) {
+			return -1;
+		}
+	}
+
+	if (tile->pptstab) {
+		if (!tile->pkthdrstream) {
+			if (!(tile->pkthdrstream = jas_stream_memopen(0, 0))) {
+				return -1;
+			}
+		}
+		pos = jas_stream_tell(tile->pkthdrstream);
+		jas_stream_seek(tile->pkthdrstream, 0, SEEK_END);
+		if (jpc_pptstabwrite(tile->pkthdrstream, tile->pptstab)) {
+			return -1;
+		}
+		jas_stream_seek(tile->pkthdrstream, pos, SEEK_SET);
+		jpc_ppxstab_destroy(tile->pptstab);
+		tile->pptstab = 0;
+	}
+
+	if (jas_getdbglevel() >= 10) {
+		jpc_dec_dump(dec, stderr);
+	}
+
+	if (jpc_dec_decodepkts(dec, (tile->pkthdrstream) ? tile->pkthdrstream :
+	  dec->in, dec->in)) {
+		fprintf(stderr, "jpc_dec_decodepkts failed\n");
+		return -1;
+	}
+
+	/* Gobble any unconsumed tile data. */
+	if (dec->curtileendoff > 0) {
+		uint_fast32_t curoff;
+		uint_fast32_t n;
+		curoff = jas_stream_getrwcount(dec->in);
+		if (curoff < dec->curtileendoff) {
+			n = dec->curtileendoff - curoff;
+			fprintf(stderr,
+			  "warning: ignoring trailing garbage (%lu bytes)\n",
+			  (unsigned long) n);
+
+			while (n-- > 0) {
+				if (jas_stream_getc(dec->in) == EOF) {
+					fprintf(stderr, "read error\n");
+					return -1;
+				}
+			}
+		} else if (curoff > dec->curtileendoff) {
+			fprintf(stderr,
+			  "warning: not enough tile data (%lu bytes)\n",
+			  (unsigned long) curoff - dec->curtileendoff);
+		}
+
+	}
+
+	if (tile->numparts > 0 && tile->partno == tile->numparts - 1) {
+		if (jpc_dec_tiledecode(dec, tile)) {
+			return -1;
+		}
+		jpc_dec_tilefini(dec, tile);
+	}
+
+	dec->curtile = 0;
+
+	/* Increment the expected tile-part number. */
+	++tile->partno;
+
+	/* We should expect to encounter a SOT marker segment next. */
+	dec->state = JPC_TPHSOT;
+
+	return 0;
+}
+
+static int jpc_dec_tileinit(jpc_dec_t *dec, jpc_dec_tile_t *tile)
+{
+	jpc_dec_tcomp_t *tcomp;
+	uint_fast16_t compno;
+	int rlvlno;
+	jpc_dec_rlvl_t *rlvl;
+	jpc_dec_band_t *band;
+	jpc_dec_prc_t *prc;
+	int bndno;
+	jpc_tsfb_band_t *bnd;
+	int bandno;
+	jpc_dec_ccp_t *ccp;
+	int prccnt;
+	jpc_dec_cblk_t *cblk;
+	int cblkcnt;
+	uint_fast32_t tlprcxstart;
+	uint_fast32_t tlprcystart;
+	uint_fast32_t brprcxend;
+	uint_fast32_t brprcyend;
+	uint_fast32_t tlcbgxstart;
+	uint_fast32_t tlcbgystart;
+	uint_fast32_t brcbgxend;
+	uint_fast32_t brcbgyend;
+	uint_fast32_t cbgxstart;
+	uint_fast32_t cbgystart;
+	uint_fast32_t cbgxend;
+	uint_fast32_t cbgyend;
+	uint_fast32_t tlcblkxstart;
+	uint_fast32_t tlcblkystart;
+	uint_fast32_t brcblkxend;
+	uint_fast32_t brcblkyend;
+	uint_fast32_t cblkxstart;
+	uint_fast32_t cblkystart;
+	uint_fast32_t cblkxend;
+	uint_fast32_t cblkyend;
+	uint_fast32_t tmpxstart;
+	uint_fast32_t tmpystart;
+	uint_fast32_t tmpxend;
+	uint_fast32_t tmpyend;
+	jpc_dec_cp_t *cp;
+	jpc_tsfb_band_t bnds[64];
+	jpc_pchg_t *pchg;
+	int pchgno;
+	jpc_dec_cmpt_t *cmpt;
+
+	cp = tile->cp;
+	tile->realmode = 0;
+	if (cp->mctid == JPC_MCT_ICT) {
+		tile->realmode = 1;
+	}
+
+	for (compno = 0, tcomp = tile->tcomps, cmpt = dec->cmpts; compno <
+	  dec->numcomps; ++compno, ++tcomp, ++cmpt) {
+		ccp = &tile->cp->ccps[compno];
+		if (ccp->qmfbid == JPC_COX_INS) {
+			tile->realmode = 1;
+		}
+		tcomp->numrlvls = ccp->numrlvls;
+		if (!(tcomp->rlvls = jas_malloc(tcomp->numrlvls *
+		  sizeof(jpc_dec_rlvl_t)))) {
+			return -1;
+		}
+		if (!(tcomp->data = jas_seq2d_create(JPC_CEILDIV(tile->xstart,
+		  cmpt->hstep), JPC_CEILDIV(tile->ystart, cmpt->vstep),
+		  JPC_CEILDIV(tile->xend, cmpt->hstep), JPC_CEILDIV(tile->yend,
+		  cmpt->vstep)))) {
+			return -1;
+		}
+		if (!(tcomp->tsfb = jpc_cod_gettsfb(ccp->qmfbid,
+		  tcomp->numrlvls - 1))) {
+			return -1;
+		}
+{
+	jpc_tsfb_getbands(tcomp->tsfb, jas_seq2d_xstart(tcomp->data), jas_seq2d_ystart(tcomp->data), jas_seq2d_xend(tcomp->data), jas_seq2d_yend(tcomp->data), bnds);
+}
+		for (rlvlno = 0, rlvl = tcomp->rlvls; rlvlno < tcomp->numrlvls;
+		  ++rlvlno, ++rlvl) {
+rlvl->bands = 0;
+			rlvl->xstart = JPC_CEILDIVPOW2(tcomp->xstart,
+			  tcomp->numrlvls - 1 - rlvlno);
+			rlvl->ystart = JPC_CEILDIVPOW2(tcomp->ystart,
+			  tcomp->numrlvls - 1 - rlvlno);
+			rlvl->xend = JPC_CEILDIVPOW2(tcomp->xend,
+			  tcomp->numrlvls - 1 - rlvlno);
+			rlvl->yend = JPC_CEILDIVPOW2(tcomp->yend,
+			  tcomp->numrlvls - 1 - rlvlno);
+			rlvl->prcwidthexpn = ccp->prcwidthexpns[rlvlno];
+			rlvl->prcheightexpn = ccp->prcheightexpns[rlvlno];
+			tlprcxstart = JPC_FLOORDIVPOW2(rlvl->xstart,
+			  rlvl->prcwidthexpn) << rlvl->prcwidthexpn;
+			tlprcystart = JPC_FLOORDIVPOW2(rlvl->ystart,
+			  rlvl->prcheightexpn) << rlvl->prcheightexpn;
+			brprcxend = JPC_CEILDIVPOW2(rlvl->xend,
+			  rlvl->prcwidthexpn) << rlvl->prcwidthexpn;
+			brprcyend = JPC_CEILDIVPOW2(rlvl->yend,
+			  rlvl->prcheightexpn) << rlvl->prcheightexpn;
+			rlvl->numhprcs = (brprcxend - tlprcxstart) >>
+			  rlvl->prcwidthexpn;
+			rlvl->numvprcs = (brprcyend - tlprcystart) >>
+			  rlvl->prcheightexpn;
+			rlvl->numprcs = rlvl->numhprcs * rlvl->numvprcs;
+
+			if (rlvl->xstart >= rlvl->xend || rlvl->ystart >= rlvl->yend) {
+				rlvl->bands = 0;
+				rlvl->numprcs = 0;
+				rlvl->numhprcs = 0;
+				rlvl->numvprcs = 0;
+				continue;
+			}	
+			if (!rlvlno) {
+				tlcbgxstart = tlprcxstart;
+				tlcbgystart = tlprcystart;
+				brcbgxend = brprcxend;
+				brcbgyend = brprcyend;
+				rlvl->cbgwidthexpn = rlvl->prcwidthexpn;
+				rlvl->cbgheightexpn = rlvl->prcheightexpn;
+			} else {
+				tlcbgxstart = JPC_CEILDIVPOW2(tlprcxstart, 1);
+				tlcbgystart = JPC_CEILDIVPOW2(tlprcystart, 1);
+				brcbgxend = JPC_CEILDIVPOW2(brprcxend, 1);
+				brcbgyend = JPC_CEILDIVPOW2(brprcyend, 1);
+				rlvl->cbgwidthexpn = rlvl->prcwidthexpn - 1;
+				rlvl->cbgheightexpn = rlvl->prcheightexpn - 1;
+			}
+			rlvl->cblkwidthexpn = JAS_MIN(ccp->cblkwidthexpn,
+			  rlvl->cbgwidthexpn);
+			rlvl->cblkheightexpn = JAS_MIN(ccp->cblkheightexpn,
+			  rlvl->cbgheightexpn);
+
+			rlvl->numbands = (!rlvlno) ? 1 : 3;
+			if (!(rlvl->bands = jas_malloc(rlvl->numbands *
+			  sizeof(jpc_dec_band_t)))) {
+				return -1;
+			}
+			for (bandno = 0, band = rlvl->bands;
+			  bandno < rlvl->numbands; ++bandno, ++band) {
+				bndno = (!rlvlno) ? 0 : (3 * (rlvlno - 1) +
+				  bandno + 1);
+				bnd = &bnds[bndno];
+
+				band->orient = bnd->orient;
+				band->stepsize = ccp->stepsizes[bndno];
+				band->analgain = JPC_NOMINALGAIN(ccp->qmfbid,
+				  tcomp->numrlvls - 1, rlvlno, band->orient);
+				band->absstepsize = jpc_calcabsstepsize(band->stepsize,
+				  cmpt->prec + band->analgain);
+				band->numbps = ccp->numguardbits +
+				  JPC_QCX_GETEXPN(band->stepsize) - 1;
+				band->roishift = (ccp->roishift + band->numbps >= JPC_PREC) ?
+				  (JPC_PREC - 1 - band->numbps) : ccp->roishift;
+				band->data = 0;
+				band->prcs = 0;
+				if (bnd->xstart == bnd->xend || bnd->ystart == bnd->yend) {
+					continue;
+				}
+				if (!(band->data = jas_seq2d_create(0, 0, 0, 0))) {
+					return -1;
+				}
+				jas_seq2d_bindsub(band->data, tcomp->data, bnd->locxstart, bnd->locystart, bnd->locxend, bnd->locyend);
+				jas_seq2d_setshift(band->data, bnd->xstart, bnd->ystart);
+
+				assert(rlvl->numprcs);
+
+				if (!(band->prcs = jas_malloc(rlvl->numprcs * sizeof(jpc_dec_prc_t)))) {
+					return -1;
+				}
+
+/************************************************/
+	cbgxstart = tlcbgxstart;
+	cbgystart = tlcbgystart;
+	for (prccnt = rlvl->numprcs, prc = band->prcs;
+	  prccnt > 0; --prccnt, ++prc) {
+		cbgxend = cbgxstart + (1 << rlvl->cbgwidthexpn);
+		cbgyend = cbgystart + (1 << rlvl->cbgheightexpn);
+		prc->xstart = JAS_MAX(cbgxstart, jas_seq2d_xstart(band->data));
+		prc->ystart = JAS_MAX(cbgystart, jas_seq2d_ystart(band->data));
+		prc->xend = JAS_MIN(cbgxend, jas_seq2d_xend(band->data));
+		prc->yend = JAS_MIN(cbgyend, jas_seq2d_yend(band->data));
+		if (prc->xend > prc->xstart && prc->yend > prc->ystart) {
+			tlcblkxstart = JPC_FLOORDIVPOW2(prc->xstart,
+			  rlvl->cblkwidthexpn) << rlvl->cblkwidthexpn;
+			tlcblkystart = JPC_FLOORDIVPOW2(prc->ystart,
+			  rlvl->cblkheightexpn) << rlvl->cblkheightexpn;
+			brcblkxend = JPC_CEILDIVPOW2U(prc->xend,
+			  rlvl->cblkwidthexpn) << rlvl->cblkwidthexpn;
+			brcblkyend = JPC_CEILDIVPOW2U(prc->yend,
+			  rlvl->cblkheightexpn) << rlvl->cblkheightexpn;
+			prc->numhcblks = (brcblkxend - tlcblkxstart) >>
+			  rlvl->cblkwidthexpn;
+			prc->numvcblks = (brcblkyend - tlcblkystart) >>
+			  rlvl->cblkheightexpn;
+			prc->numcblks = prc->numhcblks * prc->numvcblks;
+			assert(prc->numcblks > 0);
+
+			if (!(prc->incltagtree = jpc_tagtree_create(prc->numhcblks, prc->numvcblks))) {
+				return -1;
+			}
+			if (!(prc->numimsbstagtree = jpc_tagtree_create(prc->numhcblks, prc->numvcblks))) {
+				return -1;
+			}
+			if (!(prc->cblks = jas_malloc(prc->numcblks * sizeof(jpc_dec_cblk_t)))) {
+				return -1;
+			}
+
+			cblkxstart = cbgxstart;
+			cblkystart = cbgystart;
+			for (cblkcnt = prc->numcblks, cblk = prc->cblks; cblkcnt > 0;) {
+				cblkxend = cblkxstart + (1 << rlvl->cblkwidthexpn);
+				cblkyend = cblkystart + (1 << rlvl->cblkheightexpn);
+				tmpxstart = JAS_MAX(cblkxstart, prc->xstart);
+				tmpystart = JAS_MAX(cblkystart, prc->ystart);
+				tmpxend = JAS_MIN(cblkxend, prc->xend);
+				tmpyend = JAS_MIN(cblkyend, prc->yend);
+				if (tmpxend > tmpxstart && tmpyend > tmpystart) {
+					cblk->firstpassno = -1;
+					cblk->mqdec = 0;
+					cblk->nulldec = 0;
+					cblk->flags = 0;
+					cblk->numpasses = 0;
+					cblk->segs.head = 0;
+					cblk->segs.tail = 0;
+					cblk->curseg = 0;
+					cblk->numimsbs = 0;
+					cblk->numlenbits = 3;
+					cblk->flags = 0;
+					if (!(cblk->data = jas_seq2d_create(0, 0, 0, 0))) {
+						return -1;
+					}
+					jas_seq2d_bindsub(cblk->data, band->data, tmpxstart, tmpystart, tmpxend, tmpyend);
+					++cblk;
+					--cblkcnt;
+				}
+				cblkxstart += 1 << rlvl->cblkwidthexpn;
+				if (cblkxstart >= cbgxend) {
+					cblkxstart = cbgxstart;
+					cblkystart += 1 << rlvl->cblkheightexpn;
+				}
+			}
+
+		} else {
+			prc->cblks = 0;
+			prc->incltagtree = 0;
+			prc->numimsbstagtree = 0;
+		}
+		cbgxstart += 1 << rlvl->cbgwidthexpn;
+		if (cbgxstart >= brcbgxend) {
+			cbgxstart = tlcbgxstart;
+			cbgystart += 1 << rlvl->cbgheightexpn;
+		}
+
+	}
+/********************************************/
+			}
+		}
+	}
+
+if (!(tile->pi = jpc_dec_pi_create(dec, tile)))
+{
+	return -1;
+}
+
+	for (pchgno = 0; pchgno < jpc_pchglist_numpchgs(tile->cp->pchglist);
+	  ++pchgno) {
+		pchg = jpc_pchg_copy(jpc_pchglist_get(tile->cp->pchglist, pchgno));
+		assert(pchg);
+		jpc_pi_addpchg(tile->pi, pchg);
+	}
+	jpc_pi_init(tile->pi);
+
+	return 0;
+}
+
+static int jpc_dec_tilefini(jpc_dec_t *dec, jpc_dec_tile_t *tile)
+{
+	jpc_dec_tcomp_t *tcomp;
+	int compno;
+	int bandno;
+	int rlvlno;
+	jpc_dec_band_t *band;
+	jpc_dec_rlvl_t *rlvl;
+	int prcno;
+	jpc_dec_prc_t *prc;
+	jpc_dec_seg_t *seg;
+	jpc_dec_cblk_t *cblk;
+	int cblkno;
+
+if (tile->tcomps) {
+
+	for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
+	  ++compno, ++tcomp) {
+		for (rlvlno = 0, rlvl = tcomp->rlvls; rlvlno < tcomp->numrlvls;
+		  ++rlvlno, ++rlvl) {
+if (!rlvl->bands) {
+	continue;
+}
+			for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands; ++bandno, ++band) {
+if (band->prcs) {
+				for (prcno = 0, prc = band->prcs; prcno <
+				  rlvl->numprcs; ++prcno, ++prc) {
+if (!prc->cblks) {
+	continue;
+}
+					for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks; ++cblkno, ++cblk) {
+
+	while (cblk->segs.head) {
+		seg = cblk->segs.head;
+		jpc_seglist_remove(&cblk->segs, seg);
+		jpc_seg_destroy(seg);
+	}
+	jas_matrix_destroy(cblk->data);
+	if (cblk->mqdec) {
+		jpc_mqdec_destroy(cblk->mqdec);
+	}
+	if (cblk->nulldec) {
+		jpc_bitstream_close(cblk->nulldec);
+	}
+	if (cblk->flags) {
+		jas_matrix_destroy(cblk->flags);
+	}
+					}
+					if (prc->incltagtree) {
+						jpc_tagtree_destroy(prc->incltagtree);
+					}
+					if (prc->numimsbstagtree) {
+						jpc_tagtree_destroy(prc->numimsbstagtree);
+					}
+					if (prc->cblks) {
+						jas_free(prc->cblks);
+					}
+				}
+}
+				if (band->data) {
+					jas_matrix_destroy(band->data);
+				}
+				if (band->prcs) {
+					jas_free(band->prcs);
+				}
+			}
+			if (rlvl->bands) {
+				jas_free(rlvl->bands);
+			}
+		}
+		if (tcomp->rlvls) {
+			jas_free(tcomp->rlvls);
+		}
+		if (tcomp->data) {
+			jas_matrix_destroy(tcomp->data);
+		}
+		if (tcomp->tsfb) {
+			jpc_tsfb_destroy(tcomp->tsfb);
+		}
+	}
+}
+	if (tile->cp) {
+		jpc_dec_cp_destroy(tile->cp);
+		tile->cp = 0;
+	}
+	if (tile->tcomps) {
+		jas_free(tile->tcomps);
+		tile->tcomps = 0;
+	}
+	if (tile->pi) {
+		jpc_pi_destroy(tile->pi);
+		tile->pi = 0;
+	}
+	if (tile->pkthdrstream) {
+		jas_stream_close(tile->pkthdrstream);
+		tile->pkthdrstream = 0;
+	}
+	if (tile->pptstab) {
+		jpc_ppxstab_destroy(tile->pptstab);
+		tile->pptstab = 0;
+	}
+
+	tile->state = JPC_TILE_DONE;
+
+	return 0;
+}
+
+static int jpc_dec_tiledecode(jpc_dec_t *dec, jpc_dec_tile_t *tile)
+{
+	int i;
+	int j;
+	jpc_dec_tcomp_t *tcomp;
+	jpc_dec_rlvl_t *rlvl;
+	jpc_dec_band_t *band;
+	int compno;
+	int rlvlno;
+	int bandno;
+	int adjust;
+	int v;
+	jpc_dec_ccp_t *ccp;
+	jpc_dec_cmpt_t *cmpt;
+
+	if (jpc_dec_decodecblks(dec, tile)) {
+		fprintf(stderr, "jpc_dec_decodecblks failed\n");
+		return -1;
+	}
+
+	/* Perform dequantization. */
+	for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
+	  ++compno, ++tcomp) {
+		ccp = &tile->cp->ccps[compno];
+		for (rlvlno = 0, rlvl = tcomp->rlvls; rlvlno < tcomp->numrlvls;
+		  ++rlvlno, ++rlvl) {
+			if (!rlvl->bands) {
+				continue;
+			}
+			for (bandno = 0, band = rlvl->bands;
+			  bandno < rlvl->numbands; ++bandno, ++band) {
+				if (!band->data) {
+					continue;
+				}
+				jpc_undo_roi(band->data, band->roishift, ccp->roishift -
+				  band->roishift, band->numbps);
+				if (tile->realmode) {
+					jas_matrix_asl(band->data, JPC_FIX_FRACBITS);
+					jpc_dequantize(band->data, band->absstepsize);
+				}
+
+			}
+		}
+	}
+
+	/* Apply an inverse wavelet transform if necessary. */
+	for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
+	  ++compno, ++tcomp) {
+		ccp = &tile->cp->ccps[compno];
+		jpc_tsfb_synthesize(tcomp->tsfb, ((ccp->qmfbid ==
+		  JPC_COX_RFT) ? JPC_TSFB_RITIMODE : 0), tcomp->data);
+	}
+
+
+	/* Apply an inverse intercomponent transform if necessary. */
+	switch (tile->cp->mctid) {
+	case JPC_MCT_RCT:
+		assert(dec->numcomps == 3);
+		jpc_irct(tile->tcomps[0].data, tile->tcomps[1].data,
+		  tile->tcomps[2].data);
+		break;
+	case JPC_MCT_ICT:
+		assert(dec->numcomps == 3);
+		jpc_iict(tile->tcomps[0].data, tile->tcomps[1].data,
+		  tile->tcomps[2].data);
+		break;
+	}
+
+	/* Perform rounding and convert to integer values. */
+	if (tile->realmode) {
+		for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
+		  ++compno, ++tcomp) {
+			for (i = 0; i < jas_matrix_numrows(tcomp->data); ++i) {
+				for (j = 0; j < jas_matrix_numcols(tcomp->data); ++j) {
+					v = jas_matrix_get(tcomp->data, i, j);
+					v = jpc_fix_round(v);
+					jas_matrix_set(tcomp->data, i, j, jpc_fixtoint(v));
+				}
+			}
+		}
+	}
+
+	/* Perform level shift. */
+	for (compno = 0, tcomp = tile->tcomps, cmpt = dec->cmpts; compno <
+	  dec->numcomps; ++compno, ++tcomp, ++cmpt) {
+		adjust = cmpt->sgnd ? 0 : (1 << (cmpt->prec - 1));
+		for (i = 0; i < jas_matrix_numrows(tcomp->data); ++i) {
+			for (j = 0; j < jas_matrix_numcols(tcomp->data); ++j) {
+				*jas_matrix_getref(tcomp->data, i, j) += adjust;
+			}
+		}
+	}
+
+	/* Perform clipping. */
+	for (compno = 0, tcomp = tile->tcomps, cmpt = dec->cmpts; compno <
+	  dec->numcomps; ++compno, ++tcomp, ++cmpt) {
+		jpc_fix_t mn;
+		jpc_fix_t mx;
+		mn = cmpt->sgnd ? (-(1 << (cmpt->prec - 1))) : (0);
+		mx = cmpt->sgnd ? ((1 << (cmpt->prec - 1)) - 1) : ((1 <<
+		  cmpt->prec) - 1);
+		jas_matrix_clip(tcomp->data, mn, mx);
+	}
+
+	/* XXX need to free tsfb struct */
+
+	/* Write the data for each component of the image. */
+	for (compno = 0, tcomp = tile->tcomps, cmpt = dec->cmpts; compno <
+	  dec->numcomps; ++compno, ++tcomp, ++cmpt) {
+		if (jas_image_writecmpt(dec->image, compno, tcomp->xstart -
+		  JPC_CEILDIV(dec->xstart, cmpt->hstep), tcomp->ystart -
+		  JPC_CEILDIV(dec->ystart, cmpt->vstep), jas_matrix_numcols(
+		  tcomp->data), jas_matrix_numrows(tcomp->data), tcomp->data)) {
+			fprintf(stderr, "write component failed\n");
+			return -4;
+		}
+	}
+
+	return 0;
+}
+
+static int jpc_dec_process_eoc(jpc_dec_t *dec, jpc_ms_t *ms)
+{
+	int tileno;
+	jpc_dec_tile_t *tile;
+	for (tileno = 0, tile = dec->tiles; tileno < dec->numtiles; ++tileno,
+	  ++tile) {
+		if (tile->state == JPC_TILE_ACTIVE) {
+			if (jpc_dec_tiledecode(dec, tile)) {
+				return -1;
+			}
+		}
+		jpc_dec_tilefini(dec, tile);
+	}
+
+	/* We are done processing the code stream. */
+	dec->state = JPC_MT;
+
+	return 1;
+}
+
+static int jpc_dec_process_siz(jpc_dec_t *dec, jpc_ms_t *ms)
+{
+	jpc_siz_t *siz = &ms->parms.siz;
+	uint_fast16_t compno;
+	uint_fast32_t tileno;
+	jpc_dec_tile_t *tile;
+	jpc_dec_tcomp_t *tcomp;
+	uint_fast32_t htileno;
+	uint_fast32_t vtileno;
+	jpc_dec_cmpt_t *cmpt;
+
+	dec->xstart = siz->xoff;
+	dec->ystart = siz->yoff;
+	dec->xend = siz->width;
+	dec->yend = siz->height;
+	dec->tilewidth = siz->tilewidth;
+	dec->tileheight = siz->tileheight;
+	dec->tilexoff = siz->tilexoff;
+	dec->tileyoff = siz->tileyoff;
+	dec->numcomps = siz->numcomps;
+	if (!(dec->cp = jpc_dec_cp_create(dec->numcomps))) {
+		return -1;
+	}
+
+	if (!(dec->cmpts = jas_malloc(dec->numcomps * sizeof(jpc_dec_cmpt_t)))) {
+		return -1;
+	}
+
+	for (compno = 0, cmpt = dec->cmpts; compno < dec->numcomps; ++compno,
+	  ++cmpt) {
+		cmpt->prec = siz->comps[compno].prec;
+		cmpt->sgnd = siz->comps[compno].sgnd;
+		cmpt->hstep = siz->comps[compno].hsamp;
+		cmpt->vstep = siz->comps[compno].vsamp;
+		cmpt->width = JPC_CEILDIV(dec->xend, cmpt->hstep) -
+		  JPC_CEILDIV(dec->xstart, cmpt->hstep);
+		cmpt->height = JPC_CEILDIV(dec->yend, cmpt->vstep) -
+		  JPC_CEILDIV(dec->ystart, cmpt->vstep);
+		cmpt->hsubstep = 0;
+		cmpt->vsubstep = 0;
+	}
+
+	dec->image = 0;
+
+	dec->numhtiles = JPC_CEILDIV(dec->xend - dec->tilexoff, dec->tilewidth);
+	dec->numvtiles = JPC_CEILDIV(dec->yend - dec->tileyoff, dec->tileheight);
+	dec->numtiles = dec->numhtiles * dec->numvtiles;
+	if (!(dec->tiles = jas_malloc(dec->numtiles * sizeof(jpc_dec_tile_t)))) {
+		return -1;
+	}
+
+	for (tileno = 0, tile = dec->tiles; tileno < dec->numtiles; ++tileno,
+	  ++tile) {
+		htileno = tileno % dec->numhtiles;
+		vtileno = tileno / dec->numhtiles;
+		tile->realmode = 0;
+		tile->state = JPC_TILE_INIT;
+		tile->xstart = JAS_MAX(dec->tilexoff + htileno * dec->tilewidth,
+		  dec->xstart);
+		tile->ystart = JAS_MAX(dec->tileyoff + vtileno * dec->tileheight,
+		  dec->ystart);
+		tile->xend = JAS_MIN(dec->tilexoff + (htileno + 1) *
+		  dec->tilewidth, dec->xend);
+		tile->yend = JAS_MIN(dec->tileyoff + (vtileno + 1) *
+		  dec->tileheight, dec->yend);
+		tile->numparts = 0;
+		tile->partno = 0;
+		tile->pkthdrstream = 0;
+		tile->pkthdrstreampos = 0;
+		tile->pptstab = 0;
+		tile->cp = 0;
+		if (!(tile->tcomps = jas_malloc(dec->numcomps *
+		  sizeof(jpc_dec_tcomp_t)))) {
+			return -1;
+		}
+		for (compno = 0, cmpt = dec->cmpts, tcomp = tile->tcomps;
+		  compno < dec->numcomps; ++compno, ++cmpt, ++tcomp) {
+			tcomp->rlvls = 0;
+			tcomp->data = 0;
+			tcomp->xstart = JPC_CEILDIV(tile->xstart, cmpt->hstep);
+			tcomp->ystart = JPC_CEILDIV(tile->ystart, cmpt->vstep);
+			tcomp->xend = JPC_CEILDIV(tile->xend, cmpt->hstep);
+			tcomp->yend = JPC_CEILDIV(tile->yend, cmpt->vstep);
+			tcomp->tsfb = 0;
+		}
+	}
+
+	dec->pkthdrstreams = 0;
+
+	/* We should expect to encounter other main header marker segments
+	  or an SOT marker segment next. */
+	dec->state = JPC_MH;
+
+	return 0;
+}
+
+static int jpc_dec_process_cod(jpc_dec_t *dec, jpc_ms_t *ms)
+{
+	jpc_cod_t *cod = &ms->parms.cod;
+	jpc_dec_tile_t *tile;
+
+	switch (dec->state) {
+	case JPC_MH:
+		jpc_dec_cp_setfromcod(dec->cp, cod);
+		break;
+	case JPC_TPH:
+		if (!(tile = dec->curtile)) {
+			return -1;
+		}
+		if (tile->partno != 0) {
+			return -1;
+		}
+		jpc_dec_cp_setfromcod(tile->cp, cod);
+		break;
+	}
+	return 0;
+}
+
+static int jpc_dec_process_coc(jpc_dec_t *dec, jpc_ms_t *ms)
+{
+	jpc_coc_t *coc = &ms->parms.coc;
+	jpc_dec_tile_t *tile;
+
+	if (coc->compno > dec->numcomps) {
+		fprintf(stderr,
+		  "invalid component number in COC marker segment\n");
+		return -1;
+	}
+	switch (dec->state) {
+	case JPC_MH:
+		jpc_dec_cp_setfromcoc(dec->cp, coc);
+		break;
+	case JPC_TPH:
+		if (!(tile = dec->curtile)) {
+			return -1;
+		}
+		if (tile->partno > 0) {
+			return -1;
+		}
+		jpc_dec_cp_setfromcoc(tile->cp, coc);
+		break;
+	}
+	return 0;
+}
+
+static int jpc_dec_process_rgn(jpc_dec_t *dec, jpc_ms_t *ms)
+{
+	jpc_rgn_t *rgn = &ms->parms.rgn;
+	jpc_dec_tile_t *tile;
+
+	if (rgn->compno > dec->numcomps) {
+		fprintf(stderr,
+		  "invalid component number in RGN marker segment\n");
+		return -1;
+	}
+	switch (dec->state) {
+	case JPC_MH:
+		jpc_dec_cp_setfromrgn(dec->cp, rgn);
+		break;
+	case JPC_TPH:
+		if (!(tile = dec->curtile)) {
+			return -1;
+		}
+		if (tile->partno > 0) {
+			return -1;
+		}
+		jpc_dec_cp_setfromrgn(tile->cp, rgn);
+		break;
+	}
+
+	return 0;
+}
+
+static int jpc_dec_process_qcd(jpc_dec_t *dec, jpc_ms_t *ms)
+{
+	jpc_qcd_t *qcd = &ms->parms.qcd;
+	jpc_dec_tile_t *tile;
+
+	switch (dec->state) {
+	case JPC_MH:
+		jpc_dec_cp_setfromqcd(dec->cp, qcd);
+		break;
+	case JPC_TPH:
+		if (!(tile = dec->curtile)) {
+			return -1;
+		}
+		if (tile->partno > 0) {
+			return -1;
+		}
+		jpc_dec_cp_setfromqcd(tile->cp, qcd);
+		break;
+	}
+	return 0;
+}
+
+static int jpc_dec_process_qcc(jpc_dec_t *dec, jpc_ms_t *ms)
+{
+	jpc_qcc_t *qcc = &ms->parms.qcc;
+	jpc_dec_tile_t *tile;
+
+	if (qcc->compno > dec->numcomps) {
+		fprintf(stderr,
+		  "invalid component number in QCC marker segment\n");
+		return -1;
+	}
+	switch (dec->state) {
+	case JPC_MH:
+		jpc_dec_cp_setfromqcc(dec->cp, qcc);
+		break;
+	case JPC_TPH:
+		if (!(tile = dec->curtile)) {
+			return -1;
+		}
+		if (tile->partno > 0) {
+			return -1;
+		}
+		jpc_dec_cp_setfromqcc(tile->cp, qcc);
+		break;
+	}
+	return 0;
+}
+
+static int jpc_dec_process_poc(jpc_dec_t *dec, jpc_ms_t *ms)
+{
+	jpc_poc_t *poc = &ms->parms.poc;
+	jpc_dec_tile_t *tile;
+	switch (dec->state) {
+	case JPC_MH:
+		if (jpc_dec_cp_setfrompoc(dec->cp, poc, 1)) {
+			return -1;
+		}
+		break;
+	case JPC_TPH:
+		if (!(tile = dec->curtile)) {
+			return -1;
+		}
+		if (!tile->partno) {
+			if (jpc_dec_cp_setfrompoc(tile->cp, poc, (!tile->partno))) {
+				return -1;
+			}
+		} else {
+			jpc_pi_addpchgfrompoc(tile->pi, poc);
+		}
+		break;
+	}
+	return 0;
+}
+
+static int jpc_dec_process_ppm(jpc_dec_t *dec, jpc_ms_t *ms)
+{
+	jpc_ppm_t *ppm = &ms->parms.ppm;
+	jpc_ppxstabent_t *ppmstabent;
+
+	if (!dec->ppmstab) {
+		if (!(dec->ppmstab = jpc_ppxstab_create())) {
+			return -1;
+		}
+	}
+
+	if (!(ppmstabent = jpc_ppxstabent_create())) {
+		return -1;
+	}
+	ppmstabent->ind = ppm->ind;
+	ppmstabent->data = ppm->data;
+	ppm->data = 0;
+	ppmstabent->len = ppm->len;
+	if (jpc_ppxstab_insert(dec->ppmstab, ppmstabent)) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_dec_process_ppt(jpc_dec_t *dec, jpc_ms_t *ms)
+{
+	jpc_ppt_t *ppt = &ms->parms.ppt;
+	jpc_dec_tile_t *tile;
+	jpc_ppxstabent_t *pptstabent;
+
+	tile = dec->curtile;
+	if (!tile->pptstab) {
+		if (!(tile->pptstab = jpc_ppxstab_create())) {
+			return -1;
+		}
+	}
+	if (!(pptstabent = jpc_ppxstabent_create())) {
+		return -1;
+	}
+	pptstabent->ind = ppt->ind;
+	pptstabent->data = ppt->data;
+	ppt->data = 0;
+	pptstabent->len = ppt->len;
+	if (jpc_ppxstab_insert(tile->pptstab, pptstabent)) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_dec_process_com(jpc_dec_t *dec, jpc_ms_t *ms)
+{
+	return 0;
+}
+
+static int jpc_dec_process_unk(jpc_dec_t *dec, jpc_ms_t *ms)
+{
+	fprintf(stderr, "warning: ignoring unknown marker segment\n");
+	jpc_ms_dump(ms, stderr);
+	return 0;
+}
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+static jpc_dec_cp_t *jpc_dec_cp_create(uint_fast16_t numcomps)
+{
+	jpc_dec_cp_t *cp;
+	jpc_dec_ccp_t *ccp;
+	int compno;
+
+	if (!(cp = jas_malloc(sizeof(jpc_dec_cp_t)))) {
+		return 0;
+	}
+	cp->flags = 0;
+	cp->numcomps = numcomps;
+	cp->prgord = 0;
+	cp->numlyrs = 0;
+	cp->mctid = 0;
+	cp->csty = 0;
+	if (!(cp->ccps = jas_malloc(cp->numcomps * sizeof(jpc_dec_ccp_t)))) {
+		return 0;
+	}
+	if (!(cp->pchglist = jpc_pchglist_create())) {
+		jas_free(cp->ccps);
+		return 0;
+	}
+	for (compno = 0, ccp = cp->ccps; compno < cp->numcomps;
+	  ++compno, ++ccp) {
+		ccp->flags = 0;
+		ccp->numrlvls = 0;
+		ccp->cblkwidthexpn = 0;
+		ccp->cblkheightexpn = 0;
+		ccp->qmfbid = 0;
+		ccp->numstepsizes = 0;
+		ccp->numguardbits = 0;
+		ccp->roishift = 0;
+		ccp->cblkctx = 0;
+	}
+	return cp;
+}
+
+static jpc_dec_cp_t *jpc_dec_cp_copy(jpc_dec_cp_t *cp)
+{
+	jpc_dec_cp_t *newcp;
+	jpc_dec_ccp_t *newccp;
+	jpc_dec_ccp_t *ccp;
+	int compno;
+
+	if (!(newcp = jpc_dec_cp_create(cp->numcomps))) {
+		return 0;
+	}
+	newcp->flags = cp->flags;
+	newcp->prgord = cp->prgord;
+	newcp->numlyrs = cp->numlyrs;
+	newcp->mctid = cp->mctid;
+	newcp->csty = cp->csty;
+	jpc_pchglist_destroy(newcp->pchglist);
+	newcp->pchglist = 0;
+	if (!(newcp->pchglist = jpc_pchglist_copy(cp->pchglist))) {
+		jas_free(newcp);
+		return 0;
+	}
+	for (compno = 0, newccp = newcp->ccps, ccp = cp->ccps;
+	  compno < cp->numcomps;
+	  ++compno, ++newccp, ++ccp) {
+		*newccp = *ccp;
+	}
+	return newcp;
+}
+
+static void jpc_dec_cp_resetflags(jpc_dec_cp_t *cp)
+{
+	int compno;
+	jpc_dec_ccp_t *ccp;
+	cp->flags &= (JPC_CSET | JPC_QSET);
+	for (compno = 0, ccp = cp->ccps; compno < cp->numcomps;
+	  ++compno, ++ccp) {
+		ccp->flags = 0;
+	}
+}
+
+static void jpc_dec_cp_destroy(jpc_dec_cp_t *cp)
+{
+	if (cp->ccps) {
+		jas_free(cp->ccps);
+	}
+	if (cp->pchglist) {
+		jpc_pchglist_destroy(cp->pchglist);
+	}
+	jas_free(cp);
+}
+
+static int jpc_dec_cp_isvalid(jpc_dec_cp_t *cp)
+{
+	uint_fast16_t compcnt;
+	jpc_dec_ccp_t *ccp;
+
+	if (!(cp->flags & JPC_CSET) || !(cp->flags & JPC_QSET)) {
+		return 0;
+	}
+	for (compcnt = cp->numcomps, ccp = cp->ccps; compcnt > 0; --compcnt,
+	  ++ccp) {
+		/* Is there enough step sizes for the number of bands? */
+		if ((ccp->qsty != JPC_QCX_SIQNT && ccp->numstepsizes < 3 *
+		  ccp->numrlvls - 2) || (ccp->qsty == JPC_QCX_SIQNT &&
+		  ccp->numstepsizes != 1)) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+static void calcstepsizes(uint_fast16_t refstepsize, int numrlvls,
+  uint_fast16_t *stepsizes)
+{
+	int bandno;
+	int numbands;
+	uint_fast16_t expn;
+	uint_fast16_t mant;
+	expn = JPC_QCX_GETEXPN(refstepsize);
+	mant = JPC_QCX_GETMANT(refstepsize);
+	numbands = 3 * numrlvls - 2;
+	for (bandno = 0; bandno < numbands; ++bandno) {
+		stepsizes[bandno] = JPC_QCX_MANT(mant) | JPC_QCX_EXPN(expn +
+		  (numrlvls - 1) - (numrlvls - 1 - ((bandno > 0) ? ((bandno + 2) / 3) : (0))));
+	}
+}
+
+static int jpc_dec_cp_prepare(jpc_dec_cp_t *cp)
+{
+	jpc_dec_ccp_t *ccp;
+	int compno;
+	int i;
+	for (compno = 0, ccp = cp->ccps; compno < cp->numcomps;
+	  ++compno, ++ccp) {
+		if (!(ccp->csty & JPC_COX_PRT)) {
+			for (i = 0; i < JPC_MAXRLVLS; ++i) {
+				ccp->prcwidthexpns[i] = 15;
+				ccp->prcheightexpns[i] = 15;
+			}
+		}
+		if (ccp->qsty == JPC_QCX_SIQNT) {
+			calcstepsizes(ccp->stepsizes[0], ccp->numrlvls, ccp->stepsizes);
+		}
+	}
+	return 0;
+}
+
+static int jpc_dec_cp_setfromcod(jpc_dec_cp_t *cp, jpc_cod_t *cod)
+{
+	jpc_dec_ccp_t *ccp;
+	int compno;
+	cp->flags |= JPC_CSET;
+	cp->prgord = cod->prg;
+	if (cod->mctrans) {
+		cp->mctid = (cod->compparms.qmfbid == JPC_COX_INS) ? (JPC_MCT_ICT) : (JPC_MCT_RCT);
+	} else {
+		cp->mctid = JPC_MCT_NONE;
+	}
+	cp->numlyrs = cod->numlyrs;
+	cp->csty = cod->csty & (JPC_COD_SOP | JPC_COD_EPH);
+	for (compno = 0, ccp = cp->ccps; compno < cp->numcomps;
+	  ++compno, ++ccp) {
+		jpc_dec_cp_setfromcox(cp, ccp, &cod->compparms, 0);
+	}
+	cp->flags |= JPC_CSET;
+	return 0;
+}
+
+static int jpc_dec_cp_setfromcoc(jpc_dec_cp_t *cp, jpc_coc_t *coc)
+{
+	jpc_dec_cp_setfromcox(cp, &cp->ccps[coc->compno], &coc->compparms, JPC_COC);
+	return 0;
+}
+
+static int jpc_dec_cp_setfromcox(jpc_dec_cp_t *cp, jpc_dec_ccp_t *ccp,
+  jpc_coxcp_t *compparms, int flags)
+{
+	int rlvlno;
+	if ((flags & JPC_COC) || !(ccp->flags & JPC_COC)) {
+		ccp->numrlvls = compparms->numdlvls + 1;
+		ccp->cblkwidthexpn = JPC_COX_GETCBLKSIZEEXPN(
+		  compparms->cblkwidthval);
+		ccp->cblkheightexpn = JPC_COX_GETCBLKSIZEEXPN(
+		  compparms->cblkheightval);
+		ccp->qmfbid = compparms->qmfbid;
+		ccp->cblkctx = compparms->cblksty;
+		ccp->csty = compparms->csty & JPC_COX_PRT;
+		for (rlvlno = 0; rlvlno < compparms->numrlvls; ++rlvlno) {
+			ccp->prcwidthexpns[rlvlno] =
+			  compparms->rlvls[rlvlno].parwidthval;
+			ccp->prcheightexpns[rlvlno] =
+			  compparms->rlvls[rlvlno].parheightval;
+		}
+		ccp->flags |= flags | JPC_CSET;
+	}
+	return 0;
+}
+
+static int jpc_dec_cp_setfromqcd(jpc_dec_cp_t *cp, jpc_qcd_t *qcd)
+{
+	int compno;
+	jpc_dec_ccp_t *ccp;
+	for (compno = 0, ccp = cp->ccps; compno < cp->numcomps;
+	  ++compno, ++ccp) {
+		jpc_dec_cp_setfromqcx(cp, ccp, &qcd->compparms, 0);
+	}
+	cp->flags |= JPC_QSET;
+	return 0;
+}
+
+static int jpc_dec_cp_setfromqcc(jpc_dec_cp_t *cp, jpc_qcc_t *qcc)
+{
+	return jpc_dec_cp_setfromqcx(cp, &cp->ccps[qcc->compno], &qcc->compparms, JPC_QCC);
+}
+
+static int jpc_dec_cp_setfromqcx(jpc_dec_cp_t *cp, jpc_dec_ccp_t *ccp,
+  jpc_qcxcp_t *compparms, int flags)
+{
+	int bandno;
+	if ((flags & JPC_QCC) || !(ccp->flags & JPC_QCC)) {
+		ccp->flags |= flags | JPC_QSET;
+		for (bandno = 0; bandno < compparms->numstepsizes; ++bandno) {
+			ccp->stepsizes[bandno] = compparms->stepsizes[bandno];
+		}
+		ccp->numstepsizes = compparms->numstepsizes;
+		ccp->numguardbits = compparms->numguard;
+		ccp->qsty = compparms->qntsty;
+	}
+	return 0;
+}
+
+static int jpc_dec_cp_setfromrgn(jpc_dec_cp_t *cp, jpc_rgn_t *rgn)
+{
+	jpc_dec_ccp_t *ccp;
+	ccp = &cp->ccps[rgn->compno];
+	ccp->roishift = rgn->roishift;
+	return 0;
+}
+
+static int jpc_pi_addpchgfrompoc(jpc_pi_t *pi, jpc_poc_t *poc)
+{
+	int pchgno;
+	jpc_pchg_t *pchg;
+	for (pchgno = 0; pchgno < poc->numpchgs; ++pchgno) {
+		if (!(pchg = jpc_pchg_copy(&poc->pchgs[pchgno]))) {
+			return -1;
+		}
+		if (jpc_pchglist_insert(pi->pchglist, -1, pchg)) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int jpc_dec_cp_setfrompoc(jpc_dec_cp_t *cp, jpc_poc_t *poc, int reset)
+{
+	int pchgno;
+	jpc_pchg_t *pchg;
+	if (reset) {
+		while (jpc_pchglist_numpchgs(cp->pchglist) > 0) {
+			pchg = jpc_pchglist_remove(cp->pchglist, 0);
+			jpc_pchg_destroy(pchg);
+		}
+	}
+	for (pchgno = 0; pchgno < poc->numpchgs; ++pchgno) {
+		if (!(pchg = jpc_pchg_copy(&poc->pchgs[pchgno]))) {
+			return -1;
+		}
+		if (jpc_pchglist_insert(cp->pchglist, -1, pchg)) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static jpc_fix_t jpc_calcabsstepsize(int stepsize, int numbits)
+{
+	jpc_fix_t absstepsize;
+	int n;
+
+	absstepsize = jpc_inttofix(1);
+	n = JPC_FIX_FRACBITS - 11;
+	absstepsize |= (n >= 0) ? (JPC_QCX_GETMANT(stepsize) << n) :
+	  (JPC_QCX_GETMANT(stepsize) >> (-n));
+	n = numbits - JPC_QCX_GETEXPN(stepsize);
+	absstepsize = (n >= 0) ? (absstepsize << n) : (absstepsize >> (-n));
+	return absstepsize;
+}
+
+static void jpc_dequantize(jas_matrix_t *x, jpc_fix_t absstepsize)
+{
+	int i;
+	int j;
+	int t;
+
+	assert(absstepsize >= 0);
+	if (absstepsize == jpc_inttofix(1)) {
+		return;
+	}
+
+	for (i = 0; i < jas_matrix_numrows(x); ++i) {
+		for (j = 0; j < jas_matrix_numcols(x); ++j) {
+			t = jas_matrix_get(x, i, j);
+			if (t) {
+				t = jpc_fix_mul(t, absstepsize);
+			} else {
+				t = 0;
+			}
+			jas_matrix_set(x, i, j, t);
+		}
+	}
+
+}
+
+static void jpc_undo_roi(jas_matrix_t *x, int roishift, int bgshift, int numbps)
+{
+	int i;
+	int j;
+	int thresh;
+	jpc_fix_t val;
+	jpc_fix_t mag;
+	bool warn;
+	uint_fast32_t mask;
+
+	if (roishift == 0 && bgshift == 0) {
+		return;
+	}
+	thresh = 1 << roishift;
+
+	warn = false;
+	for (i = 0; i < jas_matrix_numrows(x); ++i) {
+		for (j = 0; j < jas_matrix_numcols(x); ++j) {
+			val = jas_matrix_get(x, i, j);
+			mag = JAS_ABS(val);
+			if (mag >= thresh) {
+				/* We are dealing with ROI data. */
+				mag >>= roishift;
+				val = (val < 0) ? (-mag) : mag;
+				jas_matrix_set(x, i, j, val);
+			} else {
+				/* We are dealing with non-ROI (i.e., background) data. */
+				mag <<= bgshift;
+				mask = (1 << numbps) - 1;
+				/* Perform a basic sanity check on the sample value. */
+				/* Some implementations write garbage in the unused
+				  most-significant bit planes introduced by ROI shifting.
+				  Here we ensure that any such bits are masked off. */
+				if (mag & (~mask)) {
+					if (!warn) {
+						fprintf(stderr,
+						  "warning: possibly corrupt code stream\n");
+						warn = true;
+					}
+					mag &= mask;
+				}
+				val = (val < 0) ? (-mag) : mag;
+				jas_matrix_set(x, i, j, val);
+			}
+		}
+	}
+}
+
+static jpc_dec_t *jpc_dec_create(jpc_dec_importopts_t *impopts, jas_stream_t *in)
+{
+	jpc_dec_t *dec;
+
+	if (!(dec = jas_malloc(sizeof(jpc_dec_t)))) {
+		return 0;
+	}
+
+	dec->image = 0;
+	dec->xstart = 0;
+	dec->ystart = 0;
+	dec->xend = 0;
+	dec->yend = 0;
+	dec->tilewidth = 0;
+	dec->tileheight = 0;
+	dec->tilexoff = 0;
+	dec->tileyoff = 0;
+	dec->numhtiles = 0;
+	dec->numvtiles = 0;
+	dec->numtiles = 0;
+	dec->tiles = 0;
+	dec->curtile = 0;
+	dec->numcomps = 0;
+	dec->in = in;
+	dec->cp = 0;
+	dec->maxlyrs = impopts->maxlyrs;
+	dec->maxpkts = impopts->maxpkts;
+dec->numpkts = 0;
+	dec->ppmseqno = 0;
+	dec->state = 0;
+	dec->cmpts = 0;
+	dec->pkthdrstreams = 0;
+	dec->ppmstab = 0;
+	dec->curtileendoff = 0;
+
+	return dec;
+}
+
+static void jpc_dec_destroy(jpc_dec_t *dec)
+{
+	if (dec->cstate) {
+		jpc_cstate_destroy(dec->cstate);
+	}
+	if (dec->pkthdrstreams) {
+		jpc_streamlist_destroy(dec->pkthdrstreams);
+	}
+	if (dec->image) {
+		jas_image_destroy(dec->image);
+	}
+
+	if (dec->cp) {
+		jpc_dec_cp_destroy(dec->cp);
+	}
+
+	if (dec->cmpts) {
+		jas_free(dec->cmpts);
+	}
+
+	if (dec->tiles) {
+		jas_free(dec->tiles);
+	}
+
+	jas_free(dec);
+}
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+void jpc_seglist_insert(jpc_dec_seglist_t *list, jpc_dec_seg_t *ins, jpc_dec_seg_t *node)
+{
+	jpc_dec_seg_t *prev;
+	jpc_dec_seg_t *next;
+
+	prev = ins;
+	node->prev = prev;
+	next = prev ? (prev->next) : 0;
+	node->prev = prev;
+	node->next = next;
+	if (prev) {
+		prev->next = node;
+	} else {
+		list->head = node;
+	}
+	if (next) {
+		next->prev = node;
+	} else {
+		list->tail = node;
+	}
+}
+
+void jpc_seglist_remove(jpc_dec_seglist_t *list, jpc_dec_seg_t *seg)
+{
+	jpc_dec_seg_t *prev;
+	jpc_dec_seg_t *next;
+
+	prev = seg->prev;
+	next = seg->next;
+	if (prev) {
+		prev->next = next;
+	} else {
+		list->head = next;
+	}
+	if (next) {
+		next->prev = prev;
+	} else {
+		list->tail = prev;
+	}
+	seg->prev = 0;
+	seg->next = 0;
+}
+
+jpc_dec_seg_t *jpc_seg_alloc(void)
+{
+	jpc_dec_seg_t *seg;
+
+	if (!(seg = jas_malloc(sizeof(jpc_dec_seg_t)))) {
+		return 0;
+	}
+	seg->prev = 0;
+	seg->next = 0;
+	seg->passno = -1;
+	seg->numpasses = 0;
+	seg->maxpasses = 0;
+	seg->type = JPC_SEG_INVALID;
+	seg->stream = 0;
+	seg->cnt = 0;
+	seg->complete = 0;
+	seg->lyrno = -1;
+	return seg;
+}
+
+void jpc_seg_destroy(jpc_dec_seg_t *seg)
+{
+	if (seg->stream) {
+		jas_stream_close(seg->stream);
+	}
+	jas_free(seg);
+}
+
+static int jpc_dec_dump(jpc_dec_t *dec, FILE *out)
+{
+	jpc_dec_tile_t *tile;
+	int tileno;
+	jpc_dec_tcomp_t *tcomp;
+	uint_fast16_t compno;
+	jpc_dec_rlvl_t *rlvl;
+	int rlvlno;
+	jpc_dec_band_t *band;
+	int bandno;
+	jpc_dec_prc_t *prc;
+	int prcno;
+	jpc_dec_cblk_t *cblk;
+	int cblkno;
+
+	for (tileno = 0, tile = dec->tiles; tileno < dec->numtiles;
+	  ++tileno, ++tile) {
+		for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
+		  ++compno, ++tcomp) {
+			for (rlvlno = 0, rlvl = tcomp->rlvls; rlvlno <
+			  tcomp->numrlvls; ++rlvlno, ++rlvl) {
+fprintf(out, "RESOLUTION LEVEL %d\n", rlvlno);
+fprintf(out, "xs =%d, ys = %d, xe = %d, ye = %d, w = %d, h = %d\n",
+  rlvl->xstart, rlvl->ystart, rlvl->xend, rlvl->yend, rlvl->xend -
+  rlvl->xstart, rlvl->yend - rlvl->ystart);
+				for (bandno = 0, band = rlvl->bands;
+				  bandno < rlvl->numbands; ++bandno, ++band) {
+fprintf(out, "BAND %d\n", bandno);
+fprintf(out, "xs =%d, ys = %d, xe = %d, ye = %d, w = %d, h = %d\n",
+  jas_seq2d_xstart(band->data), jas_seq2d_ystart(band->data), jas_seq2d_xend(band->data),
+  jas_seq2d_yend(band->data), jas_seq2d_xend(band->data) - jas_seq2d_xstart(band->data),
+  jas_seq2d_yend(band->data) - jas_seq2d_ystart(band->data));
+					for (prcno = 0, prc = band->prcs;
+					  prcno < rlvl->numprcs; ++prcno,
+					  ++prc) {
+fprintf(out, "CODE BLOCK GROUP %d\n", prcno);
+fprintf(out, "xs =%d, ys = %d, xe = %d, ye = %d, w = %d, h = %d\n",
+  prc->xstart, prc->ystart, prc->xend, prc->yend, prc->xend -
+  prc->xstart, prc->yend - prc->ystart);
+						for (cblkno = 0, cblk =
+						  prc->cblks; cblkno <
+						  prc->numcblks; ++cblkno,
+						  ++cblk) {
+fprintf(out, "CODE BLOCK %d\n", cblkno);
+fprintf(out, "xs =%d, ys = %d, xe = %d, ye = %d, w = %d, h = %d\n",
+  jas_seq2d_xstart(cblk->data), jas_seq2d_ystart(cblk->data), jas_seq2d_xend(cblk->data),
+  jas_seq2d_yend(cblk->data), jas_seq2d_xend(cblk->data) - jas_seq2d_xstart(cblk->data),
+  jas_seq2d_yend(cblk->data) - jas_seq2d_ystart(cblk->data));
+						}
+					}
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+jpc_streamlist_t *jpc_streamlist_create()
+{
+	jpc_streamlist_t *streamlist;
+	int i;
+
+	if (!(streamlist = jas_malloc(sizeof(jpc_streamlist_t)))) {
+		return 0;
+	}
+	streamlist->numstreams = 0;
+	streamlist->maxstreams = 100;
+	if (!(streamlist->streams = jas_malloc(streamlist->maxstreams *
+	  sizeof(jas_stream_t *)))) {
+		jas_free(streamlist);
+		return 0;
+	}
+	for (i = 0; i < streamlist->maxstreams; ++i) {
+		streamlist->streams[i] = 0;
+	}
+	return streamlist;
+}
+
+int jpc_streamlist_insert(jpc_streamlist_t *streamlist, int streamno,
+  jas_stream_t *stream)
+{
+	jas_stream_t **newstreams;
+	int newmaxstreams;
+	int i;
+	/* Grow the array of streams if necessary. */
+	if (streamlist->numstreams >= streamlist->maxstreams) {
+		newmaxstreams = streamlist->maxstreams + 1024;
+		if (!(newstreams = jas_realloc(streamlist->streams,
+		  (newmaxstreams + 1024) * sizeof(jas_stream_t *)))) {
+			return -1;
+		}
+		for (i = streamlist->numstreams; i < streamlist->maxstreams; ++i) {
+			streamlist->streams[i] = 0;
+		}
+		streamlist->maxstreams = newmaxstreams;
+		streamlist->streams = newstreams;
+	}
+	if (streamno != streamlist->numstreams) {
+		/* Can only handle insertion at start of list. */
+		return -1;
+	}
+	streamlist->streams[streamno] = stream;
+	++streamlist->numstreams;
+	return 0;
+}
+
+jas_stream_t *jpc_streamlist_remove(jpc_streamlist_t *streamlist, int streamno)
+{
+	jas_stream_t *stream;
+	int i;
+	if (streamno >= streamlist->numstreams) {
+		abort();
+	}
+	stream = streamlist->streams[streamno];
+	for (i = streamno + 1; i < streamlist->numstreams; ++i) {
+		streamlist->streams[i - 1] = streamlist->streams[i];
+	}
+	--streamlist->numstreams;
+	return stream;
+}
+
+void jpc_streamlist_destroy(jpc_streamlist_t *streamlist)
+{
+	int streamno;
+	if (streamlist->streams) {
+		for (streamno = 0; streamno < streamlist->numstreams;
+		  ++streamno) {
+			jas_stream_close(streamlist->streams[streamno]);
+		}
+		jas_free(streamlist->streams);
+	}
+	jas_free(streamlist);
+}
+
+jas_stream_t *jpc_streamlist_get(jpc_streamlist_t *streamlist, int streamno)
+{
+	assert(streamno < streamlist->numstreams);
+	return streamlist->streams[streamno];
+}
+
+int jpc_streamlist_numstreams(jpc_streamlist_t *streamlist)
+{
+	return streamlist->numstreams;
+}
+
+jpc_ppxstab_t *jpc_ppxstab_create()
+{
+	jpc_ppxstab_t *tab;
+
+	if (!(tab = jas_malloc(sizeof(jpc_ppxstab_t)))) {
+		return 0;
+	}
+	tab->numents = 0;
+	tab->maxents = 0;
+	tab->ents = 0;
+	return tab;
+}
+
+void jpc_ppxstab_destroy(jpc_ppxstab_t *tab)
+{
+	int i;
+	for (i = 0; i < tab->numents; ++i) {
+		jpc_ppxstabent_destroy(tab->ents[i]);
+	}
+	if (tab->ents) {
+		jas_free(tab->ents);
+	}
+	jas_free(tab);
+}
+
+int jpc_ppxstab_grow(jpc_ppxstab_t *tab, int maxents)
+{
+	jpc_ppxstabent_t **newents;
+	if (tab->maxents < maxents) {
+		newents = (tab->ents) ? jas_realloc(tab->ents, maxents *
+		  sizeof(jpc_ppxstabent_t *)) : jas_malloc(maxents * sizeof(jpc_ppxstabent_t *));
+		if (!newents) {
+			return -1;
+		}
+		tab->ents = newents;
+		tab->maxents = maxents;
+	}
+	return 0;
+}
+
+int jpc_ppxstab_insert(jpc_ppxstab_t *tab, jpc_ppxstabent_t *ent)
+{
+	int inspt;
+	int i;
+
+	for (i = 0; i < tab->numents; ++i) {
+		if (tab->ents[i]->ind > ent->ind) {
+			break;
+		}
+	}
+	inspt = i;
+
+	if (tab->numents >= tab->maxents) {
+		if (jpc_ppxstab_grow(tab, tab->maxents + 128)) {
+			return -1;
+		}
+	}
+
+	for (i = tab->numents; i > inspt; --i) {
+		tab->ents[i] = tab->ents[i - 1];
+	}
+	tab->ents[i] = ent;
+	++tab->numents;
+
+	return 0;
+}
+
+jpc_streamlist_t *jpc_ppmstabtostreams(jpc_ppxstab_t *tab)
+{
+	jpc_streamlist_t *streams;
+	unsigned char *dataptr;
+	uint_fast32_t datacnt;
+	uint_fast32_t tpcnt;
+	jpc_ppxstabent_t *ent;
+	int entno;
+	jas_stream_t *stream;
+	int n;
+
+	if (!(streams = jpc_streamlist_create())) {
+		goto error;
+	}
+
+	if (!tab->numents) {
+		return streams;
+	}
+
+	entno = 0;
+	ent = tab->ents[entno];
+	dataptr = ent->data;
+	datacnt = ent->len;
+	for (;;) {
+
+		/* Get the length of the packet header data for the current
+		  tile-part. */
+		if (datacnt < 4) {
+			goto error;
+		}
+		if (!(stream = jas_stream_memopen(0, 0))) {
+			goto error;
+		}
+		if (jpc_streamlist_insert(streams, jpc_streamlist_numstreams(streams),
+		  stream)) {
+			goto error;
+		}
+		tpcnt = (dataptr[0] << 24) | (dataptr[1] << 16) | (dataptr[2] << 8)
+		  | dataptr[3];
+		datacnt -= 4;
+		dataptr += 4;
+
+		/* Get the packet header data for the current tile-part. */
+		while (tpcnt) {
+			if (!datacnt) {
+				if (++entno >= tab->numents) {
+					goto error;
+				}
+				ent = tab->ents[entno];
+				dataptr = ent->data;
+				datacnt = ent->len;
+			}
+			n = JAS_MIN(tpcnt, datacnt);
+			if (jas_stream_write(stream, dataptr, n) != n) {
+				goto error;
+			}
+			tpcnt -= n;
+			dataptr += n;
+			datacnt -= n;
+		}
+		jas_stream_rewind(stream);
+		if (!datacnt) {
+			if (++entno >= tab->numents) {
+				break;
+			}
+			ent = tab->ents[entno];
+			dataptr = ent->data;
+			datacnt = ent->len;
+		}
+	}
+
+	return streams;
+
+error:
+	jpc_streamlist_destroy(streams);
+	return 0;
+}
+
+int jpc_pptstabwrite(jas_stream_t *out, jpc_ppxstab_t *tab)
+{
+	int i;
+	jpc_ppxstabent_t *ent;
+	for (i = 0; i < tab->numents; ++i) {
+		ent = tab->ents[i];
+		if (jas_stream_write(out, ent->data, ent->len) != ent->len) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+jpc_ppxstabent_t *jpc_ppxstabent_create()
+{
+	jpc_ppxstabent_t *ent;
+	if (!(ent = jas_malloc(sizeof(jpc_ppxstabent_t)))) {
+		return 0;
+	}
+	ent->data = 0;
+	ent->len = 0;
+	ent->ind = 0;
+	return ent;
+}
+
+void jpc_ppxstabent_destroy(jpc_ppxstabent_t *ent)
+{
+	if (ent->data) {
+		jas_free(ent->data);
+	}
+	jas_free(ent);
+}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_dec.h b/converter/other/jpeg2000/libjasper/jpc/jpc_dec.h
new file mode 100644
index 00000000..5231048d
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_dec.h
@@ -0,0 +1,745 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * JPEG-2000 Decoder
+ *
+ * $Id$
+ */
+
+#ifndef JPC_DEC_H
+#define JPC_DEC_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jasper/jas_stream.h"
+
+#include "jpc_tsfb.h"
+#include "jpc_bs.h"
+#include "jpc_tagtree.h"
+#include "jpc_cs.h"
+#include "jpc_cod.h"
+#include "jpc_mqdec.h"
+#include "jpc_t2cod.h"
+
+/******************************************************************************\
+* Below are some ugly warts necessary to support packed packet headers.
+\******************************************************************************/
+
+/* PPM/PPT marker segment table entry. */
+
+typedef struct {
+
+	/* The index for this entry. */
+	uint_fast16_t ind;
+
+	/* The data length. */
+	uint_fast32_t len;
+
+	/* The data. */
+	unsigned char *data;
+
+} jpc_ppxstabent_t;
+
+/* PPM/PPT marker segment table. */
+
+typedef struct {
+
+	/* The number of entries. */
+	int numents;
+
+	/* The maximum number of entries (i.e., the allocated size of the array
+	  below). */
+	int maxents;
+
+	/* The table entries. */
+	jpc_ppxstabent_t **ents;
+
+} jpc_ppxstab_t;
+
+/* Stream list class. */
+
+typedef struct {
+
+	/* The number of streams in this list. */
+	int numstreams;
+
+	/* The maximum number of streams that can be accomodated without
+	  growing the streams array. */
+	int maxstreams;
+
+	/* The streams. */
+	jas_stream_t **streams;
+
+} jpc_streamlist_t;
+
+/******************************************************************************\
+* Coding parameters class.
+\******************************************************************************/
+
+/* Per-component coding parameters. */
+
+typedef struct {
+
+	/* How were various coding parameters set? */
+	int flags;
+
+	/* Per-component coding style parameters (e.g., explicit precinct sizes) */
+	uint_fast8_t csty;
+
+	/* The number of resolution levels. */
+	uint_fast8_t numrlvls;
+
+	/* The code block width exponent. */
+	uint_fast8_t cblkwidthexpn;
+
+	/* The code block height exponent. */
+	uint_fast8_t cblkheightexpn;
+
+	/* The QMFB ID. */
+	uint_fast8_t qmfbid;
+
+	/* The quantization style. */
+	uint_fast8_t qsty;
+
+	/* The number of quantizer step sizes. */
+	uint_fast16_t numstepsizes;
+
+	/* The step sizes. */
+	uint_fast16_t stepsizes[3 * JPC_MAXRLVLS + 1];
+
+	/* The number of guard bits. */
+	uint_fast8_t numguardbits;
+
+	/* The ROI shift value. */
+	uint_fast8_t roishift;
+
+	/* The code block parameters. */
+	uint_fast8_t cblkctx;
+
+	/* The precinct width exponents. */
+	uint_fast8_t prcwidthexpns[JPC_MAXRLVLS];
+
+	/* The precinct height exponents. */
+	uint_fast8_t prcheightexpns[JPC_MAXRLVLS];
+
+} jpc_dec_ccp_t;
+
+/* Coding paramters. */
+
+typedef struct {
+
+	/* How were these coding parameters set? */
+	int flags;
+
+	/* Progression change list. */
+	jpc_pchglist_t *pchglist;
+
+	/* Progression order. */
+	uint_fast8_t prgord;
+
+	/* The number of layers. */
+	uint_fast16_t numlyrs;
+
+	/* The MCT ID. */
+	uint_fast8_t mctid;
+
+	/* The coding style parameters (e.g., SOP, EPH). */
+	uint_fast8_t csty;
+
+	/* The number of components. */
+	uint_fast16_t numcomps;
+
+	/* The per-component coding parameters. */
+	jpc_dec_ccp_t *ccps;
+
+} jpc_dec_cp_t;
+
+/******************************************************************************\
+* Decoder class.
+\******************************************************************************/
+
+/* Decoder per-segment state information. */
+
+typedef struct jpc_dec_seg_s {
+
+	/* The next segment in the list. */
+	struct jpc_dec_seg_s *next;
+
+	/* The previous segment in the list. */
+	struct jpc_dec_seg_s *prev;
+
+	/* The starting pass number for this segment. */
+	int passno;
+
+	/* The number of passes in this segment. */
+	int numpasses;
+
+	/* The maximum number of passes in this segment. */
+	int maxpasses;
+
+	/* The type of data in this segment (i.e., MQ or raw). */
+	int type;
+
+	/* A stream containing the data for this segment. */
+	jas_stream_t *stream;
+
+	/* The number of bytes destined for this segment from the packet
+	  currently being decoded. */
+	int cnt;
+
+	/* A flag indicating if this segment has been terminated. */
+	int complete;
+
+	/* The layer number to which this segment belongs. */
+	/* If the segment spans multiple layers, then the largest layer number
+	  spanned by the segment is used. */
+	int lyrno;
+
+} jpc_dec_seg_t;
+
+/* Decoder segment list. */
+
+typedef struct {
+
+	/* The first entry in the list. */
+	jpc_dec_seg_t *head;
+
+	/* The last entry in the list. */
+	jpc_dec_seg_t *tail;
+
+} jpc_dec_seglist_t;
+
+/* Decoder per-code-block state information. */
+
+typedef struct {
+
+	/* The number of passes. */
+	int numpasses;
+
+	/* A list of segments that still need to be decoded. */
+	jpc_dec_seglist_t segs;
+
+	/* The first incomplete/partial segment. */
+	jpc_dec_seg_t *curseg;
+
+	/* The number of leading insignificant bit planes for this code block. */
+	int numimsbs;
+
+	/* The number of bits used to encode pass data lengths. */
+	int numlenbits;
+
+	/* The first pass number containing data for this code block. */
+	int firstpassno;
+
+	/* The MQ decoder. */
+	jpc_mqdec_t *mqdec;
+
+	/* The raw bit stream decoder. */
+	jpc_bitstream_t *nulldec;
+
+	/* The per-sample state information for this code block. */
+	jas_matrix_t *flags;
+
+	/* The sample data associated with this code block. */
+	jas_matrix_t *data;
+
+} jpc_dec_cblk_t;
+
+/* Decoder per-code-block-group state information. */
+
+typedef struct {
+
+	/* The x-coordinate of the top-left corner of the precinct. */
+	uint_fast32_t xstart;
+
+	/* The y-coordinate of the top-left corner of the precinct. */
+	uint_fast32_t ystart;
+
+	/* The x-coordinate of the bottom-right corner of the precinct
+	  (plus one). */
+	uint_fast32_t xend;
+
+	/* The y-coordinate of the bottom-right corner of the precinct
+	  (plus one). */
+	uint_fast32_t yend;
+
+	/* The number of code blocks spanning this precinct in the horizontal
+	  direction. */
+	int numhcblks;
+
+	/* The number of code blocks spanning this precinct in the vertical
+	  direction. */
+	int numvcblks;
+
+	/* The total number of code blocks in this precinct. */
+	int numcblks;
+
+	/* The per code block information. */
+	jpc_dec_cblk_t *cblks;
+
+	/* The inclusion tag tree. */
+	jpc_tagtree_t *incltagtree;
+
+	/* The insignificant MSBs tag tree. */
+	jpc_tagtree_t *numimsbstagtree;
+
+} jpc_dec_prc_t;
+
+/* Decoder per-band state information. */
+
+typedef struct {
+
+	/* The per-code-block-group state information. */
+	jpc_dec_prc_t *prcs;
+
+	/* The sample data associated with this band. */
+	jas_matrix_t *data;
+
+	/* The orientation of this band (i.e., LL, LH, HL, or HH). */
+	int orient;
+
+	/* The encoded quantizer step size. */
+	int stepsize;
+
+	/* The absolute quantizer step size. */
+	jpc_fix_t absstepsize;
+
+	/* The number of bit planes for this band. */
+	int numbps;
+
+	/* The analysis gain associated with this band. */
+	int analgain;
+
+	/* The ROI shift value for this band. */
+	int roishift;
+
+} jpc_dec_band_t;
+
+/* Decoder per-resolution-level state information. */
+
+typedef struct {
+
+	/* The number of bands associated with this resolution level. */
+	int numbands;
+
+	/* The per-band information. */
+	jpc_dec_band_t *bands;
+
+	/* The x-coordinate of the top-left corner of the tile-component
+	  at this resolution. */
+	uint_fast32_t xstart;
+
+	/* The y-coordinate of the top-left corner of the tile-component
+	  at this resolution. */
+	uint_fast32_t ystart;
+
+	/* The x-coordinate of the bottom-right corner of the tile-component
+	  at this resolution (plus one). */
+	uint_fast32_t xend;
+
+	/* The y-coordinate of the bottom-right corner of the tile-component
+	  at this resolution (plus one). */
+	uint_fast32_t yend;
+
+	/* The exponent value for the nominal precinct width measured
+	  relative to the associated LL band. */
+	int prcwidthexpn;
+
+	/* The exponent value for the nominal precinct height measured
+	  relative to the associated LL band. */
+	int prcheightexpn;
+
+	/* The number of precincts in the horizontal direction. */
+	int numhprcs;
+
+	/* The number of precincts in the vertical direction. */
+	int numvprcs;
+
+	/* The total number of precincts. */
+	int numprcs;
+
+	/* The exponent value for the nominal code block group width.
+	  This quantity is associated with the next lower resolution level
+	  (assuming that there is one). */
+	int cbgwidthexpn;
+
+	/* The exponent value for the nominal code block group height
+	  This quantity is associated with the next lower resolution level
+	  (assuming that there is one). */
+	int cbgheightexpn;
+
+	/* The exponent value for the code block width. */
+	uint_fast16_t cblkwidthexpn;
+
+	/* The exponent value for the code block height. */
+	uint_fast16_t cblkheightexpn;
+
+} jpc_dec_rlvl_t;
+
+/* Decoder per-tile-component state information. */
+
+typedef struct {
+
+	/* The x-coordinate of the top-left corner of the tile-component
+	  in the coordinate system of the tile-component. */
+	uint_fast32_t xstart;
+
+	/* The y-coordinate of the top-left corner of the tile-component
+	  in the coordinate system of the tile-component. */
+	uint_fast32_t ystart;
+
+	/* The x-coordinate of the bottom-right corner of the tile-component
+	  in the coordinate system of the tile-component (plus one). */
+	uint_fast32_t xend;
+
+	/* The y-coordinate of the bottom-right corner of the tile-component
+	  in the coordinate system of the tile-component (plus one). */
+	uint_fast32_t yend;
+
+	/* The component data for the current tile. */
+	jas_matrix_t *data;
+
+	/* The number of resolution levels. */
+	uint_fast16_t numrlvls;
+
+	/* The per resolution level information. */
+	jpc_dec_rlvl_t *rlvls;
+
+	/* The TSFB. */
+	jpc_tsfb_t *tsfb;
+
+} jpc_dec_tcomp_t;
+
+/*
+ * Tile states.
+ */
+
+#define	JPC_TILE_INIT	0
+#define	JPC_TILE_ACTIVE	1
+#define	JPC_TILE_ACTIVELAST	2
+#define	JPC_TILE_DONE	3
+
+/* Decoder per-tile state information. */
+
+typedef struct {
+
+	/* The processing state for this tile. */
+	int state;
+
+	/* The x-coordinate of the top-left corner of the tile on the reference
+	  grid. */
+	uint_fast32_t xstart;
+
+	/* The y-coordinate of the top-left corner of the tile on the reference
+	  grid. */
+	uint_fast32_t ystart;
+
+	/* The x-coordinate of the bottom-right corner of the tile on the
+	  reference grid (plus one). */
+	uint_fast32_t xend;
+
+	/* The y-coordinate of the bottom-right corner of the tile on the
+	  reference grid (plus one). */
+	uint_fast32_t yend;
+
+	/* The packed packet header data for this tile. */
+	jpc_ppxstab_t *pptstab;
+
+	/* A stream containing the packed packet header data for this tile. */
+	jas_stream_t *pkthdrstream;
+
+	/* The current position within the packed packet header stream. */
+	long pkthdrstreampos;
+
+	/* The coding parameters for this tile. */
+	jpc_dec_cp_t *cp;
+
+	/* The per tile-component information. */
+	jpc_dec_tcomp_t *tcomps;
+
+	/* The next expected tile-part number. */
+	int partno;
+
+	/* The number of tile-parts. */
+	int numparts;
+
+	/* The coding mode. */
+	int realmode;
+
+	/* The packet iterator for this tile. */
+	jpc_pi_t *pi;
+
+} jpc_dec_tile_t;
+
+/* Decoder per-component state information. */
+
+typedef struct {
+
+	/* The horizontal sampling period. */
+	uint_fast32_t hstep;
+
+	/* The vertical sampling period. */
+	uint_fast32_t vstep;
+
+	/* The number of samples in the horizontal direction. */
+	uint_fast32_t width;
+
+	/* The number of samples in the vertical direction. */
+	uint_fast32_t height;
+
+	/* The precision of the sample data. */
+	uint_fast16_t prec;
+
+	/* The signedness of the sample data. */
+	bool sgnd;
+
+	/* The sample alignment horizontal offset. */
+	uint_fast32_t hsubstep;
+	
+	/* The sample alignment vertical offset. */
+	uint_fast32_t vsubstep;
+
+} jpc_dec_cmpt_t;
+
+/* Decoder state information. */
+
+typedef struct {
+
+	/* The decoded image. */
+	jas_image_t *image;
+
+	/* The x-coordinate of the top-left corner of the image area on
+	  the reference grid. */
+	uint_fast32_t xstart;
+
+	/* The y-coordinate of the top-left corner of the image area on
+	  the reference grid. */
+	uint_fast32_t ystart;
+
+	/* The x-coordinate of the bottom-right corner of the image area on
+	  the reference grid (plus one). */
+	uint_fast32_t xend;
+
+	/* The y-coordinate of the bottom-right corner of the image area on
+	  the reference grid (plus one). */
+	uint_fast32_t yend;
+
+	/* The nominal tile width in units of the image reference grid. */
+	uint_fast32_t tilewidth;
+
+	/* The nominal tile height in units of the image reference grid. */
+	uint_fast32_t tileheight;
+
+	/* The horizontal offset from the origin of the reference grid to the
+	  left side of the first tile. */
+	uint_fast32_t tilexoff;
+
+	/* The vertical offset from the origin of the reference grid to the
+	  top side of the first tile. */
+	uint_fast32_t tileyoff;
+
+	/* The number of tiles spanning the image area in the vertical
+	  direction. */
+	int numhtiles;
+
+	/* The number of tiles spanning the image area in the horizontal
+	  direction. */
+	int numvtiles;
+
+	/* The total number of tiles. */
+	int numtiles;
+
+	/* The per-tile information. */
+	jpc_dec_tile_t *tiles;
+
+	/* The tile currently being processed. */
+	jpc_dec_tile_t *curtile;
+
+	/* The number of components. */
+	int numcomps;
+
+	/* The stream containing the input JPEG-2000 code stream data. */
+	jas_stream_t *in;
+
+	/* The default coding parameters for all tiles. */
+	jpc_dec_cp_t *cp;
+
+	/* The maximum number of layers that may be decoded. */
+	int maxlyrs;
+
+	/* The maximum number of packets that may be decoded. */
+	int maxpkts;
+
+	/* The number of packets decoded so far in the processing of the entire
+	  code stream. */
+	int numpkts;
+
+	/* The next expected PPM marker segment sequence number. */
+	int ppmseqno;
+
+	/* The current state for code stream processing. */
+	int state;
+
+	/* The per-component information. */
+	jpc_dec_cmpt_t *cmpts;
+
+	/* The information from PPM marker segments. */
+	jpc_ppxstab_t *ppmstab;
+
+	/* A list of streams containing packet header data from PPM marker
+	  segments. */
+	jpc_streamlist_t *pkthdrstreams;
+
+	/* The expected ending offset for a tile-part. */
+	long curtileendoff;
+
+	/* This is required by the tier-2 decoder. */
+	jpc_cstate_t *cstate;
+
+} jpc_dec_t;
+
+/* Decoder options. */
+
+typedef struct {
+
+	/* The debug level for the decoder. */
+	int debug;
+
+	/* The maximum number of layers to decode. */
+	int maxlyrs;
+
+	/* The maximum number of packets to decode. */
+	int maxpkts;
+
+} jpc_dec_importopts_t;
+
+/******************************************************************************\
+* Functions.
+\******************************************************************************/
+
+/* Create a decoder segment object. */
+jpc_dec_seg_t *jpc_seg_alloc(void);
+
+/* Destroy a decoder segment object. */
+void jpc_seg_destroy(jpc_dec_seg_t *seg);
+
+/* Remove a segment from a segment list. */
+void jpc_seglist_remove(jpc_dec_seglist_t *list, jpc_dec_seg_t *node);
+
+/* Insert a segment into a segment list. */
+void jpc_seglist_insert(jpc_dec_seglist_t *list, jpc_dec_seg_t *ins,
+  jpc_dec_seg_t *node);
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c b/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c
new file mode 100644
index 00000000..3284dfeb
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c
@@ -0,0 +1,2624 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <math.h>
+#include <float.h>
+
+#include "jasper/jas_string.h"
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_image.h"
+#include "jasper/jas_fix.h"
+#include "jasper/jas_tvp.h"
+#include "jasper/jas_version.h"
+#include "jasper/jas_math.h"
+#include "jasper/jas_debug.h"
+
+#include "jpc_flt.h"
+#include "jpc_fix.h"
+#include "jpc_tagtree.h"
+#include "jpc_enc.h"
+#include "jpc_cs.h"
+#include "jpc_mct.h"
+#include "jpc_tsfb.h"
+#include "jpc_qmfb.h"
+#include "jpc_t1enc.h"
+#include "jpc_t2enc.h"
+#include "jpc_cod.h"
+#include "jpc_math.h"
+#include "jpc_util.h"
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+#define JPC_POW2(n) \
+	(1 << (n))
+
+#define JPC_FLOORTOMULTPOW2(x, n) \
+  (((n) > 0) ? ((x) & (~((1 << n) - 1))) : (x))
+/* Round to the nearest multiple of the specified power of two in the
+  direction of negative infinity. */
+
+#define	JPC_CEILTOMULTPOW2(x, n) \
+  (((n) > 0) ? JPC_FLOORTOMULTPOW2(((x) + (1 << (n)) - 1), n) : (x))
+/* Round to the nearest multiple of the specified power of two in the
+  direction of positive infinity. */
+
+#define	JPC_POW2(n)	\
+  (1 << (n))
+
+jpc_enc_tile_t *jpc_enc_tile_create(jpc_enc_cp_t *cp, jas_image_t *image, int tileno);
+void jpc_enc_tile_destroy(jpc_enc_tile_t *tile);
+
+static jpc_enc_tcmpt_t *tcmpt_create(jpc_enc_tcmpt_t *tcmpt, jpc_enc_cp_t *cp,
+  jas_image_t *image, jpc_enc_tile_t *tile);
+static void tcmpt_destroy(jpc_enc_tcmpt_t *tcmpt);
+static jpc_enc_rlvl_t *rlvl_create(jpc_enc_rlvl_t *rlvl, jpc_enc_cp_t *cp,
+  jpc_enc_tcmpt_t *tcmpt, jpc_tsfb_band_t *bandinfos);
+static void rlvl_destroy(jpc_enc_rlvl_t *rlvl);
+static jpc_enc_band_t *band_create(jpc_enc_band_t *band, jpc_enc_cp_t *cp,
+  jpc_enc_rlvl_t *rlvl, jpc_tsfb_band_t *bandinfos);
+static void band_destroy(jpc_enc_band_t *bands);
+static jpc_enc_prc_t *prc_create(jpc_enc_prc_t *prc, jpc_enc_cp_t *cp,
+  jpc_enc_band_t *band);
+static void prc_destroy(jpc_enc_prc_t *prcs);
+static jpc_enc_cblk_t *cblk_create(jpc_enc_cblk_t *cblk, jpc_enc_cp_t *cp,
+  jpc_enc_prc_t *prc);
+static void cblk_destroy(jpc_enc_cblk_t *cblks);
+int ratestrtosize(const char *s, uint_fast32_t rawsize, uint_fast32_t *size);
+static void pass_destroy(jpc_enc_pass_t *pass);
+void jpc_enc_dump(jpc_enc_t *enc);
+
+/******************************************************************************\
+* Local prototypes.
+\******************************************************************************/
+
+void quantize(jas_matrix_t *data, jpc_fix_t stepsize);
+static int jpc_enc_encodemainhdr(jpc_enc_t *enc);
+static int jpc_enc_encodemainbody(jpc_enc_t *enc);
+int jpc_enc_encodetiledata(jpc_enc_t *enc);
+jpc_enc_t *jpc_enc_create(jpc_enc_cp_t *cp, jas_stream_t *out, jas_image_t *image);
+void jpc_enc_destroy(jpc_enc_t *enc);
+static int jpc_enc_encodemainhdr(jpc_enc_t *enc);
+static int jpc_enc_encodemainbody(jpc_enc_t *enc);
+int jpc_enc_encodetiledata(jpc_enc_t *enc);
+int rateallocate(jpc_enc_t *enc, int numlyrs, uint_fast32_t *cumlens);
+int setins(int numvalues, jpc_flt_t *values, jpc_flt_t value);
+static jpc_enc_cp_t *cp_create(char *optstr, jas_image_t *image);
+void jpc_enc_cp_destroy(jpc_enc_cp_t *cp);
+
+static uint_fast32_t jpc_abstorelstepsize(jpc_fix_t absdelta, int scaleexpn)
+{
+	int p;
+	uint_fast32_t mant;
+	uint_fast32_t expn;
+	int n;
+
+	if (absdelta < 0) {
+		abort();
+	}
+
+	p = jpc_firstone(absdelta) - JPC_FIX_FRACBITS;
+	n = 11 - jpc_firstone(absdelta);
+	mant = ((n < 0) ? (absdelta >> (-n)) : (absdelta << n)) & 0x7ff;
+	expn = scaleexpn - p;
+	if (scaleexpn < p) {
+		abort();
+	}
+	return JPC_QCX_EXPN(expn) | JPC_QCX_MANT(mant);
+}
+
+typedef enum {
+	OPT_DEBUG,
+	OPT_IMGAREAOFFX,
+	OPT_IMGAREAOFFY,
+	OPT_TILEGRDOFFX,
+	OPT_TILEGRDOFFY,
+	OPT_TILEWIDTH,
+	OPT_TILEHEIGHT,
+	OPT_PRCWIDTH,
+	OPT_PRCHEIGHT,
+	OPT_CBLKWIDTH,
+	OPT_CBLKHEIGHT,
+	OPT_MODE,
+	OPT_PRG,
+	OPT_NOMCT,
+	OPT_MAXRLVLS,
+	OPT_SOP,
+	OPT_EPH,
+	OPT_LAZY,
+	OPT_TERMALL,
+	OPT_SEGSYM,
+	OPT_VCAUSAL,
+	OPT_RESET,
+	OPT_PTERM,
+	OPT_NUMGBITS,
+	OPT_RATE,
+	OPT_ILYRRATES,
+	OPT_JP2OVERHEAD
+} optid_t;
+
+jas_taginfo_t encopts[] = {
+	{OPT_DEBUG, "debug"},
+	{OPT_IMGAREAOFFX, "imgareatlx"},
+	{OPT_IMGAREAOFFY, "imgareatly"},
+	{OPT_TILEGRDOFFX, "tilegrdtlx"},
+	{OPT_TILEGRDOFFY, "tilegrdtly"},
+	{OPT_TILEWIDTH, "tilewidth"},
+	{OPT_TILEHEIGHT, "tileheight"},
+	{OPT_PRCWIDTH, "prcwidth"},
+	{OPT_PRCHEIGHT, "prcheight"},
+	{OPT_CBLKWIDTH, "cblkwidth"},
+	{OPT_CBLKHEIGHT, "cblkheight"},
+	{OPT_MODE, "mode"},
+	{OPT_PRG, "prg"},
+	{OPT_NOMCT, "nomct"},
+	{OPT_MAXRLVLS, "numrlvls"},
+	{OPT_SOP, "sop"},
+	{OPT_EPH, "eph"},
+	{OPT_LAZY, "lazy"},
+	{OPT_TERMALL, "termall"},
+	{OPT_SEGSYM, "segsym"},
+	{OPT_VCAUSAL, "vcausal"},
+	{OPT_PTERM, "pterm"},
+	{OPT_RESET, "resetprob"},
+	{OPT_NUMGBITS, "numgbits"},
+	{OPT_RATE, "rate"},
+	{OPT_ILYRRATES, "ilyrrates"},
+	{OPT_JP2OVERHEAD, "_jp2overhead"},
+	{-1, 0}
+};
+
+typedef enum {
+	PO_L = 0,
+	PO_R
+} poid_t;
+
+
+jas_taginfo_t prgordtab[] = {
+	{JPC_COD_LRCPPRG, "lrcp"},
+	{JPC_COD_RLCPPRG, "rlcp"},
+	{JPC_COD_RPCLPRG, "rpcl"},
+	{JPC_COD_PCRLPRG, "pcrl"},
+	{JPC_COD_CPRLPRG, "cprl"},
+	{-1, 0}
+};
+
+typedef enum {
+	MODE_INT,
+	MODE_REAL
+} modeid_t;
+
+jas_taginfo_t modetab[] = {
+	{MODE_INT, "int"},
+	{MODE_REAL, "real"},
+	{-1, 0}
+};
+
+/******************************************************************************\
+* The main encoder entry point.
+\******************************************************************************/
+
+int jpc_encode(jas_image_t *image, jas_stream_t *out, char *optstr)
+{
+	jpc_enc_t *enc;
+	jpc_enc_cp_t *cp;
+
+	enc = 0;
+	cp = 0;
+
+	jpc_initluts();
+
+	if (!(cp = cp_create(optstr, image))) {
+		fprintf(stderr, "invalid JP encoder options\n");
+		goto error;
+	}
+
+	if (!(enc = jpc_enc_create(cp, out, image))) {
+		goto error;
+	}
+	cp = 0;
+
+	/* Encode the main header. */
+	if (jpc_enc_encodemainhdr(enc)) {
+		goto error;
+	}
+
+	/* Encode the main body.  This constitutes most of the encoding work. */
+	if (jpc_enc_encodemainbody(enc)) {
+		goto error;
+	}
+
+	/* Write EOC marker segment. */
+	if (!(enc->mrk = jpc_ms_create(JPC_MS_EOC))) {
+		goto error;
+	}
+	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
+		fprintf(stderr, "cannot write EOI marker\n");
+		goto error;
+	}
+	jpc_ms_destroy(enc->mrk);
+	enc->mrk = 0;
+
+	if (jas_stream_flush(enc->out)) {
+		goto error;
+	}
+
+	jpc_enc_destroy(enc);
+
+	return 0;
+
+error:
+	if (cp) {
+		jpc_enc_cp_destroy(cp);
+	}
+	if (enc) {
+		jpc_enc_destroy(enc);
+	}
+	return -1;
+}
+
+/******************************************************************************\
+* Option parsing code.
+\******************************************************************************/
+
+static jpc_enc_cp_t *cp_create(char *optstr, jas_image_t *image)
+{
+	jpc_enc_cp_t *cp;
+	jas_tvparser_t *tvp;
+	int ret;
+	int numilyrrates;
+	double *ilyrrates;
+	int i;
+	int tagid;
+	jpc_enc_tcp_t *tcp;
+	jpc_enc_tccp_t *tccp;
+	jpc_enc_ccp_t *ccp;
+	uint_fast16_t cmptno;
+	uint_fast16_t rlvlno;
+	uint_fast16_t prcwidthexpn;
+	uint_fast16_t prcheightexpn;
+	bool enablemct;
+	uint_fast32_t jp2overhead;
+	uint_fast16_t lyrno;
+	uint_fast32_t hsteplcm;
+	uint_fast32_t vsteplcm;
+	bool mctvalid;
+
+	tvp = 0;
+	cp = 0;
+	ilyrrates = 0;
+	numilyrrates = 0;
+
+	if (!(cp = jas_malloc(sizeof(jpc_enc_cp_t)))) {
+		goto error;
+	}
+
+	prcwidthexpn = 15;
+	prcheightexpn = 15;
+	enablemct = true;
+	jp2overhead = 0;
+
+	cp->ccps = 0;
+	cp->debug = 0;
+	cp->imgareatlx = UINT_FAST32_MAX;
+	cp->imgareatly = UINT_FAST32_MAX;
+	cp->refgrdwidth = 0;
+	cp->refgrdheight = 0;
+	cp->tilegrdoffx = UINT_FAST32_MAX;
+	cp->tilegrdoffy = UINT_FAST32_MAX;
+	cp->tilewidth = 0;
+	cp->tileheight = 0;
+	cp->numcmpts = jas_image_numcmpts(image);
+
+	hsteplcm = 1;
+	vsteplcm = 1;
+	for (cmptno = 0; cmptno < jas_image_numcmpts(image); ++cmptno) {
+		if (jas_image_cmptbrx(image, cmptno) + jas_image_cmpthstep(image, cmptno) <=
+		  jas_image_brx(image) || jas_image_cmptbry(image, cmptno) +
+		  jas_image_cmptvstep(image, cmptno) <= jas_image_bry(image)) {
+			fprintf(stderr, "unsupported image type\n");
+			goto error;
+		}
+		/* Note: We ought to be calculating the LCMs here.  Fix some day. */
+		hsteplcm *= jas_image_cmpthstep(image, cmptno);
+		vsteplcm *= jas_image_cmptvstep(image, cmptno);
+	}
+
+	if (!(cp->ccps = jas_malloc(cp->numcmpts * sizeof(jpc_enc_ccp_t)))) {
+		goto error;
+	}
+	for (cmptno = 0, ccp = cp->ccps; cmptno < cp->numcmpts; ++cmptno,
+	  ++ccp) {
+		ccp->sampgrdstepx = jas_image_cmpthstep(image, cmptno);
+		ccp->sampgrdstepy = jas_image_cmptvstep(image, cmptno);
+		/* XXX - this isn't quite correct for more general image */
+		ccp->sampgrdsubstepx = 0;
+		ccp->sampgrdsubstepx = 0;
+		ccp->prec = jas_image_cmptprec(image, cmptno);
+		ccp->sgnd = jas_image_cmptsgnd(image, cmptno);
+		ccp->numstepsizes = 0;
+		memset(ccp->stepsizes, 0, sizeof(ccp->stepsizes));
+	}
+
+	cp->rawsize = jas_image_rawsize(image);
+	cp->totalsize = UINT_FAST32_MAX;
+
+	tcp = &cp->tcp;
+	tcp->csty = 0;
+	tcp->intmode = true;
+	tcp->prg = JPC_COD_LRCPPRG;
+	tcp->numlyrs = 1;
+	tcp->ilyrrates = 0;
+
+	tccp = &cp->tccp;
+	tccp->csty = 0;
+	tccp->maxrlvls = 6;
+	tccp->cblkwidthexpn = 6;
+	tccp->cblkheightexpn = 6;
+	tccp->cblksty = 0;
+	tccp->numgbits = 2;
+
+	if (!(tvp = jas_tvparser_create(optstr ? optstr : ""))) {
+		goto error;
+	}
+
+	while (!(ret = jas_tvparser_next(tvp))) {
+		switch (jas_taginfo_nonull(jas_taginfos_lookup(encopts,
+		  jas_tvparser_gettag(tvp)))->id) {
+		case OPT_DEBUG:
+			cp->debug = atoi(jas_tvparser_getval(tvp));
+			break;
+		case OPT_IMGAREAOFFX:
+			cp->imgareatlx = atoi(jas_tvparser_getval(tvp));
+			break;
+		case OPT_IMGAREAOFFY:
+			cp->imgareatly = atoi(jas_tvparser_getval(tvp));
+			break;
+		case OPT_TILEGRDOFFX:
+			cp->tilegrdoffx = atoi(jas_tvparser_getval(tvp));
+			break;
+		case OPT_TILEGRDOFFY:
+			cp->tilegrdoffy = atoi(jas_tvparser_getval(tvp));
+			break;
+		case OPT_TILEWIDTH:
+			cp->tilewidth = atoi(jas_tvparser_getval(tvp));
+			break;
+		case OPT_TILEHEIGHT:
+			cp->tileheight = atoi(jas_tvparser_getval(tvp));
+			break;
+		case OPT_PRCWIDTH:
+			prcwidthexpn = jpc_floorlog2(atoi(jas_tvparser_getval(tvp)));
+			break;
+		case OPT_PRCHEIGHT:
+			prcheightexpn = jpc_floorlog2(atoi(jas_tvparser_getval(tvp)));
+			break;
+		case OPT_CBLKWIDTH:
+			tccp->cblkwidthexpn =
+			  jpc_floorlog2(atoi(jas_tvparser_getval(tvp)));
+			break;
+		case OPT_CBLKHEIGHT:
+			tccp->cblkheightexpn =
+			  jpc_floorlog2(atoi(jas_tvparser_getval(tvp)));
+			break;
+		case OPT_MODE:
+			if ((tagid = jas_taginfo_nonull(jas_taginfos_lookup(modetab,
+			  jas_tvparser_getval(tvp)))->id) < 0) {
+				fprintf(stderr,
+				  "ignoring invalid mode %s\n",
+				  jas_tvparser_getval(tvp));
+			} else {
+				tcp->intmode = (tagid == MODE_INT);
+			}
+			break;
+		case OPT_PRG:
+			if ((tagid = jas_taginfo_nonull(jas_taginfos_lookup(prgordtab,
+			  jas_tvparser_getval(tvp)))->id) < 0) {
+				fprintf(stderr,
+				  "ignoring invalid progression order %s\n",
+				  jas_tvparser_getval(tvp));
+			} else {
+				tcp->prg = tagid;
+			}
+			break;
+		case OPT_NOMCT:
+			enablemct = false;
+			break;
+		case OPT_MAXRLVLS:
+			tccp->maxrlvls = atoi(jas_tvparser_getval(tvp));
+			break;
+		case OPT_SOP:
+			cp->tcp.csty |= JPC_COD_SOP;
+			break;
+		case OPT_EPH:
+			cp->tcp.csty |= JPC_COD_EPH;
+			break;
+		case OPT_LAZY:
+			tccp->cblksty |= JPC_COX_LAZY;
+			break;
+		case OPT_TERMALL:
+			tccp->cblksty |= JPC_COX_TERMALL;
+			break;
+		case OPT_SEGSYM:
+			tccp->cblksty |= JPC_COX_SEGSYM;
+			break;
+		case OPT_VCAUSAL:
+			tccp->cblksty |= JPC_COX_VSC;
+			break;
+		case OPT_RESET:
+			tccp->cblksty |= JPC_COX_RESET;
+			break;
+		case OPT_PTERM:
+			tccp->cblksty |= JPC_COX_PTERM;
+			break;
+		case OPT_NUMGBITS:
+			cp->tccp.numgbits = atoi(jas_tvparser_getval(tvp));
+			break;
+		case OPT_RATE:
+			if (ratestrtosize(jas_tvparser_getval(tvp), cp->rawsize,
+			  &cp->totalsize)) {
+				fprintf(stderr,
+				  "ignoring bad rate specifier %s\n",
+				  jas_tvparser_getval(tvp));
+			}
+			break;
+		case OPT_ILYRRATES:
+			if (jpc_atoaf(jas_tvparser_getval(tvp), &numilyrrates,
+			  &ilyrrates)) {
+				fprintf(stderr,
+				  "warning: invalid intermediate layer rates specifier ignored (%s)\n",
+				  jas_tvparser_getval(tvp));
+			}
+			break;
+
+		case OPT_JP2OVERHEAD:
+			jp2overhead = atoi(jas_tvparser_getval(tvp));
+			break;
+		default:
+			fprintf(stderr, "warning: ignoring invalid option %s\n",
+			 jas_tvparser_gettag(tvp));
+			break;
+		}
+	}
+
+	jas_tvparser_destroy(tvp);
+	tvp = 0;
+
+	if (cp->totalsize != UINT_FAST32_MAX) {
+		cp->totalsize = (cp->totalsize > jp2overhead) ?
+		  (cp->totalsize - jp2overhead) : 0;
+	}
+
+	if (cp->imgareatlx == UINT_FAST32_MAX) {
+		cp->imgareatlx = 0;
+	} else {
+		if (hsteplcm != 1) {
+			fprintf(stderr, "warning: overriding imgareatlx value\n");
+		}
+		cp->imgareatlx *= hsteplcm;
+	}
+	if (cp->imgareatly == UINT_FAST32_MAX) {
+		cp->imgareatly = 0;
+	} else {
+		if (vsteplcm != 1) {
+			fprintf(stderr, "warning: overriding imgareatly value\n");
+		}
+		cp->imgareatly *= vsteplcm;
+	}
+	cp->refgrdwidth = cp->imgareatlx + jas_image_width(image);
+	cp->refgrdheight = cp->imgareatly + jas_image_height(image);
+	if (cp->tilegrdoffx == UINT_FAST32_MAX) {
+		cp->tilegrdoffx = cp->imgareatlx;
+	}
+	if (cp->tilegrdoffy == UINT_FAST32_MAX) {
+		cp->tilegrdoffy = cp->imgareatly;
+	}
+	if (!cp->tilewidth) {
+		cp->tilewidth = cp->refgrdwidth - cp->tilegrdoffx;
+	}
+	if (!cp->tileheight) {
+		cp->tileheight = cp->refgrdheight - cp->tilegrdoffy;
+	}
+
+	if (cp->numcmpts == 3) {
+		mctvalid = true;
+		for (cmptno = 0; cmptno < jas_image_numcmpts(image); ++cmptno) {
+			if (jas_image_cmptprec(image, cmptno) != jas_image_cmptprec(image, 0) ||
+			  jas_image_cmptsgnd(image, cmptno) != jas_image_cmptsgnd(image, 0) ||
+			  jas_image_cmptwidth(image, cmptno) != jas_image_cmptwidth(image, 0) ||
+			  jas_image_cmptheight(image, cmptno) != jas_image_cmptheight(image, 0)) {
+				mctvalid = false;
+			}
+		}
+	} else {
+		mctvalid = false;
+	}
+	if (mctvalid && enablemct && jas_image_colorspace(image) != JAS_IMAGE_CS_RGB) {
+		fprintf(stderr, "warning: color model apparently not RGB\n");
+	}
+	if (mctvalid && enablemct && jas_image_colorspace(image) == JAS_IMAGE_CS_RGB) {
+		tcp->mctid = (tcp->intmode) ? (JPC_MCT_RCT) : (JPC_MCT_ICT);
+	} else {
+		tcp->mctid = JPC_MCT_NONE;
+	}
+	tccp->qmfbid = (tcp->intmode) ? (JPC_COX_RFT) : (JPC_COX_INS);
+
+	for (rlvlno = 0; rlvlno < tccp->maxrlvls; ++rlvlno) {
+		tccp->prcwidthexpns[rlvlno] = prcwidthexpn;
+		tccp->prcheightexpns[rlvlno] = prcheightexpn;
+	}
+	if (prcwidthexpn != 15 || prcheightexpn != 15) {
+		tccp->csty |= JPC_COX_PRT;
+	}
+
+	/* Ensure that the tile width and height is valid. */
+	if (!cp->tilewidth) {
+		fprintf(stderr, "invalid tile width %lu\n", (unsigned long)
+		  cp->tilewidth);
+		goto error;
+	}
+	if (!cp->tileheight) {
+		fprintf(stderr, "invalid tile height %lu\n", (unsigned long)
+		  cp->tileheight);
+		goto error;
+	}
+
+	/* Ensure that the tile grid offset is valid. */
+	if (cp->tilegrdoffx > cp->imgareatlx ||
+	  cp->tilegrdoffy > cp->imgareatly ||
+	  cp->tilegrdoffx + cp->tilewidth < cp->imgareatlx ||
+	  cp->tilegrdoffy + cp->tileheight < cp->imgareatly) {
+		fprintf(stderr, "invalid tile grid offset (%lu, %lu)\n",
+		  (unsigned long) cp->tilegrdoffx, (unsigned long)
+		  cp->tilegrdoffy);
+		goto error;
+	}
+
+	cp->numhtiles = JPC_CEILDIV(cp->refgrdwidth - cp->tilegrdoffx,
+	  cp->tilewidth);
+	cp->numvtiles = JPC_CEILDIV(cp->refgrdheight - cp->tilegrdoffy,
+	  cp->tileheight);
+	cp->numtiles = cp->numhtiles * cp->numvtiles;
+
+	if (ilyrrates && numilyrrates > 0) {
+		tcp->numlyrs = numilyrrates + 1;
+		if (!(tcp->ilyrrates = jas_malloc((tcp->numlyrs - 1) *
+		  sizeof(jpc_fix_t)))) {
+			goto error;
+		}
+		for (i = 0; i < tcp->numlyrs - 1; ++i) {
+			tcp->ilyrrates[i] = jpc_dbltofix(ilyrrates[i]);
+		}
+	}
+
+	/* Ensure that the integer mode is used in the case of lossless
+	  coding. */
+	if (cp->totalsize == UINT_FAST32_MAX && (!cp->tcp.intmode)) {
+		fprintf(stderr, "cannot use real mode for lossless coding\n");
+		goto error;
+	}
+
+	/* Ensure that the precinct width is valid. */
+	if (prcwidthexpn > 15) {
+		fprintf(stderr, "invalid precinct width\n");
+		goto error;
+	}
+
+	/* Ensure that the precinct height is valid. */
+	if (prcheightexpn > 15) {
+		fprintf(stderr, "invalid precinct height\n");
+		goto error;
+	}
+
+	/* Ensure that the code block width is valid. */
+	if (cp->tccp.cblkwidthexpn < 2 || cp->tccp.cblkwidthexpn > 12) {
+		fprintf(stderr, "invalid code block width %d\n",
+		  JPC_POW2(cp->tccp.cblkwidthexpn));
+		goto error;
+	}
+
+	/* Ensure that the code block height is valid. */
+	if (cp->tccp.cblkheightexpn < 2 || cp->tccp.cblkheightexpn > 12) {
+		fprintf(stderr, "invalid code block height %d\n",
+		  JPC_POW2(cp->tccp.cblkheightexpn));
+		goto error;
+	}
+
+	/* Ensure that the code block size is not too large. */
+	if (cp->tccp.cblkwidthexpn + cp->tccp.cblkheightexpn > 12) {
+		fprintf(stderr, "code block size too large\n");
+		goto error;
+	}
+
+	/* Ensure that the number of layers is valid. */
+	if (cp->tcp.numlyrs > 16384) {
+		fprintf(stderr, "too many layers\n");
+		goto error;
+	}
+
+	/* There must be at least one resolution level. */
+	if (cp->tccp.maxrlvls < 1) {
+		fprintf(stderr, "must be at least one resolution level\n");
+		goto error;
+	}
+
+	/* Ensure that the number of guard bits is valid. */
+	if (cp->tccp.numgbits > 8) {
+		fprintf(stderr, "invalid number of guard bits\n");
+		goto error;
+	}
+
+	/* Ensure that the rate is within the legal range. */
+	if (cp->totalsize != UINT_FAST32_MAX && cp->totalsize > cp->rawsize) {
+		fprintf(stderr, "warning: specified rate is unreasonably large (%lu > %lu)\n", (unsigned long) cp->totalsize, (unsigned long) cp->rawsize);
+	}
+
+	/* Ensure that the intermediate layer rates are valid. */
+	if (tcp->numlyrs > 1) {
+		/* The intermediate layers rates must increase monotonically. */
+		for (lyrno = 0; lyrno + 2 < tcp->numlyrs; ++lyrno) {
+			if (tcp->ilyrrates[lyrno] >= tcp->ilyrrates[lyrno + 1]) {
+				fprintf(stderr, "intermediate layer rates must increase monotonically\n");
+				goto error;
+			}
+		}
+		/* The intermediate layer rates must be less than the overall rate. */
+		if (cp->totalsize != UINT_FAST32_MAX) {
+			for (lyrno = 0; lyrno < tcp->numlyrs - 1; ++lyrno) {
+				if (jpc_fixtodbl(tcp->ilyrrates[lyrno]) > ((double) cp->totalsize)
+				  / cp->rawsize) {
+					fprintf(stderr, "warning: intermediate layer rates must be less than overall rate\n");
+					goto error;
+				}
+			}
+		}
+	}
+
+	if (ilyrrates) {
+		jas_free(ilyrrates);
+	}
+
+	return cp;
+
+error:
+
+	if (ilyrrates) {
+		jas_free(ilyrrates);
+	}
+	if (tvp) {
+		jas_tvparser_destroy(tvp);
+	}
+	if (cp) {
+		jpc_enc_cp_destroy(cp);
+	}
+	return 0;
+}
+
+void jpc_enc_cp_destroy(jpc_enc_cp_t *cp)
+{
+	if (cp->ccps) {
+		if (cp->tcp.ilyrrates) {
+			jas_free(cp->tcp.ilyrrates);
+		}
+		jas_free(cp->ccps);
+	}
+	jas_free(cp);
+}
+
+int ratestrtosize(const char *s, uint_fast32_t rawsize, uint_fast32_t *size)
+{
+	char *cp;
+	jpc_flt_t f;
+
+	/* Note: This function must not modify output size on failure. */
+	if ((cp = strchr(s, 'B'))) {
+		*size = atoi(s);
+	} else {
+		f = atof(s);
+		if (f < 0) {
+			*size = 0;
+		} else if (f > 1.0) {
+			*size = rawsize + 1;
+		} else {
+			*size = f * rawsize;
+		}
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* Encoder constructor and destructor.
+\******************************************************************************/
+
+jpc_enc_t *jpc_enc_create(jpc_enc_cp_t *cp, jas_stream_t *out, jas_image_t *image)
+{
+	jpc_enc_t *enc;
+
+	enc = 0;
+
+	if (!(enc = jas_malloc(sizeof(jpc_enc_t)))) {
+		goto error;
+	}
+
+	enc->image = image;
+	enc->out = out;
+	enc->cp = cp;
+	enc->cstate = 0;
+	enc->tmpstream = 0;
+	enc->mrk = 0;
+	enc->curtile = 0;
+
+	if (!(enc->cstate = jpc_cstate_create())) {
+		goto error;
+	}
+	enc->len = 0;
+	enc->mainbodysize = 0;
+
+	return enc;
+
+error:
+
+	if (enc) {
+		jpc_enc_destroy(enc);
+	}
+	return 0;
+}
+
+void jpc_enc_destroy(jpc_enc_t *enc)
+{
+	/* The image object (i.e., enc->image) and output stream object
+	(i.e., enc->out) are created outside of the encoder.
+	Therefore, they must not be destroyed here. */
+
+	if (enc->curtile) {
+		jpc_enc_tile_destroy(enc->curtile);
+	}
+	if (enc->cp) {
+		jpc_enc_cp_destroy(enc->cp);
+	}
+	if (enc->cstate) {
+		jpc_cstate_destroy(enc->cstate);
+	}
+	if (enc->tmpstream) {
+		jas_stream_close(enc->tmpstream);
+	}
+
+	jas_free(enc);
+}
+
+/******************************************************************************\
+* Code.
+\******************************************************************************/
+
+static int jpc_enc_encodemainhdr(jpc_enc_t *enc)
+{
+	jpc_siz_t *siz;
+	jpc_cod_t *cod;
+	jpc_qcd_t *qcd;
+	int i;
+long startoff;
+long mainhdrlen;
+	jpc_enc_cp_t *cp;
+	jpc_qcc_t *qcc;
+	jpc_enc_tccp_t *tccp;
+	uint_fast16_t cmptno;
+	jpc_tsfb_band_t bandinfos[JPC_MAXBANDS];
+	jpc_fix_t mctsynweight;
+	jpc_enc_tcp_t *tcp;
+	jpc_tsfb_t *tsfb;
+	jpc_tsfb_band_t *bandinfo;
+	uint_fast16_t numbands;
+	uint_fast16_t bandno;
+	uint_fast16_t rlvlno;
+	uint_fast16_t analgain;
+	jpc_fix_t absstepsize;
+	char buf[1024];
+	jpc_com_t *com;
+
+	cp = enc->cp;
+
+startoff = jas_stream_getrwcount(enc->out);
+
+	/* Write SOC marker segment. */
+	if (!(enc->mrk = jpc_ms_create(JPC_MS_SOC))) {
+		return -1;
+	}
+	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
+		fprintf(stderr, "cannot write SOC marker\n");
+		return -1;
+	}
+	jpc_ms_destroy(enc->mrk);
+	enc->mrk = 0;
+
+	/* Write SIZ marker segment. */
+	if (!(enc->mrk = jpc_ms_create(JPC_MS_SIZ))) {
+		return -1;
+	}
+	siz = &enc->mrk->parms.siz;
+	siz->caps = 0;
+	siz->xoff = cp->imgareatlx;
+	siz->yoff = cp->imgareatly;
+	siz->width = cp->refgrdwidth;
+	siz->height = cp->refgrdheight;
+	siz->tilexoff = cp->tilegrdoffx;
+	siz->tileyoff = cp->tilegrdoffy;
+	siz->tilewidth = cp->tilewidth;
+	siz->tileheight = cp->tileheight;
+	siz->numcomps = cp->numcmpts;
+	siz->comps = jas_malloc(siz->numcomps * sizeof(jpc_sizcomp_t));
+	assert(siz->comps);
+	for (i = 0; i < cp->numcmpts; ++i) {
+		siz->comps[i].prec = cp->ccps[i].prec;
+		siz->comps[i].sgnd = cp->ccps[i].sgnd;
+		siz->comps[i].hsamp = cp->ccps[i].sampgrdstepx;
+		siz->comps[i].vsamp = cp->ccps[i].sampgrdstepy;
+	}
+	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
+		fprintf(stderr, "cannot write SIZ marker\n");
+		return -1;
+	}
+	jpc_ms_destroy(enc->mrk);
+	enc->mrk = 0;
+
+	if (!(enc->mrk = jpc_ms_create(JPC_MS_COM))) {
+		return -1;
+	}
+	sprintf(buf, "Creator: JasPer Version %s", jas_getversion());
+	com = &enc->mrk->parms.com;
+	com->len = strlen(buf);
+	com->regid = JPC_COM_LATIN;
+	if (!(com->data = JAS_CAST(unsigned char *, jas_strdup(buf)))) {
+		abort();
+	}
+	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
+		fprintf(stderr, "cannot write COM marker\n");
+		return -1;
+	}
+	jpc_ms_destroy(enc->mrk);
+	enc->mrk = 0;
+
+#if 0
+	if (!(enc->mrk = jpc_ms_create(JPC_MS_CRG))) {
+		return -1;
+	}
+	crg = &enc->mrk->parms.crg;
+	crg->comps = jas_malloc(crg->numcomps * sizeof(jpc_crgcomp_t));
+	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
+		fprintf(stderr, "cannot write CRG marker\n");
+		return -1;
+	}
+	jpc_ms_destroy(enc->mrk);
+	enc->mrk = 0;
+#endif
+
+	tcp = &cp->tcp;
+	tccp = &cp->tccp;
+	for (cmptno = 0; cmptno < cp->numcmpts; ++cmptno) {
+		tsfb = jpc_cod_gettsfb(tccp->qmfbid, tccp->maxrlvls - 1);
+		jpc_tsfb_getbands(tsfb, 0, 0, 1 << tccp->maxrlvls, 1 << tccp->maxrlvls,
+		  bandinfos);
+		jpc_tsfb_destroy(tsfb);
+		mctsynweight = jpc_mct_getsynweight(tcp->mctid, cmptno);
+		numbands = 3 * tccp->maxrlvls - 2;
+		for (bandno = 0, bandinfo = bandinfos; bandno < numbands;
+		  ++bandno, ++bandinfo) {
+			rlvlno = (bandno) ? ((bandno - 1) / 3 + 1) : 0;
+			analgain = JPC_NOMINALGAIN(tccp->qmfbid, tccp->maxrlvls,
+			  rlvlno, bandinfo->orient);
+			if (!tcp->intmode) {
+				absstepsize = jpc_fix_div(jpc_inttofix(1 <<
+				  (analgain + 1)), bandinfo->synenergywt);
+			} else {
+				absstepsize = jpc_inttofix(1);
+			}	
+			cp->ccps[cmptno].stepsizes[bandno] =
+			  jpc_abstorelstepsize(absstepsize,
+			  cp->ccps[cmptno].prec + analgain);
+		}
+		cp->ccps[cmptno].numstepsizes = numbands;
+	}
+
+	if (!(enc->mrk = jpc_ms_create(JPC_MS_COD))) {
+		return -1;
+	}
+	cod = &enc->mrk->parms.cod;
+	cod->csty = cp->tccp.csty | cp->tcp.csty;
+	cod->compparms.csty = cp->tccp.csty | cp->tcp.csty;
+	cod->compparms.numdlvls = cp->tccp.maxrlvls - 1;
+	cod->compparms.numrlvls = cp->tccp.maxrlvls;
+	cod->prg = cp->tcp.prg;
+	cod->numlyrs = cp->tcp.numlyrs;
+	cod->compparms.cblkwidthval = JPC_COX_CBLKSIZEEXPN(cp->tccp.cblkwidthexpn);
+	cod->compparms.cblkheightval = JPC_COX_CBLKSIZEEXPN(cp->tccp.cblkheightexpn);
+	cod->compparms.cblksty = cp->tccp.cblksty;
+	cod->compparms.qmfbid = cp->tccp.qmfbid;
+	cod->mctrans = (cp->tcp.mctid != JPC_MCT_NONE);
+	if (tccp->csty & JPC_COX_PRT) {
+		for (rlvlno = 0; rlvlno < tccp->maxrlvls; ++rlvlno) {
+			cod->compparms.rlvls[rlvlno].parwidthval = tccp->prcwidthexpns[rlvlno];
+			cod->compparms.rlvls[rlvlno].parheightval = tccp->prcheightexpns[rlvlno];
+		}
+	}
+	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
+		fprintf(stderr, "cannot write COD marker\n");
+		return -1;
+	}
+	jpc_ms_destroy(enc->mrk);
+	enc->mrk = 0;
+
+	if (!(enc->mrk = jpc_ms_create(JPC_MS_QCD))) {
+		return -1;
+	}
+	qcd = &enc->mrk->parms.qcd;
+	qcd->compparms.qntsty = (tccp->qmfbid == JPC_COX_INS) ?
+	  JPC_QCX_SEQNT : JPC_QCX_NOQNT;
+	qcd->compparms.numstepsizes = cp->ccps[0].numstepsizes;
+	qcd->compparms.numguard = cp->tccp.numgbits;
+	qcd->compparms.stepsizes = cp->ccps[0].stepsizes;
+	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
+		return -1;
+	}
+	/* We do not want the step size array to be freed! */
+	qcd->compparms.stepsizes = 0;
+	jpc_ms_destroy(enc->mrk);
+	enc->mrk = 0;
+
+	tccp = &cp->tccp;
+	for (cmptno = 1; cmptno < cp->numcmpts; ++cmptno) {
+		if (!(enc->mrk = jpc_ms_create(JPC_MS_QCC))) {
+			return -1;
+		}
+		qcc = &enc->mrk->parms.qcc;
+		qcc->compno = cmptno;
+		qcc->compparms.qntsty = (tccp->qmfbid == JPC_COX_INS) ?
+		  JPC_QCX_SEQNT : JPC_QCX_NOQNT;
+		qcc->compparms.numstepsizes = cp->ccps[cmptno].numstepsizes;
+		qcc->compparms.numguard = cp->tccp.numgbits;
+		qcc->compparms.stepsizes = cp->ccps[cmptno].stepsizes;
+		if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
+			return -1;
+		}
+		/* We do not want the step size array to be freed! */
+		qcc->compparms.stepsizes = 0;
+		jpc_ms_destroy(enc->mrk);
+		enc->mrk = 0;
+	}
+
+#define MAINTLRLEN	2
+	mainhdrlen = jas_stream_getrwcount(enc->out) - startoff;
+	enc->len += mainhdrlen;
+	if (enc->cp->totalsize != UINT_FAST32_MAX) {
+		uint_fast32_t overhead;
+		overhead = mainhdrlen + MAINTLRLEN;
+		enc->mainbodysize = (enc->cp->totalsize >= overhead) ?
+		  (enc->cp->totalsize - overhead) : 0;
+	} else {
+		enc->mainbodysize = UINT_FAST32_MAX;
+	}
+
+	return 0;
+}
+
+static int jpc_enc_encodemainbody(jpc_enc_t *enc)
+{
+	int tileno;
+	int tilex;
+	int tiley;
+	int i;
+	jpc_sot_t *sot;
+	jpc_enc_tcmpt_t *comp;
+	jpc_enc_tcmpt_t *endcomps;
+	jpc_enc_band_t *band;
+	jpc_enc_band_t *endbands;
+	jpc_enc_rlvl_t *lvl;
+	int rlvlno;
+	jpc_qcc_t *qcc;
+	jpc_cod_t *cod;
+	int adjust;
+	int j;
+	int absbandno;
+	long numbytes;
+	long tilehdrlen;
+	long tilelen;
+	jpc_enc_tile_t *tile;
+	jpc_enc_cp_t *cp;
+	double rho;
+	uint_fast16_t lyrno;
+	uint_fast16_t cmptno;
+	int samestepsizes;
+	jpc_enc_ccp_t *ccps;
+	jpc_enc_tccp_t *tccp;
+int bandno;
+uint_fast32_t x;
+uint_fast32_t y;
+int mingbits;
+int actualnumbps;
+jpc_fix_t mxmag;
+jpc_fix_t mag;
+int numgbits;
+
+	cp = enc->cp;
+
+	/* Avoid compile warnings. */
+	numbytes = 0;
+
+	for (tileno = 0; tileno < cp->numtiles; ++tileno) {
+		tilex = tileno % cp->numhtiles;
+		tiley = tileno / cp->numhtiles;
+
+		if (!(enc->curtile = jpc_enc_tile_create(enc->cp, enc->image, tileno))) {
+			abort();
+		}
+
+		tile = enc->curtile;
+
+		if (jas_getdbglevel() >= 10) {
+			jpc_enc_dump(enc);
+		}
+
+		endcomps = &tile->tcmpts[tile->numtcmpts];
+		for (cmptno = 0, comp = tile->tcmpts; cmptno < tile->numtcmpts; ++cmptno, ++comp) {
+			if (!cp->ccps[cmptno].sgnd) {
+				adjust = 1 << (cp->ccps[cmptno].prec - 1);
+				for (i = 0; i < jas_matrix_numrows(comp->data); ++i) {
+					for (j = 0; j < jas_matrix_numcols(comp->data); ++j) {
+						*jas_matrix_getref(comp->data, i, j) -= adjust;
+					}
+				}
+			}
+		}
+
+		if (!tile->intmode) {
+				endcomps = &tile->tcmpts[tile->numtcmpts];
+				for (comp = tile->tcmpts; comp != endcomps; ++comp) {
+					jas_matrix_asl(comp->data, JPC_FIX_FRACBITS);
+				}
+		}
+
+		switch (tile->mctid) {
+		case JPC_MCT_RCT:
+assert(jas_image_numcmpts(enc->image) == 3);
+			jpc_rct(tile->tcmpts[0].data, tile->tcmpts[1].data,
+			  tile->tcmpts[2].data);
+			break;
+		case JPC_MCT_ICT:
+assert(jas_image_numcmpts(enc->image) == 3);
+			jpc_ict(tile->tcmpts[0].data, tile->tcmpts[1].data,
+			  tile->tcmpts[2].data);
+			break;
+		default:
+			break;
+		}
+
+		for (i = 0; i < jas_image_numcmpts(enc->image); ++i) {
+			comp = &tile->tcmpts[i];
+			jpc_tsfb_analyze(comp->tsfb, ((comp->qmfbid == JPC_COX_RFT) ? JPC_TSFB_RITIMODE : 0), comp->data);
+
+		}
+
+
+		endcomps = &tile->tcmpts[tile->numtcmpts];
+		for (cmptno = 0, comp = tile->tcmpts; comp != endcomps; ++cmptno, ++comp) {
+			mingbits = 0;
+			absbandno = 0;
+			/* All bands must have a corresponding quantizer step size,
+			  even if they contain no samples and are never coded. */
+			/* Some bands may not be hit by the loop below, so we must
+			  initialize all of the step sizes to a sane value. */
+			memset(comp->stepsizes, 0, sizeof(comp->stepsizes));
+			for (rlvlno = 0, lvl = comp->rlvls; rlvlno < comp->numrlvls; ++rlvlno, ++lvl) {
+				if (!lvl->bands) {
+					absbandno += rlvlno ? 3 : 1;
+					continue;
+				}
+				endbands = &lvl->bands[lvl->numbands];
+				for (band = lvl->bands; band != endbands; ++band) {
+					if (!band->data) {
+						++absbandno;
+						continue;
+					}
+					actualnumbps = 0;
+					mxmag = 0;
+					for (y = 0; y < jas_matrix_numrows(band->data); ++y) {
+						for (x = 0; x < jas_matrix_numcols(band->data); ++x) {
+							mag = abs(jas_matrix_get(band->data, y, x));
+							if (mag > mxmag) {
+								mxmag = mag;
+							}
+						}
+					}
+					if (tile->intmode) {
+						actualnumbps = jpc_firstone(mxmag) + 1;
+					} else {
+						actualnumbps = jpc_firstone(mxmag) + 1 - JPC_FIX_FRACBITS;
+					}
+					numgbits = actualnumbps - (cp->ccps[cmptno].prec - 1 +
+					  band->analgain);
+#if 0
+fprintf(stderr, "%d %d mag=%d actual=%d numgbits=%d\n", cp->ccps[cmptno].prec, band->analgain, mxmag, actualnumbps, numgbits);
+#endif
+					if (numgbits > mingbits) {
+						mingbits = numgbits;
+					}
+					if (!tile->intmode) {
+						band->absstepsize = jpc_fix_div(jpc_inttofix(1
+						  << (band->analgain + 1)),
+						  band->synweight);
+					} else {
+						band->absstepsize = jpc_inttofix(1);
+					}
+					band->stepsize = jpc_abstorelstepsize(
+					  band->absstepsize, cp->ccps[cmptno].prec +
+					  band->analgain);
+					band->numbps = cp->tccp.numgbits +
+					  JPC_QCX_GETEXPN(band->stepsize) - 1;
+
+					if ((!tile->intmode) && band->data) {
+						quantize(band->data, band->absstepsize);
+					}
+
+					comp->stepsizes[absbandno] = band->stepsize;
+					++absbandno;
+				}
+			}
+
+			assert(JPC_FIX_FRACBITS >= JPC_NUMEXTRABITS);
+			if (!tile->intmode) {
+				jas_matrix_divpow2(comp->data, JPC_FIX_FRACBITS - JPC_NUMEXTRABITS);
+			} else {
+				jas_matrix_asl(comp->data, JPC_NUMEXTRABITS);
+			}
+		}
+#if 0
+fprintf(stderr, "mingbits %d\n", mingbits);
+#endif
+
+		if (mingbits > cp->tccp.numgbits) {
+			fprintf(stderr, "error: too few guard bits (need at least %d)\n",
+			  mingbits);
+			return -1;
+		}
+
+		if (!(enc->tmpstream = jas_stream_memopen(0, 0))) {
+			fprintf(stderr, "cannot open tmp file\n");
+			return -1;
+		}
+
+		/* Write the tile header. */
+		if (!(enc->mrk = jpc_ms_create(JPC_MS_SOT))) {
+			return -1;
+		}
+		sot = &enc->mrk->parms.sot;
+		sot->len = 0;
+		sot->tileno = tileno;
+		sot->partno = 0;
+		sot->numparts = 1;
+		if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
+			fprintf(stderr, "cannot write SOT marker\n");
+			return -1;
+		}
+		jpc_ms_destroy(enc->mrk);
+		enc->mrk = 0;
+
+/************************************************************************/
+/************************************************************************/
+/************************************************************************/
+
+		tccp = &cp->tccp;
+		for (cmptno = 0; cmptno < cp->numcmpts; ++cmptno) {
+			comp = &tile->tcmpts[cmptno];
+			if (comp->numrlvls != tccp->maxrlvls) {
+				if (!(enc->mrk = jpc_ms_create(JPC_MS_COD))) {
+					return -1;
+				}
+/* XXX = this is not really correct. we are using comp #0's precint sizes
+and other characteristics */
+				comp = &tile->tcmpts[0];
+				cod = &enc->mrk->parms.cod;
+				cod->compparms.csty = 0;
+				cod->compparms.numdlvls = comp->numrlvls - 1;
+				cod->prg = tile->prg;
+				cod->numlyrs = tile->numlyrs;
+				cod->compparms.cblkwidthval = JPC_COX_CBLKSIZEEXPN(comp->cblkwidthexpn);
+				cod->compparms.cblkheightval = JPC_COX_CBLKSIZEEXPN(comp->cblkheightexpn);
+				cod->compparms.cblksty = comp->cblksty;
+				cod->compparms.qmfbid = comp->qmfbid;
+				cod->mctrans = (tile->mctid != JPC_MCT_NONE);
+				for (i = 0; i < comp->numrlvls; ++i) {
+					cod->compparms.rlvls[i].parwidthval = comp->rlvls[i].prcwidthexpn;
+					cod->compparms.rlvls[i].parheightval = comp->rlvls[i].prcheightexpn;
+				}
+				if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
+					return -1;
+				}
+				jpc_ms_destroy(enc->mrk);
+				enc->mrk = 0;
+			}
+		}
+
+		for (cmptno = 0, comp = tile->tcmpts; cmptno < cp->numcmpts; ++cmptno, ++comp) {
+			ccps = &cp->ccps[cmptno];
+			if (ccps->numstepsizes == comp->numstepsizes) {
+				samestepsizes = 1;
+				for (bandno = 0; bandno < ccps->numstepsizes; ++bandno) {
+					if (ccps->stepsizes[bandno] != comp->stepsizes[bandno]) {
+						samestepsizes = 0;
+						break;
+					}
+				}
+			} else {
+				samestepsizes = 0;
+			}
+			if (!samestepsizes) {
+				if (!(enc->mrk = jpc_ms_create(JPC_MS_QCC))) {
+					return -1;
+				}
+				qcc = &enc->mrk->parms.qcc;
+				qcc->compno = cmptno;
+				qcc->compparms.numguard = cp->tccp.numgbits;
+				qcc->compparms.qntsty = (comp->qmfbid == JPC_COX_INS) ?
+				  JPC_QCX_SEQNT : JPC_QCX_NOQNT;
+				qcc->compparms.numstepsizes = comp->numstepsizes;
+				qcc->compparms.stepsizes = comp->stepsizes;
+				if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
+					return -1;
+				}
+				qcc->compparms.stepsizes = 0;
+				jpc_ms_destroy(enc->mrk);
+				enc->mrk = 0;
+			}
+		}
+
+		/* Write a SOD marker to indicate the end of the tile header. */
+		if (!(enc->mrk = jpc_ms_create(JPC_MS_SOD))) {
+			return -1;
+		}
+		if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
+			fprintf(stderr, "cannot write SOD marker\n");
+			return -1;
+		}
+		jpc_ms_destroy(enc->mrk);
+		enc->mrk = 0;
+tilehdrlen = jas_stream_getrwcount(enc->tmpstream);
+
+/************************************************************************/
+/************************************************************************/
+/************************************************************************/
+
+if (jpc_enc_enccblks(enc)) {
+	abort();
+	return -1;
+}
+
+		cp = enc->cp;
+		rho = (double) (tile->brx - tile->tlx) * (tile->bry - tile->tly) /
+		  ((cp->refgrdwidth - cp->imgareatlx) * (cp->refgrdheight -
+		  cp->imgareatly));
+		tile->rawsize = cp->rawsize * rho;
+
+		for (lyrno = 0; lyrno < tile->numlyrs - 1; ++lyrno) {
+			tile->lyrsizes[lyrno] = tile->rawsize * jpc_fixtodbl(
+			  cp->tcp.ilyrrates[lyrno]);
+		}
+		tile->lyrsizes[tile->numlyrs - 1] = (cp->totalsize != UINT_FAST32_MAX) ?
+		  (rho * enc->mainbodysize) : UINT_FAST32_MAX;
+		for (lyrno = 0; lyrno < tile->numlyrs; ++lyrno) {
+			if (tile->lyrsizes[lyrno] != UINT_FAST32_MAX) {
+				if (tilehdrlen <= tile->lyrsizes[lyrno]) {
+					tile->lyrsizes[lyrno] -= tilehdrlen;
+				} else {
+					tile->lyrsizes[lyrno] = 0;
+				}
+			}
+		}
+
+		if (rateallocate(enc, tile->numlyrs, tile->lyrsizes)) {
+			return -1;
+		}
+
+#if 0
+fprintf(stderr, "ENCODE TILE DATA\n");
+#endif
+		if (jpc_enc_encodetiledata(enc)) {
+			fprintf(stderr, "dotile failed\n");
+			return -1;
+		}
+
+/************************************************************************/
+/************************************************************************/
+/************************************************************************/
+
+/************************************************************************/
+/************************************************************************/
+/************************************************************************/
+
+		tilelen = jas_stream_tell(enc->tmpstream);
+
+		if (jas_stream_seek(enc->tmpstream, 6, SEEK_SET) < 0) {
+			return -1;
+		}
+		jpc_putuint32(enc->tmpstream, tilelen);
+
+		if (jas_stream_seek(enc->tmpstream, 0, SEEK_SET) < 0) {
+			return -1;
+		}
+		if (jpc_putdata(enc->out, enc->tmpstream, -1)) {
+			return -1;
+		}
+		enc->len += tilelen;
+
+		jas_stream_close(enc->tmpstream);
+		enc->tmpstream = 0;
+
+		jpc_enc_tile_destroy(enc->curtile);
+		enc->curtile = 0;
+
+	}
+
+	return 0;
+}
+
+int jpc_enc_encodetiledata(jpc_enc_t *enc)
+{
+assert(enc->tmpstream);
+	if (jpc_enc_encpkts(enc, enc->tmpstream)) {
+		return -1;
+	}
+	return 0;
+}
+
+void quantize(jas_matrix_t *data, jpc_fix_t stepsize)
+{
+	int i;
+	int j;
+	jpc_fix_t t;
+
+	if (stepsize == jpc_inttofix(1)) {
+		return;
+	}
+
+	for (i = 0; i < jas_matrix_numrows(data); ++i) {
+		for (j = 0; j < jas_matrix_numcols(data); ++j) {
+			t = jas_matrix_get(data, i, j);
+
+{
+	if (t < 0) {
+		t = jpc_fix_neg(jpc_fix_div(jpc_fix_neg(t), stepsize));
+	} else {
+		t = jpc_fix_div(t, stepsize);
+	}
+}
+
+			jas_matrix_set(data, i, j, t);
+		}
+	}
+}
+
+static void calcrdslopes(jpc_enc_cblk_t *cblk)
+{
+	jpc_enc_pass_t *endpasses;
+	jpc_enc_pass_t *pass0;
+	jpc_enc_pass_t *pass1;
+	jpc_enc_pass_t *pass2;
+	jpc_flt_t slope0;
+	jpc_flt_t slope;
+	jpc_flt_t dd;
+	long dr;
+
+	endpasses = &cblk->passes[cblk->numpasses];
+	pass2 = cblk->passes;
+	slope0 = 0;
+	while (pass2 != endpasses) {
+		pass0 = 0;
+		for (pass1 = cblk->passes; pass1 != endpasses; ++pass1) {
+			dd = pass1->cumwmsedec;
+			dr = pass1->end;
+			if (pass0) {
+				dd -= pass0->cumwmsedec;
+				dr -= pass0->end;
+			}
+			if (dd <= 0) {
+				pass1->rdslope = JPC_BADRDSLOPE;
+				if (pass1 >= pass2) {
+					pass2 = &pass1[1];
+				}
+				continue;
+			}
+			if (pass1 < pass2 && pass1->rdslope <= 0) {
+				continue;
+			}
+			if (!dr) {
+				assert(pass0);
+				pass0->rdslope = 0;
+				break;
+			}
+			slope = dd / dr;
+			if (pass0 && slope >= slope0) {
+				pass0->rdslope = 0;
+				break;
+			}
+			pass1->rdslope = slope;
+			if (pass1 >= pass2) {
+				pass2 = &pass1[1];
+			}
+			pass0 = pass1;
+			slope0 = slope;
+		}
+	}
+
+#if 0
+	for (pass0 = cblk->passes; pass0 != endpasses; ++pass0) {
+if (pass0->rdslope > 0.0) {
+		fprintf(stderr, "pass %02d nmsedec=%lf dec=%lf end=%d %lf\n", pass0 - cblk->passes,
+		  fixtodbl(pass0->nmsedec), pass0->wmsedec, pass0->end, pass0->rdslope);
+}
+	}
+#endif
+}
+
+static void dump_layeringinfo(jpc_enc_t *enc)
+{
+
+	jpc_enc_tcmpt_t *tcmpt;
+	uint_fast16_t tcmptno;
+	jpc_enc_rlvl_t *rlvl;
+	uint_fast16_t rlvlno;
+	jpc_enc_band_t *band;
+	uint_fast16_t bandno;
+	jpc_enc_prc_t *prc;
+	uint_fast32_t prcno;
+	jpc_enc_cblk_t *cblk;
+	uint_fast16_t cblkno;
+	jpc_enc_pass_t *pass;
+	uint_fast16_t passno;
+	int lyrno;
+	jpc_enc_tile_t *tile;
+
+	tile = enc->curtile;
+
+	for (lyrno = 0; lyrno < tile->numlyrs; ++lyrno) {
+		fprintf(stderr, "lyrno = %02d\n", lyrno);
+		for (tcmptno = 0, tcmpt = tile->tcmpts; tcmptno < tile->numtcmpts;
+		  ++tcmptno, ++tcmpt) {
+			for (rlvlno = 0, rlvl = tcmpt->rlvls; rlvlno < tcmpt->numrlvls;
+			  ++rlvlno, ++rlvl) {
+				if (!rlvl->bands) {
+					continue;
+				}
+				for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
+				  ++bandno, ++band) {
+					if (!band->data) {
+						continue;
+					}
+					for (prcno = 0, prc = band->prcs; prcno < rlvl->numprcs;
+					  ++prcno, ++prc) {
+						if (!prc->cblks) {
+							continue;
+						}
+						for (cblkno = 0, cblk = prc->cblks; cblkno <
+						  prc->numcblks; ++cblkno, ++cblk) {
+							for (passno = 0, pass = cblk->passes; passno <
+							  cblk->numpasses && pass->lyrno == lyrno;
+							  ++passno, ++pass) {
+								fprintf(stderr, "lyrno=%02d cmptno=%02d rlvlno=%02d bandno=%02d prcno=%02d cblkno=%03d passno=%03d\n", lyrno, tcmptno, rlvlno, bandno, prcno, cblkno, passno);
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+}
+
+int rateallocate(jpc_enc_t *enc, int numlyrs, uint_fast32_t *cumlens)
+{
+	jpc_flt_t lo;
+	jpc_flt_t hi;
+	jas_stream_t *out;
+	long cumlen;
+	int lyrno;
+	jpc_flt_t thresh;
+	jpc_flt_t goodthresh;
+	int success;
+	long pos;
+	long oldpos;
+	int numiters;
+
+	jpc_enc_tcmpt_t *comp;
+	jpc_enc_tcmpt_t *endcomps;
+	jpc_enc_rlvl_t *lvl;
+	jpc_enc_rlvl_t *endlvls;
+	jpc_enc_band_t *band;
+	jpc_enc_band_t *endbands;
+	jpc_enc_cblk_t *cblk;
+	jpc_enc_cblk_t *endcblks;
+	jpc_enc_pass_t *pass;
+	jpc_enc_pass_t *endpasses;
+	jpc_enc_pass_t *pass1;
+	jpc_flt_t mxrdslope;
+	jpc_flt_t mnrdslope;
+	jpc_enc_tile_t *tile;
+	jpc_enc_prc_t *prc;
+	uint_fast32_t prcno;
+
+	tile = enc->curtile;
+
+	for (lyrno = 1; lyrno < numlyrs - 1; ++lyrno) {
+		if (cumlens[lyrno - 1] > cumlens[lyrno]) {
+			abort();
+		}
+	}
+
+	if (!(out = jas_stream_memopen(0, 0))) {
+		return -1;
+	}
+
+
+	/* Find minimum and maximum R-D slope values. */
+	mnrdslope = DBL_MAX;
+	mxrdslope = 0;
+	endcomps = &tile->tcmpts[tile->numtcmpts];
+	for (comp = tile->tcmpts; comp != endcomps; ++comp) {
+		endlvls = &comp->rlvls[comp->numrlvls];
+		for (lvl = comp->rlvls; lvl != endlvls; ++lvl) {
+			if (!lvl->bands) {
+				continue;
+			}
+			endbands = &lvl->bands[lvl->numbands];
+			for (band = lvl->bands; band != endbands; ++band) {
+				if (!band->data) {
+					continue;
+				}
+				for (prcno = 0, prc = band->prcs; prcno < lvl->numprcs; ++prcno, ++prc) {
+					if (!prc->cblks) {
+						continue;
+					}
+					endcblks = &prc->cblks[prc->numcblks];
+					for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
+						calcrdslopes(cblk);
+						endpasses = &cblk->passes[cblk->numpasses];
+						for (pass = cblk->passes; pass != endpasses; ++pass) {
+							if (pass->rdslope > 0) {
+								if (pass->rdslope < mnrdslope) {
+									mnrdslope = pass->rdslope;
+								}
+								if (pass->rdslope > mxrdslope) {
+									mxrdslope = pass->rdslope;
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+if (jas_getdbglevel()) {
+	fprintf(stderr, "min rdslope = %f max rdslope = %f\n", mnrdslope, mxrdslope);
+}
+
+	jpc_init_t2state(enc, 1);
+
+	for (lyrno = 0; lyrno < numlyrs; ++lyrno) {
+
+		lo = mnrdslope;
+		hi = mxrdslope;
+
+		success = 0;
+		goodthresh = 0;
+		numiters = 0;
+
+		do {
+
+			cumlen = cumlens[lyrno];
+			if (cumlen == UINT_FAST32_MAX) {
+				/* Only the last layer can be free of a rate
+				  constraint (e.g., for lossless coding). */
+				assert(lyrno == numlyrs - 1);
+				goodthresh = -1;
+				success = 1;
+				break;
+			}
+
+			thresh = (lo + hi) / 2;
+
+			/* Save the tier 2 coding state. */
+			jpc_save_t2state(enc);
+			oldpos = jas_stream_tell(out);
+			assert(oldpos >= 0);
+
+			/* Assign all passes with R-D slopes greater than or
+			  equal to the current threshold to this layer. */
+			endcomps = &tile->tcmpts[tile->numtcmpts];
+			for (comp = tile->tcmpts; comp != endcomps; ++comp) {
+				endlvls = &comp->rlvls[comp->numrlvls];
+				for (lvl = comp->rlvls; lvl != endlvls; ++lvl) {
+					if (!lvl->bands) {
+						continue;
+					}
+					endbands = &lvl->bands[lvl->numbands];
+					for (band = lvl->bands; band != endbands; ++band) {
+						if (!band->data) {
+							continue;
+						}
+						for (prcno = 0, prc = band->prcs; prcno < lvl->numprcs; ++prcno, ++prc) {
+							if (!prc->cblks) {
+								continue;
+							}
+							endcblks = &prc->cblks[prc->numcblks];
+							for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
+								if (cblk->curpass) {
+									endpasses = &cblk->passes[cblk->numpasses];
+									pass1 = cblk->curpass;
+									for (pass = cblk->curpass; pass != endpasses; ++pass) {
+										if (pass->rdslope >= thresh) {
+											pass1 = &pass[1];
+										}
+									}
+									for (pass = cblk->curpass; pass != pass1; ++pass) {
+										pass->lyrno = lyrno;
+									}
+									for (; pass != endpasses; ++pass) {
+										pass->lyrno = -1;
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+
+			/* Perform tier 2 coding. */
+			endcomps = &tile->tcmpts[tile->numtcmpts];
+			for (comp = tile->tcmpts; comp != endcomps; ++comp) {
+				endlvls = &comp->rlvls[comp->numrlvls];
+				for (lvl = comp->rlvls; lvl != endlvls; ++lvl) {
+					if (!lvl->bands) {
+						continue;
+					}
+					for (prcno = 0; prcno < lvl->numprcs; ++prcno) {
+						if (jpc_enc_encpkt(enc, out, comp - tile->tcmpts, lvl - comp->rlvls, prcno, lyrno)) {
+							return -1;
+						}
+					}
+				}
+			}
+
+			pos = jas_stream_tell(out);
+
+			/* Check the rate constraint. */
+			assert(pos >= 0);
+			if (pos > cumlen) {
+				/* The rate is too high. */
+				lo = thresh;
+			} else if (pos <= cumlen) {
+				/* The rate is low enough, so try higher. */
+				hi = thresh;
+				if (!success || thresh < goodthresh) {
+					goodthresh = thresh;
+					success = 1;
+				}
+			}
+
+			/* Save the tier 2 coding state. */
+			jpc_restore_t2state(enc);
+			if (jas_stream_seek(out, oldpos, SEEK_SET) < 0) {
+				abort();
+			}
+
+if (jas_getdbglevel()) {
+fprintf(stderr, "maxlen=%08ld actuallen=%08ld thresh=%f\n", cumlen, pos, thresh);
+}
+
+			++numiters;
+		} while (lo < hi - 1e-3 && numiters < 32);
+
+		if (!success) {
+			fprintf(stderr, "warning: empty layer generated\n");
+		}
+
+if (jas_getdbglevel()) {
+fprintf(stderr, "success %d goodthresh %f\n", success, goodthresh);
+}
+
+		/* Assign all passes with R-D slopes greater than or
+		  equal to the selected threshold to this layer. */
+		endcomps = &tile->tcmpts[tile->numtcmpts];
+		for (comp = tile->tcmpts; comp != endcomps; ++comp) {
+			endlvls = &comp->rlvls[comp->numrlvls];
+			for (lvl = comp->rlvls; lvl != endlvls; ++lvl) {
+if (!lvl->bands) {
+	continue;
+}
+				endbands = &lvl->bands[lvl->numbands];
+				for (band = lvl->bands; band != endbands; ++band) {
+					if (!band->data) {
+						continue;
+					}
+					for (prcno = 0, prc = band->prcs; prcno < lvl->numprcs; ++prcno, ++prc) {
+						if (!prc->cblks) {
+							continue;
+						}
+						endcblks = &prc->cblks[prc->numcblks];
+						for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
+							if (cblk->curpass) {
+								endpasses = &cblk->passes[cblk->numpasses];
+								pass1 = cblk->curpass;
+								if (success) {
+									for (pass = cblk->curpass; pass != endpasses; ++pass) {
+										if (pass->rdslope >= goodthresh) {
+											pass1 = &pass[1];
+										}
+									}
+								}
+								for (pass = cblk->curpass; pass != pass1; ++pass) {
+									pass->lyrno = lyrno;
+								}
+								for (; pass != endpasses; ++pass) {
+									pass->lyrno = -1;
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+
+		/* Perform tier 2 coding. */
+		endcomps = &tile->tcmpts[tile->numtcmpts];
+		for (comp = tile->tcmpts; comp != endcomps; ++comp) {
+			endlvls = &comp->rlvls[comp->numrlvls];
+			for (lvl = comp->rlvls; lvl != endlvls; ++lvl) {
+				if (!lvl->bands) {
+					continue;
+				}
+				for (prcno = 0; prcno < lvl->numprcs; ++prcno) {
+					if (jpc_enc_encpkt(enc, out, comp - tile->tcmpts, lvl - comp->rlvls, prcno, lyrno)) {
+						return -1;
+					}
+				}
+			}
+		}
+	}
+
+	if (jas_getdbglevel() >= 5) {
+		dump_layeringinfo(enc);
+	}
+
+	jas_stream_close(out);
+
+	JAS_DBGLOG(10, ("done doing rateallocation\n"));
+#if 0
+fprintf(stderr, "DONE RATE ALLOCATE\n");
+#endif
+
+	return 0;
+}
+
+/******************************************************************************\
+* Tile constructors and destructors.
+\******************************************************************************/
+
+jpc_enc_tile_t *jpc_enc_tile_create(jpc_enc_cp_t *cp, jas_image_t *image, int tileno)
+{
+	jpc_enc_tile_t *tile;
+	uint_fast32_t htileno;
+	uint_fast32_t vtileno;
+	uint_fast16_t lyrno;
+	uint_fast16_t cmptno;
+	jpc_enc_tcmpt_t *tcmpt;
+
+	if (!(tile = jas_malloc(sizeof(jpc_enc_tile_t)))) {
+		goto error;
+	}
+
+	/* Initialize a few members used in error recovery. */
+	tile->tcmpts = 0;
+	tile->lyrsizes = 0;
+	tile->numtcmpts = cp->numcmpts;
+	tile->pi = 0;
+
+	tile->tileno = tileno;
+	htileno = tileno % cp->numhtiles;
+	vtileno = tileno / cp->numhtiles;
+
+	/* Calculate the coordinates of the top-left and bottom-right
+	  corners of the tile. */
+	tile->tlx = JAS_MAX(cp->tilegrdoffx + htileno * cp->tilewidth,
+	  cp->imgareatlx);
+	tile->tly = JAS_MAX(cp->tilegrdoffy + vtileno * cp->tileheight,
+	  cp->imgareatly);
+	tile->brx = JAS_MIN(cp->tilegrdoffx + (htileno + 1) * cp->tilewidth,
+	  cp->refgrdwidth);
+	tile->bry = JAS_MIN(cp->tilegrdoffy + (vtileno + 1) * cp->tileheight,
+	  cp->refgrdheight);
+
+	/* Initialize some tile coding parameters. */
+	tile->intmode = cp->tcp.intmode;
+	tile->csty = cp->tcp.csty;
+	tile->prg = cp->tcp.prg;
+	tile->mctid = cp->tcp.mctid;
+
+	tile->numlyrs = cp->tcp.numlyrs;
+	if (!(tile->lyrsizes = jas_malloc(tile->numlyrs *
+	  sizeof(uint_fast32_t)))) {
+		goto error;
+	}
+	for (lyrno = 0; lyrno < tile->numlyrs; ++lyrno) {
+		tile->lyrsizes[lyrno] = 0;
+	}
+
+	/* Allocate an array for the per-tile-component information. */
+	if (!(tile->tcmpts = jas_malloc(cp->numcmpts * sizeof(jpc_enc_tcmpt_t)))) {
+		goto error;
+	}
+	/* Initialize a few members critical for error recovery. */
+	for (cmptno = 0, tcmpt = tile->tcmpts; cmptno < cp->numcmpts;
+	  ++cmptno, ++tcmpt) {
+		tcmpt->rlvls = 0;
+		tcmpt->tsfb = 0;
+		tcmpt->data = 0;
+	}
+	/* Initialize the per-tile-component information. */
+	for (cmptno = 0, tcmpt = tile->tcmpts; cmptno < cp->numcmpts;
+	  ++cmptno, ++tcmpt) {
+		if (!tcmpt_create(tcmpt, cp, image, tile)) {
+			goto error;
+		}
+	}
+
+	/* Initialize the synthesis weights for the MCT. */
+	switch (tile->mctid) {
+	case JPC_MCT_RCT:
+		tile->tcmpts[0].synweight = jpc_dbltofix(sqrt(3.0));
+		tile->tcmpts[1].synweight = jpc_dbltofix(sqrt(0.6875));
+		tile->tcmpts[2].synweight = jpc_dbltofix(sqrt(0.6875));
+		break;
+	case JPC_MCT_ICT:
+		tile->tcmpts[0].synweight = jpc_dbltofix(sqrt(3.0000));
+		tile->tcmpts[1].synweight = jpc_dbltofix(sqrt(3.2584));
+		tile->tcmpts[2].synweight = jpc_dbltofix(sqrt(2.4755));
+		break;
+	default:
+	case JPC_MCT_NONE:
+		for (cmptno = 0, tcmpt = tile->tcmpts; cmptno < cp->numcmpts;
+		  ++cmptno, ++tcmpt) {
+			tcmpt->synweight = JPC_FIX_ONE;
+		}
+		break;
+	}
+
+	if (!(tile->pi = jpc_enc_pi_create(cp, tile))) {
+		goto error;
+	}
+
+	return tile;
+
+error:
+
+	if (tile) {
+		jpc_enc_tile_destroy(tile);
+	}
+	return 0;
+}
+
+void jpc_enc_tile_destroy(jpc_enc_tile_t *tile)
+{
+	jpc_enc_tcmpt_t *tcmpt;
+	uint_fast16_t cmptno;
+
+	if (tile->tcmpts) {
+		for (cmptno = 0, tcmpt = tile->tcmpts; cmptno <
+		  tile->numtcmpts; ++cmptno, ++tcmpt) {
+			tcmpt_destroy(tcmpt);
+		}
+		jas_free(tile->tcmpts);
+	}
+	if (tile->lyrsizes) {
+		jas_free(tile->lyrsizes);
+	}
+	if (tile->pi) {
+		jpc_pi_destroy(tile->pi);
+	}
+	jas_free(tile);
+}
+
+static jpc_enc_tcmpt_t *tcmpt_create(jpc_enc_tcmpt_t *tcmpt, jpc_enc_cp_t *cp,
+  jas_image_t *image, jpc_enc_tile_t *tile)
+{
+	uint_fast16_t cmptno;
+	uint_fast16_t rlvlno;
+	jpc_enc_rlvl_t *rlvl;
+	uint_fast32_t tlx;
+	uint_fast32_t tly;
+	uint_fast32_t brx;
+	uint_fast32_t bry;
+	uint_fast32_t cmpttlx;
+	uint_fast32_t cmpttly;
+	jpc_enc_ccp_t *ccp;
+	jpc_tsfb_band_t bandinfos[JPC_MAXBANDS];
+
+	tcmpt->tile = tile;
+	tcmpt->tsfb = 0;
+	tcmpt->data = 0;
+	tcmpt->rlvls = 0;
+
+	/* Deduce the component number. */
+	cmptno = tcmpt - tile->tcmpts;
+
+	ccp = &cp->ccps[cmptno];
+
+	/* Compute the coordinates of the top-left and bottom-right
+	  corners of this tile-component. */
+	tlx = JPC_CEILDIV(tile->tlx, ccp->sampgrdstepx);
+	tly = JPC_CEILDIV(tile->tly, ccp->sampgrdstepy);
+	brx = JPC_CEILDIV(tile->brx, ccp->sampgrdstepx);
+	bry = JPC_CEILDIV(tile->bry, ccp->sampgrdstepy);
+
+	/* Create a sequence to hold the tile-component sample data. */
+	if (!(tcmpt->data = jas_seq2d_create(tlx, tly, brx, bry))) {
+		goto error;
+	}
+
+	/* Get the image data associated with this tile-component. */
+	cmpttlx = JPC_CEILDIV(cp->imgareatlx, ccp->sampgrdstepx);
+	cmpttly = JPC_CEILDIV(cp->imgareatly, ccp->sampgrdstepy);
+	if (jas_image_readcmpt(image, cmptno, tlx - cmpttlx, tly - cmpttly,
+	  brx - tlx, bry - tly, tcmpt->data)) {
+		goto error;
+	}
+
+	tcmpt->synweight = 0;
+	tcmpt->qmfbid = cp->tccp.qmfbid;
+	tcmpt->numrlvls = cp->tccp.maxrlvls;
+	tcmpt->numbands = 3 * tcmpt->numrlvls - 2;
+	if (!(tcmpt->tsfb = jpc_cod_gettsfb(tcmpt->qmfbid, tcmpt->numrlvls - 1))) {
+		goto error;
+	}
+
+	for (rlvlno = 0; rlvlno < tcmpt->numrlvls; ++rlvlno) {
+		tcmpt->prcwidthexpns[rlvlno] = cp->tccp.prcwidthexpns[rlvlno];
+		tcmpt->prcheightexpns[rlvlno] = cp->tccp.prcheightexpns[rlvlno];
+	}
+	tcmpt->cblkwidthexpn = cp->tccp.cblkwidthexpn;
+	tcmpt->cblkheightexpn = cp->tccp.cblkheightexpn;
+	tcmpt->cblksty = cp->tccp.cblksty;
+	tcmpt->csty = cp->tccp.csty;
+
+	tcmpt->numstepsizes = tcmpt->numbands;
+	assert(tcmpt->numstepsizes <= JPC_MAXBANDS);
+	memset(tcmpt->stepsizes, 0, sizeof(tcmpt->numstepsizes *
+	  sizeof(uint_fast16_t)));
+
+	/* Retrieve information about the various bands. */
+	jpc_tsfb_getbands(tcmpt->tsfb, jas_seq2d_xstart(tcmpt->data),
+	  jas_seq2d_ystart(tcmpt->data), jas_seq2d_xend(tcmpt->data),
+	  jas_seq2d_yend(tcmpt->data), bandinfos);
+
+	if (!(tcmpt->rlvls = jas_malloc(tcmpt->numrlvls * sizeof(jpc_enc_rlvl_t)))) {
+		goto error;
+	}
+	for (rlvlno = 0, rlvl = tcmpt->rlvls; rlvlno < tcmpt->numrlvls;
+	  ++rlvlno, ++rlvl) {
+		rlvl->bands = 0;
+		rlvl->tcmpt = tcmpt;
+	}
+	for (rlvlno = 0, rlvl = tcmpt->rlvls; rlvlno < tcmpt->numrlvls;
+	  ++rlvlno, ++rlvl) {
+		if (!rlvl_create(rlvl, cp, tcmpt, bandinfos)) {
+			goto error;
+		}
+	}
+
+	return tcmpt;
+
+error:
+
+	tcmpt_destroy(tcmpt);
+	return 0;
+
+}
+
+static void tcmpt_destroy(jpc_enc_tcmpt_t *tcmpt)
+{
+	jpc_enc_rlvl_t *rlvl;
+	uint_fast16_t rlvlno;
+
+	if (tcmpt->rlvls) {
+		for (rlvlno = 0, rlvl = tcmpt->rlvls; rlvlno < tcmpt->numrlvls;
+		  ++rlvlno, ++rlvl) {
+			rlvl_destroy(rlvl);
+		}
+		jas_free(tcmpt->rlvls);
+	}
+
+	if (tcmpt->data) {
+		jas_seq2d_destroy(tcmpt->data);
+	}
+	if (tcmpt->tsfb) {
+		jpc_tsfb_destroy(tcmpt->tsfb);
+	}
+}
+
+static jpc_enc_rlvl_t *rlvl_create(jpc_enc_rlvl_t *rlvl, jpc_enc_cp_t *cp,
+  jpc_enc_tcmpt_t *tcmpt, jpc_tsfb_band_t *bandinfos)
+{
+	uint_fast16_t rlvlno;
+	uint_fast32_t tlprctlx;
+	uint_fast32_t tlprctly;
+	uint_fast32_t brprcbrx;
+	uint_fast32_t brprcbry;
+	uint_fast16_t bandno;
+	jpc_enc_band_t *band;
+
+	/* Deduce the resolution level. */
+	rlvlno = rlvl - tcmpt->rlvls;
+
+	/* Initialize members required for error recovery. */
+	rlvl->bands = 0;
+	rlvl->tcmpt = tcmpt;
+
+	/* Compute the coordinates of the top-left and bottom-right
+	  corners of the tile-component at this resolution. */
+	rlvl->tlx = JPC_CEILDIVPOW2(jas_seq2d_xstart(tcmpt->data), tcmpt->numrlvls -
+	  1 - rlvlno);
+	rlvl->tly = JPC_CEILDIVPOW2(jas_seq2d_ystart(tcmpt->data), tcmpt->numrlvls -
+	  1 - rlvlno);
+	rlvl->brx = JPC_CEILDIVPOW2(jas_seq2d_xend(tcmpt->data), tcmpt->numrlvls -
+	  1 - rlvlno);
+	rlvl->bry = JPC_CEILDIVPOW2(jas_seq2d_yend(tcmpt->data), tcmpt->numrlvls -
+	  1 - rlvlno);
+
+	if (rlvl->tlx >= rlvl->brx || rlvl->tly >= rlvl->bry) {
+		rlvl->numhprcs = 0;
+		rlvl->numvprcs = 0;
+		rlvl->numprcs = 0;
+		return rlvl;
+	}
+
+	rlvl->numbands = (!rlvlno) ? 1 : 3;
+	rlvl->prcwidthexpn = cp->tccp.prcwidthexpns[rlvlno];
+	rlvl->prcheightexpn = cp->tccp.prcheightexpns[rlvlno];
+	if (!rlvlno) {
+		rlvl->cbgwidthexpn = rlvl->prcwidthexpn;
+		rlvl->cbgheightexpn = rlvl->prcheightexpn;
+	} else {
+		rlvl->cbgwidthexpn = rlvl->prcwidthexpn - 1;
+		rlvl->cbgheightexpn = rlvl->prcheightexpn - 1;
+	}
+	rlvl->cblkwidthexpn = JAS_MIN(cp->tccp.cblkwidthexpn, rlvl->cbgwidthexpn);
+	rlvl->cblkheightexpn = JAS_MIN(cp->tccp.cblkheightexpn, rlvl->cbgheightexpn);
+
+	/* Compute the number of precincts. */
+	tlprctlx = JPC_FLOORTOMULTPOW2(rlvl->tlx, rlvl->prcwidthexpn);
+	tlprctly = JPC_FLOORTOMULTPOW2(rlvl->tly, rlvl->prcheightexpn);
+	brprcbrx = JPC_CEILTOMULTPOW2(rlvl->brx, rlvl->prcwidthexpn);
+	brprcbry = JPC_CEILTOMULTPOW2(rlvl->bry, rlvl->prcheightexpn);
+	rlvl->numhprcs = JPC_FLOORDIVPOW2(brprcbrx - tlprctlx, rlvl->prcwidthexpn);
+	rlvl->numvprcs = JPC_FLOORDIVPOW2(brprcbry - tlprctly, rlvl->prcheightexpn);
+	rlvl->numprcs = rlvl->numhprcs * rlvl->numvprcs;
+
+	if (!(rlvl->bands = jas_malloc(rlvl->numbands * sizeof(jpc_enc_band_t)))) {
+		goto error;
+	}
+	for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
+	  ++bandno, ++band) {
+		band->prcs = 0;
+		band->data = 0;
+		band->rlvl = rlvl;
+	}
+	for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
+	  ++bandno, ++band) {
+		if (!band_create(band, cp, rlvl, bandinfos)) {
+			goto error;
+		}
+	}
+
+	return rlvl;
+error:
+
+	rlvl_destroy(rlvl);
+	return 0;
+}
+
+static void rlvl_destroy(jpc_enc_rlvl_t *rlvl)
+{
+	jpc_enc_band_t *band;
+	uint_fast16_t bandno;
+
+	if (rlvl->bands) {
+		for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
+		  ++bandno, ++band) {
+			band_destroy(band);
+		}
+		jas_free(rlvl->bands);
+	}
+}
+
+static jpc_enc_band_t *band_create(jpc_enc_band_t *band, jpc_enc_cp_t *cp,
+  jpc_enc_rlvl_t *rlvl, jpc_tsfb_band_t *bandinfos)
+{
+	uint_fast16_t bandno;
+	uint_fast16_t gblbandno;
+	uint_fast16_t rlvlno;
+	jpc_tsfb_band_t *bandinfo;
+	jpc_enc_tcmpt_t *tcmpt;
+	uint_fast32_t prcno;
+	jpc_enc_prc_t *prc;
+
+	tcmpt = rlvl->tcmpt;
+	band->data = 0;
+	band->prcs = 0;
+	band->rlvl = rlvl;
+
+	/* Deduce the resolution level and band number. */
+	rlvlno = rlvl - rlvl->tcmpt->rlvls;
+	bandno = band - rlvl->bands;
+	gblbandno = (!rlvlno) ? 0 : (3 * (rlvlno - 1) + bandno + 1);
+
+	bandinfo = &bandinfos[gblbandno];
+
+if (bandinfo->xstart != bandinfo->xend && bandinfo->ystart != bandinfo->yend) {
+	if (!(band->data = jas_seq2d_create(0, 0, 0, 0))) {
+		goto error;
+	}
+	jas_seq2d_bindsub(band->data, tcmpt->data, bandinfo->locxstart,
+	  bandinfo->locystart, bandinfo->locxend, bandinfo->locyend);
+	jas_seq2d_setshift(band->data, bandinfo->xstart, bandinfo->ystart);
+}
+	band->orient = bandinfo->orient;
+	band->analgain = JPC_NOMINALGAIN(cp->tccp.qmfbid, tcmpt->numrlvls, rlvlno,
+	  band->orient);
+	band->numbps = 0;
+	band->absstepsize = 0;
+	band->stepsize = 0;
+	band->synweight = bandinfo->synenergywt;
+
+if (band->data) {
+	if (!(band->prcs = jas_malloc(rlvl->numprcs * sizeof(jpc_enc_prc_t)))) {
+		goto error;
+	}
+	for (prcno = 0, prc = band->prcs; prcno < rlvl->numprcs; ++prcno,
+	  ++prc) {
+		prc->cblks = 0;
+		prc->incltree = 0;
+		prc->nlibtree = 0;
+		prc->savincltree = 0;
+		prc->savnlibtree = 0;
+		prc->band = band;
+	}
+	for (prcno = 0, prc = band->prcs; prcno < rlvl->numprcs; ++prcno,
+	  ++prc) {
+		if (!prc_create(prc, cp, band)) {
+			goto error;
+		}
+	}
+}
+
+	return band;
+
+error:
+	band_destroy(band);
+	return 0;
+}
+
+static void band_destroy(jpc_enc_band_t *band)
+{
+	jpc_enc_prc_t *prc;
+	jpc_enc_rlvl_t *rlvl;
+	uint_fast32_t prcno;
+
+	if (band->prcs) {
+		rlvl = band->rlvl;
+		for (prcno = 0, prc = band->prcs; prcno < rlvl->numprcs;
+		  ++prcno, ++prc) {
+			prc_destroy(prc);
+		}
+		jas_free(band->prcs);
+	}
+	if (band->data) {
+		jas_seq2d_destroy(band->data);
+	}
+}
+
+static jpc_enc_prc_t *prc_create(jpc_enc_prc_t *prc, jpc_enc_cp_t *cp, jpc_enc_band_t *band)
+{
+	uint_fast32_t prcno;
+	uint_fast32_t prcxind;
+	uint_fast32_t prcyind;
+	uint_fast32_t cbgtlx;
+	uint_fast32_t cbgtly;
+	uint_fast32_t tlprctlx;
+	uint_fast32_t tlprctly;
+	uint_fast32_t tlcbgtlx;
+	uint_fast32_t tlcbgtly;
+	uint_fast16_t rlvlno;
+	jpc_enc_rlvl_t *rlvl;
+	uint_fast32_t tlcblktlx;
+	uint_fast32_t tlcblktly;
+	uint_fast32_t brcblkbrx;
+	uint_fast32_t brcblkbry;
+	uint_fast32_t cblkno;
+	jpc_enc_cblk_t *cblk;
+	jpc_enc_tcmpt_t *tcmpt;
+
+	prc->cblks = 0;
+	prc->incltree = 0;
+	prc->savincltree = 0;
+	prc->nlibtree = 0;
+	prc->savnlibtree = 0;
+
+	rlvl = band->rlvl;
+	tcmpt = rlvl->tcmpt;
+rlvlno = rlvl - tcmpt->rlvls;
+	prcno = prc - band->prcs;
+	prcxind = prcno % rlvl->numhprcs;
+	prcyind = prcno / rlvl->numhprcs;
+	prc->band = band;
+
+tlprctlx = JPC_FLOORTOMULTPOW2(rlvl->tlx, rlvl->prcwidthexpn);
+tlprctly = JPC_FLOORTOMULTPOW2(rlvl->tly, rlvl->prcheightexpn);
+if (!rlvlno) {
+	tlcbgtlx = tlprctlx;
+	tlcbgtly = tlprctly;
+} else {
+	tlcbgtlx = JPC_CEILDIVPOW2(tlprctlx, 1);
+	tlcbgtly = JPC_CEILDIVPOW2(tlprctly, 1);
+}
+
+	/* Compute the coordinates of the top-left and bottom-right
+	  corners of the precinct. */
+	cbgtlx = tlcbgtlx + (prcxind << rlvl->cbgwidthexpn);
+	cbgtly = tlcbgtly + (prcyind << rlvl->cbgheightexpn);
+	prc->tlx = JAS_MAX(jas_seq2d_xstart(band->data), cbgtlx);
+	prc->tly = JAS_MAX(jas_seq2d_ystart(band->data), cbgtly);
+	prc->brx = JAS_MIN(jas_seq2d_xend(band->data), cbgtlx +
+	  (1 << rlvl->cbgwidthexpn));
+	prc->bry = JAS_MIN(jas_seq2d_yend(band->data), cbgtly +
+	  (1 << rlvl->cbgheightexpn));
+
+	if (prc->tlx < prc->brx && prc->tly < prc->bry) {
+		/* The precinct contains at least one code block. */
+
+		tlcblktlx = JPC_FLOORTOMULTPOW2(prc->tlx, rlvl->cblkwidthexpn);
+		tlcblktly = JPC_FLOORTOMULTPOW2(prc->tly, rlvl->cblkheightexpn);
+		brcblkbrx = JPC_CEILTOMULTPOW2(prc->brx, rlvl->cblkwidthexpn);
+		brcblkbry = JPC_CEILTOMULTPOW2(prc->bry, rlvl->cblkheightexpn);
+		prc->numhcblks = JPC_FLOORDIVPOW2(brcblkbrx - tlcblktlx,
+		  rlvl->cblkwidthexpn);
+		prc->numvcblks = JPC_FLOORDIVPOW2(brcblkbry - tlcblktly,
+		  rlvl->cblkheightexpn);
+		prc->numcblks = prc->numhcblks * prc->numvcblks;
+
+		if (!(prc->incltree = jpc_tagtree_create(prc->numhcblks,
+		  prc->numvcblks))) {
+			goto error;
+		}
+		if (!(prc->nlibtree = jpc_tagtree_create(prc->numhcblks,
+		  prc->numvcblks))) {
+			goto error;
+		}
+		if (!(prc->savincltree = jpc_tagtree_create(prc->numhcblks,
+		  prc->numvcblks))) {
+			goto error;
+		}
+		if (!(prc->savnlibtree = jpc_tagtree_create(prc->numhcblks,
+		  prc->numvcblks))) {
+			goto error;
+		}
+
+		if (!(prc->cblks = jas_malloc(prc->numcblks * sizeof(jpc_enc_cblk_t)))) {
+			goto error;
+		}
+		for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks;
+		  ++cblkno, ++cblk) {
+			cblk->passes = 0;
+			cblk->stream = 0;
+			cblk->mqenc = 0;
+			cblk->data = 0;
+			cblk->flags = 0;
+			cblk->prc = prc;
+		}
+		for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks;
+		  ++cblkno, ++cblk) {
+			if (!cblk_create(cblk, cp, prc)) {
+				goto error;
+			}
+		}
+	} else {
+		/* The precinct does not contain any code blocks. */
+		prc->tlx = prc->brx;
+		prc->tly = prc->bry;
+		prc->numcblks = 0;
+		prc->numhcblks = 0;
+		prc->numvcblks = 0;
+		prc->cblks = 0;
+		prc->incltree = 0;
+		prc->nlibtree = 0;
+		prc->savincltree = 0;
+		prc->savnlibtree = 0;
+	}
+
+	return prc;
+
+error:
+	prc_destroy(prc);
+	return 0;
+}
+
+static void prc_destroy(jpc_enc_prc_t *prc)
+{
+	jpc_enc_cblk_t *cblk;
+	uint_fast32_t cblkno;
+
+	if (prc->cblks) {
+		for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks;
+		  ++cblkno, ++cblk) {
+			cblk_destroy(cblk);
+		}
+		jas_free(prc->cblks);
+	}
+	if (prc->incltree) {
+		jpc_tagtree_destroy(prc->incltree);
+	}
+	if (prc->nlibtree) {
+		jpc_tagtree_destroy(prc->nlibtree);
+	}
+	if (prc->savincltree) {
+		jpc_tagtree_destroy(prc->savincltree);
+	}
+	if (prc->savnlibtree) {
+		jpc_tagtree_destroy(prc->savnlibtree);
+	}
+}
+
+static jpc_enc_cblk_t *cblk_create(jpc_enc_cblk_t *cblk, jpc_enc_cp_t *cp, jpc_enc_prc_t *prc)
+{
+	jpc_enc_band_t *band;
+	uint_fast32_t cblktlx;
+	uint_fast32_t cblktly;
+	uint_fast32_t cblkbrx;
+	uint_fast32_t cblkbry;
+	jpc_enc_rlvl_t *rlvl;
+	uint_fast32_t cblkxind;
+	uint_fast32_t cblkyind;
+	uint_fast32_t cblkno;
+	uint_fast32_t tlcblktlx;
+	uint_fast32_t tlcblktly;
+
+	cblkno = cblk - prc->cblks;
+	cblkxind = cblkno % prc->numhcblks;
+	cblkyind = cblkno / prc->numhcblks;
+	rlvl = prc->band->rlvl;
+	cblk->prc = prc;
+
+	cblk->numpasses = 0;
+	cblk->passes = 0;
+	cblk->numencpasses = 0;
+	cblk->numimsbs = 0;
+	cblk->numlenbits = 0;
+	cblk->stream = 0;
+	cblk->mqenc = 0;
+	cblk->flags = 0;
+	cblk->numbps = 0;
+	cblk->curpass = 0;
+	cblk->data = 0;
+	cblk->savedcurpass = 0;
+	cblk->savednumlenbits = 0;
+	cblk->savednumencpasses = 0;
+
+	band = prc->band;
+	tlcblktlx = JPC_FLOORTOMULTPOW2(prc->tlx, rlvl->cblkwidthexpn);
+	tlcblktly = JPC_FLOORTOMULTPOW2(prc->tly, rlvl->cblkheightexpn);
+	cblktlx = JAS_MAX(tlcblktlx + (cblkxind << rlvl->cblkwidthexpn), prc->tlx);
+	cblktly = JAS_MAX(tlcblktly + (cblkyind << rlvl->cblkheightexpn), prc->tly);
+	cblkbrx = JAS_MIN(tlcblktlx + ((cblkxind + 1) << rlvl->cblkwidthexpn),
+	  prc->brx);
+	cblkbry = JAS_MIN(tlcblktly + ((cblkyind + 1) << rlvl->cblkheightexpn),
+	  prc->bry);
+
+	assert(cblktlx < cblkbrx && cblktly < cblkbry);
+	if (!(cblk->data = jas_seq2d_create(0, 0, 0, 0))) {
+		goto error;
+	}
+	jas_seq2d_bindsub(cblk->data, band->data, cblktlx, cblktly, cblkbrx, cblkbry);
+
+	return cblk;
+
+error:
+	cblk_destroy(cblk);
+	return 0;
+}
+
+static void cblk_destroy(jpc_enc_cblk_t *cblk)
+{
+	uint_fast16_t passno;
+	jpc_enc_pass_t *pass;
+	if (cblk->passes) {
+		for (passno = 0, pass = cblk->passes; passno < cblk->numpasses;
+		  ++passno, ++pass) {
+			pass_destroy(pass);
+		}
+		jas_free(cblk->passes);
+	}
+	if (cblk->stream) {
+		jas_stream_close(cblk->stream);
+	}
+	if (cblk->mqenc) {
+		jpc_mqenc_destroy(cblk->mqenc);
+	}
+	if (cblk->data) {
+		jas_seq2d_destroy(cblk->data);
+	}
+	if (cblk->flags) {
+		jas_seq2d_destroy(cblk->flags);
+	}
+}
+
+static void pass_destroy(jpc_enc_pass_t *pass)
+{
+	/* XXX - need to free resources here */
+}
+
+void jpc_enc_dump(jpc_enc_t *enc)
+{
+	jpc_enc_tile_t *tile;
+	jpc_enc_tcmpt_t *tcmpt;
+	jpc_enc_rlvl_t *rlvl;
+	jpc_enc_band_t *band;
+	jpc_enc_prc_t *prc;
+	jpc_enc_cblk_t *cblk;
+	uint_fast16_t cmptno;
+	uint_fast16_t rlvlno;
+	uint_fast16_t bandno;
+	uint_fast32_t prcno;
+	uint_fast32_t cblkno;
+
+	tile = enc->curtile;
+
+	for (cmptno = 0, tcmpt = tile->tcmpts; cmptno < tile->numtcmpts; ++cmptno,
+	  ++tcmpt) {
+		fprintf(stderr, "  tcmpt %5d %5d %5d %5d\n", jas_seq2d_xstart(tcmpt->data), jas_seq2d_ystart(tcmpt->data), jas_seq2d_xend(tcmpt->data), jas_seq2d_yend(tcmpt->data));
+		for (rlvlno = 0, rlvl = tcmpt->rlvls; rlvlno < tcmpt->numrlvls;
+		  ++rlvlno, ++rlvl) {
+			fprintf(stderr, "    rlvl %5d %5d %5d %5d\n", rlvl->tlx, rlvl->tly, rlvl->brx, rlvl->bry);
+			for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
+			  ++bandno, ++band) {
+				if (!band->data) {
+					continue;
+				}
+				fprintf(stderr, "      band %5d %5d %5d %5d\n", jas_seq2d_xstart(band->data), jas_seq2d_ystart(band->data), jas_seq2d_xend(band->data), jas_seq2d_yend(band->data));
+				for (prcno = 0, prc = band->prcs; prcno < rlvl->numprcs;
+				  ++prcno, ++prc) {
+					fprintf(stderr, "        prc %5d %5d %5d %5d (%5d %5d)\n", prc->tlx, prc->tly, prc->brx, prc->bry, prc->brx - prc->tlx, prc->bry - prc->tly);
+					if (!prc->cblks) {
+						continue;
+					}
+					for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks;
+					  ++cblkno, ++cblk) {
+						fprintf(stderr, "         cblk %5d %5d %5d %5d\n", jas_seq2d_xstart(cblk->data), jas_seq2d_ystart(cblk->data), jas_seq2d_xend(cblk->data), jas_seq2d_yend(cblk->data));
+					}
+				}
+			}
+		}
+	}
+}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_enc.h b/converter/other/jpeg2000/libjasper/jpc/jpc_enc.h
new file mode 100644
index 00000000..cfd754c9
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_enc.h
@@ -0,0 +1,695 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef JPC_ENC_H
+#define JPC_ENC_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jasper/jas_seq.h"
+
+#include "jpc_t2cod.h"
+#include "jpc_mqenc.h"
+#include "jpc_cod.h"
+#include "jpc_tagtree.h"
+#include "jpc_cs.h"
+#include "jpc_flt.h"
+#include "jpc_tsfb.h"
+
+/******************************************************************************\
+* Constants.
+\******************************************************************************/
+
+/* The number of bits used in various lookup tables. */
+#define	JPC_NUMEXTRABITS	JPC_NMSEDEC_FRACBITS
+
+/* An invalid R-D slope value. */
+#define	JPC_BADRDSLOPE	(-1)
+
+/******************************************************************************\
+* Coding parameters types.
+\******************************************************************************/
+
+/* Per-component coding paramters. */
+
+typedef struct {
+
+	/* The horizontal sampling period. */
+	uint_fast8_t sampgrdstepx;
+
+	/* The vertical sampling period. */
+	uint_fast8_t sampgrdstepy;
+
+	/* The sample alignment horizontal offset. */
+	uint_fast8_t sampgrdsubstepx;
+
+	/* The sample alignment vertical offset. */
+	uint_fast8_t sampgrdsubstepy;
+
+	/* The precision of the samples. */
+	uint_fast8_t prec;
+
+	/* The signedness of the samples. */
+	bool sgnd;
+
+	/* The number of step sizes. */
+	uint_fast16_t numstepsizes;
+
+	/* The quantizer step sizes. */
+	uint_fast16_t stepsizes[JPC_MAXBANDS];
+
+} jpc_enc_ccp_t;
+
+/* Per-tile coding parameters. */
+
+typedef struct {
+
+	/* The coding mode. */
+	bool intmode;
+
+	/* The coding style (i.e., SOP, EPH). */
+	uint_fast8_t csty;
+
+	/* The progression order. */
+	uint_fast8_t prg;
+
+	/* The multicomponent transform. */
+	uint_fast8_t mctid;
+
+	/* The number of layers. */
+	uint_fast16_t numlyrs;
+
+	/* The normalized bit rates associated with the various
+	  intermediate layers. */
+	jpc_fix_t *ilyrrates;
+
+} jpc_enc_tcp_t;
+
+/* Per tile-component coding parameters. */
+
+typedef struct {
+
+	/* The coding style (i.e., explicit precinct sizes). */
+	uint_fast8_t csty;
+
+	/* The maximum number of resolution levels allowed. */
+	uint_fast8_t maxrlvls;
+
+	/* The exponent for the nominal code block width. */
+	uint_fast16_t cblkwidthexpn;
+
+	/* The exponent for the nominal code block height. */
+	uint_fast16_t cblkheightexpn;
+
+	/* The code block style parameters (e.g., lazy, terminate all,
+	  segmentation symbols, causal, reset probability models). */
+	uint_fast8_t cblksty;
+
+	/* The QMFB. */
+	uint_fast8_t qmfbid;
+
+	/* The precinct width values. */
+	uint_fast16_t prcwidthexpns[JPC_MAXRLVLS];
+
+	/* The precinct height values. */
+	uint_fast16_t prcheightexpns[JPC_MAXRLVLS];
+
+	/* The number of guard bits. */
+	uint_fast8_t numgbits;
+
+} jpc_enc_tccp_t;
+
+/* Coding parameters. */
+
+typedef struct {
+
+	/* The debug level. */
+	int debug;
+
+	/* The horizontal offset from the origin of the reference grid to the
+	  left edge of the image area. */
+	uint_fast32_t imgareatlx;
+
+	/* The vertical offset from the origin of the reference grid to the
+	  top edge of the image area. */
+	uint_fast32_t imgareatly;
+
+	/* The horizontal offset from the origin of the reference grid to the
+	  right edge of the image area (plus one). */
+	uint_fast32_t refgrdwidth;
+
+	/* The vertical offset from the origin of the reference grid to the
+	  bottom edge of the image area (plus one). */
+	uint_fast32_t refgrdheight;
+
+	/* The horizontal offset from the origin of the tile grid to the
+	  origin of the reference grid. */
+	uint_fast32_t tilegrdoffx;
+
+	/* The vertical offset from the origin of the tile grid to the
+	  origin of the reference grid. */
+	uint_fast32_t tilegrdoffy;
+
+	/* The nominal tile width in units of the image reference grid. */
+	uint_fast32_t tilewidth;
+
+	/* The nominal tile height in units of the image reference grid. */
+	uint_fast32_t tileheight;
+
+	/* The number of tiles spanning the image area in the horizontal
+	  direction. */
+	uint_fast32_t numhtiles;
+
+	/* The number of tiles spanning the image area in the vertical
+	  direction. */
+	uint_fast32_t numvtiles;
+
+	/* The number of tiles. */
+	uint_fast32_t numtiles;
+
+	/* The number of components. */
+	uint_fast16_t numcmpts;
+
+	/* The per-component coding parameters. */
+	jpc_enc_ccp_t *ccps;
+
+	/* The per-tile coding parameters. */
+	jpc_enc_tcp_t tcp;
+
+	/* The per-tile-component coding parameters. */
+	jpc_enc_tccp_t tccp;
+
+	/* The target code stream length in bytes. */
+	uint_fast32_t totalsize;
+
+	/* The raw (i.e., uncompressed) size of the image in bytes. */
+	uint_fast32_t rawsize;
+
+} jpc_enc_cp_t;
+
+/******************************************************************************\
+* Encoder class.
+\******************************************************************************/
+
+/* Encoder per-coding-pass state information. */
+
+typedef struct {
+
+	/* The starting offset for this pass. */
+	int start;
+
+	/* The ending offset for this pass. */
+	int end;
+
+	/* The type of data in this pass (i.e., MQ or raw). */
+	int type;
+
+	/* Flag indicating that this pass is terminated. */
+	int term;
+
+	/* The entropy coder state after coding this pass. */
+	jpc_mqencstate_t mqencstate;
+
+	/* The layer to which this pass has been assigned. */
+	int lyrno;
+
+	/* The R-D slope for this pass. */
+	jpc_flt_t rdslope;
+
+	/* The weighted MSE reduction associated with this pass. */
+	jpc_flt_t wmsedec;
+
+	/* The cumulative weighted MSE reduction. */
+	jpc_flt_t cumwmsedec;
+
+	/* The normalized MSE reduction. */
+	long nmsedec;
+
+} jpc_enc_pass_t;
+
+/* Encoder per-code-block state information. */
+
+typedef struct {
+
+	/* The number of passes. */
+	int numpasses;
+
+	/* The per-pass information. */
+	jpc_enc_pass_t *passes;
+
+	/* The number of passes encoded so far. */
+	int numencpasses;
+
+	/* The number of insignificant MSBs. */
+	int numimsbs;
+
+	/* The number of bits used to encode pass data lengths. */
+	int numlenbits;
+
+	/* The byte stream for this code block. */
+	jas_stream_t *stream;
+
+	/* The entropy encoder. */
+	jpc_mqenc_t *mqenc;
+
+	/* The data for this code block. */
+	jas_matrix_t *data;
+
+	/* The state for this code block. */
+	jas_matrix_t *flags;
+
+	/* The number of bit planes required for this code block. */
+	int numbps;
+
+	/* The next pass to be encoded. */
+	jpc_enc_pass_t *curpass;
+
+	/* The per-code-block-group state information. */
+	struct jpc_enc_prc_s *prc;
+
+	/* The saved current pass. */
+	/* This is used by the rate control code. */
+	jpc_enc_pass_t *savedcurpass;
+
+	/* The saved length indicator size. */
+	/* This is used by the rate control code. */
+	int savednumlenbits;
+
+	/* The saved number of encoded passes. */
+	/* This is used by the rate control code. */
+	int savednumencpasses;
+
+} jpc_enc_cblk_t;
+
+/* Encoder per-code-block-group state information. */
+
+typedef struct jpc_enc_prc_s {
+
+	/* The x-coordinate of the top-left corner of the precinct. */
+	uint_fast32_t tlx;
+
+	/* The y-coordinate of the top-left corner of the precinct. */
+	uint_fast32_t tly;
+
+	/* The x-coordinate of the bottom-right corner of the precinct
+	  (plus one). */
+	uint_fast32_t brx;
+
+	/* The y-coordinate of the bottom-right corner of the precinct
+	  (plus one). */
+	uint_fast32_t bry;
+
+	/* The number of code blocks spanning the precinct in the horizontal
+	direction. */
+	int numhcblks;
+
+	/* The number of code blocks spanning the precinct in the vertical
+	direction. */
+	int numvcblks;
+
+	/* The total number of code blocks. */
+	int numcblks;
+
+	/* The per-code-block information. */
+	jpc_enc_cblk_t *cblks;
+
+	/* The inclusion tag tree. */
+	jpc_tagtree_t *incltree;
+
+	/* The insignifcant MSBs tag tree. */
+	jpc_tagtree_t *nlibtree;
+
+	/* The per-band information. */
+	struct jpc_enc_band_s *band;
+
+	/* The saved inclusion tag tree. */
+	/* This is used by rate control. */
+	jpc_tagtree_t *savincltree;
+
+	/* The saved leading-insignificant-bit-planes tag tree. */
+	/* This is used by rate control. */
+	jpc_tagtree_t *savnlibtree;
+
+} jpc_enc_prc_t;
+
+/* Encoder per-band state information. */
+
+typedef struct jpc_enc_band_s {
+
+	/* The per precinct information. */
+	jpc_enc_prc_t *prcs;
+
+	/* The coefficient data for this band. */
+	jas_matrix_t *data;
+
+	/* The orientation of this band (i.e., LL, LH, HL, or HH). */
+	int orient;
+
+	/* The number of bit planes associated with this band. */
+	int numbps;
+
+	/* The quantizer step size. */
+	jpc_fix_t absstepsize;
+
+	/* The encoded quantizer step size. */
+	int stepsize;
+
+	/* The L2 norm of the synthesis basis functions associated with
+	  this band.  (The MCT is not considered in this value.) */
+	jpc_fix_t synweight;
+
+	/* The analysis gain for this band. */
+	int analgain;
+
+	/* The per-resolution-level information. */
+	struct jpc_enc_rlvl_s *rlvl;
+
+} jpc_enc_band_t;
+
+/* Encoder per-resolution-level state information. */
+
+typedef struct jpc_enc_rlvl_s {
+
+	/* The x-coordinate of the top-left corner of the tile-component
+	  at this resolution. */
+	uint_fast32_t tlx;
+
+	/* The y-coordinate of the top-left corner of the tile-component
+	  at this resolution. */
+	uint_fast32_t tly;
+
+	/* The x-coordinate of the bottom-right corner of the tile-component
+	  at this resolution (plus one). */
+	uint_fast32_t brx;
+
+	/* The y-coordinate of the bottom-right corner of the tile-component
+	  at this resolution (plus one). */
+	uint_fast32_t bry;
+
+	/* The exponent value for the nominal precinct width measured
+	  relative to the associated LL band. */
+	int prcwidthexpn;
+
+	/* The exponent value for the nominal precinct height measured
+	  relative to the associated LL band. */
+	int prcheightexpn;
+
+	/* The number of precincts spanning the resolution level in the
+	  horizontal direction. */
+	int numhprcs;
+
+	/* The number of precincts spanning the resolution level in the
+	  vertical direction. */
+	int numvprcs;
+
+	/* The total number of precincts. */
+	int numprcs;
+
+	/* The exponent value for the nominal code block group width.
+	  This quantity is associated with the next lower resolution level
+	  (assuming that there is one). */
+	int cbgwidthexpn;
+
+	/* The exponent value for the nominal code block group height.
+	  This quantity is associated with the next lower resolution level
+	  (assuming that there is one). */
+	int cbgheightexpn;
+
+	/* The exponent value for the code block width. */
+	uint_fast16_t cblkwidthexpn;
+
+	/* The exponent value for the code block height. */
+	uint_fast16_t cblkheightexpn;
+
+	/* The number of bands associated with this resolution level. */
+	int numbands;
+
+	/* The per-band information. */
+	jpc_enc_band_t *bands;
+
+	/* The parent tile-component. */
+	struct jpc_enc_tcmpt_s *tcmpt;
+
+} jpc_enc_rlvl_t;
+
+/* Encoder per-tile-component state information. */
+
+typedef struct jpc_enc_tcmpt_s {
+
+	/* The number of resolution levels. */
+	uint_fast16_t numrlvls;
+
+	/* The per-resolution-level information. */
+	jpc_enc_rlvl_t *rlvls;
+
+	/* The tile-component data. */
+	jas_matrix_t *data;
+
+	/* The QMFB. */
+	int qmfbid;
+
+	/* The number of bands. */
+	int numbands;
+
+	/* The TSFB. */
+	jpc_tsfb_t *tsfb;
+
+	/* The synthesis energy weight (for the MCT). */
+	jpc_fix_t synweight;
+
+	/* The precinct width exponents. */
+	int prcwidthexpns[JPC_MAXRLVLS];
+
+	/* The precinct height exponents. */
+	int prcheightexpns[JPC_MAXRLVLS];
+
+	/* The code block width exponent. */
+	int cblkwidthexpn;
+
+	/* The code block height exponent. */
+	int cblkheightexpn;
+
+	/* Coding style (i.e., explicit precinct sizes). */
+	int csty;
+
+	/* Code block style. */
+	int cblksty;
+
+	/* The number of quantizer step sizes. */
+	uint_fast16_t numstepsizes;
+
+	/* The encoded quantizer step sizes. */
+	uint_fast16_t stepsizes[JPC_MAXBANDS];
+
+	/* The parent tile. */
+	struct jpc_enc_tile_s *tile;
+
+} jpc_enc_tcmpt_t;
+
+/* Encoder per-tile state information. */
+
+typedef struct jpc_enc_tile_s {
+
+	/* The tile number. */
+	uint_fast32_t tileno;
+
+	/* The x-coordinate of the top-left corner of the tile measured with
+	  respect to the reference grid. */
+	uint_fast32_t tlx;
+
+	/* The y-coordinate of the top-left corner of the tile measured with
+	  respect to the reference grid. */
+	uint_fast32_t tly;
+
+	/* The x-coordinate of the bottom-right corner of the tile measured
+	  with respect to the reference grid (plus one). */
+	uint_fast32_t brx;
+
+	/* The y-coordinate of the bottom-right corner of the tile measured
+	  with respect to the reference grid (plus one). */
+	uint_fast32_t bry;
+
+	/* The coding style. */
+	uint_fast8_t csty;
+
+	/* The progression order. */
+	uint_fast8_t prg;
+
+	/* The number of layers. */
+	uint_fast16_t numlyrs;
+
+	/* The MCT to employ (if any). */
+	uint_fast8_t mctid;
+
+	/* The packet iterator (used to determine the order of packet
+	  generation). */
+	jpc_pi_t *pi;
+
+	/* The coding mode (i.e., integer or real). */
+	bool intmode;
+
+	/* The number of bytes to allocate to the various layers. */
+	uint_fast32_t *lyrsizes;
+
+	/* The number of tile-components. */
+	int numtcmpts;
+
+	/* The per tile-component information. */
+	jpc_enc_tcmpt_t *tcmpts;
+
+	/* The raw (i.e., uncompressed) size of this tile. */
+	uint_fast32_t rawsize;
+
+} jpc_enc_tile_t;
+
+/* Encoder class. */
+
+typedef struct jpc_enc_s {
+
+	/* The image being encoded. */
+	jas_image_t *image;
+
+	/* The output stream. */
+	jas_stream_t *out;
+
+	/* The coding parameters. */
+	jpc_enc_cp_t *cp;
+
+	/* The tile currently being processed. */
+	jpc_enc_tile_t *curtile;
+
+	/* The code stream state. */
+	jpc_cstate_t *cstate;
+
+	/* The number of bytes output so far. */
+	uint_fast32_t len;
+
+	/* The number of bytes available for the main body of the code stream. */
+	/* This is used for rate allocation purposes. */
+	uint_fast32_t mainbodysize;
+
+	/* The marker segment currently being processed. */
+	/* This member is a convenience for making cleanup easier. */
+	jpc_ms_t *mrk;
+
+	/* The stream used to temporarily hold tile-part data. */
+	jas_stream_t *tmpstream;
+
+} jpc_enc_t;
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_fix.h b/converter/other/jpeg2000/libjasper/jpc/jpc_fix.h
new file mode 100644
index 00000000..5ff0c9f8
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_fix.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Fixed-Point Number Class
+ *
+ * $Id$
+ */
+
+#ifndef JPC_FIX_H
+#define JPC_FIX_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jasper/jas_types.h"
+#include "jasper/jas_fix.h"
+
+/******************************************************************************\
+* Basic parameters of the fixed-point type.
+\******************************************************************************/
+
+/* The integral type used to represent a fixed-point number.  This
+  type must be capable of representing values from -(2^31) to 2^31-1
+  (inclusive). */
+typedef int_fast32_t jpc_fix_t;
+
+/* The integral type used to respresent higher-precision intermediate results.
+  This type should be capable of representing values from -(2^63) to 2^63-1
+  (inclusive). */
+typedef int_fast64_t jpc_fix_big_t;
+
+/* The number of bits used for the fractional part of a fixed-point number. */
+#define JPC_FIX_FRACBITS	13
+
+/******************************************************************************\
+* Instantiations of the generic fixed-point number macros for the
+* parameters given above.  (Too bad C does not support templates, eh?)
+* The purpose of these macros is self-evident if one examines the
+* corresponding macros in the jasper/jas_fix.h header file.
+\******************************************************************************/
+
+#define	JPC_FIX_ZERO	JAS_FIX_ZERO(jpc_fix_t, JPC_FIX_FRACBITS)
+#define	JPC_FIX_ONE		JAS_FIX_ONE(jpc_fix_t, JPC_FIX_FRACBITS)
+#define	JPC_FIX_HALF	JAS_FIX_HALF(jpc_fix_t, JPC_FIX_FRACBITS)
+
+#define jpc_inttofix(x)	JAS_INTTOFIX(jpc_fix_t, JPC_FIX_FRACBITS, x)
+#define jpc_fixtoint(x)	JAS_FIXTOINT(jpc_fix_t, JPC_FIX_FRACBITS, x)
+#define jpc_fixtodbl(x)	JAS_FIXTODBL(jpc_fix_t, JPC_FIX_FRACBITS, x)
+#define jpc_dbltofix(x)	JAS_DBLTOFIX(jpc_fix_t, JPC_FIX_FRACBITS, x)
+
+#define	jpc_fix_add(x, y)	JAS_FIX_ADD(jpc_fix_t, JPC_FIX_FRACBITS, x, y)
+#define	jpc_fix_sub(x, y)	JAS_FIX_SUB(jpc_fix_t, JPC_FIX_FRACBITS, x, y)
+#define	jpc_fix_mul(x, y) \
+	JAS_FIX_MUL(jpc_fix_t, JPC_FIX_FRACBITS, jpc_fix_big_t, x, y)
+#define	jpc_fix_mulbyint(x, y) \
+	JAS_FIX_MULBYINT(jpc_fix_t, JPC_FIX_FRACBITS, x, y)
+#define	jpc_fix_div(x, y) \
+	JAS_FIX_DIV(jpc_fix_t, JPC_FIX_FRACBITS, jpc_fix_big_t, x, y)
+#define	jpc_fix_neg(x)		JAS_FIX_NEG(jpc_fix_t, JPC_FIX_FRACBITS, x)
+#define	jpc_fix_asl(x, n)	JAS_FIX_ASL(jpc_fix_t, JPC_FIX_FRACBITS, x, n)
+#define	jpc_fix_asr(x, n)	JAS_FIX_ASR(jpc_fix_t, JPC_FIX_FRACBITS, x, n)
+
+#define jpc_fix_pluseq(x, y)	JAS_FIX_PLUSEQ(jpc_fix_t, JPC_FIX_FRACBITS, x, y)
+#define jpc_fix_minuseq(x, y)	JAS_FIX_MINUSEQ(jpc_fix_t, JPC_FIX_FRACBITS, x, y)
+#define	jpc_fix_muleq(x, y)	\
+	JAS_FIX_MULEQ(jpc_fix_t, JPC_FIX_FRACBITS, jpc_fix_big_t, x, y)
+
+#define	jpc_fix_abs(x)		JAS_FIX_ABS(jpc_fix_t, JPC_FIX_FRACBITS, x)
+#define	jpc_fix_isint(x)	JAS_FIX_ISINT(jpc_fix_t, JPC_FIX_FRACBITS, x)
+#define jpc_fix_sgn(x)		JAS_FIX_SGN(jpc_fix_t, JPC_FIX_FRACBITS, x)
+#define	jpc_fix_round(x)	JAS_FIX_ROUND(jpc_fix_t, JPC_FIX_FRACBITS, x)
+#define	jpc_fix_floor(x)	JAS_FIX_FLOOR(jpc_fix_t, JPC_FIX_FRACBITS, x)
+#define jpc_fix_trunc(x)	JAS_FIX_TRUNC(jpc_fix_t, JPC_FIX_FRACBITS, x)
+
+/******************************************************************************\
+* Extra macros for convenience.
+\******************************************************************************/
+
+/* Compute the sum of three fixed-point numbers. */
+#define jpc_fix_add3(x, y, z)	jpc_fix_add(jpc_fix_add(x, y), z)
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_flt.h b/converter/other/jpeg2000/libjasper/jpc/jpc_flt.h
new file mode 100644
index 00000000..088eb00c
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_flt.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Floating-Point Class
+ *
+ * $Id$
+ */
+
+#ifndef JPC_FLT_H
+#define JPC_FLT_H
+
+#include <float.h>
+
+/* The code ought to be modified so this type is not used at all. */
+/* Very few places in the code rely on floating-point arithmetic, aside
+  from conversions in printf's. */
+typedef double jpc_flt_t;
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_math.c b/converter/other/jpeg2000/libjasper/jpc/jpc_math.c
new file mode 100644
index 00000000..d860847d
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_math.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Math Library
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes
+\******************************************************************************/
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "jpc_math.h"
+
+/******************************************************************************\
+* Miscellaneous Functions
+\******************************************************************************/
+
+/* Calculate the integer quantity floor(log2(x)), where x is a positive
+  integer. */
+int jpc_floorlog2(int x)
+{
+	int y;
+
+	/* The argument must be positive. */
+	assert(x > 0);
+
+	y = 0;
+	while (x > 1) {
+		x >>= 1;
+		++y;
+	}
+	return y;
+}
+
+/* Calculate the bit position of the first leading one in a nonnegative
+  integer. */
+/* This function is the basically the same as ceillog2(x), except that the
+  allowable range for x is slightly different. */
+int jpc_firstone(int x)
+{
+	int n;
+
+	/* The argument must be nonnegative. */
+	assert(x >= 0);
+
+	n = -1;
+	while (x > 0) {
+		x >>= 1;
+		++n;
+	}
+	return n;
+}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_math.h b/converter/other/jpeg2000/libjasper/jpc/jpc_math.h
new file mode 100644
index 00000000..e343bab1
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_math.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+#ifndef	JPC_MATH_H
+#define	JPC_MATH_H
+
+/******************************************************************************\
+* Includes
+\******************************************************************************/
+
+#include	<assert.h>
+
+/******************************************************************************\
+* Macros
+\******************************************************************************/
+
+/* Compute the floor of the quotient of two integers. */
+#define	JPC_FLOORDIV(x, y)	(assert(x >= 0 && y > 0), (x) / (y))
+
+/* Compute the ceiling of the quotient of two integers. */
+#define	JPC_CEILDIV(x, y)	(assert(x >= 0 && y > 0), ((x) + (y) - 1) / (y))
+
+/* Compute the floor of (x / 2^y). */
+#define	JPC_FLOORDIVPOW2(x, y) \
+	(assert(x >= 0 && y > 0), (x) >> (y))
+
+/* Compute the ceiling of (x / 2^y). */
+#define	JPC_CEILDIVPOW2(x, y) \
+	(assert(x >= 0 && y >= 0), ((x) + (1 << (y)) - 1) >> (y))
+/* JPC_CEILDIVPOW2U is for unsigned arguments (JPC_CEILDIVPOW2 would generate
+   compiler warnings due to its superfluous >= 0 check)
+*/
+#define	JPC_CEILDIVPOW2U(x, y) \
+	(((x) + (1 << (y)) - 1) >> (y))
+
+/******************************************************************************\
+* Functions.
+\******************************************************************************/
+
+/* Calculate the bit position of the first leading one in a nonnegative
+  integer. */
+int jpc_firstone(int x);
+
+/* Calculate the integer quantity floor(log2(x)), where x is a positive
+  integer. */
+int jpc_floorlog2(int x);
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_mct.c b/converter/other/jpeg2000/libjasper/jpc/jpc_mct.c
new file mode 100644
index 00000000..7c16c3e5
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_mct.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Multicomponent Transform Code
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <assert.h>
+
+#include "jasper/jas_seq.h"
+
+#include "jpc_fix.h"
+#include "jpc_mct.h"
+
+/******************************************************************************\
+* Code.
+\******************************************************************************/
+
+/* Compute the forward RCT. */
+
+void jpc_rct(jas_matrix_t *c0, jas_matrix_t *c1, jas_matrix_t *c2)
+{
+	int numrows;
+	int numcols;
+	int i;
+	int j;
+	jpc_fix_t *c0p;
+	jpc_fix_t *c1p;
+	jpc_fix_t *c2p;
+
+	numrows = jas_matrix_numrows(c0);
+	numcols = jas_matrix_numcols(c0);
+
+	/* All three matrices must have the same dimensions. */
+	assert(jas_matrix_numrows(c1) == numrows && jas_matrix_numcols(c1) == numcols
+	  && jas_matrix_numrows(c2) == numrows && jas_matrix_numcols(c2) == numcols);
+
+	for (i = 0; i < numrows; i++) {
+		c0p = jas_matrix_getref(c0, i, 0);
+		c1p = jas_matrix_getref(c1, i, 0);
+		c2p = jas_matrix_getref(c2, i, 0);
+		for (j = numcols; j > 0; --j) {
+			int r;
+			int g;
+			int b;
+			int y;
+			int u;
+			int v;
+			r = *c0p;
+			g = *c1p;
+			b = *c2p;
+			y = (r + (g << 1) + b) >> 2;
+			u = b - g;
+			v = r - g;
+			*c0p++ = y;
+			*c1p++ = u;
+			*c2p++ = v;
+		}
+	}
+}
+
+/* Compute the inverse RCT. */
+
+void jpc_irct(jas_matrix_t *c0, jas_matrix_t *c1, jas_matrix_t *c2)
+{
+	int numrows;
+	int numcols;
+	int i;
+	int j;
+	jpc_fix_t *c0p;
+	jpc_fix_t *c1p;
+	jpc_fix_t *c2p;
+
+	numrows = jas_matrix_numrows(c0);
+	numcols = jas_matrix_numcols(c0);
+
+	/* All three matrices must have the same dimensions. */
+	assert(jas_matrix_numrows(c1) == numrows && jas_matrix_numcols(c1) == numcols
+	  && jas_matrix_numrows(c2) == numrows && jas_matrix_numcols(c2) == numcols);
+
+	for (i = 0; i < numrows; i++) {
+		c0p = jas_matrix_getref(c0, i, 0);
+		c1p = jas_matrix_getref(c1, i, 0);
+		c2p = jas_matrix_getref(c2, i, 0);
+		for (j = numcols; j > 0; --j) {
+			int r;
+			int g;
+			int b;
+			int y;
+			int u;
+			int v;
+			y = *c0p;
+			u = *c1p;
+			v = *c2p;
+			g = y - ((u + v) >> 2);
+			r = v + g;
+			b = u + g;
+			*c0p++ = r;
+			*c1p++ = g;
+			*c2p++ = b;
+		}
+	}
+}
+
+void jpc_ict(jas_matrix_t *c0, jas_matrix_t *c1, jas_matrix_t *c2)
+{
+	int numrows;
+	int numcols;
+	int i;
+	int j;
+	jpc_fix_t r;
+	jpc_fix_t g;
+	jpc_fix_t b;
+	jpc_fix_t y;
+	jpc_fix_t u;
+	jpc_fix_t v;
+	jpc_fix_t *c0p;
+	jpc_fix_t *c1p;
+	jpc_fix_t *c2p;
+
+	numrows = jas_matrix_numrows(c0);
+	assert(jas_matrix_numrows(c1) == numrows && jas_matrix_numrows(c2) == numrows);
+	numcols = jas_matrix_numcols(c0);
+	assert(jas_matrix_numcols(c1) == numcols && jas_matrix_numcols(c2) == numcols);
+	for (i = 0; i < numrows; ++i) {
+		c0p = jas_matrix_getref(c0, i, 0);
+		c1p = jas_matrix_getref(c1, i, 0);
+		c2p = jas_matrix_getref(c2, i, 0);
+		for (j = numcols; j > 0; --j) {
+			r = *c0p;
+			g = *c1p;
+			b = *c2p;
+			y = jpc_fix_add3(jpc_fix_mul(jpc_dbltofix(0.299), r), jpc_fix_mul(jpc_dbltofix(0.587), g),
+			  jpc_fix_mul(jpc_dbltofix(0.114), b));
+			u = jpc_fix_add3(jpc_fix_mul(jpc_dbltofix(-0.16875), r), jpc_fix_mul(jpc_dbltofix(-0.33126), g),
+			  jpc_fix_mul(jpc_dbltofix(0.5), b));
+			v = jpc_fix_add3(jpc_fix_mul(jpc_dbltofix(0.5), r), jpc_fix_mul(jpc_dbltofix(-0.41869), g),
+			  jpc_fix_mul(jpc_dbltofix(-0.08131), b));
+			*c0p++ = y;
+			*c1p++ = u;
+			*c2p++ = v;
+		}
+	}
+}
+
+void jpc_iict(jas_matrix_t *c0, jas_matrix_t *c1, jas_matrix_t *c2)
+{
+	int numrows;
+	int numcols;
+	int i;
+	int j;
+	jpc_fix_t r;
+	jpc_fix_t g;
+	jpc_fix_t b;
+	jpc_fix_t y;
+	jpc_fix_t u;
+	jpc_fix_t v;
+	jpc_fix_t *c0p;
+	jpc_fix_t *c1p;
+	jpc_fix_t *c2p;
+
+	numrows = jas_matrix_numrows(c0);
+	assert(jas_matrix_numrows(c1) == numrows && jas_matrix_numrows(c2) == numrows);
+	numcols = jas_matrix_numcols(c0);
+	assert(jas_matrix_numcols(c1) == numcols && jas_matrix_numcols(c2) == numcols);
+	for (i = 0; i < numrows; ++i) {
+		c0p = jas_matrix_getref(c0, i, 0);
+		c1p = jas_matrix_getref(c1, i, 0);
+		c2p = jas_matrix_getref(c2, i, 0);
+		for (j = numcols; j > 0; --j) {
+			y = *c0p;
+			u = *c1p;
+			v = *c2p;
+			r = jpc_fix_add(y, jpc_fix_mul(jpc_dbltofix(1.402), v));
+			g = jpc_fix_add3(y, jpc_fix_mul(jpc_dbltofix(-0.34413), u),
+			  jpc_fix_mul(jpc_dbltofix(-0.71414), v));
+			b = jpc_fix_add(y, jpc_fix_mul(jpc_dbltofix(1.772), u));
+			*c0p++ = r;
+			*c1p++ = g;
+			*c2p++ = b;
+		}
+	}
+}
+
+jpc_fix_t jpc_mct_getsynweight(int mctid, int cmptno)
+{
+	jpc_fix_t synweight;
+
+	switch (mctid) {
+	case JPC_MCT_RCT:
+		switch (cmptno) {
+		case 0:
+			synweight = jpc_dbltofix(sqrt(3.0));
+			break;
+		case 1:
+			synweight = jpc_dbltofix(sqrt(0.6875));
+			break;
+		case 2:
+			synweight = jpc_dbltofix(sqrt(0.6875));
+			break;
+		}
+		break;
+	case JPC_MCT_ICT:
+		switch (cmptno) {
+		case 0:
+			synweight = jpc_dbltofix(sqrt(3.0000));
+			break;
+		case 1:
+			synweight = jpc_dbltofix(sqrt(3.2584));
+			break;
+		case 2:
+			synweight = jpc_dbltofix(sqrt(2.4755));
+			break;
+		}
+		break;
+	default:
+		synweight = JPC_FIX_ONE;
+		break;
+	}
+
+	return synweight;
+}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_mct.h b/converter/other/jpeg2000/libjasper/jpc/jpc_mct.h
new file mode 100644
index 00000000..d03d6ce1
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_mct.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Multicomponent Transform Code
+ *
+ * $Id$
+ */
+
+#ifndef JPC_MCT_H
+#define JPC_MCT_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jasper/jas_seq.h"
+#include "jasper/jas_fix.h"
+
+/******************************************************************************\
+* Constants.
+\******************************************************************************/
+
+/*
+ * Multicomponent transform IDs.
+ */
+
+#define JPC_MCT_NONE	0
+#define JPC_MCT_ICT		1
+#define JPC_MCT_RCT		2
+
+/******************************************************************************\
+* Functions.
+\******************************************************************************/
+
+/* Calculate the forward RCT. */
+void jpc_rct(jas_matrix_t *c0, jas_matrix_t *c1, jas_matrix_t *c2);
+
+/* Calculate the inverse RCT. */
+void jpc_irct(jas_matrix_t *c0, jas_matrix_t *c1, jas_matrix_t *c2);
+
+/* Calculate the forward ICT. */
+void jpc_ict(jas_matrix_t *c0, jas_matrix_t *c1, jas_matrix_t *c2);
+
+/* Calculate the inverse ICT. */
+void jpc_iict(jas_matrix_t *c0, jas_matrix_t *c1, jas_matrix_t *c2);
+
+/* Get the synthesis weight associated with a particular component. */
+jpc_fix_t jpc_mct_getsynweight(int mctid, int cmptno);
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.c b/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.c
new file mode 100644
index 00000000..535eaa2d
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * MQ Arithmetic Coder
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jasper/jas_malloc.h"
+
+#include "jpc_mqcod.h"
+
+/******************************************************************************\
+* Data.
+\******************************************************************************/
+
+/* MQ coder per-state information. */
+
+jpc_mqstate_t jpc_mqstates[47 * 2] = {
+	{0x5601, 0, &jpc_mqstates[ 2], &jpc_mqstates[ 3]},
+	{0x5601, 1, &jpc_mqstates[ 3], &jpc_mqstates[ 2]},
+	{0x3401, 0, &jpc_mqstates[ 4], &jpc_mqstates[12]},
+	{0x3401, 1, &jpc_mqstates[ 5], &jpc_mqstates[13]},
+	{0x1801, 0, &jpc_mqstates[ 6], &jpc_mqstates[18]},
+	{0x1801, 1, &jpc_mqstates[ 7], &jpc_mqstates[19]},
+	{0x0ac1, 0, &jpc_mqstates[ 8], &jpc_mqstates[24]},
+	{0x0ac1, 1, &jpc_mqstates[ 9], &jpc_mqstates[25]},
+	{0x0521, 0, &jpc_mqstates[10], &jpc_mqstates[58]},
+	{0x0521, 1, &jpc_mqstates[11], &jpc_mqstates[59]},
+	{0x0221, 0, &jpc_mqstates[76], &jpc_mqstates[66]},
+	{0x0221, 1, &jpc_mqstates[77], &jpc_mqstates[67]},
+	{0x5601, 0, &jpc_mqstates[14], &jpc_mqstates[13]},
+	{0x5601, 1, &jpc_mqstates[15], &jpc_mqstates[12]},
+	{0x5401, 0, &jpc_mqstates[16], &jpc_mqstates[28]},
+	{0x5401, 1, &jpc_mqstates[17], &jpc_mqstates[29]},
+	{0x4801, 0, &jpc_mqstates[18], &jpc_mqstates[28]},
+	{0x4801, 1, &jpc_mqstates[19], &jpc_mqstates[29]},
+	{0x3801, 0, &jpc_mqstates[20], &jpc_mqstates[28]},
+	{0x3801, 1, &jpc_mqstates[21], &jpc_mqstates[29]},
+	{0x3001, 0, &jpc_mqstates[22], &jpc_mqstates[34]},
+	{0x3001, 1, &jpc_mqstates[23], &jpc_mqstates[35]},
+	{0x2401, 0, &jpc_mqstates[24], &jpc_mqstates[36]},
+	{0x2401, 1, &jpc_mqstates[25], &jpc_mqstates[37]},
+	{0x1c01, 0, &jpc_mqstates[26], &jpc_mqstates[40]},
+	{0x1c01, 1, &jpc_mqstates[27], &jpc_mqstates[41]},
+	{0x1601, 0, &jpc_mqstates[58], &jpc_mqstates[42]},
+	{0x1601, 1, &jpc_mqstates[59], &jpc_mqstates[43]},
+	{0x5601, 0, &jpc_mqstates[30], &jpc_mqstates[29]},
+	{0x5601, 1, &jpc_mqstates[31], &jpc_mqstates[28]},
+	{0x5401, 0, &jpc_mqstates[32], &jpc_mqstates[28]},
+	{0x5401, 1, &jpc_mqstates[33], &jpc_mqstates[29]},
+	{0x5101, 0, &jpc_mqstates[34], &jpc_mqstates[30]},
+	{0x5101, 1, &jpc_mqstates[35], &jpc_mqstates[31]},
+	{0x4801, 0, &jpc_mqstates[36], &jpc_mqstates[32]},
+	{0x4801, 1, &jpc_mqstates[37], &jpc_mqstates[33]},
+	{0x3801, 0, &jpc_mqstates[38], &jpc_mqstates[34]},
+	{0x3801, 1, &jpc_mqstates[39], &jpc_mqstates[35]},
+	{0x3401, 0, &jpc_mqstates[40], &jpc_mqstates[36]},
+	{0x3401, 1, &jpc_mqstates[41], &jpc_mqstates[37]},
+	{0x3001, 0, &jpc_mqstates[42], &jpc_mqstates[38]},
+	{0x3001, 1, &jpc_mqstates[43], &jpc_mqstates[39]},
+	{0x2801, 0, &jpc_mqstates[44], &jpc_mqstates[38]},
+	{0x2801, 1, &jpc_mqstates[45], &jpc_mqstates[39]},
+	{0x2401, 0, &jpc_mqstates[46], &jpc_mqstates[40]},
+	{0x2401, 1, &jpc_mqstates[47], &jpc_mqstates[41]},
+	{0x2201, 0, &jpc_mqstates[48], &jpc_mqstates[42]},
+	{0x2201, 1, &jpc_mqstates[49], &jpc_mqstates[43]},
+	{0x1c01, 0, &jpc_mqstates[50], &jpc_mqstates[44]},
+	{0x1c01, 1, &jpc_mqstates[51], &jpc_mqstates[45]},
+	{0x1801, 0, &jpc_mqstates[52], &jpc_mqstates[46]},
+	{0x1801, 1, &jpc_mqstates[53], &jpc_mqstates[47]},
+	{0x1601, 0, &jpc_mqstates[54], &jpc_mqstates[48]},
+	{0x1601, 1, &jpc_mqstates[55], &jpc_mqstates[49]},
+	{0x1401, 0, &jpc_mqstates[56], &jpc_mqstates[50]},
+	{0x1401, 1, &jpc_mqstates[57], &jpc_mqstates[51]},
+	{0x1201, 0, &jpc_mqstates[58], &jpc_mqstates[52]},
+	{0x1201, 1, &jpc_mqstates[59], &jpc_mqstates[53]},
+	{0x1101, 0, &jpc_mqstates[60], &jpc_mqstates[54]},
+	{0x1101, 1, &jpc_mqstates[61], &jpc_mqstates[55]},
+	{0x0ac1, 0, &jpc_mqstates[62], &jpc_mqstates[56]},
+	{0x0ac1, 1, &jpc_mqstates[63], &jpc_mqstates[57]},
+	{0x09c1, 0, &jpc_mqstates[64], &jpc_mqstates[58]},
+	{0x09c1, 1, &jpc_mqstates[65], &jpc_mqstates[59]},
+	{0x08a1, 0, &jpc_mqstates[66], &jpc_mqstates[60]},
+	{0x08a1, 1, &jpc_mqstates[67], &jpc_mqstates[61]},
+	{0x0521, 0, &jpc_mqstates[68], &jpc_mqstates[62]},
+	{0x0521, 1, &jpc_mqstates[69], &jpc_mqstates[63]},
+	{0x0441, 0, &jpc_mqstates[70], &jpc_mqstates[64]},
+	{0x0441, 1, &jpc_mqstates[71], &jpc_mqstates[65]},
+	{0x02a1, 0, &jpc_mqstates[72], &jpc_mqstates[66]},
+	{0x02a1, 1, &jpc_mqstates[73], &jpc_mqstates[67]},
+	{0x0221, 0, &jpc_mqstates[74], &jpc_mqstates[68]},
+	{0x0221, 1, &jpc_mqstates[75], &jpc_mqstates[69]},
+	{0x0141, 0, &jpc_mqstates[76], &jpc_mqstates[70]},
+	{0x0141, 1, &jpc_mqstates[77], &jpc_mqstates[71]},
+	{0x0111, 0, &jpc_mqstates[78], &jpc_mqstates[72]},
+	{0x0111, 1, &jpc_mqstates[79], &jpc_mqstates[73]},
+	{0x0085, 0, &jpc_mqstates[80], &jpc_mqstates[74]},
+	{0x0085, 1, &jpc_mqstates[81], &jpc_mqstates[75]},
+	{0x0049, 0, &jpc_mqstates[82], &jpc_mqstates[76]},
+	{0x0049, 1, &jpc_mqstates[83], &jpc_mqstates[77]},
+	{0x0025, 0, &jpc_mqstates[84], &jpc_mqstates[78]},
+	{0x0025, 1, &jpc_mqstates[85], &jpc_mqstates[79]},
+	{0x0015, 0, &jpc_mqstates[86], &jpc_mqstates[80]},
+	{0x0015, 1, &jpc_mqstates[87], &jpc_mqstates[81]},
+	{0x0009, 0, &jpc_mqstates[88], &jpc_mqstates[82]},
+	{0x0009, 1, &jpc_mqstates[89], &jpc_mqstates[83]},
+	{0x0005, 0, &jpc_mqstates[90], &jpc_mqstates[84]},
+	{0x0005, 1, &jpc_mqstates[91], &jpc_mqstates[85]},
+	{0x0001, 0, &jpc_mqstates[90], &jpc_mqstates[86]},
+	{0x0001, 1, &jpc_mqstates[91], &jpc_mqstates[87]},
+	{0x5601, 0, &jpc_mqstates[92], &jpc_mqstates[92]},
+	{0x5601, 1, &jpc_mqstates[93], &jpc_mqstates[93]},
+};
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.h b/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.h
new file mode 100644
index 00000000..914f8e8a
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * MQ Arithmetic Coder
+ *
+ * $Id$
+ */
+
+#ifndef JPC_MQCOD_H
+#define JPC_MQCOD_H
+
+/*****************************************************************************\
+* Includes.
+\*****************************************************************************/
+
+#include "pm_c_util.h"
+#include "jasper/jas_types.h"
+
+/*****************************************************************************\
+* Types.
+\*****************************************************************************/
+
+/*
+ * MQ coder context information.
+ */
+
+typedef struct {
+
+	/* The most probable symbol (MPS). */
+	bool mps;
+
+	/* The state index. */
+	int_fast16_t ind;
+
+} jpc_mqctx_t;
+
+/*
+ * MQ coder state table entry.
+ */
+
+typedef struct jpc_mqstate_s {
+
+	/* The Qe value. */
+	uint_fast16_t qeval;
+
+	/* The MPS. */
+	uint_fast16_t mps;
+
+	/* The NMPS state. */
+	struct jpc_mqstate_s *nmps;
+
+	/* The NLPS state. */
+	struct jpc_mqstate_s *nlps;
+
+} jpc_mqstate_t;
+
+/******************************************************************************\
+* Data.
+\******************************************************************************/
+
+/* The state table for the MQ coder. */
+extern jpc_mqstate_t jpc_mqstates[];
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_mqdec.c b/converter/other/jpeg2000/libjasper/jpc/jpc_mqdec.c
new file mode 100644
index 00000000..f58a1c7d
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_mqdec.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * MQ Arithmetic Decoder
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "jasper/jas_types.h"
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_math.h"
+#include "jasper/jas_debug.h"
+
+#include "jpc_mqdec.h"
+
+/******************************************************************************\
+* Local macros.
+\******************************************************************************/
+
+#if defined(DEBUG)
+#define	MQDEC_CALL(n, x) \
+	((jas_getdbglevel() >= (n)) ? ((void)(x)) : ((void)0))
+#else
+#define	MQDEC_CALL(n, x)
+#endif
+
+/******************************************************************************\
+* Local function prototypes.
+\******************************************************************************/
+
+static void jpc_mqdec_bytein(jpc_mqdec_t *mqdec);
+
+/******************************************************************************\
+* Code for creation and destruction of a MQ decoder.
+\******************************************************************************/
+
+/* Create a MQ decoder. */
+jpc_mqdec_t *jpc_mqdec_create(int maxctxs, jas_stream_t *in)
+{
+	jpc_mqdec_t *mqdec;
+
+	/* There must be at least one context. */
+	assert(maxctxs > 0);
+
+	/* Allocate memory for the MQ decoder. */
+	if (!(mqdec = jas_malloc(sizeof(jpc_mqdec_t)))) {
+		goto error;
+	}
+	mqdec->in = in;
+	mqdec->maxctxs = maxctxs;
+	/* Allocate memory for the per-context state information. */
+	if (!(mqdec->ctxs = jas_malloc(mqdec->maxctxs * sizeof(jpc_mqstate_t *)))) {
+		goto error;
+	}
+	/* Set the current context to the first context. */
+	mqdec->curctx = mqdec->ctxs;
+
+	/* If an input stream has been associated with the MQ decoder,
+	  initialize the decoder state from the stream. */
+	if (mqdec->in) {
+		jpc_mqdec_init(mqdec);
+	}
+	/* Initialize the per-context state information. */
+	jpc_mqdec_setctxs(mqdec, 0, 0);
+
+	return mqdec;
+
+error:
+	/* Oops...  Something has gone wrong. */
+	if (mqdec) {
+		jpc_mqdec_destroy(mqdec);
+	}
+	return 0;
+}
+
+/* Destroy a MQ decoder. */
+void jpc_mqdec_destroy(jpc_mqdec_t *mqdec)
+{
+	if (mqdec->ctxs) {
+		jas_free(mqdec->ctxs);
+	}
+	jas_free(mqdec);
+}
+
+/******************************************************************************\
+* Code for initialization of a MQ decoder.
+\******************************************************************************/
+
+/* Initialize the state of a MQ decoder. */
+
+void jpc_mqdec_init(jpc_mqdec_t *mqdec)
+{
+	int c;
+
+	mqdec->eof = 0;
+	mqdec->creg = 0;
+	/* Get the next byte from the input stream. */
+	if ((c = jas_stream_getc(mqdec->in)) == EOF) {
+		/* We have encountered an I/O error or EOF. */
+		c = 0xff;
+		mqdec->eof = 1;
+	}
+	mqdec->inbuffer = c;
+	mqdec->creg += mqdec->inbuffer << 16;
+	jpc_mqdec_bytein(mqdec);
+	mqdec->creg <<= 7;
+	mqdec->ctreg -= 7;
+	mqdec->areg = 0x8000;
+}
+
+/* Set the input stream for a MQ decoder. */
+
+void jpc_mqdec_setinput(jpc_mqdec_t *mqdec, jas_stream_t *in)
+{
+	mqdec->in = in;
+}
+
+/* Initialize one or more contexts. */
+
+void jpc_mqdec_setctxs(jpc_mqdec_t *mqdec, int numctxs, jpc_mqctx_t *ctxs)
+{
+	jpc_mqstate_t **ctx;
+	int n;
+
+	ctx = mqdec->ctxs;
+	n = JAS_MIN(mqdec->maxctxs, numctxs);
+	while (--n >= 0) {
+		*ctx = &jpc_mqstates[2 * ctxs->ind + ctxs->mps];
+		++ctx;
+		++ctxs;
+	}
+	n = mqdec->maxctxs - numctxs;
+	while (--n >= 0) {
+		*ctx = &jpc_mqstates[0];
+		++ctx;
+	}
+}
+
+/* Initialize a context. */
+
+void jpc_mqdec_setctx(jpc_mqdec_t *mqdec, int ctxno, jpc_mqctx_t *ctx)
+{
+	jpc_mqstate_t **ctxi;
+	ctxi = &mqdec->ctxs[ctxno];
+	*ctxi = &jpc_mqstates[2 * ctx->ind + ctx->mps];
+}
+
+/******************************************************************************\
+* Code for decoding a bit.
+\******************************************************************************/
+
+/* Decode a bit. */
+
+int jpc_mqdec_getbit_func(register jpc_mqdec_t *mqdec)
+{
+	int bit;
+	JAS_DBGLOG(100, ("jpc_mqdec_getbit_func(%p)\n", mqdec));
+	MQDEC_CALL(100, jpc_mqdec_dump(mqdec, stderr));
+	bit = jpc_mqdec_getbit_macro(mqdec);
+	MQDEC_CALL(100, jpc_mqdec_dump(mqdec, stderr));
+	JAS_DBGLOG(100, ("ctx = %d, decoded %d\n", mqdec->curctx -
+	  mqdec->ctxs, bit));
+	return bit;
+}
+
+/* Apply MPS_EXCHANGE algorithm (with RENORMD). */
+int jpc_mqdec_mpsexchrenormd(register jpc_mqdec_t *mqdec)
+{
+	int ret;
+	register jpc_mqstate_t *state = *mqdec->curctx;
+	jpc_mqdec_mpsexchange(mqdec->areg, state->qeval, mqdec->curctx, ret);
+	jpc_mqdec_renormd(mqdec->areg, mqdec->creg, mqdec->ctreg, mqdec->in,
+	  mqdec->eof, mqdec->inbuffer);
+	return ret;
+}
+
+/* Apply LPS_EXCHANGE algorithm (with RENORMD). */
+int jpc_mqdec_lpsexchrenormd(register jpc_mqdec_t *mqdec)
+{
+	int ret;
+	register jpc_mqstate_t *state = *mqdec->curctx;
+	jpc_mqdec_lpsexchange(mqdec->areg, state->qeval, mqdec->curctx, ret);
+	jpc_mqdec_renormd(mqdec->areg, mqdec->creg, mqdec->ctreg, mqdec->in,
+	  mqdec->eof, mqdec->inbuffer);
+	return ret;
+}
+
+/******************************************************************************\
+* Support code.
+\******************************************************************************/
+
+/* Apply the BYTEIN algorithm. */
+static void jpc_mqdec_bytein(jpc_mqdec_t *mqdec)
+{
+	int c;
+	unsigned char prevbuf;
+
+	if (!mqdec->eof) {
+		if ((c = jas_stream_getc(mqdec->in)) == EOF) {
+			mqdec->eof = 1;
+			c = 0xff;
+		}
+		prevbuf = mqdec->inbuffer;
+		mqdec->inbuffer = c;
+		if (prevbuf == 0xff) {
+			if (c > 0x8f) {
+				mqdec->creg += 0xff00;
+				mqdec->ctreg = 8;
+			} else {
+				mqdec->creg += c << 9;
+				mqdec->ctreg = 7;
+			}
+		} else {
+			mqdec->creg += c << 8;
+			mqdec->ctreg = 8;
+		}
+	} else {
+		mqdec->creg += 0xff00;
+		mqdec->ctreg = 8;
+	}
+}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_mqdec.h b/converter/other/jpeg2000/libjasper/jpc/jpc_mqdec.h
new file mode 100644
index 00000000..30185506
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_mqdec.h
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * MQ Arithmetic Decoder
+ *
+ * $Id$
+ */
+
+#ifndef JPC_MQDEC_H
+#define JPC_MQDEC_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jasper/jas_types.h"
+#include "jasper/jas_stream.h"
+
+#include "jpc_mqcod.h"
+
+/******************************************************************************\
+* Types.
+\******************************************************************************/
+
+/* MQ arithmetic decoder. */
+
+typedef struct {
+
+	/* The C register. */
+	uint_fast32_t creg;
+
+	/* The A register. */
+	uint_fast32_t areg;
+
+	/* The CT register. */
+	uint_fast32_t ctreg;
+
+	/* The current context. */
+	jpc_mqstate_t **curctx;
+
+	/* The per-context information. */
+	jpc_mqstate_t **ctxs;
+
+	/* The maximum number of contexts. */
+	int maxctxs;
+
+	/* The stream from which to read data. */
+	jas_stream_t *in;
+
+	/* The last character read. */
+	unsigned char inbuffer;
+
+	/* The EOF indicator. */
+	int eof;
+
+} jpc_mqdec_t;
+
+/******************************************************************************\
+* Functions/macros for construction and destruction.
+\******************************************************************************/
+
+/* Create a MQ decoder. */
+jpc_mqdec_t *jpc_mqdec_create(int maxctxs, jas_stream_t *in);
+
+/* Destroy a MQ decoder. */
+void jpc_mqdec_destroy(jpc_mqdec_t *dec);
+
+/******************************************************************************\
+* Functions/macros for initialization.
+\******************************************************************************/
+
+/* Set the input stream associated with a MQ decoder. */
+void jpc_mqdec_setinput(jpc_mqdec_t *dec, jas_stream_t *in);
+
+/* Initialize a MQ decoder. */
+void jpc_mqdec_init(jpc_mqdec_t *dec);
+
+/******************************************************************************\
+* Functions/macros for manipulating contexts.
+\******************************************************************************/
+
+/* Set the current context for a MQ decoder. */
+#define	jpc_mqdec_setcurctx(dec, ctxno) \
+	((mqdec)->curctx = &(mqdec)->ctxs[ctxno]);
+
+/* Set the state information for a particular context of a MQ decoder. */
+void jpc_mqdec_setctx(jpc_mqdec_t *dec, int ctxno, jpc_mqctx_t *ctx);
+
+/* Set the state information for all contexts of a MQ decoder. */
+void jpc_mqdec_setctxs(jpc_mqdec_t *dec, int numctxs, jpc_mqctx_t *ctxs);
+
+/******************************************************************************\
+* Functions/macros for decoding bits.
+\******************************************************************************/
+
+/* Decode a symbol. */
+#if !defined(DEBUG)
+#define	jpc_mqdec_getbit(dec) \
+	jpc_mqdec_getbit_macro(dec)
+#else
+#define	jpc_mqdec_getbit(dec) \
+	jpc_mqdec_getbit_func(dec)
+#endif
+
+/* Decode a symbol (assuming an unskewed probability distribution). */
+#if !defined(DEBUG)
+#define	jpc_mqdec_getbitnoskew(dec) \
+	jpc_mqdec_getbit_macro(dec)
+#else
+#define	jpc_mqdec_getbitnoskew(dec) \
+	jpc_mqdec_getbit_func(dec)
+#endif
+
+/******************************************************************************\
+* Functions/macros for debugging.
+\******************************************************************************/
+
+/* Dump the MQ decoder state for debugging. */
+void mqdec_dump(jpc_mqdec_t *dec, FILE *out);
+
+/******************************************************************************\
+* EVERYTHING BELOW THIS POINT IS IMPLEMENTATION SPECIFIC AND NOT PART OF THE
+* APPLICATION INTERFACE.  DO NOT RELY ON ANY OF THE INTERNAL FUNCTIONS/MACROS
+* GIVEN BELOW.
+\******************************************************************************/
+
+#define	jpc_mqdec_getbit_macro(dec) \
+	((((dec)->areg -= (*(dec)->curctx)->qeval), \
+	  (dec)->creg >> 16 >= (*(dec)->curctx)->qeval) ? \
+	  ((((dec)->creg -= (*(dec)->curctx)->qeval << 16), \
+	  (dec)->areg & 0x8000) ?  (*(dec)->curctx)->mps : \
+	  jpc_mqdec_mpsexchrenormd(dec)) : \
+	  jpc_mqdec_lpsexchrenormd(dec))
+
+#define	jpc_mqdec_mpsexchange(areg, delta, curctx, bit) \
+{ \
+	if ((areg) < (delta)) { \
+		register jpc_mqstate_t *state = *(curctx); \
+		/* LPS decoded. */ \
+		(bit) = state->mps ^ 1; \
+		*(curctx) = state->nlps; \
+	} else { \
+		register jpc_mqstate_t *state = *(curctx); \
+		/* MPS decoded. */ \
+		(bit) = state->mps; \
+		*(curctx) = state->nmps; \
+	} \
+}
+
+#define	jpc_mqdec_lpsexchange(areg, delta, curctx, bit) \
+{ \
+	if ((areg) >= (delta)) { \
+		register jpc_mqstate_t *state = *(curctx); \
+		(areg) = (delta); \
+		(bit) = state->mps ^ 1; \
+		*(curctx) = state->nlps; \
+	} else { \
+		register jpc_mqstate_t *state = *(curctx); \
+		(areg) = (delta); \
+		(bit) = state->mps; \
+		*(curctx) = state->nmps; \
+	} \
+}
+
+#define	jpc_mqdec_renormd(areg, creg, ctreg, in, eof, inbuf) \
+{ \
+	do { \
+		if (!(ctreg)) { \
+			jpc_mqdec_bytein2(creg, ctreg, in, eof, inbuf); \
+		} \
+		(areg) <<= 1; \
+		(creg) <<= 1; \
+		--(ctreg); \
+	} while (!((areg) & 0x8000)); \
+}
+
+#define	jpc_mqdec_bytein2(creg, ctreg, in, eof, inbuf) \
+{ \
+	int c; \
+	unsigned char prevbuf; \
+	if (!(eof)) { \
+		if ((c = jas_stream_getc(in)) == EOF) { \
+			(eof) = 1; \
+			c = 0xff; \
+		} \
+		prevbuf = (inbuf); \
+		(inbuf) = c; \
+		if (prevbuf == 0xff) { \
+			if (c > 0x8f) { \
+				(creg) += 0xff00; \
+				(ctreg) = 8; \
+			} else { \
+				(creg) += c << 9; \
+				(ctreg) = 7; \
+			} \
+		} else { \
+			(creg) += c << 8; \
+			(ctreg) = 8; \
+		} \
+	} else { \
+		(creg) += 0xff00; \
+		(ctreg) = 8; \
+	} \
+}
+
+int jpc_mqdec_getbit_func(jpc_mqdec_t *dec);
+int jpc_mqdec_mpsexchrenormd(jpc_mqdec_t *dec);
+int jpc_mqdec_lpsexchrenormd(jpc_mqdec_t *dec);
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_mqenc.c b/converter/other/jpeg2000/libjasper/jpc/jpc_mqenc.c
new file mode 100644
index 00000000..0219a000
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_mqenc.c
@@ -0,0 +1,441 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * MQ Arithmetic Encoder
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "jasper/jas_stream.h"
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_math.h"
+#include "jasper/jas_debug.h"
+
+#include "jpc_mqenc.h"
+
+/******************************************************************************\
+* Macros
+\******************************************************************************/
+
+#if defined(DEBUG)
+#define	JPC_MQENC_CALL(n, x) \
+	((jas_getdbglevel() >= (n)) ? ((void)(x)) : ((void)0))
+#else
+#define	JPC_MQENC_CALL(n, x)
+#endif
+
+#define	jpc_mqenc_codemps9(areg, creg, ctreg, curctx, enc) \
+{ \
+	jpc_mqstate_t *state = *(curctx); \
+	(areg) -= state->qeval; \
+	if (!((areg) & 0x8000)) { \
+		if ((areg) < state->qeval) { \
+			(areg) = state->qeval; \
+		} else { \
+			(creg) += state->qeval; \
+		} \
+		*(curctx) = state->nmps; \
+		jpc_mqenc_renorme((areg), (creg), (ctreg), (enc)); \
+	} else { \
+		(creg) += state->qeval; \
+	} \
+}
+
+#define	jpc_mqenc_codelps2(areg, creg, ctreg, curctx, enc) \
+{ \
+	jpc_mqstate_t *state = *(curctx); \
+	(areg) -= state->qeval; \
+	if ((areg) < state->qeval) { \
+		(creg) += state->qeval; \
+	} else { \
+		(areg) = state->qeval; \
+	} \
+	*(curctx) = state->nlps; \
+	jpc_mqenc_renorme((areg), (creg), (ctreg), (enc)); \
+}
+
+#define	jpc_mqenc_renorme(areg, creg, ctreg, enc) \
+{ \
+	do { \
+		(areg) <<= 1; \
+		(creg) <<= 1; \
+		if (!--(ctreg)) { \
+			jpc_mqenc_byteout((areg), (creg), (ctreg), (enc)); \
+		} \
+	} while (!((areg) & 0x8000)); \
+}
+
+#define	jpc_mqenc_byteout(areg, creg, ctreg, enc) \
+{ \
+	if ((enc)->outbuf != 0xff) { \
+		if ((creg) & 0x8000000) { \
+			if (++((enc)->outbuf) == 0xff) { \
+				(creg) &= 0x7ffffff; \
+				jpc_mqenc_byteout2(enc); \
+				enc->outbuf = ((creg) >> 20) & 0xff; \
+				(creg) &= 0xfffff; \
+				(ctreg) = 7; \
+			} else { \
+				jpc_mqenc_byteout2(enc); \
+				enc->outbuf = ((creg) >> 19) & 0xff; \
+				(creg) &= 0x7ffff; \
+				(ctreg) = 8; \
+			} \
+		} else { \
+			jpc_mqenc_byteout2(enc); \
+			(enc)->outbuf = ((creg) >> 19) & 0xff; \
+			(creg) &= 0x7ffff; \
+			(ctreg) = 8; \
+		} \
+	} else { \
+		jpc_mqenc_byteout2(enc); \
+		(enc)->outbuf = ((creg) >> 20) & 0xff; \
+		(creg) &= 0xfffff; \
+		(ctreg) = 7; \
+	} \
+}
+
+#define	jpc_mqenc_byteout2(enc) \
+{ \
+	if (enc->outbuf >= 0) { \
+		if (jas_stream_putc(enc->out, (unsigned char)enc->outbuf) == EOF) { \
+			enc->err |= 1; \
+		} \
+	} \
+	enc->lastbyte = enc->outbuf; \
+}
+
+/******************************************************************************\
+* Local function protoypes.
+\******************************************************************************/
+
+static void jpc_mqenc_setbits(jpc_mqenc_t *mqenc);
+
+/******************************************************************************\
+* Code for creation and destruction of encoder.
+\******************************************************************************/
+
+/* Create a MQ encoder. */
+
+jpc_mqenc_t *jpc_mqenc_create(int maxctxs, jas_stream_t *out)
+{
+	jpc_mqenc_t *mqenc;
+
+	/* Allocate memory for the MQ encoder. */
+	if (!(mqenc = jas_malloc(sizeof(jpc_mqenc_t)))) {
+		goto error;
+	}
+	mqenc->out = out;
+	mqenc->maxctxs = maxctxs;
+
+	/* Allocate memory for the per-context state information. */
+	if (!(mqenc->ctxs = jas_malloc(mqenc->maxctxs * sizeof(jpc_mqstate_t *)))) {
+		goto error;
+	}
+
+	/* Set the current context to the first one. */
+	mqenc->curctx = mqenc->ctxs;
+
+	jpc_mqenc_init(mqenc);
+
+	/* Initialize the per-context state information to something sane. */
+	jpc_mqenc_setctxs(mqenc, 0, 0);
+
+	return mqenc;
+
+error:
+	if (mqenc) {
+		jpc_mqenc_destroy(mqenc);
+	}
+	return 0;
+}
+
+/* Destroy a MQ encoder. */
+
+void jpc_mqenc_destroy(jpc_mqenc_t *mqenc)
+{
+	if (mqenc->ctxs) {
+		jas_free(mqenc->ctxs);
+	}
+	jas_free(mqenc);
+}
+
+/******************************************************************************\
+* State initialization code.
+\******************************************************************************/
+
+/* Initialize the coding state of a MQ encoder. */
+
+void jpc_mqenc_init(jpc_mqenc_t *mqenc)
+{
+	mqenc->areg = 0x8000;
+	mqenc->outbuf = -1;
+	mqenc->creg = 0;
+	mqenc->ctreg = 12;
+	mqenc->lastbyte = -1;
+	mqenc->err = 0;
+}
+
+/* Initialize one or more contexts. */
+
+void jpc_mqenc_setctxs(jpc_mqenc_t *mqenc, int numctxs, jpc_mqctx_t *ctxs)
+{
+	jpc_mqstate_t **ctx;
+	int n;
+
+	ctx = mqenc->ctxs;
+	n = JAS_MIN(mqenc->maxctxs, numctxs);
+	while (--n >= 0) {
+		*ctx = &jpc_mqstates[2 * ctxs->ind + ctxs->mps];
+		++ctx;
+		++ctxs;
+	}
+	n = mqenc->maxctxs - numctxs;
+	while (--n >= 0) {
+		*ctx = &jpc_mqstates[0];
+		++ctx;
+	}
+
+}
+
+/* Get the coding state for a MQ encoder. */
+
+void jpc_mqenc_getstate(jpc_mqenc_t *mqenc, jpc_mqencstate_t *state)
+{
+	state->areg = mqenc->areg;
+	state->creg = mqenc->creg;
+	state->ctreg = mqenc->ctreg;
+	state->lastbyte = mqenc->lastbyte;
+}
+
+/******************************************************************************\
+* Code for coding symbols.
+\******************************************************************************/
+
+/* Encode a bit. */
+
+int jpc_mqenc_putbit_func(jpc_mqenc_t *mqenc, int bit)
+{
+	const jpc_mqstate_t *state;
+	JAS_DBGLOG(100, ("jpc_mqenc_putbit(%p, %d)\n", mqenc, bit));
+	JPC_MQENC_CALL(100, jpc_mqenc_dump(mqenc, stderr));
+
+	state = *(mqenc->curctx);
+
+	if (state->mps == bit) {
+		/* Apply the CODEMPS algorithm as defined in the standard. */
+		mqenc->areg -= state->qeval;
+		if (!(mqenc->areg & 0x8000)) {
+			jpc_mqenc_codemps2(mqenc);
+		} else {
+			mqenc->creg += state->qeval;
+		}
+	} else {
+		/* Apply the CODELPS algorithm as defined in the standard. */
+		jpc_mqenc_codelps2(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc->curctx, mqenc);
+	}
+
+	return jpc_mqenc_error(mqenc) ? (-1) : 0;
+}
+
+int jpc_mqenc_codemps2(jpc_mqenc_t *mqenc)
+{
+	/* Note: This function only performs part of the work associated with
+	the CODEMPS algorithm from the standard.  Some of the work is also
+	performed by the caller. */
+
+	jpc_mqstate_t *state = *(mqenc->curctx);
+	if (mqenc->areg < state->qeval) {
+		mqenc->areg = state->qeval;
+	} else {
+		mqenc->creg += state->qeval;
+	}
+	*mqenc->curctx = state->nmps;
+	jpc_mqenc_renorme(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc);
+	return jpc_mqenc_error(mqenc) ? (-1) : 0;
+}
+
+int jpc_mqenc_codelps(jpc_mqenc_t *mqenc)
+{
+	jpc_mqenc_codelps2(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc->curctx, mqenc);
+	return jpc_mqenc_error(mqenc) ? (-1) : 0;
+}
+
+/******************************************************************************\
+* Miscellaneous code.
+\******************************************************************************/
+
+/* Terminate the code word. */
+
+int jpc_mqenc_flush(jpc_mqenc_t *mqenc, int termmode)
+{
+	int_fast16_t k;
+
+	switch (termmode) {
+	case JPC_MQENC_PTERM:
+		k = 11 - mqenc->ctreg + 1;
+		while (k > 0) {
+			mqenc->creg <<= mqenc->ctreg;
+			mqenc->ctreg = 0;
+			jpc_mqenc_byteout(mqenc->areg, mqenc->creg, mqenc->ctreg,
+			  mqenc);
+			k -= mqenc->ctreg;
+		}
+		if (mqenc->outbuf != 0xff) {
+			jpc_mqenc_byteout(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc);
+		}
+		break;
+	case JPC_MQENC_DEFTERM:
+		jpc_mqenc_setbits(mqenc);
+		mqenc->creg <<= mqenc->ctreg;
+		jpc_mqenc_byteout(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc);
+		mqenc->creg <<= mqenc->ctreg;
+		jpc_mqenc_byteout(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc);
+		if (mqenc->outbuf != 0xff) {
+			jpc_mqenc_byteout(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc);
+		}
+		break;
+	default:
+		abort();
+		break;
+	}
+	return 0;
+}
+
+static void jpc_mqenc_setbits(jpc_mqenc_t *mqenc)
+{
+	uint_fast32_t tmp = mqenc->creg + mqenc->areg;
+	mqenc->creg |= 0xffff;
+	if (mqenc->creg >= tmp) {
+		mqenc->creg -= 0x8000;
+	}
+}
+
+/* Dump a MQ encoder to a stream for debugging. */
+
+int jpc_mqenc_dump(jpc_mqenc_t *mqenc, FILE *out)
+{
+	fprintf(out, "AREG = %08x, CREG = %08x, CTREG = %d\n",
+	  mqenc->areg, mqenc->creg, mqenc->ctreg);
+	fprintf(out, "IND = %02d, MPS = %d, QEVAL = %04x\n",
+	  *mqenc->curctx - jpc_mqstates, (*mqenc->curctx)->mps,
+	  (*mqenc->curctx)->qeval);
+	return 0;
+}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_mqenc.h b/converter/other/jpeg2000/libjasper/jpc/jpc_mqenc.h
new file mode 100644
index 00000000..1421ae4d
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_mqenc.h
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * MQ Arithmetic Encoder
+ *
+ * $Id$
+ */
+
+#ifndef JPC_MQENC_H
+#define JPC_MQENC_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jasper/jas_types.h"
+#include "jasper/jas_stream.h"
+
+#include "jpc_mqcod.h"
+
+/******************************************************************************\
+* Constants.
+\******************************************************************************/
+
+/*
+ * Termination modes.
+ */
+
+#define	JPC_MQENC_DEFTERM	0	/* default termination */
+#define	JPC_MQENC_PTERM		1	/* predictable termination */
+
+/******************************************************************************\
+* Types.
+\******************************************************************************/
+
+/* MQ arithmetic encoder class. */
+
+typedef struct {
+
+	/* The C register. */
+	uint_fast32_t creg;
+
+	/* The A register. */
+	uint_fast32_t areg;
+
+	/* The CT register. */
+	uint_fast32_t ctreg;
+
+	/* The maximum number of contexts. */
+	int maxctxs;
+
+	/* The per-context information. */
+	jpc_mqstate_t **ctxs;
+
+	/* The current context. */
+	jpc_mqstate_t **curctx;
+
+	/* The stream for encoder output. */
+	jas_stream_t *out;
+
+	/* The byte buffer (i.e., the B variable in the standard). */
+	int_fast16_t outbuf;
+
+	/* The last byte output. */
+	int_fast16_t lastbyte;
+
+	/* The error indicator. */
+	int err;
+	
+} jpc_mqenc_t;
+
+/* MQ arithmetic encoder state information. */
+
+typedef struct {
+
+	/* The A register. */
+	unsigned areg;
+
+	/* The C register. */
+	unsigned creg;
+
+	/* The CT register. */
+	unsigned ctreg;
+
+	/* The last byte output by the encoder. */
+	int lastbyte;
+
+} jpc_mqencstate_t;
+
+/******************************************************************************\
+* Functions/macros for construction and destruction.
+\******************************************************************************/
+
+/* Create a MQ encoder. */
+jpc_mqenc_t *jpc_mqenc_create(int maxctxs, jas_stream_t *out);
+
+/* Destroy a MQ encoder. */
+void jpc_mqenc_destroy(jpc_mqenc_t *enc);
+
+/******************************************************************************\
+* Functions/macros for initialization.
+\******************************************************************************/
+
+/* Initialize a MQ encoder. */
+void jpc_mqenc_init(jpc_mqenc_t *enc);
+
+/******************************************************************************\
+* Functions/macros for context manipulation.
+\******************************************************************************/
+
+/* Set the current context. */
+#define	jpc_mqenc_setcurctx(enc, ctxno) \
+        ((enc)->curctx = &(enc)->ctxs[ctxno]);
+
+/* Set the state information for a particular context. */
+void jpc_mqenc_setctx(jpc_mqenc_t *enc, int ctxno, jpc_mqctx_t *ctx);
+
+/* Set the state information for multiple contexts. */
+void jpc_mqenc_setctxs(jpc_mqenc_t *enc, int numctxs, jpc_mqctx_t *ctxs);
+
+/******************************************************************************\
+* Miscellaneous functions/macros.
+\******************************************************************************/
+
+/* Get the error state of a MQ encoder. */
+#define	jpc_mqenc_error(enc) \
+	((enc)->err)
+
+/* Get the current encoder state. */
+void jpc_mqenc_getstate(jpc_mqenc_t *enc, jpc_mqencstate_t *state);
+
+/* Terminate the code. */
+int jpc_mqenc_flush(jpc_mqenc_t *enc, int termmode);
+
+/******************************************************************************\
+* Functions/macros for encoding bits.
+\******************************************************************************/
+
+/* Encode a bit. */
+#if !defined(DEBUG)
+#define	jpc_mqenc_putbit(enc, bit)	jpc_mqenc_putbit_macro(enc, bit)
+#else
+#define	jpc_mqenc_putbit(enc, bit)	jpc_mqenc_putbit_func(enc, bit)
+#endif
+
+/******************************************************************************\
+* Functions/macros for debugging.
+\******************************************************************************/
+
+int jpc_mqenc_dump(jpc_mqenc_t *mqenc, FILE *out);
+
+/******************************************************************************\
+* Implementation-specific details.
+\******************************************************************************/
+
+/* Note: This macro is included only to satisfy the needs of
+  the mqenc_putbit macro. */
+#define	jpc_mqenc_putbit_macro(enc, bit) \
+	(((*((enc)->curctx))->mps == (bit)) ? \
+	  (((enc)->areg -= (*(enc)->curctx)->qeval), \
+	  ((!((enc)->areg & 0x8000)) ? (jpc_mqenc_codemps2(enc)) : \
+	  ((enc)->creg += (*(enc)->curctx)->qeval))) : \
+	  jpc_mqenc_codelps(enc))
+
+/* Note: These function prototypes are included only to satisfy the
+  needs of the mqenc_putbit_macro macro.  Do not call any of these
+  functions directly. */
+int jpc_mqenc_codemps2(jpc_mqenc_t *enc);
+int jpc_mqenc_codelps(jpc_mqenc_t *enc);
+
+/* Note: This function prototype is included only to satisfy the needs of
+  the mqenc_putbit macro. */
+int jpc_mqenc_putbit_func(jpc_mqenc_t *enc, int bit);
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c b/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c
new file mode 100644
index 00000000..1ed0dd90
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c
@@ -0,0 +1,1125 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Quadrature Mirror-Image Filter Bank (QMFB) Library
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <assert.h>
+
+#include "jasper/jas_fix.h"
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_math.h"
+
+#include "jpc_qmfb.h"
+#include "jpc_tsfb.h"
+#include "jpc_math.h"
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+static jpc_qmfb1d_t *jpc_qmfb1d_create(void);
+
+static int jpc_ft_getnumchans(jpc_qmfb1d_t *qmfb);
+static int jpc_ft_getanalfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters);
+static int jpc_ft_getsynfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters);
+static void jpc_ft_analyze(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x);
+static void jpc_ft_synthesize(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x);
+
+static int jpc_ns_getnumchans(jpc_qmfb1d_t *qmfb);
+static int jpc_ns_getanalfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters);
+static int jpc_ns_getsynfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters);
+static void jpc_ns_analyze(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x);
+static void jpc_ns_synthesize(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x);
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+jpc_qmfb1dops_t jpc_ft_ops = {
+	jpc_ft_getnumchans,
+	jpc_ft_getanalfilters,
+	jpc_ft_getsynfilters,
+	jpc_ft_analyze,
+	jpc_ft_synthesize
+};
+
+jpc_qmfb1dops_t jpc_ns_ops = {
+	jpc_ns_getnumchans,
+	jpc_ns_getanalfilters,
+	jpc_ns_getsynfilters,
+	jpc_ns_analyze,
+	jpc_ns_synthesize
+};
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+static void jpc_qmfb1d_setup(jpc_fix_t *startptr, int startind, int endind,
+  int intrastep, jpc_fix_t **lstartptr, int *lstartind, int *lendind,
+  jpc_fix_t **hstartptr, int *hstartind, int *hendind)
+{
+	*lstartind = JPC_CEILDIVPOW2(startind, 1);
+	*lendind = JPC_CEILDIVPOW2(endind, 1);
+	*hstartind = JPC_FLOORDIVPOW2(startind, 1);
+	*hendind = JPC_FLOORDIVPOW2(endind, 1);
+	*lstartptr = startptr;
+	*hstartptr = &startptr[(*lendind - *lstartind) * intrastep];
+}
+
+static void jpc_qmfb1d_split(jpc_fix_t *startptr, int startind, int endind,
+  register int step, jpc_fix_t *lstartptr, int lstartind, int lendind,
+  jpc_fix_t *hstartptr, int hstartind, int hendind)
+{
+#define QMFB_SPLITBUFSIZE 4096
+	jpc_fix_t splitbuf[QMFB_SPLITBUFSIZE];
+	jpc_fix_t *buf = splitbuf;
+	int llen;
+	int hlen;
+	int twostep;
+	jpc_fix_t *tmpptr;
+	register jpc_fix_t *ptr;
+	register jpc_fix_t *hptr;
+	register jpc_fix_t *lptr;
+	register int n;
+	int state;
+
+	twostep = step << 1;
+	llen = lendind - lstartind;
+	hlen = hendind - hstartind;
+
+#if defined(WIN32)
+	/* Get a buffer. */
+	if (bufsize > QMFB_SPLITBUFSIZE) {
+		if (!(buf = jas_malloc(bufsize * sizeof(jpc_fix_t)))) {
+			/* We have no choice but to commit suicide in this case. */
+			abort();
+		}
+	}
+#endif
+
+	if (hstartind < lstartind) {
+		/* The first sample in the input signal is to appear
+		  in the highpass subband signal. */
+		/* Copy the appropriate samples into the lowpass subband
+		  signal, saving any samples destined for the highpass subband
+		  signal as they are overwritten. */
+		tmpptr = buf;
+		ptr = &startptr[step];
+		lptr = lstartptr;
+		n = llen;
+		state = 1;
+		while (n-- > 0) {
+			if (state) {
+				*tmpptr = *lptr;
+				++tmpptr;
+			}
+			*lptr = *ptr;
+			ptr += twostep;
+			lptr += step;
+			state ^= 1;
+		}
+		/* Copy the appropriate samples into the highpass subband
+		  signal. */
+		/* Handle the nonoverwritten samples. */
+		hptr = &hstartptr[(hlen - 1) * step];
+		ptr = &startptr[(((llen + hlen - 1) >> 1) << 1) * step];
+		n = hlen - (tmpptr - buf);
+		while (n-- > 0) {
+			*hptr = *ptr;
+			hptr -= step;
+			ptr -= twostep;
+		}
+		/* Handle the overwritten samples. */
+		n = tmpptr - buf;
+		while (n-- > 0) {
+			--tmpptr;
+			*hptr = *tmpptr;
+			hptr -= step;
+		}
+	} else {
+		/* The first sample in the input signal is to appear
+		  in the lowpass subband signal. */
+		/* Copy the appropriate samples into the lowpass subband
+		  signal, saving any samples for the highpass subband
+		  signal as they are overwritten. */
+		state = 0;
+		ptr = startptr;
+		lptr = lstartptr;
+		tmpptr = buf;
+		n = llen;
+		while (n-- > 0) {
+			if (state) {
+				*tmpptr = *lptr;
+				++tmpptr;
+			}
+			*lptr = *ptr;
+			ptr += twostep;
+			lptr += step;
+			state ^= 1;
+		}
+		/* Copy the appropriate samples into the highpass subband
+		  signal. */
+		/* Handle the nonoverwritten samples. */
+		ptr = &startptr[((((llen + hlen) >> 1) << 1) - 1) * step];
+		hptr = &hstartptr[(hlen - 1) * step];
+		n = hlen - (tmpptr - buf);
+		while (n-- > 0) {
+			*hptr = *ptr;
+			ptr -= twostep;
+			hptr -= step;
+		}
+		/* Handle the overwritten samples. */
+		n = tmpptr - buf;
+		while (n-- > 0) {
+			--tmpptr;
+			*hptr = *tmpptr;
+			hptr -= step;
+		}
+	}
+
+#if defined(WIN32)
+	/* If the split buffer was allocated on the heap, free this memory. */
+	if (buf != splitbuf) {
+		jas_free(buf);
+	}
+#endif
+}
+
+static void jpc_qmfb1d_join(jpc_fix_t *startptr, int startind, int endind,
+  register int step, jpc_fix_t *lstartptr, int lstartind, int lendind,
+  jpc_fix_t *hstartptr, int hstartind, int hendind)
+{
+#define	QMFB_JOINBUFSIZE	4096
+	jpc_fix_t joinbuf[QMFB_JOINBUFSIZE];
+	jpc_fix_t *buf = joinbuf;
+	int llen;
+	int hlen;
+	int twostep;
+	jpc_fix_t *tmpptr;
+	register jpc_fix_t *ptr;
+	register jpc_fix_t *hptr;
+	register jpc_fix_t *lptr;
+	register int n;
+	int state;
+
+#if defined(WIN32)
+	/* Allocate memory for the join buffer from the heap. */
+	if (bufsize > QMFB_JOINBUFSIZE) {
+		if (!(buf = jas_malloc(bufsize * sizeof(jpc_fix_t)))) {
+			/* We have no choice but to commit suicide. */
+			abort();
+		}
+	}
+#endif
+
+	twostep = step << 1;
+	llen = lendind - lstartind;
+	hlen = hendind - hstartind;
+
+	if (hstartind < lstartind) {
+		/* The first sample in the highpass subband signal is to
+		  appear first in the output signal. */
+		/* Copy the appropriate samples into the first phase of the
+		  output signal. */
+		tmpptr = buf;
+		hptr = hstartptr;
+		ptr = startptr;
+		n = (llen + 1) >> 1;
+		while (n-- > 0) {
+			*tmpptr = *ptr;
+			*ptr = *hptr;
+			++tmpptr;
+			ptr += twostep;
+			hptr += step;
+		}
+		n = hlen - ((llen + 1) >> 1);
+		while (n-- > 0) {
+			*ptr = *hptr;
+			ptr += twostep;
+			hptr += step;
+		}
+		/* Copy the appropriate samples into the second phase of
+		  the output signal. */
+		ptr -= (lendind > hendind) ? (step) : (step + twostep);
+		state = !((llen - 1) & 1);
+		lptr = &lstartptr[(llen - 1) * step];
+		n = llen;
+		while (n-- > 0) {
+			if (state) {
+				--tmpptr;
+				*ptr = *tmpptr;
+			} else {
+				*ptr = *lptr;
+			}
+			lptr -= step;
+			ptr -= twostep;
+			state ^= 1;
+		}
+	} else {
+		/* The first sample in the lowpass subband signal is to
+		  appear first in the output signal. */
+		/* Copy the appropriate samples into the first phase of the
+		  output signal (corresponding to even indexed samples). */
+		lptr = &lstartptr[(llen - 1) * step];
+		ptr = &startptr[((llen - 1) << 1) * step];
+		n = llen >> 1;
+		tmpptr = buf;
+		while (n-- > 0) {
+			*tmpptr = *ptr;
+			*ptr = *lptr;
+			++tmpptr;
+			ptr -= twostep;
+			lptr -= step;
+		}
+		n = llen - (llen >> 1);
+		while (n-- > 0) {
+			*ptr = *lptr;
+			ptr -= twostep;
+			lptr -= step;
+		}
+		/* Copy the appropriate samples into the second phase of
+		  the output signal (corresponding to odd indexed
+		  samples). */
+		ptr = &startptr[step];
+		hptr = hstartptr;
+		state = !(llen & 1);
+		n = hlen;
+		while (n-- > 0) {
+			if (state) {
+				--tmpptr;
+				*ptr = *tmpptr;
+			} else {
+				*ptr = *hptr;
+			}
+			hptr += step;
+			ptr += twostep;
+			state ^= 1;
+		}
+	}
+
+#if defined(WIN32)
+	/* If the join buffer was allocated on the heap, free this memory. */
+	if (buf != joinbuf) {
+		jas_free(buf);
+	}
+#endif
+}
+
+/******************************************************************************\
+* Code for 5/3 transform.
+\******************************************************************************/
+
+static int jpc_ft_getnumchans(jpc_qmfb1d_t *qmfb)
+{
+	return 2;
+}
+
+static int jpc_ft_getanalfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters)
+{
+	abort();
+	return -1;
+}
+
+static int jpc_ft_getsynfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters)
+{
+	jas_seq_t *lf;
+	jas_seq_t *hf;
+
+	lf = 0;
+	hf = 0;
+
+	if (len > 1 || (!len)) {
+		if (!(lf = jas_seq_create(-1, 2))) {
+			goto error;
+		}
+		jas_seq_set(lf, -1, jpc_dbltofix(0.5));
+		jas_seq_set(lf, 0, jpc_dbltofix(1.0));
+		jas_seq_set(lf, 1, jpc_dbltofix(0.5));
+		if (!(hf = jas_seq_create(-1, 4))) {
+			goto error;
+		}
+		jas_seq_set(hf, -1, jpc_dbltofix(-0.125));
+		jas_seq_set(hf, 0, jpc_dbltofix(-0.25));
+		jas_seq_set(hf, 1, jpc_dbltofix(0.75));
+		jas_seq_set(hf, 2, jpc_dbltofix(-0.25));
+		jas_seq_set(hf, 3, jpc_dbltofix(-0.125));
+	} else if (len == 1) {
+		if (!(lf = jas_seq_create(0, 1))) {
+			goto error;
+		}
+		jas_seq_set(lf, 0, jpc_dbltofix(1.0));
+		if (!(hf = jas_seq_create(0, 1))) {
+			goto error;
+		}
+		jas_seq_set(hf, 0, jpc_dbltofix(2.0));
+	} else {
+		abort();
+	}
+
+	filters[0] = lf;
+	filters[1] = hf;
+
+	return 0;
+
+error:
+	if (lf) {
+		jas_seq_destroy(lf);
+	}
+	if (hf) {
+		jas_seq_destroy(hf);
+	}
+	return -1;
+}
+
+#define	NFT_LIFT0(lstartptr, lstartind, lendind, hstartptr, hstartind, hendind, step, pluseq) \
+{ \
+	register jpc_fix_t *lptr = (lstartptr); \
+	register jpc_fix_t *hptr = (hstartptr); \
+	register int n = (hendind) - (hstartind); \
+	if ((hstartind) < (lstartind)) { \
+		pluseq(*hptr, *lptr); \
+		hptr += (step); \
+		--n; \
+	} \
+	if ((hendind) >= (lendind)) { \
+		--n; \
+	} \
+	while (n-- > 0) { \
+		pluseq(*hptr, jpc_fix_asr(jpc_fix_add(*lptr, lptr[(step)]), 1)); \
+		hptr += (step); \
+		lptr += (step); \
+	} \
+	if ((hendind) >= (lendind)) { \
+		pluseq(*hptr, *lptr); \
+	} \
+}
+
+#define	NFT_LIFT1(lstartptr, lstartind, lendind, hstartptr, hstartind, hendind, step, pluseq) \
+{ \
+	register jpc_fix_t *lptr = (lstartptr); \
+	register jpc_fix_t *hptr = (hstartptr); \
+	register int n = (lendind) - (lstartind); \
+	if ((hstartind) >= (lstartind)) { \
+		pluseq(*lptr, *hptr); \
+		lptr += (step); \
+		--n; \
+	} \
+	if ((lendind) > (hendind)) { \
+		--n; \
+	} \
+	while (n-- > 0) { \
+		pluseq(*lptr, jpc_fix_asr(jpc_fix_add(*hptr, hptr[(step)]), 2)); \
+		lptr += (step); \
+		hptr += (step); \
+	} \
+	if ((lendind) > (hendind)) { \
+		pluseq(*lptr, *hptr); \
+	} \
+}
+
+#define	RFT_LIFT0(lstartptr, lstartind, lendind, hstartptr, hstartind, hendind, step, pmeqop) \
+{ \
+	register jpc_fix_t *lptr = (lstartptr); \
+	register jpc_fix_t *hptr = (hstartptr); \
+	register int n = (hendind) - (hstartind); \
+	if ((hstartind) < (lstartind)) { \
+		*hptr pmeqop *lptr; \
+		hptr += (step); \
+		--n; \
+	} \
+	if ((hendind) >= (lendind)) { \
+		--n; \
+	} \
+	while (n-- > 0) { \
+		*hptr pmeqop (*lptr + lptr[(step)]) >> 1; \
+		hptr += (step); \
+		lptr += (step); \
+	} \
+	if ((hendind) >= (lendind)) { \
+		*hptr pmeqop *lptr; \
+	} \
+}
+
+#define	RFT_LIFT1(lstartptr, lstartind, lendind, hstartptr, hstartind, hendind, step, pmeqop) \
+{ \
+	register jpc_fix_t *lptr = (lstartptr); \
+	register jpc_fix_t *hptr = (hstartptr); \
+	register int n = (lendind) - (lstartind); \
+	if ((hstartind) >= (lstartind)) { \
+		*lptr pmeqop ((*hptr << 1) + 2) >> 2; \
+		lptr += (step); \
+		--n; \
+	} \
+	if ((lendind) > (hendind)) { \
+		--n; \
+	} \
+	while (n-- > 0) { \
+		*lptr pmeqop ((*hptr + hptr[(step)]) + 2) >> 2; \
+		lptr += (step); \
+		hptr += (step); \
+	} \
+	if ((lendind) > (hendind)) { \
+		*lptr pmeqop ((*hptr << 1) + 2) >> 2; \
+	} \
+}
+
+static void jpc_ft_analyze(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x)
+{
+	jpc_fix_t *startptr;
+	int startind;
+	int endind;
+	jpc_fix_t *  lstartptr;
+	int   lstartind;
+	int   lendind;
+	jpc_fix_t *  hstartptr;
+	int   hstartind;
+	int   hendind;
+	int interstep;
+	int intrastep;
+	int numseq;
+
+	if (flags & JPC_QMFB1D_VERT) {
+		interstep = 1;
+		intrastep = jas_seq2d_rowstep(x);
+		numseq = jas_seq2d_width(x);
+		startind = jas_seq2d_ystart(x);
+		endind = jas_seq2d_yend(x);
+	} else {
+		interstep = jas_seq2d_rowstep(x);
+		intrastep = 1;
+		numseq = jas_seq2d_height(x);
+		startind = jas_seq2d_xstart(x);
+		endind = jas_seq2d_xend(x);
+	}
+
+	assert(startind < endind);
+
+	startptr = jas_seq2d_getref(x, jas_seq2d_xstart(x), jas_seq2d_ystart(x));
+	if (flags & JPC_QMFB1D_RITIMODE) {
+		while (numseq-- > 0) {
+			jpc_qmfb1d_setup(startptr, startind, endind, intrastep,
+			  &lstartptr, &lstartind, &lendind, &hstartptr,
+			  &hstartind, &hendind);
+			if (endind - startind > 1) {
+				jpc_qmfb1d_split(startptr, startind, endind,
+				  intrastep, lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind);
+				RFT_LIFT0(lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind, intrastep, -=);
+				RFT_LIFT1(lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind, intrastep, +=);
+			} else {
+				if (lstartind == lendind) {
+					*startptr <<= 1;
+				}
+			}
+			startptr += interstep;
+		}
+	} else {
+		while (numseq-- > 0) {
+			jpc_qmfb1d_setup(startptr, startind, endind, intrastep,
+			  &lstartptr, &lstartind, &lendind, &hstartptr,
+			  &hstartind, &hendind);
+			if (endind - startind > 1) {
+				jpc_qmfb1d_split(startptr, startind, endind,
+				  intrastep, lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind);
+				NFT_LIFT0(lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind, intrastep,
+				  jpc_fix_minuseq);
+				NFT_LIFT1(lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind, intrastep,
+				  jpc_fix_pluseq);
+			} else {
+				if (lstartind == lendind) {
+					*startptr = jpc_fix_asl(*startptr, 1);
+				}
+			}
+			startptr += interstep;
+		}
+	}
+}
+
+static void jpc_ft_synthesize(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x)
+{
+	jpc_fix_t *startptr;
+	int startind;
+	int endind;
+	jpc_fix_t *lstartptr;
+	int lstartind;
+	int lendind;
+	jpc_fix_t *hstartptr;
+	int hstartind;
+	int hendind;
+	int interstep;
+	int intrastep;
+	int numseq;
+
+	if (flags & JPC_QMFB1D_VERT) {
+		interstep = 1;
+		intrastep = jas_seq2d_rowstep(x);
+		numseq = jas_seq2d_width(x);
+		startind = jas_seq2d_ystart(x);
+		endind = jas_seq2d_yend(x);
+	} else {
+		interstep = jas_seq2d_rowstep(x);
+		intrastep = 1;
+		numseq = jas_seq2d_height(x);
+		startind = jas_seq2d_xstart(x);
+		endind = jas_seq2d_xend(x);
+	}
+
+	assert(startind < endind);
+
+	startptr = jas_seq2d_getref(x, jas_seq2d_xstart(x), jas_seq2d_ystart(x));
+	if (flags & JPC_QMFB1D_RITIMODE) {
+		while (numseq-- > 0) {
+			jpc_qmfb1d_setup(startptr, startind, endind, intrastep,
+			  &lstartptr, &lstartind, &lendind, &hstartptr,
+			  &hstartind, &hendind);
+			if (endind - startind > 1) {
+				RFT_LIFT1(lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind, intrastep, -=);
+				RFT_LIFT0(lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind, intrastep, +=);
+				jpc_qmfb1d_join(startptr, startind, endind,
+				  intrastep, lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind);
+			} else {
+				if (lstartind == lendind) {
+					*startptr >>= 1;
+				}
+			}
+			startptr += interstep;
+		}
+	} else {
+		while (numseq-- > 0) {
+			jpc_qmfb1d_setup(startptr, startind, endind, intrastep,
+			  &lstartptr, &lstartind, &lendind, &hstartptr,
+			  &hstartind, &hendind);
+			if (endind - startind > 1) {
+				NFT_LIFT1(lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind, intrastep,
+				  jpc_fix_minuseq);
+				NFT_LIFT0(lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind, intrastep,
+				  jpc_fix_pluseq);
+				jpc_qmfb1d_join(startptr, startind, endind,
+				  intrastep, lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind);
+			} else {
+				if (lstartind == lendind) {
+					*startptr = jpc_fix_asr(*startptr, 1);
+				}
+			}
+			startptr += interstep;
+		}
+	}
+}
+
+/******************************************************************************\
+* Code for 9/7 transform.
+\******************************************************************************/
+
+static int jpc_ns_getnumchans(jpc_qmfb1d_t *qmfb)
+{
+	return 2;
+}
+
+static int jpc_ns_getanalfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters)
+{
+	abort();
+	return -1;
+}
+
+static int jpc_ns_getsynfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters)
+{
+	jas_seq_t *lf;
+	jas_seq_t *hf;
+
+	lf = 0;
+	hf = 0;
+
+	if (len > 1 || (!len)) {
+		if (!(lf = jas_seq_create(-3, 4))) {
+			goto error;
+		}
+		jas_seq_set(lf, -3, jpc_dbltofix(-0.09127176311424948));
+		jas_seq_set(lf, -2, jpc_dbltofix(-0.05754352622849957));
+		jas_seq_set(lf, -1, jpc_dbltofix(0.5912717631142470));
+		jas_seq_set(lf, 0, jpc_dbltofix(1.115087052456994));
+		jas_seq_set(lf, 1, jpc_dbltofix(0.5912717631142470));
+		jas_seq_set(lf, 2, jpc_dbltofix(-0.05754352622849957));
+		jas_seq_set(lf, 3, jpc_dbltofix(-0.09127176311424948));
+		if (!(hf = jas_seq_create(-3, 6))) {
+			goto error;
+		}
+		jas_seq_set(hf, -3, jpc_dbltofix(-0.02674875741080976 * 2.0));
+		jas_seq_set(hf, -2, jpc_dbltofix(-0.01686411844287495 * 2.0));
+		jas_seq_set(hf, -1, jpc_dbltofix(0.07822326652898785 * 2.0));
+		jas_seq_set(hf, 0, jpc_dbltofix(0.2668641184428723 * 2.0));
+		jas_seq_set(hf, 1, jpc_dbltofix(-0.6029490182363579 * 2.0));
+		jas_seq_set(hf, 2, jpc_dbltofix(0.2668641184428723 * 2.0));
+		jas_seq_set(hf, 3, jpc_dbltofix(0.07822326652898785 * 2.0));
+		jas_seq_set(hf, 4, jpc_dbltofix(-0.01686411844287495 * 2.0));
+		jas_seq_set(hf, 5, jpc_dbltofix(-0.02674875741080976 * 2.0));
+	} else if (len == 1) {
+		if (!(lf = jas_seq_create(0, 1))) {
+			goto error;
+		}
+		jas_seq_set(lf, 0, jpc_dbltofix(1.0));
+		if (!(hf = jas_seq_create(0, 1))) {
+			goto error;
+		}
+		jas_seq_set(hf, 0, jpc_dbltofix(2.0));
+	} else {
+		abort();
+	}
+
+	filters[0] = lf;
+	filters[1] = hf;
+
+	return 0;
+
+error:
+	if (lf) {
+		jas_seq_destroy(lf);
+	}
+	if (hf) {
+		jas_seq_destroy(hf);
+	}
+	return -1;
+}
+
+#define	NNS_LIFT0(lstartptr, lstartind, lendind, hstartptr, hstartind, hendind, step, alpha) \
+{ \
+	register jpc_fix_t *lptr = (lstartptr); \
+	register jpc_fix_t *hptr = (hstartptr); \
+	register int n = (hendind) - (hstartind); \
+	jpc_fix_t twoalpha = jpc_fix_mulbyint(alpha, 2); \
+	if ((hstartind) < (lstartind)) { \
+		jpc_fix_pluseq(*hptr, jpc_fix_mul(*lptr, (twoalpha))); \
+		hptr += (step); \
+		--n; \
+	} \
+	if ((hendind) >= (lendind)) { \
+		--n; \
+	} \
+	while (n-- > 0) { \
+		jpc_fix_pluseq(*hptr, jpc_fix_mul(jpc_fix_add(*lptr, lptr[(step)]), (alpha))); \
+		hptr += (step); \
+		lptr += (step); \
+	} \
+	if ((hendind) >= (lendind)) { \
+		jpc_fix_pluseq(*hptr, jpc_fix_mul(*lptr, (twoalpha))); \
+	} \
+}
+
+#define	NNS_LIFT1(lstartptr, lstartind, lendind, hstartptr, hstartind, hendind, step, alpha) \
+{ \
+	register jpc_fix_t *lptr = (lstartptr); \
+	register jpc_fix_t *hptr = (hstartptr); \
+	register int n = (lendind) - (lstartind); \
+	int twoalpha = jpc_fix_mulbyint(alpha, 2); \
+	if ((hstartind) >= (lstartind)) { \
+		jpc_fix_pluseq(*lptr, jpc_fix_mul(*hptr, (twoalpha))); \
+		lptr += (step); \
+		--n; \
+	} \
+	if ((lendind) > (hendind)) { \
+		--n; \
+	} \
+	while (n-- > 0) { \
+		jpc_fix_pluseq(*lptr, jpc_fix_mul(jpc_fix_add(*hptr, hptr[(step)]), (alpha))); \
+		lptr += (step); \
+		hptr += (step); \
+	} \
+	if ((lendind) > (hendind)) { \
+		jpc_fix_pluseq(*lptr, jpc_fix_mul(*hptr, (twoalpha))); \
+	} \
+}
+
+#define	NNS_SCALE(startptr, startind, endind, step, alpha) \
+{ \
+	register jpc_fix_t *ptr = (startptr); \
+	register int n = (endind) - (startind); \
+	while (n-- > 0) { \
+		jpc_fix_muleq(*ptr, alpha); \
+		ptr += (step); \
+	} \
+}
+
+static void jpc_ns_analyze(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x)
+{
+	jpc_fix_t *startptr;
+	int startind;
+	int endind;
+	jpc_fix_t *lstartptr;
+	int lstartind;
+	int lendind;
+	jpc_fix_t *hstartptr;
+	int hstartind;
+	int hendind;
+	int interstep;
+	int intrastep;
+	int numseq;
+
+	if (flags & JPC_QMFB1D_VERT) {
+		interstep = 1;
+		intrastep = jas_seq2d_rowstep(x);
+		numseq = jas_seq2d_width(x);
+		startind = jas_seq2d_ystart(x);
+		endind = jas_seq2d_yend(x);
+	} else {
+		interstep = jas_seq2d_rowstep(x);
+		intrastep = 1;
+		numseq = jas_seq2d_height(x);
+		startind = jas_seq2d_xstart(x);
+		endind = jas_seq2d_xend(x);
+	}
+
+	assert(startind < endind);
+
+	startptr = jas_seq2d_getref(x, jas_seq2d_xstart(x), jas_seq2d_ystart(x));
+	if (!(flags & JPC_QMFB1D_RITIMODE)) {
+		while (numseq-- > 0) {
+			jpc_qmfb1d_setup(startptr, startind, endind, intrastep,
+			  &lstartptr, &lstartind, &lendind, &hstartptr,
+			  &hstartind, &hendind);
+			if (endind - startind > 1) {
+				jpc_qmfb1d_split(startptr, startind, endind,
+				  intrastep, lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind);
+				NNS_LIFT0(lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind, intrastep,
+				  jpc_dbltofix(-1.586134342));
+				NNS_LIFT1(lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind, intrastep,
+				  jpc_dbltofix(-0.052980118));
+				NNS_LIFT0(lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind, intrastep,
+				  jpc_dbltofix(0.882911075));
+				NNS_LIFT1(lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind, intrastep,
+				  jpc_dbltofix(0.443506852));
+				NNS_SCALE(lstartptr, lstartind, lendind,
+				  intrastep, jpc_dbltofix(1.0/1.23017410558578));
+				NNS_SCALE(hstartptr, hstartind, hendind,
+				  intrastep, jpc_dbltofix(1.0/1.62578613134411));
+			} else {
+#if 0
+				if (lstartind == lendind) {
+					*startptr = jpc_fix_asl(*startptr, 1);
+				}
+#endif
+			}
+			startptr += interstep;
+		}
+	} else {
+		/* The reversible integer-to-integer mode is not supported
+		  for this transform. */
+		abort();
+	}
+}
+
+static void jpc_ns_synthesize(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x)
+{
+	jpc_fix_t *startptr;
+	int startind;
+	int endind;
+	jpc_fix_t *lstartptr;
+	int lstartind;
+	int lendind;
+	jpc_fix_t *hstartptr;
+	int hstartind;
+	int hendind;
+	int interstep;
+	int intrastep;
+	int numseq;
+
+	if (flags & JPC_QMFB1D_VERT) {
+		interstep = 1;
+		intrastep = jas_seq2d_rowstep(x);
+		numseq = jas_seq2d_width(x);
+		startind = jas_seq2d_ystart(x);
+		endind = jas_seq2d_yend(x);
+	} else {
+		interstep = jas_seq2d_rowstep(x);
+		intrastep = 1;
+		numseq = jas_seq2d_height(x);
+		startind = jas_seq2d_xstart(x);
+		endind = jas_seq2d_xend(x);
+	}
+
+	assert(startind < endind);
+
+	startptr = jas_seq2d_getref(x, jas_seq2d_xstart(x), jas_seq2d_ystart(x));
+	if (!(flags & JPC_QMFB1D_RITIMODE)) {
+		while (numseq-- > 0) {
+			jpc_qmfb1d_setup(startptr, startind, endind, intrastep,
+			  &lstartptr, &lstartind, &lendind, &hstartptr,
+			  &hstartind, &hendind);
+			if (endind - startind > 1) {
+				NNS_SCALE(lstartptr, lstartind, lendind,
+				  intrastep, jpc_dbltofix(1.23017410558578));
+				NNS_SCALE(hstartptr, hstartind, hendind,
+				  intrastep, jpc_dbltofix(1.62578613134411));
+				NNS_LIFT1(lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind, intrastep,
+				  jpc_dbltofix(-0.443506852));
+				NNS_LIFT0(lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind, intrastep,
+				  jpc_dbltofix(-0.882911075));
+				NNS_LIFT1(lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind, intrastep,
+				  jpc_dbltofix(0.052980118));
+				NNS_LIFT0(lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind, intrastep,
+				  jpc_dbltofix(1.586134342));
+				jpc_qmfb1d_join(startptr, startind, endind,
+				  intrastep, lstartptr, lstartind, lendind,
+				  hstartptr, hstartind, hendind);
+			} else {
+#if 0
+				if (lstartind == lendind) {
+					*startptr = jpc_fix_asr(*startptr, 1);
+				}
+#endif
+			}
+			startptr += interstep;
+		}
+	} else {
+		/* The reversible integer-to-integer mode is not supported
+		  for this transform. */
+		abort();
+	}
+}
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+jpc_qmfb1d_t *jpc_qmfb1d_make(int qmfbid)
+{
+	jpc_qmfb1d_t *qmfb;
+	if (!(qmfb = jpc_qmfb1d_create())) {
+		return 0;
+	}
+	switch (qmfbid) {
+	case JPC_QMFB1D_FT:
+		qmfb->ops = &jpc_ft_ops;
+		break;
+	case JPC_QMFB1D_NS:
+		qmfb->ops = &jpc_ns_ops;
+		break;
+	default:
+		jpc_qmfb1d_destroy(qmfb);
+		return 0;
+		break;
+	}
+	return qmfb;
+}
+
+static jpc_qmfb1d_t *jpc_qmfb1d_create()
+{
+	jpc_qmfb1d_t *qmfb;
+	if (!(qmfb = jas_malloc(sizeof(jpc_qmfb1d_t)))) {
+		return 0;
+	}
+	qmfb->ops = 0;
+	return qmfb;
+}
+
+jpc_qmfb1d_t *jpc_qmfb1d_copy(jpc_qmfb1d_t *qmfb)
+{
+	jpc_qmfb1d_t *newqmfb;
+
+	if (!(newqmfb = jpc_qmfb1d_create())) {
+		return 0;
+	}
+	newqmfb->ops = qmfb->ops;
+	return newqmfb;
+}
+
+void jpc_qmfb1d_destroy(jpc_qmfb1d_t *qmfb)
+{
+	jas_free(qmfb);
+}
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+void jpc_qmfb1d_getbands(jpc_qmfb1d_t *qmfb, int flags, uint_fast32_t xstart,
+  uint_fast32_t ystart, uint_fast32_t xend, uint_fast32_t yend, int maxbands,
+  int *numbandsptr, jpc_qmfb1dband_t *bands)
+{
+	int start;
+	int end;
+	if (flags & JPC_QMFB1D_VERT) {
+		start = ystart;
+		end = yend;
+	} else {
+		start = xstart;
+		end = xend;
+	}
+	assert(jpc_qmfb1d_getnumchans(qmfb) == 2);
+	assert(start <= end);
+	bands[0].start = JPC_CEILDIVPOW2(start, 1);
+	bands[0].end = JPC_CEILDIVPOW2(end, 1);
+	bands[0].locstart = start;
+	bands[0].locend = start + bands[0].end - bands[0].start;
+	bands[1].start = JPC_FLOORDIVPOW2(start, 1);
+	bands[1].end = JPC_FLOORDIVPOW2(end, 1);
+	bands[1].locstart = bands[0].locend;
+	bands[1].locend = bands[1].locstart + bands[1].end - bands[1].start;
+	assert(bands[1].locend == end);
+	*numbandsptr = 2;
+}
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+int jpc_qmfb1d_getnumchans(jpc_qmfb1d_t *qmfb)
+{
+	return (*qmfb->ops->getnumchans)(qmfb);
+}
+
+int jpc_qmfb1d_getanalfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters)
+{
+	return (*qmfb->ops->getanalfilters)(qmfb, len, filters);
+}
+
+int jpc_qmfb1d_getsynfilters(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters)
+{
+	return (*qmfb->ops->getsynfilters)(qmfb, len, filters);
+}
+
+void jpc_qmfb1d_analyze(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x)
+{
+	(*qmfb->ops->analyze)(qmfb, flags, x);
+}
+
+void jpc_qmfb1d_synthesize(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x)
+{
+	(*qmfb->ops->synthesize)(qmfb, flags, x);
+}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.h b/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.h
new file mode 100644
index 00000000..e10b6c13
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.h
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Quadrature Mirror-Image Filter Bank (QMFB) Routines
+ *
+ * $Id$
+ */
+
+#ifndef JPC_QMFB_H
+#define JPC_QMFB_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jasper/jas_seq.h"
+
+/******************************************************************************\
+* Constants.
+\******************************************************************************/
+
+/* The maximum number of channels for a QMF bank. */
+#define	JPC_QMFB1D_MAXCHANS	2
+
+/* Select reversible integer-to-integer mode. */
+#define	JPC_QMFB1D_RITIMODE	1
+
+/* Vertical filtering. */
+#define	JPC_QMFB1D_VERT	0x10000
+
+/* QMFB IDs. */
+#define	JPC_QMFB1D_FT	1	/* 5/3 */
+#define	JPC_QMFB1D_NS	2	/* 9/7 */
+
+/******************************************************************************\
+* Types.
+\******************************************************************************/
+
+/* Forward declaration. */
+struct jpc_qmfb1dops_s;
+
+/* Band information. */
+
+typedef struct {
+
+	/* The starting index for the band in the downsampled domain. */
+	int start;
+
+	/* The ending index for the band in the downsampled domain. */
+	int end;
+
+	/* The location of the start of the band. */
+	int locstart;
+
+	/* The location of the end of the band. */
+	int locend;
+
+} jpc_qmfb1dband_t;
+
+/* QMF bank */
+
+typedef struct {
+
+	/* The operations for this QMFB. */
+	struct jpc_qmfb1dops_s *ops;
+
+} jpc_qmfb1d_t;
+
+/* QMFB operations. */
+
+typedef struct jpc_qmfb1dops_s {
+
+	/* The number of channels in the QMFB. */
+	int (*getnumchans)(jpc_qmfb1d_t *qmfb);
+
+	/* Get the analysis filters for this QMFB. */
+	int (*getanalfilters)(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters);
+
+	/* Get the synthesis filters for this QMFB. */
+	int (*getsynfilters)(jpc_qmfb1d_t *qmfb, int len, jas_seq2d_t **filters);
+
+	/* Do analysis. */
+	void (*analyze)(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x);
+
+	/* Do synthesis. */
+	void (*synthesize)(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x);
+
+} jpc_qmfb1dops_t;
+
+/******************************************************************************\
+* Functions.
+\******************************************************************************/
+
+/* Create a QMFB from a QMFB ID. */
+jpc_qmfb1d_t *jpc_qmfb1d_make(int qmfbid);
+
+/* Create a copy of a QMFB. */
+jpc_qmfb1d_t *jpc_qmfb1d_copy(jpc_qmfb1d_t *qmfb);
+
+/* Destroy a QMFB. */
+void jpc_qmfb1d_destroy(jpc_qmfb1d_t *qmfb);
+
+/* Get the number of channels for a QMFB. */
+int jpc_qmfb1d_getnumchans(jpc_qmfb1d_t *qmfb);
+
+/* Get the analysis filters for a QMFB. */
+int jpc_qmfb1d_getanalfilters(jpc_qmfb1d_t *qmfb, int len,
+  jas_seq2d_t **filters);
+
+/* Get the synthesis filters for a QMFB. */
+int jpc_qmfb1d_getsynfilters(jpc_qmfb1d_t *qmfb, int len,
+  jas_seq2d_t **filters);
+
+/* Get the bands for a QMFB. */
+void jpc_qmfb1d_getbands(jpc_qmfb1d_t *qmfb, int flags, uint_fast32_t xstart,
+  uint_fast32_t ystart, uint_fast32_t xend, uint_fast32_t yend, int maxbands,
+  int *numbandsptr, jpc_qmfb1dband_t *bands);
+
+/* Perform analysis. */
+void jpc_qmfb1d_analyze(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x);
+
+/* Perform synthesis. */
+void jpc_qmfb1d_synthesize(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x);
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_t1cod.c b/converter/other/jpeg2000/libjasper/jpc/jpc_t1cod.c
new file mode 100644
index 00000000..650cb854
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_t1cod.c
@@ -0,0 +1,537 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <math.h>
+
+#include "jasper/jas_types.h"
+#include "jasper/jas_math.h"
+
+#include "jpc_bs.h"
+#include "jpc_dec.h"
+#include "jpc_cs.h"
+#include "jpc_mqcod.h"
+#include "jpc_t1cod.h"
+#include "jpc_tsfb.h"
+
+double jpc_pow2i(int n);
+
+/******************************************************************************\
+* Global data.
+\******************************************************************************/
+
+int jpc_zcctxnolut[4 * 256];
+int jpc_spblut[256];
+int jpc_scctxnolut[256];
+int jpc_magctxnolut[4096];
+
+jpc_fix_t jpc_signmsedec[1 << JPC_NMSEDEC_BITS];
+jpc_fix_t jpc_refnmsedec[1 << JPC_NMSEDEC_BITS];
+jpc_fix_t jpc_signmsedec0[1 << JPC_NMSEDEC_BITS];
+jpc_fix_t jpc_refnmsedec0[1 << JPC_NMSEDEC_BITS];
+
+jpc_mqctx_t jpc_mqctxs[JPC_NUMCTXS];
+
+/******************************************************************************\
+* Code.
+\******************************************************************************/
+
+int JPC_PASSTYPE(int passno)
+{
+	int passtype;
+	switch (passno % 3) {
+	case 0:
+		passtype = JPC_CLNPASS;
+		break;
+	case 1:
+		passtype = JPC_SIGPASS;
+		break;
+	case 2:
+		passtype = JPC_REFPASS;
+		break;
+	default:
+		passtype = -1;
+		assert(0);
+		break;
+	}
+	return passtype;
+}
+
+int JPC_NOMINALGAIN(int qmfbid, int numlvls, int lvlno, int orient)
+{
+if (qmfbid == JPC_COX_INS) {
+	return 0;
+}
+	assert(qmfbid == JPC_COX_RFT);
+	if (lvlno == 0) {
+		assert(orient == JPC_TSFB_LL);
+		return 0;
+	} else {
+		switch (orient) {
+		case JPC_TSFB_LH:
+		case JPC_TSFB_HL:
+			return 1;
+			break;
+		case JPC_TSFB_HH:
+			return 2;
+			break;
+		}
+	}
+	abort();
+}
+
+/******************************************************************************\
+* Coding pass related functions.
+\******************************************************************************/
+
+int JPC_SEGTYPE(int passno, int firstpassno, int bypass)
+{
+	int passtype;
+	if (bypass) {
+		passtype = JPC_PASSTYPE(passno);
+		if (passtype == JPC_CLNPASS) {
+			return JPC_SEG_MQ;
+		}
+		return ((passno < firstpassno + 10) ? JPC_SEG_MQ : JPC_SEG_RAW);
+	} else {
+		return JPC_SEG_MQ;
+	}
+}
+
+int JPC_SEGPASSCNT(int passno, int firstpassno, int numpasses, int bypass, int termall)
+{
+	int ret;
+	int passtype;
+
+	if (termall) {
+		ret = 1;
+	} else if (bypass) {
+		if (passno < firstpassno + 10) {
+			ret = 10 - (passno - firstpassno);
+		} else {
+			passtype = JPC_PASSTYPE(passno);
+			switch (passtype) {
+			case JPC_SIGPASS:
+				ret = 2;
+				break;
+			case JPC_REFPASS:
+				ret = 1;
+				break;
+			case JPC_CLNPASS:
+				ret = 1;
+				break;
+			default:
+				ret = -1;
+				assert(0);
+				break;
+			}
+		}
+	} else {
+		ret = JPC_PREC * 3 - 2;
+	}
+	ret = JAS_MIN(ret, numpasses - passno);
+	return ret;
+}
+
+int JPC_ISTERMINATED(int passno, int firstpassno, int numpasses, int termall,
+  int lazy)
+{
+	int ret;
+	int n;
+	if (passno - firstpassno == numpasses - 1) {
+		ret = 1;
+	} else {
+		n = JPC_SEGPASSCNT(passno, firstpassno, numpasses, lazy, termall);
+		ret = (n <= 1) ? 1 : 0;
+	}
+
+	return ret;
+}
+
+/******************************************************************************\
+* Lookup table code.
+\******************************************************************************/
+
+static void jpc_initmqctxs(void)
+{
+	jpc_initctxs(jpc_mqctxs);
+}
+
+void jpc_initluts()
+{
+	int i;
+	int orient;
+	int refine;
+	float u;
+	float v;
+	float t;
+
+/* XXX - hack */
+jpc_initmqctxs();
+
+	for (orient = 0; orient < 4; ++orient) {
+		for (i = 0; i < 256; ++i) {
+			jpc_zcctxnolut[(orient << 8) | i] = jpc_getzcctxno(i, orient);
+		}
+	}
+
+	for (i = 0; i < 256; ++i) {
+		jpc_spblut[i] = jpc_getspb(i << 4);
+	}
+
+	for (i = 0; i < 256; ++i) {
+		jpc_scctxnolut[i] = jpc_getscctxno(i << 4);
+	}
+
+	for (refine = 0; refine < 2; ++refine) {
+		for (i = 0; i < 2048; ++i) {
+			jpc_magctxnolut[(refine << 11) + i] = jpc_getmagctxno((refine ? JPC_REFINE : 0) | i);
+		}
+	}
+
+	for (i = 0; i < (1 << JPC_NMSEDEC_BITS); ++i) {
+		t = i * jpc_pow2i(-JPC_NMSEDEC_FRACBITS);
+		u = t;
+		v = t - 1.5;
+		jpc_signmsedec[i] = jpc_dbltofix(floor((u * u - v * v) * jpc_pow2i(JPC_NMSEDEC_FRACBITS) + 0.5) / jpc_pow2i(JPC_NMSEDEC_FRACBITS));
+/* XXX - this calc is not correct */
+		jpc_signmsedec0[i] = jpc_dbltofix(floor((u * u) * jpc_pow2i(JPC_NMSEDEC_FRACBITS) + 0.5) / jpc_pow2i(JPC_NMSEDEC_FRACBITS));
+		u = t - 1.0;
+		if (i & (1 << (JPC_NMSEDEC_BITS - 1))) {
+			v = t - 1.5;
+		} else {
+			v = t - 0.5;
+		}
+		jpc_refnmsedec[i] = jpc_dbltofix(floor((u * u - v * v) * jpc_pow2i(JPC_NMSEDEC_FRACBITS) + 0.5) / jpc_pow2i(JPC_NMSEDEC_FRACBITS));
+/* XXX - this calc is not correct */
+		jpc_refnmsedec0[i] = jpc_dbltofix(floor((u * u) * jpc_pow2i(JPC_NMSEDEC_FRACBITS) + 0.5) / jpc_pow2i(JPC_NMSEDEC_FRACBITS));
+	}
+}
+
+jpc_fix_t jpc_getsignmsedec_func(jpc_fix_t x, int bitpos)
+{
+	jpc_fix_t y;
+	assert(!(x & (~JAS_ONES(bitpos + 1))));
+	y = jpc_getsignmsedec_macro(x, bitpos);
+	return y;
+}
+
+int jpc_getzcctxno(int f, int orient)
+{
+	int h;
+	int v;
+	int d;
+	int n;
+	int t;
+	int hv;
+
+	/* Avoid compiler warning. */
+	n = 0;
+
+	h = ((f & JPC_WSIG) != 0) + ((f & JPC_ESIG) != 0);
+	v = ((f & JPC_NSIG) != 0) + ((f & JPC_SSIG) != 0);
+	d = ((f & JPC_NWSIG) != 0) + ((f & JPC_NESIG) != 0) + ((f & JPC_SESIG) != 0) + ((f & JPC_SWSIG) != 0);
+	switch (orient) {
+	case JPC_TSFB_HL:
+		t = h;
+		h = v;
+		v = t;
+	case JPC_TSFB_LL:
+	case JPC_TSFB_LH:
+		if (!h) {
+			if (!v) {
+				if (!d) {
+					n = 0;
+				} else if (d == 1) {
+					n = 1;
+				} else {
+					n = 2;
+				}
+			} else if (v == 1) {
+				n = 3;
+			} else {
+				n = 4;
+			}
+		} else if (h == 1) {
+			if (!v) {
+				if (!d) {
+					n = 5;
+				} else {
+					n = 6;
+				}
+			} else {
+				n = 7;
+			}
+		} else {
+			n = 8;
+		}
+		break;
+	case JPC_TSFB_HH:
+		hv = h + v;
+		if (!d) {
+			if (!hv) {
+				n = 0;
+			} else if (hv == 1) {
+				n = 1;
+			} else {
+				n = 2;
+			}
+		} else if (d == 1) {
+			if (!hv) {
+				n = 3;
+			} else if (hv == 1) {
+				n = 4;
+			} else {
+				n = 5;
+			}
+		} else if (d == 2) {
+			if (!hv) {
+				n = 6;
+			} else {
+				n = 7;
+			}
+		} else {
+			n = 8;
+		}
+		break;
+	}
+	assert(n < JPC_NUMZCCTXS);
+	return JPC_ZCCTXNO + n;
+}
+
+int jpc_getspb(int f)
+{
+	int hc;
+	int vc;
+	int n;
+
+	hc = JAS_MIN(((f & (JPC_ESIG | JPC_ESGN)) == JPC_ESIG) + ((f & (JPC_WSIG | JPC_WSGN)) == JPC_WSIG), 1) -
+	  JAS_MIN(((f & (JPC_ESIG | JPC_ESGN)) == (JPC_ESIG | JPC_ESGN)) + ((f & (JPC_WSIG | JPC_WSGN)) == (JPC_WSIG | JPC_WSGN)), 1);
+	vc = JAS_MIN(((f & (JPC_NSIG | JPC_NSGN)) == JPC_NSIG) + ((f & (JPC_SSIG | JPC_SSGN)) == JPC_SSIG), 1) -
+	  JAS_MIN(((f & (JPC_NSIG | JPC_NSGN)) == (JPC_NSIG | JPC_NSGN)) + ((f & (JPC_SSIG | JPC_SSGN)) == (JPC_SSIG | JPC_SSGN)), 1);
+	if (!hc && !vc) {
+		n = 0;
+	} else {
+		n = (!(hc > 0 || (!hc && vc > 0)));
+	}
+	return n;
+}
+
+int jpc_getscctxno(int f)
+{
+	int hc;
+	int vc;
+	int n;
+
+	/* Avoid compiler warning. */
+	n = 0;
+
+	hc = JAS_MIN(((f & (JPC_ESIG | JPC_ESGN)) == JPC_ESIG) + ((f & (JPC_WSIG | JPC_WSGN)) == JPC_WSIG),
+	  1) - JAS_MIN(((f & (JPC_ESIG | JPC_ESGN)) == (JPC_ESIG | JPC_ESGN)) +
+	  ((f & (JPC_WSIG | JPC_WSGN)) == (JPC_WSIG | JPC_WSGN)), 1);
+	vc = JAS_MIN(((f & (JPC_NSIG | JPC_NSGN)) == JPC_NSIG) + ((f & (JPC_SSIG | JPC_SSGN)) == JPC_SSIG),
+	  1) - JAS_MIN(((f & (JPC_NSIG | JPC_NSGN)) == (JPC_NSIG | JPC_NSGN)) +
+	  ((f & (JPC_SSIG | JPC_SSGN)) == (JPC_SSIG | JPC_SSGN)), 1);
+	assert(hc >= -1 && hc <= 1 && vc >= -1 && vc <= 1);
+	if (hc < 0) {
+		hc = -hc;
+		vc = -vc;
+	}
+	if (!hc) {
+		if (vc == -1) {
+			n = 1;
+		} else if (!vc) {
+			n = 0;
+		} else {
+			n = 1;
+		}
+	} else if (hc == 1) {
+		if (vc == -1) {
+			n = 2;
+		} else if (!vc) {
+			n = 3;
+		} else {
+			n = 4;
+		}
+	}
+	assert(n < JPC_NUMSCCTXS);
+	return JPC_SCCTXNO + n;
+}
+
+int jpc_getmagctxno(int f)
+{
+	int n;
+
+	if (!(f & JPC_REFINE)) {
+		n = (f & (JPC_OTHSIGMSK)) ? 1 : 0;
+	} else {
+		n = 2;
+	}
+
+	assert(n < JPC_NUMMAGCTXS);
+	return JPC_MAGCTXNO + n;
+}
+
+void jpc_initctxs(jpc_mqctx_t *ctxs)
+{
+	jpc_mqctx_t *ctx;
+	int i;
+
+	ctx = ctxs;
+	for (i = 0; i < JPC_NUMCTXS; ++i) {
+		ctx->mps = 0;
+		switch (i) {
+		case JPC_UCTXNO:
+			ctx->ind = 46;
+			break;
+		case JPC_ZCCTXNO:
+			ctx->ind = 4;
+			break;
+		case JPC_AGGCTXNO:
+			ctx->ind = 3;
+			break;
+		default:
+			ctx->ind = 0;
+			break;
+		}
+		++ctx;
+	}
+}
+
+/* Calculate the real quantity exp2(n), where x is an integer. */
+double jpc_pow2i(int n)
+{
+	double x;
+	double a;
+
+	x = 1.0;
+	if (n < 0) {
+		a = 0.5;
+		n = -n;
+	} else {
+		a = 2.0;
+	}
+	while (--n >= 0) {
+		x *= a;
+	}
+	return x;
+}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_t1cod.h b/converter/other/jpeg2000/libjasper/jpc/jpc_t1cod.h
new file mode 100644
index 00000000..3e061d20
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_t1cod.h
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef JPC_T1COD_H
+#define JPC_T1COD_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jasper/jas_fix.h"
+#include "jasper/jas_math.h"
+
+#include "jpc_mqcod.h"
+#include "jpc_tsfb.h"
+
+/******************************************************************************\
+* Constants.
+\******************************************************************************/
+
+/* The number of bits used to index into various lookup tables. */
+#define JPC_NMSEDEC_BITS	7
+#define JPC_NMSEDEC_FRACBITS	(JPC_NMSEDEC_BITS - 1)
+
+/*
+ * Segment types.
+ */
+
+/* Invalid. */
+#define JPC_SEG_INVALID	0
+/* MQ. */
+#define JPC_SEG_MQ		1
+/* Raw. */
+#define JPC_SEG_RAW		2
+
+/* The nominal word size. */
+#define	JPC_PREC	32
+
+/* Tier-1 coding pass types. */
+#define	JPC_SIGPASS	0	/* significance */
+#define	JPC_REFPASS	1	/* refinement */
+#define	JPC_CLNPASS	2	/* cleanup */
+
+/*
+ * Per-sample state information for tier-1 coding.
+ */
+
+/* The northeast neighbour has been found to be significant. */
+#define	JPC_NESIG	0x0001
+/* The southeast neighbour has been found to be significant. */
+#define	JPC_SESIG	0x0002
+/* The southwest neighbour has been found to be significant. */
+#define	JPC_SWSIG	0x0004
+/* The northwest neighbour has been found to be significant. */
+#define	JPC_NWSIG	0x0008
+/* The north neighbour has been found to be significant. */
+#define	JPC_NSIG	0x0010
+/* The east neighbour has been found to be significant. */
+#define	JPC_ESIG	0x0020
+/* The south neighbour has been found to be significant. */
+#define	JPC_SSIG	0x0040
+/* The west neighbour has been found to be significant. */
+#define	JPC_WSIG	0x0080
+/* The significance mask for 8-connected neighbours. */
+#define	JPC_OTHSIGMSK \
+	(JPC_NSIG | JPC_NESIG | JPC_ESIG | JPC_SESIG | JPC_SSIG | JPC_SWSIG | JPC_WSIG | JPC_NWSIG)
+/* The significance mask for 4-connected neighbours. */
+#define	JPC_PRIMSIGMSK	(JPC_NSIG | JPC_ESIG | JPC_SSIG | JPC_WSIG)
+
+/* The north neighbour is negative in value. */
+#define	JPC_NSGN	0x0100
+/* The east neighbour is negative in value. */
+#define	JPC_ESGN	0x0200
+/* The south neighbour is negative in value. */
+#define	JPC_SSGN	0x0400
+/* The west neighbour is negative in value. */
+#define	JPC_WSGN	0x0800
+/* The sign mask for 4-connected neighbours. */
+#define	JPC_SGNMSK	(JPC_NSGN | JPC_ESGN | JPC_SSGN | JPC_WSGN)
+
+/* This sample has been found to be significant. */
+#define JPC_SIG		0x1000
+/* The sample has been refined. */
+#define	JPC_REFINE	0x2000
+/* This sample has been processed during the significance pass. */
+#define	JPC_VISIT	0x4000
+
+/* The number of aggregation contexts. */
+#define	JPC_NUMAGGCTXS	1
+/* The number of zero coding contexts. */
+#define	JPC_NUMZCCTXS	9
+/* The number of magnitude contexts. */
+#define	JPC_NUMMAGCTXS	3
+/* The number of sign coding contexts. */
+#define	JPC_NUMSCCTXS	5
+/* The number of uniform contexts. */
+#define	JPC_NUMUCTXS	1
+
+/* The context ID for the first aggregation context. */
+#define	JPC_AGGCTXNO	0
+/* The context ID for the first zero coding context. */
+#define	JPC_ZCCTXNO		(JPC_AGGCTXNO + JPC_NUMAGGCTXS)
+/* The context ID for the first magnitude context. */
+#define	JPC_MAGCTXNO	(JPC_ZCCTXNO + JPC_NUMZCCTXS)
+/* The context ID for the first sign coding context. */
+#define	JPC_SCCTXNO		(JPC_MAGCTXNO + JPC_NUMMAGCTXS)
+/* The context ID for the first uniform context. */
+#define	JPC_UCTXNO		(JPC_SCCTXNO + JPC_NUMSCCTXS)
+/* The total number of contexts. */
+#define	JPC_NUMCTXS		(JPC_UCTXNO + JPC_NUMUCTXS)
+
+/******************************************************************************\
+* External data.
+\******************************************************************************/
+
+/* These lookup tables are used by various macros/functions. */
+/* Do not access these lookup tables directly. */
+extern int jpc_zcctxnolut[];
+extern int jpc_spblut[];
+extern int jpc_scctxnolut[];
+extern int jpc_magctxnolut[];
+extern jpc_fix_t jpc_refnmsedec[];
+extern jpc_fix_t jpc_signmsedec[];
+extern jpc_fix_t jpc_refnmsedec0[];
+extern jpc_fix_t jpc_signmsedec0[];
+
+/* The initial settings for the MQ contexts. */
+extern jpc_mqctx_t jpc_mqctxs[];
+
+/******************************************************************************\
+* Functions and macros.
+\******************************************************************************/
+
+/* Initialize the MQ contexts. */
+void jpc_initctxs(jpc_mqctx_t *ctxs);
+
+/* Get the zero coding context. */
+int jpc_getzcctxno(int f, int orient);
+#define	JPC_GETZCCTXNO(f, orient) \
+	(jpc_zcctxnolut[((orient) << 8) | ((f) & JPC_OTHSIGMSK)])
+
+/* Get the sign prediction bit. */
+int jpc_getspb(int f);
+#define	JPC_GETSPB(f) \
+	(jpc_spblut[((f) & (JPC_PRIMSIGMSK | JPC_SGNMSK)) >> 4])
+
+/* Get the sign coding context. */
+int jpc_getscctxno(int f);
+#define	JPC_GETSCCTXNO(f) \
+	(jpc_scctxnolut[((f) & (JPC_PRIMSIGMSK | JPC_SGNMSK)) >> 4])
+
+/* Get the magnitude context. */
+int jpc_getmagctxno(int f);
+#define	JPC_GETMAGCTXNO(f) \
+	(jpc_magctxnolut[((f) & JPC_OTHSIGMSK) | ((((f) & JPC_REFINE) != 0) << 11)])
+
+/* Get the normalized MSE reduction for significance passes. */
+#define	JPC_GETSIGNMSEDEC(x, bitpos)	jpc_getsignmsedec_macro(x, bitpos)
+jpc_fix_t jpc_getsignmsedec_func(jpc_fix_t x, int bitpos);
+#define	jpc_getsignmsedec_macro(x, bitpos) \
+	((bitpos > JPC_NMSEDEC_FRACBITS) ? jpc_signmsedec[JPC_ASR(x, bitpos - JPC_NMSEDEC_FRACBITS) & JAS_ONES(JPC_NMSEDEC_BITS)] : \
+	  (jpc_signmsedec0[JPC_ASR(x, bitpos - JPC_NMSEDEC_FRACBITS) & JAS_ONES(JPC_NMSEDEC_BITS)]))
+
+/* Get the normalized MSE reduction for refinement passes. */
+#define	JPC_GETREFNMSEDEC(x, bitpos)	jpc_getrefnmsedec_macro(x, bitpos)
+jpc_fix_t jpc_refsignmsedec_func(jpc_fix_t x, int bitpos);
+#define	jpc_getrefnmsedec_macro(x, bitpos) \
+	((bitpos > JPC_NMSEDEC_FRACBITS) ? jpc_refnmsedec[JPC_ASR(x, bitpos - JPC_NMSEDEC_FRACBITS) & JAS_ONES(JPC_NMSEDEC_BITS)] : \
+	  (jpc_refnmsedec0[JPC_ASR(x, bitpos - JPC_NMSEDEC_FRACBITS) & JAS_ONES(JPC_NMSEDEC_BITS)]))
+
+/* Arithmetic shift right (with ability to shift left also). */
+#define	JPC_ASR(x, n) \
+	(((n) >= 0) ? ((x) >> (n)) : ((x) << (-(n))))
+
+/* Update the per-sample state information. */
+#define	JPC_UPDATEFLAGS4(fp, rowstep, s, vcausalflag) \
+{ \
+	register jpc_fix_t *np = (fp) - (rowstep); \
+	register jpc_fix_t *sp = (fp) + (rowstep); \
+	if ((vcausalflag)) { \
+		sp[-1] |= JPC_NESIG; \
+		sp[1] |= JPC_NWSIG; \
+		if (s) { \
+			*sp |= JPC_NSIG | JPC_NSGN; \
+			(fp)[-1] |= JPC_ESIG | JPC_ESGN; \
+			(fp)[1] |= JPC_WSIG | JPC_WSGN; \
+		} else { \
+			*sp |= JPC_NSIG; \
+			(fp)[-1] |= JPC_ESIG; \
+			(fp)[1] |= JPC_WSIG; \
+		} \
+	} else { \
+		np[-1] |= JPC_SESIG; \
+		np[1] |= JPC_SWSIG; \
+		sp[-1] |= JPC_NESIG; \
+		sp[1] |= JPC_NWSIG; \
+		if (s) { \
+			*np |= JPC_SSIG | JPC_SSGN; \
+			*sp |= JPC_NSIG | JPC_NSGN; \
+			(fp)[-1] |= JPC_ESIG | JPC_ESGN; \
+			(fp)[1] |= JPC_WSIG | JPC_WSGN; \
+		} else { \
+			*np |= JPC_SSIG; \
+			*sp |= JPC_NSIG; \
+			(fp)[-1] |= JPC_ESIG; \
+			(fp)[1] |= JPC_WSIG; \
+		} \
+	} \
+}
+
+/* Initialize the lookup tables used by the codec. */
+void jpc_initluts(void);
+
+/* Get the nominal gain associated with a particular band. */
+int JPC_NOMINALGAIN(int qmfbid, int numlvls, int lvlno, int orient);
+
+/* Get the coding pass type. */
+int JPC_PASSTYPE(int passno);
+
+/* Get the segment type. */
+int JPC_SEGTYPE(int passno, int firstpassno, int bypass);
+
+/* Get the number of coding passess in the segment. */
+int JPC_SEGPASSCNT(int passno, int firstpassno, int numpasses, int bypass,
+  int termall);
+
+/* Is the coding pass terminated? */
+int JPC_ISTERMINATED(int passno, int firstpassno, int numpasses, int termall,
+  int lazy);
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_t1dec.c b/converter/other/jpeg2000/libjasper/jpc/jpc_t1dec.c
new file mode 100644
index 00000000..01a54ea8
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_t1dec.c
@@ -0,0 +1,954 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Tier 1 Decoder
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "jasper/jas_fix.h"
+#include "jasper/jas_stream.h"
+#include "jasper/jas_math.h"
+
+#include "jpc_bs.h"
+#include "jpc_mqdec.h"
+#include "jpc_t1dec.h"
+#include "jpc_t1cod.h"
+#include "jpc_dec.h"
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+static int jpc_dec_decodecblk(jpc_dec_t *dec, jpc_dec_tile_t *tile, jpc_dec_tcomp_t *tcomp, jpc_dec_band_t *band,
+  jpc_dec_cblk_t *cblk, int dopartial, int maxlyrs);
+static int dec_sigpass(jpc_dec_t *dec, jpc_mqdec_t *mqdec, int bitpos, int orient,
+  int vcausalflag, jas_matrix_t *flags, jas_matrix_t *data);
+static int dec_rawsigpass(jpc_dec_t *dec, jpc_bitstream_t *in, int bitpos,
+  int vcausalflag, jas_matrix_t *flags, jas_matrix_t *data);
+static int dec_refpass(jpc_dec_t *dec, jpc_mqdec_t *mqdec, int bitpos, int vcausalflag,
+  jas_matrix_t *flags, jas_matrix_t *data);
+static int dec_rawrefpass(jpc_dec_t *dec, jpc_bitstream_t *in, int bitpos,
+  int vcausalflag, jas_matrix_t *flags, jas_matrix_t *data);
+static int dec_clnpass(jpc_dec_t *dec, jpc_mqdec_t *mqdec, int bitpos, int orient,
+  int vcausalflag, int segsymflag, jas_matrix_t *flags, jas_matrix_t *data);
+
+#if defined(DEBUG)
+static long t1dec_cnt = 0;
+#endif
+
+#if !defined(DEBUG)
+#define	JPC_T1D_GETBIT(mqdec, v, passtypename, symtypename) \
+	((v) = jpc_mqdec_getbit(mqdec))
+#else
+#define	JPC_T1D_GETBIT(mqdec, v, passtypename, symtypename) \
+{ \
+	(v) = jpc_mqdec_getbit(mqdec); \
+	if (jas_getdbglevel() >= 100) { \
+		fprintf(stderr, "index = %ld; passtype = %s; symtype = %s; sym = %d\n", t1dec_cnt, passtypename, symtypename, v); \
+		++t1dec_cnt; \
+	} \
+}
+#endif
+#define	JPC_T1D_GETBITNOSKEW(mqdec, v, passtypename, symtypename) \
+	JPC_T1D_GETBIT(mqdec, v, passtypename, symtypename)
+
+#if !defined(DEBUG)
+#define	JPC_T1D_RAWGETBIT(bitstream, v, passtypename, symtypename) \
+	((v) = jpc_bitstream_getbit(bitstream))
+#else
+#define	JPC_T1D_RAWGETBIT(bitstream, v, passtypename, symtypename) \
+{ \
+	(v) = jpc_bitstream_getbit(bitstream); \
+	if (jas_getdbglevel() >= 100) { \
+		fprintf(stderr, "index = %ld; passtype = %s; symtype = %s; sym = %d\n", t1dec_cnt, passtypename, symtypename, v); \
+		++t1dec_cnt; \
+	} \
+}
+#endif
+
+/******************************************************************************\
+* Code.
+\******************************************************************************/
+
+int jpc_dec_decodecblks(jpc_dec_t *dec, jpc_dec_tile_t *tile)
+{
+	jpc_dec_tcomp_t *tcomp;
+	int compcnt;
+	jpc_dec_rlvl_t *rlvl;
+	int rlvlcnt;
+	jpc_dec_band_t *band;
+	int bandcnt;
+	jpc_dec_prc_t *prc;
+	int prccnt;
+	jpc_dec_cblk_t *cblk;
+	int cblkcnt;
+
+	for (compcnt = dec->numcomps, tcomp = tile->tcomps; compcnt > 0;
+	  --compcnt, ++tcomp) {
+		for (rlvlcnt = tcomp->numrlvls, rlvl = tcomp->rlvls;
+		  rlvlcnt > 0; --rlvlcnt, ++rlvl) {
+			if (!rlvl->bands) {
+				continue;
+			}
+			for (bandcnt = rlvl->numbands, band = rlvl->bands;
+			  bandcnt > 0; --bandcnt, ++band) {
+				if (!band->data) {
+					continue;
+				}
+				for (prccnt = rlvl->numprcs, prc = band->prcs;
+				  prccnt > 0; --prccnt, ++prc) {
+					if (!prc->cblks) {
+						continue;
+					}
+					for (cblkcnt = prc->numcblks,
+					  cblk = prc->cblks; cblkcnt > 0;
+					  --cblkcnt, ++cblk) {
+						if (jpc_dec_decodecblk(dec, tile, tcomp,
+						  band, cblk, 1, JPC_MAXLYRS)) {
+							return -1;
+						}
+					}
+				}
+
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int jpc_dec_decodecblk(jpc_dec_t *dec, jpc_dec_tile_t *tile, jpc_dec_tcomp_t *tcomp, jpc_dec_band_t *band,
+  jpc_dec_cblk_t *cblk, int dopartial, int maxlyrs)
+{
+	jpc_dec_seg_t *seg;
+	int i;
+	int bpno;
+	int passtype;
+	int ret;
+	int compno;
+	int filldata;
+	int fillmask;
+	jpc_dec_ccp_t *ccp;
+
+	compno = tcomp - tile->tcomps;
+
+	if (!cblk->flags) {
+		/* Note: matrix is assumed to be zeroed */
+		if (!(cblk->flags = jas_matrix_create(jas_matrix_numrows(cblk->data) +
+		  2, jas_matrix_numcols(cblk->data) + 2))) {
+			return -1;
+		}
+	}
+
+	seg = cblk->segs.head;
+	while (seg && (seg != cblk->curseg || dopartial) && (maxlyrs < 0 ||
+	  seg->lyrno < maxlyrs)) {
+		assert(seg->numpasses >= seg->maxpasses || dopartial);
+		assert(seg->stream);
+		jas_stream_rewind(seg->stream);
+		jas_stream_setrwcount(seg->stream, 0);
+		if (seg->type == JPC_SEG_MQ) {
+			if (!cblk->mqdec) {
+				if (!(cblk->mqdec = jpc_mqdec_create(JPC_NUMCTXS, 0))) {
+					return -1;
+				}
+				jpc_mqdec_setctxs(cblk->mqdec, JPC_NUMCTXS, jpc_mqctxs);
+			}
+			jpc_mqdec_setinput(cblk->mqdec, seg->stream);
+			jpc_mqdec_init(cblk->mqdec);
+		} else {
+			assert(seg->type == JPC_SEG_RAW);
+			if (!cblk->nulldec) {
+				if (!(cblk->nulldec = jpc_bitstream_sopen(seg->stream, "r"))) {
+					assert(0);
+				}
+			}
+		}
+
+
+		for (i = 0; i < seg->numpasses; ++i) {
+			if (cblk->numimsbs > band->numbps) {
+				ccp = &tile->cp->ccps[compno];
+				if (ccp->roishift <= 0) {
+					fprintf(stderr, "warning: corrupt code stream\n");
+				} else {
+					if (cblk->numimsbs < ccp->roishift - band->numbps) {
+						fprintf(stderr, "warning: corrupt code stream\n");
+					}
+				}
+			}
+			bpno = band->roishift + band->numbps - 1 - (cblk->numimsbs +
+			  (seg->passno + i - cblk->firstpassno + 2) / 3);
+if (bpno < 0) {
+	goto premature_exit;
+}
+#if 1
+			passtype = (seg->passno + i + 2) % 3;
+#else
+			passtype = JPC_PASSTYPE(seg->passno + i + 2);
+#endif
+			assert(bpno >= 0 && bpno < 31);
+			switch (passtype) {
+			case JPC_SIGPASS:
+				ret = (seg->type == JPC_SEG_MQ) ? dec_sigpass(dec,
+				  cblk->mqdec, bpno, band->orient,
+				  (tile->cp->ccps[compno].cblkctx & JPC_COX_VSC) != 0,
+				  cblk->flags, cblk->data) :
+				  dec_rawsigpass(dec, cblk->nulldec, bpno,
+				  (tile->cp->ccps[compno].cblkctx & JPC_COX_VSC) != 0,
+				  cblk->flags, cblk->data);
+				break;
+			case JPC_REFPASS:
+				ret = (seg->type == JPC_SEG_MQ) ?
+				  dec_refpass(dec, cblk->mqdec, bpno,
+				  (tile->cp->ccps[compno].cblkctx & JPC_COX_VSC) != 0,
+				  cblk->flags, cblk->data) :
+				  dec_rawrefpass(dec, cblk->nulldec, bpno,
+				  (tile->cp->ccps[compno].cblkctx & JPC_COX_VSC) != 0,
+				  cblk->flags, cblk->data);
+				break;
+			case JPC_CLNPASS:
+				assert(seg->type == JPC_SEG_MQ);
+				ret = dec_clnpass(dec, cblk->mqdec, bpno,
+				  band->orient, (tile->cp->ccps[compno].cblkctx &
+				  JPC_COX_VSC) != 0, (tile->cp->ccps[compno].cblkctx &
+				  JPC_COX_SEGSYM) != 0, cblk->flags,
+				  cblk->data);
+				break;
+			default:
+				ret = -1;
+				break;
+			}
+			/* Do we need to reset after each coding pass? */
+			if (tile->cp->ccps[compno].cblkctx & JPC_COX_RESET) {
+				jpc_mqdec_setctxs(cblk->mqdec, JPC_NUMCTXS, jpc_mqctxs);
+			}
+
+			if (ret) {
+				fprintf(stderr, "coding pass failed passtype=%d segtype=%d\n", passtype, seg->type);
+				return -1;
+			}
+
+		}
+
+		if (seg->type == JPC_SEG_MQ) {
+/* Note: dont destroy mq decoder because context info will be lost */
+		} else {
+			assert(seg->type == JPC_SEG_RAW);
+			if (tile->cp->ccps[compno].cblkctx & JPC_COX_PTERM) {
+				fillmask = 0x7f;
+				filldata = 0x2a;
+			} else {
+				fillmask = 0;
+				filldata = 0;
+			}
+			if ((ret = jpc_bitstream_inalign(cblk->nulldec, fillmask,
+			  filldata)) < 0) {
+				return -1;
+			} else if (ret > 0) {
+				fprintf(stderr, "warning: bad termination pattern detected\n");
+			}
+			jpc_bitstream_close(cblk->nulldec);
+			cblk->nulldec = 0;
+		}
+
+		cblk->curseg = seg->next;
+		jpc_seglist_remove(&cblk->segs, seg);
+		jpc_seg_destroy(seg);
+		seg = cblk->curseg;
+	}
+
+	assert(dopartial ? (!cblk->curseg) : 1);
+
+premature_exit:
+	return 0;
+}
+
+/******************************************************************************\
+* Code for significance pass.
+\******************************************************************************/
+
+#define	jpc_sigpass_step(fp, frowstep, dp, bitpos, oneplushalf, orient, mqdec, vcausalflag) \
+{ \
+	int f; \
+	int v; \
+	f = *(fp); \
+	if ((f & JPC_OTHSIGMSK) && !(f & (JPC_SIG | JPC_VISIT))) { \
+		jpc_mqdec_setcurctx((mqdec), JPC_GETZCCTXNO(f, (orient))); \
+		JPC_T1D_GETBIT((mqdec), v, "SIG", "ZC"); \
+		if (v) { \
+			jpc_mqdec_setcurctx((mqdec), JPC_GETSCCTXNO(f)); \
+			JPC_T1D_GETBIT((mqdec), v, "SIG", "SC"); \
+			v ^= JPC_GETSPB(f); \
+			JPC_UPDATEFLAGS4((fp), (frowstep), v, (vcausalflag)); \
+			*(fp) |= JPC_SIG; \
+			*(dp) = (v) ? (-(oneplushalf)) : (oneplushalf); \
+		} \
+		*(fp) |= JPC_VISIT; \
+	} \
+}
+
+static int dec_sigpass(jpc_dec_t *dec, register jpc_mqdec_t *mqdec, int bitpos, int orient,
+  int vcausalflag, jas_matrix_t *flags, jas_matrix_t *data)
+{
+	int i;
+	int j;
+	int one;
+	int half;
+	int oneplushalf;
+	int vscanlen;
+	int width;
+	int height;
+	jpc_fix_t *fp;
+	int frowstep;
+	int fstripestep;
+	jpc_fix_t *fstripestart;
+	jpc_fix_t *fvscanstart;
+	jpc_fix_t *dp;
+	int drowstep;
+	int dstripestep;
+	jpc_fix_t *dstripestart;
+	jpc_fix_t *dvscanstart;
+	int k;
+
+	width = jas_matrix_numcols(data);
+	height = jas_matrix_numrows(data);
+	frowstep = jas_matrix_rowstep(flags);
+	drowstep = jas_matrix_rowstep(data);
+	fstripestep = frowstep << 2;
+	dstripestep = drowstep << 2;
+
+	one = 1 << bitpos;
+	half = one >> 1;
+	oneplushalf = one | half;
+
+	fstripestart = jas_matrix_getref(flags, 1, 1);
+	dstripestart = jas_matrix_getref(data, 0, 0);
+	for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
+	  dstripestart += dstripestep) {
+		fvscanstart = fstripestart;
+		dvscanstart = dstripestart;
+		vscanlen = JAS_MIN(i, 4);
+		for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
+			fp = fvscanstart;
+			dp = dvscanstart;
+			k = vscanlen;
+
+			/* Process first sample in vertical scan. */
+			jpc_sigpass_step(fp, frowstep, dp, bitpos, oneplushalf,
+			  orient, mqdec, vcausalflag);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+			/* Process second sample in vertical scan. */
+			jpc_sigpass_step(fp, frowstep, dp, bitpos, oneplushalf,
+			  orient, mqdec, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+			/* Process third sample in vertical scan. */
+			jpc_sigpass_step(fp, frowstep, dp, bitpos, oneplushalf,
+			  orient, mqdec, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+			/* Process fourth sample in vertical scan. */
+			jpc_sigpass_step(fp, frowstep, dp, bitpos, oneplushalf,
+			  orient, mqdec, 0);
+		}
+	}
+	return 0;
+}
+
+#define	jpc_rawsigpass_step(fp, frowstep, dp, oneplushalf, in, vcausalflag) \
+{ \
+	jpc_fix_t f = *(fp); \
+	jpc_fix_t v; \
+	if ((f & JPC_OTHSIGMSK) && !(f & (JPC_SIG | JPC_VISIT))) { \
+		JPC_T1D_RAWGETBIT(in, v, "SIG", "ZC"); \
+		if (v < 0) { \
+			return -1; \
+		} \
+		if (v) { \
+			JPC_T1D_RAWGETBIT(in, v, "SIG", "SC"); \
+			if (v < 0) { \
+				return -1; \
+			} \
+			JPC_UPDATEFLAGS4((fp), (frowstep), v, (vcausalflag)); \
+			*(fp) |= JPC_SIG; \
+			*(dp) = v ? (-oneplushalf) : (oneplushalf); \
+		} \
+		*(fp) |= JPC_VISIT; \
+	} \
+}
+
+static int dec_rawsigpass(jpc_dec_t *dec, jpc_bitstream_t *in, int bitpos, int vcausalflag,
+  jas_matrix_t *flags, jas_matrix_t *data)
+{
+	int i;
+	int j;
+	int k;
+	int one;
+	int half;
+	int oneplushalf;
+	int vscanlen;
+	int width;
+	int height;
+	jpc_fix_t *fp;
+	int frowstep;
+	int fstripestep;
+	jpc_fix_t *fstripestart;
+	jpc_fix_t *fvscanstart;
+	jpc_fix_t *dp;
+	int drowstep;
+	int dstripestep;
+	jpc_fix_t *dstripestart;
+	jpc_fix_t *dvscanstart;
+
+	width = jas_matrix_numcols(data);
+	height = jas_matrix_numrows(data);
+	frowstep = jas_matrix_rowstep(flags);
+	drowstep = jas_matrix_rowstep(data);
+	fstripestep = frowstep << 2;
+	dstripestep = drowstep << 2;
+
+	one = 1 << bitpos;
+	half = one >> 1;
+	oneplushalf = one | half;
+
+	fstripestart = jas_matrix_getref(flags, 1, 1);
+	dstripestart = jas_matrix_getref(data, 0, 0);
+	for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
+	  dstripestart += dstripestep) {
+		fvscanstart = fstripestart;
+		dvscanstart = dstripestart;
+		vscanlen = JAS_MIN(i, 4);
+		for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
+			fp = fvscanstart;
+			dp = dvscanstart;
+			k = vscanlen;
+
+			/* Process first sample in vertical scan. */
+			jpc_rawsigpass_step(fp, frowstep, dp, oneplushalf,
+			  in, vcausalflag);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+			/* Process second sample in vertical scan. */
+			jpc_rawsigpass_step(fp, frowstep, dp, oneplushalf,
+			  in, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+			/* Process third sample in vertical scan. */
+			jpc_rawsigpass_step(fp, frowstep, dp, oneplushalf,
+			  in, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+			/* Process fourth sample in vertical scan. */
+			jpc_rawsigpass_step(fp, frowstep, dp, oneplushalf,
+			  in, 0);
+
+		}
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* Code for refinement pass.
+\******************************************************************************/
+
+#define	jpc_refpass_step(fp, dp, poshalf, neghalf, mqdec, vcausalflag) \
+{ \
+	int v; \
+	int t; \
+	if (((*(fp)) & (JPC_SIG | JPC_VISIT)) == JPC_SIG) { \
+		jpc_mqdec_setcurctx((mqdec), JPC_GETMAGCTXNO(*(fp))); \
+		JPC_T1D_GETBITNOSKEW((mqdec), v, "REF", "MR"); \
+		t = (v ? (poshalf) : (neghalf)); \
+		*(dp) += (*(dp) < 0) ? (-t) : t; \
+		*(fp) |= JPC_REFINE; \
+	} \
+}
+
+static int dec_refpass(jpc_dec_t *dec, register jpc_mqdec_t *mqdec, int bitpos,
+  int vcausalflag, jas_matrix_t *flags, jas_matrix_t *data)
+{
+	int i;
+	int j;
+	int vscanlen;
+	int width;
+	int height;
+	int one;
+	int poshalf;
+	int neghalf;
+	jpc_fix_t *fp;
+	int frowstep;
+	int fstripestep;
+	jpc_fix_t *fstripestart;
+	jpc_fix_t *fvscanstart;
+	jpc_fix_t *dp;
+	int drowstep;
+	int dstripestep;
+	jpc_fix_t *dstripestart;
+	jpc_fix_t *dvscanstart;
+	int k;
+
+	width = jas_matrix_numcols(data);
+	height = jas_matrix_numrows(data);
+	frowstep = jas_matrix_rowstep(flags);
+	drowstep = jas_matrix_rowstep(data);
+	fstripestep = frowstep << 2;
+	dstripestep = drowstep << 2;
+
+	one = 1 << bitpos;
+	poshalf = one >> 1;
+	neghalf = (bitpos > 0) ? (-poshalf) : (-1);
+
+	fstripestart = jas_matrix_getref(flags, 1, 1);
+	dstripestart = jas_matrix_getref(data, 0, 0);
+	for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
+	  dstripestart += dstripestep) {
+		fvscanstart = fstripestart;
+		dvscanstart = dstripestart;
+		vscanlen = JAS_MIN(i, 4);
+		for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
+			fp = fvscanstart;
+			dp = dvscanstart;
+			k = vscanlen;
+
+			/* Process first sample in vertical scan. */
+			jpc_refpass_step(fp, dp, poshalf, neghalf, mqdec,
+			  vcausalflag);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+			/* Process second sample in vertical scan. */
+			jpc_refpass_step(fp, dp, poshalf, neghalf, mqdec, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+			/* Process third sample in vertical scan. */
+			jpc_refpass_step(fp, dp, poshalf, neghalf, mqdec, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+			/* Process fourth sample in vertical scan. */
+			jpc_refpass_step(fp, dp, poshalf, neghalf, mqdec, 0);
+		}
+	}
+
+	return 0;
+}
+
+#define	jpc_rawrefpass_step(fp, dp, poshalf, neghalf, in, vcausalflag) \
+{ \
+	jpc_fix_t v; \
+	jpc_fix_t t; \
+	if (((*(fp)) & (JPC_SIG | JPC_VISIT)) == JPC_SIG) { \
+		JPC_T1D_RAWGETBIT(in, v, "REF", "MAGREF"); \
+		if (v < 0) { \
+			return -1; \
+		} \
+		t = (v ? poshalf : neghalf); \
+		*(dp) += (*(dp) < 0) ? (-t) : t; \
+		*(fp) |= JPC_REFINE; \
+	} \
+}
+
+static int dec_rawrefpass(jpc_dec_t *dec, jpc_bitstream_t *in, int bitpos, int vcausalflag,
+  jas_matrix_t *flags, jas_matrix_t *data)
+{
+	int i;
+	int j;
+	int k;
+	int vscanlen;
+	int width;
+	int height;
+	int one;
+	int poshalf;
+	int neghalf;
+	jpc_fix_t *fp;
+	int frowstep;
+	int fstripestep;
+	jpc_fix_t *fstripestart;
+	jpc_fix_t *fvscanstart;
+	jpc_fix_t *dp;
+	int drowstep;
+	int dstripestep;
+	jpc_fix_t *dstripestart;
+	jpc_fix_t *dvscanstart;
+
+	width = jas_matrix_numcols(data);
+	height = jas_matrix_numrows(data);
+	frowstep = jas_matrix_rowstep(flags);
+	drowstep = jas_matrix_rowstep(data);
+	fstripestep = frowstep << 2;
+	dstripestep = drowstep << 2;
+
+	one = 1 << bitpos;
+	poshalf = one >> 1;
+	neghalf = (bitpos > 0) ? (-poshalf) : (-1);
+
+	fstripestart = jas_matrix_getref(flags, 1, 1);
+	dstripestart = jas_matrix_getref(data, 0, 0);
+	for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
+	  dstripestart += dstripestep) {
+		fvscanstart = fstripestart;
+		dvscanstart = dstripestart;
+		vscanlen = JAS_MIN(i, 4);
+		for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
+			fp = fvscanstart;
+			dp = dvscanstart;
+			k = vscanlen;
+
+			/* Process first sample in vertical scan. */
+			jpc_rawrefpass_step(fp, dp, poshalf, neghalf, in,
+			  vcausalflag);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+			/* Process second sample in vertical scan. */
+			jpc_rawrefpass_step(fp, dp, poshalf, neghalf, in, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+			/* Process third sample in vertical scan. */
+			jpc_rawrefpass_step(fp, dp, poshalf, neghalf, in, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+			/* Process fourth sample in vertical scan. */
+			jpc_rawrefpass_step(fp, dp, poshalf, neghalf, in, 0);
+		}
+	}
+	return 0;
+}
+
+/******************************************************************************\
+* Code for cleanup pass.
+\******************************************************************************/
+
+#define	jpc_clnpass_step(f, fp, frowstep, dp, oneplushalf, orient, mqdec, flabel, plabel, vcausalflag) \
+{ \
+	int v; \
+flabel \
+	if (!((f) & (JPC_SIG | JPC_VISIT))) { \
+		jpc_mqdec_setcurctx((mqdec), JPC_GETZCCTXNO((f), (orient))); \
+		JPC_T1D_GETBIT((mqdec), v, "CLN", "ZC"); \
+		if (v) { \
+plabel \
+			/* Coefficient is significant. */ \
+			jpc_mqdec_setcurctx((mqdec), JPC_GETSCCTXNO(f)); \
+			JPC_T1D_GETBIT((mqdec), v, "CLN", "SC"); \
+			v ^= JPC_GETSPB(f); \
+			*(dp) = (v) ? (-(oneplushalf)) : (oneplushalf); \
+			JPC_UPDATEFLAGS4((fp), (frowstep), v, (vcausalflag)); \
+			*(fp) |= JPC_SIG; \
+		} \
+	} \
+	/* XXX - Is this correct?  Can aggregation cause some VISIT bits not to be reset?  Check. */ \
+	*(fp) &= ~JPC_VISIT; \
+}
+
+static int dec_clnpass(jpc_dec_t *dec, register jpc_mqdec_t *mqdec, int bitpos, int orient,
+  int vcausalflag, int segsymflag, jas_matrix_t *flags, jas_matrix_t *data)
+{
+	int i;
+	int j;
+	int k;
+	int vscanlen;
+	int v;
+	int half;
+	int runlen;
+	int f;
+	int width;
+	int height;
+	int one;
+	int oneplushalf;
+
+	jpc_fix_t *fp;
+	int frowstep;
+	int fstripestep;
+	jpc_fix_t *fstripestart;
+	jpc_fix_t *fvscanstart;
+
+	jpc_fix_t *dp;
+	int drowstep;
+	int dstripestep;
+	jpc_fix_t *dstripestart;
+	jpc_fix_t *dvscanstart;
+
+	one = 1 << bitpos;
+	half = one >> 1;
+	oneplushalf = one | half;
+
+	width = jas_matrix_numcols(data);
+	height = jas_matrix_numrows(data);
+
+	frowstep = jas_matrix_rowstep(flags);
+	drowstep = jas_matrix_rowstep(data);
+	fstripestep = frowstep << 2;
+	dstripestep = drowstep << 2;
+
+	fstripestart = jas_matrix_getref(flags, 1, 1);
+	dstripestart = jas_matrix_getref(data, 0, 0);
+	for (i = 0; i < height; i += 4, fstripestart += fstripestep,
+	  dstripestart += dstripestep) {
+		fvscanstart = fstripestart;
+		dvscanstart = dstripestart;
+		vscanlen = JAS_MIN(4, height - i);
+		for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
+			fp = fvscanstart;
+			if (vscanlen >= 4 && (!((*fp) & (JPC_SIG | JPC_VISIT |
+			  JPC_OTHSIGMSK))) && (fp += frowstep, !((*fp) & (JPC_SIG |
+			  JPC_VISIT | JPC_OTHSIGMSK))) && (fp += frowstep, !((*fp) &
+			  (JPC_SIG | JPC_VISIT | JPC_OTHSIGMSK))) && (fp += frowstep,
+			  !((*fp) & (JPC_SIG | JPC_VISIT | JPC_OTHSIGMSK)))) {
+
+				jpc_mqdec_setcurctx(mqdec, JPC_AGGCTXNO);
+				JPC_T1D_GETBIT(mqdec, v, "CLN", "AGG");
+				if (!v) {
+					continue;
+				}
+				jpc_mqdec_setcurctx(mqdec, JPC_UCTXNO);
+				JPC_T1D_GETBITNOSKEW(mqdec, v, "CLN", "RL");
+				runlen = v;
+				JPC_T1D_GETBITNOSKEW(mqdec, v, "CLN", "RL");
+				runlen = (runlen << 1) | v;
+				f = *(fp = fvscanstart + frowstep * runlen);
+				dp = dvscanstart + drowstep * runlen;
+				k = vscanlen - runlen;
+				switch (runlen) {
+				case 0:
+					goto clnpass_partial0;
+					break;
+				case 1:
+					goto clnpass_partial1;
+					break;
+				case 2:
+					goto clnpass_partial2;
+					break;
+				case 3:
+					goto clnpass_partial3;
+					break;
+				}
+			} else {
+				f = *(fp = fvscanstart);
+				dp = dvscanstart;
+				k = vscanlen;
+				goto clnpass_full0;
+			}
+
+			/* Process first sample in vertical scan. */
+			jpc_clnpass_step(f, fp, frowstep, dp, oneplushalf, orient,
+			  mqdec, clnpass_full0:, clnpass_partial0:,
+			  vcausalflag);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+			/* Process second sample in vertical scan. */
+			f = *fp;
+			jpc_clnpass_step(f, fp, frowstep, dp, oneplushalf, orient,
+				mqdec, ;, clnpass_partial1:, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+			/* Process third sample in vertical scan. */
+			f = *fp;
+			jpc_clnpass_step(f, fp, frowstep, dp, oneplushalf, orient,
+				mqdec, ;, clnpass_partial2:, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+			/* Process fourth sample in vertical scan. */
+			f = *fp;
+			jpc_clnpass_step(f, fp, frowstep, dp, oneplushalf, orient,
+				mqdec, ;, clnpass_partial3:, 0);
+		}
+	}
+
+	if (segsymflag) {
+		int segsymval;
+		segsymval = 0;
+		jpc_mqdec_setcurctx(mqdec, JPC_UCTXNO);
+		JPC_T1D_GETBITNOSKEW(mqdec, v, "CLN", "SEGSYM");
+		segsymval = (segsymval << 1) | (v & 1);
+		JPC_T1D_GETBITNOSKEW(mqdec, v, "CLN", "SEGSYM");
+		segsymval = (segsymval << 1) | (v & 1);
+		JPC_T1D_GETBITNOSKEW(mqdec, v, "CLN", "SEGSYM");
+		segsymval = (segsymval << 1) | (v & 1);
+		JPC_T1D_GETBITNOSKEW(mqdec, v, "CLN", "SEGSYM");
+		segsymval = (segsymval << 1) | (v & 1);
+		if (segsymval != 0xa) {
+			fprintf(stderr, "warning: bad segmentation symbol\n");
+		}
+	}
+
+	return 0;
+}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_t1dec.h b/converter/other/jpeg2000/libjasper/jpc/jpc_t1dec.h
new file mode 100644
index 00000000..e28a1f57
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_t1dec.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Tier 1 Decoder
+ *
+ * $Id$
+ */
+
+#ifndef JPC_T1DEC_H
+#define JPC_T1DEC_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jpc_dec.h"
+#include "jpc_mqdec.h"
+#include "jpc_t1cod.h"
+
+/******************************************************************************\
+* Functions.
+\******************************************************************************/
+
+/* Decode all of the code blocks for a particular tile. */
+int jpc_dec_decodecblks(jpc_dec_t *dec, jpc_dec_tile_t *tile);
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c b/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c
new file mode 100644
index 00000000..8aa024a9
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c
@@ -0,0 +1,1008 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Tier 1 Encoder
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "jasper/jas_fix.h"
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_math.h"
+
+#include "jpc_t1enc.h"
+#include "jpc_t1cod.h"
+#include "jpc_enc.h"
+#include "jpc_cod.h"
+#include "jpc_math.h"
+
+static int jpc_encsigpass(jpc_mqenc_t *mqenc, int bitpos, int orient, int,
+  jas_matrix_t *flags, jas_matrix_t *data, int term, long *nmsedec);
+
+static int jpc_encrefpass(jpc_mqenc_t *mqenc, int bitpos, int, jas_matrix_t *flags,
+  jas_matrix_t *data, int term, long *nmsedec);
+
+static int jpc_encclnpass(jpc_mqenc_t *mqenc, int bitpos, int orient, int,
+  int, jas_matrix_t *flags, jas_matrix_t *data, int term, long *nmsedec);
+
+static int jpc_encrawsigpass(jpc_bitstream_t *out, int bitpos, int,
+  jas_matrix_t *flags, jas_matrix_t *data, int term, long *nmsedec);
+
+static int jpc_encrawrefpass(jpc_bitstream_t *out, int bitpos, int,
+  jas_matrix_t *flags, jas_matrix_t *data, int term, long *nmsedec);
+
+/******************************************************************************\
+* Code for encoding code blocks.
+\******************************************************************************/
+
+/* Encode all of the code blocks associated with the current tile. */
+int jpc_enc_enccblks(jpc_enc_t *enc)
+{
+	jpc_enc_tcmpt_t *tcmpt;
+	jpc_enc_tcmpt_t *endcomps;
+	jpc_enc_rlvl_t *lvl;
+	jpc_enc_rlvl_t *endlvls;
+	jpc_enc_band_t *band;
+	jpc_enc_band_t *endbands;
+	jpc_enc_cblk_t *cblk;
+	jpc_enc_cblk_t *endcblks;
+	int i;
+	int j;
+	int mx;
+	int bmx;
+	int v;
+	jpc_enc_tile_t *tile;
+	uint_fast32_t prcno;
+	jpc_enc_prc_t *prc;
+
+	tile = enc->curtile;
+
+	endcomps = &tile->tcmpts[tile->numtcmpts];
+	for (tcmpt = tile->tcmpts; tcmpt != endcomps; ++tcmpt) {
+		endlvls = &tcmpt->rlvls[tcmpt->numrlvls];
+		for (lvl = tcmpt->rlvls; lvl != endlvls; ++lvl) {
+			if (!lvl->bands) {
+				continue;
+			}
+			endbands = &lvl->bands[lvl->numbands];
+			for (band = lvl->bands; band != endbands; ++band) {
+				if (!band->data) {
+					continue;
+				}
+				for (prcno = 0, prc = band->prcs; prcno < lvl->numprcs; ++prcno, ++prc) {
+					if (!prc->cblks) {
+						continue;
+					}
+					bmx = 0;
+					endcblks = &prc->cblks[prc->numcblks];
+					for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
+						mx = 0;
+						for (i = 0; i < jas_matrix_numrows(cblk->data); ++i) {
+							for (j = 0; j < jas_matrix_numcols(cblk->data); ++j) {
+								v = abs(jas_matrix_get(cblk->data, i, j));
+								if (v > mx) {
+									mx = v;
+								}
+							}
+						}
+						if (mx > bmx) {
+							bmx = mx;
+						}
+						cblk->numbps = JAS_MAX(jpc_firstone(mx) + 1 - JPC_NUMEXTRABITS, 0);
+					}
+
+					for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
+						cblk->numimsbs = band->numbps - cblk->numbps;
+						assert(cblk->numimsbs >= 0);
+					}
+
+					for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
+						if (jpc_enc_enccblk(enc, cblk->stream, tcmpt, band, cblk)) {
+							return -1;
+						}
+					}
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+static int getthebyte(jas_stream_t *in, long off)
+{
+	int c;
+	long oldpos;
+	oldpos = jas_stream_tell(in);
+	assert(oldpos >= 0);
+	jas_stream_seek(in, off, SEEK_SET);
+	c = jas_stream_peekc(in);
+	jas_stream_seek(in, oldpos, SEEK_SET);
+	return c;
+}
+
+/* Encode a single code block. */
+int jpc_enc_enccblk(jpc_enc_t *enc, jas_stream_t *out, jpc_enc_tcmpt_t *tcmpt, jpc_enc_band_t *band, jpc_enc_cblk_t *cblk)
+{
+	jpc_enc_pass_t *pass;
+	jpc_enc_pass_t *endpasses;
+	int bitpos;
+	int n;
+	int adjust;
+	int ret;
+	int passtype;
+	int t;
+	jpc_bitstream_t *bout;
+	jpc_enc_pass_t *termpass;
+	jpc_enc_rlvl_t *rlvl;
+	int vcausal;
+	int segsym;
+	int termmode;
+	int c;
+
+	bout = 0;
+	rlvl = band->rlvl;
+
+	cblk->stream = jas_stream_memopen(0, 0);
+	assert(cblk->stream);
+	cblk->mqenc = jpc_mqenc_create(JPC_NUMCTXS, cblk->stream);
+	assert(cblk->mqenc);
+	jpc_mqenc_setctxs(cblk->mqenc, JPC_NUMCTXS, jpc_mqctxs);
+
+	cblk->numpasses = (cblk->numbps > 0) ? (3 * cblk->numbps - 2) : 0;
+	if (cblk->numpasses > 0) {
+		cblk->passes = jas_malloc(cblk->numpasses * sizeof(jpc_enc_pass_t));
+		assert(cblk->passes);
+	} else {
+		cblk->passes = 0;
+	}
+	endpasses = &cblk->passes[cblk->numpasses];
+	for (pass = cblk->passes; pass != endpasses; ++pass) {
+		pass->start = 0;
+		pass->end = 0;
+		pass->term = JPC_ISTERMINATED(pass - cblk->passes, 0, cblk->numpasses, (tcmpt->cblksty & JPC_COX_TERMALL) != 0, (tcmpt->cblksty & JPC_COX_LAZY) != 0);
+		pass->type = JPC_SEGTYPE(pass - cblk->passes, 0, (tcmpt->cblksty & JPC_COX_LAZY) != 0);
+		pass->lyrno = -1;
+if (pass == endpasses - 1) {
+assert(pass->term == 1);
+	pass->term = 1;
+}
+	}
+
+	cblk->flags = jas_matrix_create(jas_matrix_numrows(cblk->data) + 2,
+	  jas_matrix_numcols(cblk->data) + 2);
+	assert(cblk->flags);
+
+
+	bitpos = cblk->numbps - 1;
+	pass = cblk->passes;
+	n = cblk->numpasses;
+	while (--n >= 0) {
+
+		if (pass->type == JPC_SEG_MQ) {
+			/* NOP */
+		} else {
+			assert(pass->type == JPC_SEG_RAW);
+			if (!bout) {
+				bout = jpc_bitstream_sopen(cblk->stream, "w");
+				assert(bout);
+			}
+		}
+
+#if 1
+		passtype = (pass - cblk->passes + 2) % 3;
+#else
+		passtype = JPC_PASSTYPE(pass - cblk->passes + 2);
+#endif
+		pass->start = jas_stream_tell(cblk->stream);
+#if 0
+assert(jas_stream_tell(cblk->stream) == jas_stream_getrwcount(cblk->stream));
+#endif
+		assert(bitpos >= 0);
+		vcausal = (tcmpt->cblksty & JPC_COX_VSC) != 0;
+		segsym = (tcmpt->cblksty & JPC_COX_SEGSYM) != 0;
+		if (pass->term) {
+			termmode = ((tcmpt->cblksty & JPC_COX_PTERM) ?
+			  JPC_MQENC_PTERM : JPC_MQENC_DEFTERM) + 1;
+		} else {
+			termmode = 0;
+		}
+		switch (passtype) {
+		case JPC_SIGPASS:
+			ret = (pass->type == JPC_SEG_MQ) ? jpc_encsigpass(cblk->mqenc,
+			  bitpos, band->orient, vcausal, cblk->flags,
+			  cblk->data, termmode, &pass->nmsedec) :
+			  jpc_encrawsigpass(bout, bitpos, vcausal, cblk->flags,
+			  cblk->data, termmode, &pass->nmsedec);
+			break;
+		case JPC_REFPASS:
+			ret = (pass->type == JPC_SEG_MQ) ? jpc_encrefpass(cblk->mqenc,
+			  bitpos, vcausal, cblk->flags, cblk->data, termmode,
+			  &pass->nmsedec) : jpc_encrawrefpass(bout, bitpos,
+			  vcausal, cblk->flags, cblk->data, termmode,
+			  &pass->nmsedec);
+			break;
+		case JPC_CLNPASS:
+			assert(pass->type == JPC_SEG_MQ);
+			ret = jpc_encclnpass(cblk->mqenc, bitpos, band->orient,
+			  vcausal, segsym, cblk->flags, cblk->data, termmode,
+			  &pass->nmsedec);
+			break;
+		default:
+			assert(0);
+			break;
+		}
+
+		if (pass->type == JPC_SEG_MQ) {
+			if (pass->term) {
+				jpc_mqenc_init(cblk->mqenc);
+			}
+			jpc_mqenc_getstate(cblk->mqenc, &pass->mqencstate);
+			pass->end = jas_stream_tell(cblk->stream);
+			if (tcmpt->cblksty & JPC_COX_RESET) {
+				jpc_mqenc_setctxs(cblk->mqenc, JPC_NUMCTXS, jpc_mqctxs);
+			}
+		} else {
+			if (pass->term) {
+				if (jpc_bitstream_pending(bout)) {
+					jpc_bitstream_outalign(bout, 0x2a);
+				}
+				jpc_bitstream_close(bout);
+				bout = 0;
+				pass->end = jas_stream_tell(cblk->stream);
+			} else {
+				pass->end = jas_stream_tell(cblk->stream) +
+				  jpc_bitstream_pending(bout);
+/* NOTE - This will not work.  need to adjust by # of pending output bytes */
+			}
+		}
+#if 0
+/* XXX - This assertion fails sometimes when various coding modes are used.
+This seems to be harmless, but why does it happen at all? */
+assert(jas_stream_tell(cblk->stream) == jas_stream_getrwcount(cblk->stream));
+#endif
+
+		pass->wmsedec = jpc_fixtodbl(band->rlvl->tcmpt->synweight) *
+		  jpc_fixtodbl(band->rlvl->tcmpt->synweight) *
+		  jpc_fixtodbl(band->synweight) *
+		  jpc_fixtodbl(band->synweight) *
+		  jpc_fixtodbl(band->absstepsize) * jpc_fixtodbl(band->absstepsize) *
+		  ((double) (1 << bitpos)) * ((double)(1 << bitpos)) *
+		  jpc_fixtodbl(pass->nmsedec);
+		pass->cumwmsedec = pass->wmsedec;
+		if (pass != cblk->passes) {
+			pass->cumwmsedec += pass[-1].cumwmsedec;
+		}
+		if (passtype == JPC_CLNPASS) {
+			--bitpos;
+		}
+		++pass;
+	}
+
+#if 0
+dump_passes(cblk->passes, cblk->numpasses, cblk);
+#endif
+
+	n = 0;
+	endpasses = &cblk->passes[cblk->numpasses];
+	for (pass = cblk->passes; pass != endpasses; ++pass) {
+		if (pass->start < n) {
+			pass->start = n;
+		}
+		if (pass->end < n) {
+			pass->end = n;
+		}
+		if (!pass->term) {
+			termpass = pass;
+			while (termpass - pass < cblk->numpasses &&
+			  !termpass->term) {
+				++termpass;
+			}
+			if (pass->type == JPC_SEG_MQ) {
+				t = (pass->mqencstate.lastbyte == 0xff) ? 1 : 0;
+				if (pass->mqencstate.ctreg >= 5) {
+					adjust = 4 + t;
+				} else {
+					adjust = 5 + t;
+				}
+				pass->end += adjust;
+			}
+			if (pass->end > termpass->end) {
+				pass->end = termpass->end;
+			}
+			if ((c = getthebyte(cblk->stream, pass->end - 1)) == EOF) {
+				abort();
+			}
+			if (c == 0xff) {
+				++pass->end;
+			}
+			n = JAS_MAX(n, pass->end);
+		} else {
+			n = JAS_MAX(n, pass->end);
+		}
+	}
+
+#if 0
+dump_passes(cblk->passes, cblk->numpasses, cblk);
+#endif
+
+	if (bout) {
+		jpc_bitstream_close(bout);
+	}
+
+	return 0;
+}
+
+/******************************************************************************\
+* Code for significance pass.
+\******************************************************************************/
+
+#define	sigpass_step(fp, frowstep, dp, bitpos, one, nmsedec, orient, mqenc, vcausalflag) \
+{ \
+	int f; \
+	int v; \
+	f = *(fp); \
+	if ((f & JPC_OTHSIGMSK) && !(f & (JPC_SIG | JPC_VISIT))) { \
+		v = (abs(*(dp)) & (one)) ? 1 : 0; \
+		jpc_mqenc_setcurctx(mqenc, JPC_GETZCCTXNO(f, (orient))); \
+		jpc_mqenc_putbit(mqenc, v); \
+		if (v) { \
+			*(nmsedec) += JPC_GETSIGNMSEDEC(abs(*(dp)), (bitpos) + JPC_NUMEXTRABITS); \
+			v = ((*(dp) < 0) ? 1 : 0); \
+			jpc_mqenc_setcurctx(mqenc, JPC_GETSCCTXNO(f)); \
+			jpc_mqenc_putbit(mqenc, v ^ JPC_GETSPB(f)); \
+			JPC_UPDATEFLAGS4(fp, frowstep, v, vcausalflag); \
+			*(fp) |= JPC_SIG; \
+		} \
+		*(fp) |= JPC_VISIT; \
+	} \
+}
+
+static int jpc_encsigpass(jpc_mqenc_t *mqenc, int bitpos, int orient, int vcausalflag,
+  jas_matrix_t *flags, jas_matrix_t *data, int term, long *nmsedec)
+{
+	int i;
+	int j;
+	int one;
+	int vscanlen;
+	int width;
+	int height;
+	int frowstep;
+	int drowstep;
+	int fstripestep;
+	int dstripestep;
+	jpc_fix_t *fstripestart;
+	jpc_fix_t *dstripestart;
+	jpc_fix_t *fp;
+	jpc_fix_t *dp;
+	jpc_fix_t *fvscanstart;
+	jpc_fix_t *dvscanstart;
+	int k;
+
+	*nmsedec = 0;
+	width = jas_matrix_numcols(data);
+	height = jas_matrix_numrows(data);
+	frowstep = jas_matrix_rowstep(flags);
+	drowstep = jas_matrix_rowstep(data);
+	fstripestep = frowstep << 2;
+	dstripestep = drowstep << 2;
+
+	one = 1 << (bitpos + JPC_NUMEXTRABITS);
+
+	fstripestart = jas_matrix_getref(flags, 1, 1);
+	dstripestart = jas_matrix_getref(data, 0, 0);
+	for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
+	  dstripestart += dstripestep) {
+		fvscanstart = fstripestart;
+		dvscanstart = dstripestart;
+		vscanlen = JAS_MIN(i, 4);
+		for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
+			fp = fvscanstart;
+			dp = dvscanstart;
+			k = vscanlen;
+
+			sigpass_step(fp, frowstep, dp, bitpos, one,
+			  nmsedec, orient, mqenc, vcausalflag);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+			sigpass_step(fp, frowstep, dp, bitpos, one,
+			  nmsedec, orient, mqenc, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+			sigpass_step(fp, frowstep, dp, bitpos, one,
+			  nmsedec, orient, mqenc, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+			sigpass_step(fp, frowstep, dp, bitpos, one,
+			  nmsedec, orient, mqenc, 0);
+
+		}
+	}
+
+	if (term) {
+		jpc_mqenc_flush(mqenc, term - 1);
+	}
+
+	return jpc_mqenc_error(mqenc) ? (-1) : 0;
+}
+
+#define	rawsigpass_step(fp, frowstep, dp, bitpos, one, nmsedec, out, vcausalflag) \
+{ \
+	jpc_fix_t f = *(fp); \
+	jpc_fix_t v; \
+	if ((f & JPC_OTHSIGMSK) && !(f & (JPC_SIG | JPC_VISIT))) { \
+		v = (abs(*(dp)) & (one)) ? 1 : 0; \
+		if ((jpc_bitstream_putbit((out), v)) == EOF) { \
+			return -1; \
+		} \
+		if (v) { \
+			*(nmsedec) += JPC_GETSIGNMSEDEC(abs(*(dp)), (bitpos) + JPC_NUMEXTRABITS); \
+			v = ((*(dp) < 0) ? 1 : 0); \
+			if (jpc_bitstream_putbit(out, v) == EOF) { \
+				return -1; \
+			} \
+			JPC_UPDATEFLAGS4(fp, frowstep, v, vcausalflag); \
+			*(fp) |= JPC_SIG; \
+		} \
+		*(fp) |= JPC_VISIT; \
+	} \
+}
+
+static int jpc_encrawsigpass(jpc_bitstream_t *out, int bitpos, int vcausalflag, jas_matrix_t *flags,
+  jas_matrix_t *data, int term, long *nmsedec)
+{
+	int i;
+	int j;
+	int k;
+	int one;
+	int vscanlen;
+	int width;
+	int height;
+	int frowstep;
+	int drowstep;
+	int fstripestep;
+	int dstripestep;
+	jpc_fix_t *fstripestart;
+	jpc_fix_t *dstripestart;
+	jpc_fix_t *fp;
+	jpc_fix_t *dp;
+	jpc_fix_t *fvscanstart;
+	jpc_fix_t *dvscanstart;
+
+	*nmsedec = 0;
+	width = jas_matrix_numcols(data);
+	height = jas_matrix_numrows(data);
+	frowstep = jas_matrix_rowstep(flags);
+	drowstep = jas_matrix_rowstep(data);
+	fstripestep = frowstep << 2;
+	dstripestep = drowstep << 2;
+
+	one = 1 << (bitpos + JPC_NUMEXTRABITS);
+
+	fstripestart = jas_matrix_getref(flags, 1, 1);
+	dstripestart = jas_matrix_getref(data, 0, 0);
+	for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
+	  dstripestart += dstripestep) {
+		fvscanstart = fstripestart;
+		dvscanstart = dstripestart;
+		vscanlen = JAS_MIN(i, 4);
+		for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
+			fp = fvscanstart;
+			dp = dvscanstart;
+			k = vscanlen;
+
+			rawsigpass_step(fp, frowstep, dp, bitpos, one,
+			  nmsedec, out, vcausalflag);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+			rawsigpass_step(fp, frowstep, dp, bitpos, one,
+			  nmsedec, out, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+			rawsigpass_step(fp, frowstep, dp, bitpos, one,
+			  nmsedec, out, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+			rawsigpass_step(fp, frowstep, dp, bitpos, one,
+			  nmsedec, out, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+
+		}
+	}
+
+	if (term) {
+		jpc_bitstream_outalign(out, 0x2a);
+	}
+
+	return 0;
+}
+
+/******************************************************************************\
+* Code for refinement pass.
+\******************************************************************************/
+
+#define	refpass_step(fp, dp, bitpos, one, nmsedec, mqenc, vcausalflag) \
+{ \
+	int v; \
+	if (((*(fp)) & (JPC_SIG | JPC_VISIT)) == JPC_SIG) { \
+		(d) = *(dp); \
+		*(nmsedec) += JPC_GETREFNMSEDEC(abs(d), (bitpos) + JPC_NUMEXTRABITS); \
+		jpc_mqenc_setcurctx((mqenc), JPC_GETMAGCTXNO(*(fp))); \
+		v = (abs(d) & (one)) ? 1 : 0; \
+		jpc_mqenc_putbit((mqenc), v); \
+		*(fp) |= JPC_REFINE; \
+	} \
+}
+
+static int jpc_encrefpass(jpc_mqenc_t *mqenc, int bitpos, int vcausalflag, jas_matrix_t *flags, jas_matrix_t *data,
+  int term, long *nmsedec)
+{
+	int i;
+	int j;
+	int one;
+	int vscanlen;
+	int d;
+	int width;
+	int height;
+	int frowstep;
+	int drowstep;
+	int fstripestep;
+	int dstripestep;
+	jpc_fix_t *fstripestart;
+	jpc_fix_t *dstripestart;
+	jpc_fix_t *fvscanstart;
+	jpc_fix_t *dvscanstart;
+	jpc_fix_t *dp;
+	jpc_fix_t *fp;
+int k;
+
+	*nmsedec = 0;
+	width = jas_matrix_numcols(data);
+	height = jas_matrix_numrows(data);
+	frowstep = jas_matrix_rowstep(flags);
+	drowstep = jas_matrix_rowstep(data);
+	fstripestep = frowstep << 2;
+	dstripestep = drowstep << 2;
+
+	one = 1 << (bitpos + JPC_NUMEXTRABITS);
+
+	fstripestart = jas_matrix_getref(flags, 1, 1);
+	dstripestart = jas_matrix_getref(data, 0, 0);
+	for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
+	  dstripestart += dstripestep) {
+		fvscanstart = fstripestart;
+		dvscanstart = dstripestart;
+		vscanlen = JAS_MIN(i, 4);
+		for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
+			fp = fvscanstart;
+			dp = dvscanstart;
+			k = vscanlen;
+
+			refpass_step(fp, dp, bitpos, one, nmsedec,
+			  mqenc, vcausalflag);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+			refpass_step(fp, dp, bitpos, one, nmsedec,
+			  mqenc, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+			refpass_step(fp, dp, bitpos, one, nmsedec,
+			  mqenc, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+			refpass_step(fp, dp, bitpos, one, nmsedec,
+			  mqenc, 0);
+
+		}
+	}
+
+	if (term) {
+		jpc_mqenc_flush(mqenc, term - 1);
+	}
+
+	return jpc_mqenc_error(mqenc) ? (-1) : 0;
+}
+
+#define	rawrefpass_step(fp, dp, bitpos, one, nmsedec, out, vcausalflag) \
+{ \
+	jpc_fix_t d; \
+	jpc_fix_t v; \
+	if (((*(fp)) & (JPC_SIG | JPC_VISIT)) == JPC_SIG) { \
+		d = *(dp); \
+		*(nmsedec) += JPC_GETREFNMSEDEC(abs(d), (bitpos) + JPC_NUMEXTRABITS); \
+		v = (abs(d) & (one)) ? 1 : 0; \
+		if (jpc_bitstream_putbit((out), v) == EOF) { \
+			return -1; \
+		} \
+		*(fp) |= JPC_REFINE; \
+	} \
+}
+
+static int jpc_encrawrefpass(jpc_bitstream_t *out, int bitpos, int vcausalflag, jas_matrix_t *flags,
+  jas_matrix_t *data, int term, long *nmsedec)
+{
+	int i;
+	int j;
+	int k;
+	int one;
+	int vscanlen;
+	int width;
+	int height;
+	int frowstep;
+	int drowstep;
+	int fstripestep;
+	int dstripestep;
+	jpc_fix_t *fstripestart;
+	jpc_fix_t *dstripestart;
+	jpc_fix_t *fvscanstart;
+	jpc_fix_t *dvscanstart;
+	jpc_fix_t *dp;
+	jpc_fix_t *fp;
+
+	*nmsedec = 0;
+	width = jas_matrix_numcols(data);
+	height = jas_matrix_numrows(data);
+	frowstep = jas_matrix_rowstep(flags);
+	drowstep = jas_matrix_rowstep(data);
+	fstripestep = frowstep << 2;
+	dstripestep = drowstep << 2;
+
+	one = 1 << (bitpos + JPC_NUMEXTRABITS);
+
+	fstripestart = jas_matrix_getref(flags, 1, 1);
+	dstripestart = jas_matrix_getref(data, 0, 0);
+	for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
+	  dstripestart += dstripestep) {
+		fvscanstart = fstripestart;
+		dvscanstart = dstripestart;
+		vscanlen = JAS_MIN(i, 4);
+		for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
+			fp = fvscanstart;
+			dp = dvscanstart;
+			k = vscanlen;
+
+			rawrefpass_step(fp, dp, bitpos, one, nmsedec,
+			  out, vcausalflag);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+			rawrefpass_step(fp, dp, bitpos, one, nmsedec,
+			  out, vcausalflag);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+			rawrefpass_step(fp, dp, bitpos, one, nmsedec,
+			  out, vcausalflag);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+			rawrefpass_step(fp, dp, bitpos, one, nmsedec,
+			  out, vcausalflag);
+
+		}
+	}
+
+	if (term) {
+		jpc_bitstream_outalign(out, 0x2a);
+	}
+
+	return 0;
+}
+
+/******************************************************************************\
+* Code for cleanup pass.
+\******************************************************************************/
+
+#define	clnpass_step(fp, frowstep, dp, bitpos, one, orient, nmsedec, mqenc, label1, label2, vcausalflag) \
+{ \
+	int f; \
+	int v; \
+label1 \
+	f = *(fp); \
+	if (!(f & (JPC_SIG | JPC_VISIT))) { \
+		jpc_mqenc_setcurctx(mqenc, JPC_GETZCCTXNO(f, (orient))); \
+		v = (abs(*(dp)) & (one)) ? 1 : 0; \
+		jpc_mqenc_putbit((mqenc), v); \
+		if (v) { \
+label2 \
+			f = *(fp); \
+			/* Coefficient is significant. */ \
+			*(nmsedec) += JPC_GETSIGNMSEDEC(abs(*(dp)), (bitpos) + JPC_NUMEXTRABITS); \
+			jpc_mqenc_setcurctx((mqenc), JPC_GETSCCTXNO(f)); \
+			v = ((*(dp) < 0) ? 1 : 0); \
+			jpc_mqenc_putbit((mqenc), v ^ JPC_GETSPB(f)); \
+			JPC_UPDATEFLAGS4((fp), (frowstep), v, vcausalflag); \
+			*(fp) |= JPC_SIG; \
+		} \
+	} \
+	*(fp) &= ~JPC_VISIT; \
+}
+
+static int jpc_encclnpass(jpc_mqenc_t *mqenc, int bitpos, int orient, int vcausalflag, int segsymflag, jas_matrix_t *flags,
+  jas_matrix_t *data, int term, long *nmsedec)
+{
+	int i;
+	int j;
+	int k;
+	int vscanlen;
+	int v;
+	int runlen;
+	jpc_fix_t *fp;
+	int width;
+	int height;
+	jpc_fix_t *dp;
+	int one;
+	int frowstep;
+	int drowstep;
+	int fstripestep;
+	int dstripestep;
+	jpc_fix_t *fstripestart;
+	jpc_fix_t *dstripestart;
+	jpc_fix_t *fvscanstart;
+	jpc_fix_t *dvscanstart;
+
+	*nmsedec = 0;
+	width = jas_matrix_numcols(data);
+	height = jas_matrix_numrows(data);
+	frowstep = jas_matrix_rowstep(flags);
+	drowstep = jas_matrix_rowstep(data);
+	fstripestep = frowstep << 2;
+	dstripestep = drowstep << 2;
+
+	one = 1 << (bitpos + JPC_NUMEXTRABITS);
+
+	fstripestart = jas_matrix_getref(flags, 1, 1);
+	dstripestart = jas_matrix_getref(data, 0, 0);
+	for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
+	  dstripestart += dstripestep) {
+		fvscanstart = fstripestart;
+		dvscanstart = dstripestart;
+		vscanlen = JAS_MIN(i, 4);
+		for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
+
+			fp = fvscanstart;
+			if (vscanlen >= 4 && !((*fp) & (JPC_SIG | JPC_VISIT |
+			  JPC_OTHSIGMSK)) && (fp += frowstep, !((*fp) & (JPC_SIG |
+			  JPC_VISIT | JPC_OTHSIGMSK))) && (fp += frowstep, !((*fp) &
+			  (JPC_SIG | JPC_VISIT | JPC_OTHSIGMSK))) && (fp += frowstep,
+			  !((*fp) & (JPC_SIG | JPC_VISIT | JPC_OTHSIGMSK)))) {
+				dp = dvscanstart;
+				for (k = 0; k < vscanlen; ++k) {
+					v = (abs(*dp) & one) ? 1 : 0;
+					if (v) {
+						break;
+					}
+					dp += drowstep;
+				}
+				runlen = k;
+				if (runlen >= 4) {
+					jpc_mqenc_setcurctx(mqenc, JPC_AGGCTXNO);
+					jpc_mqenc_putbit(mqenc, 0);
+					continue;
+				}
+				jpc_mqenc_setcurctx(mqenc, JPC_AGGCTXNO);
+				jpc_mqenc_putbit(mqenc, 1);
+				jpc_mqenc_setcurctx(mqenc, JPC_UCTXNO);
+				jpc_mqenc_putbit(mqenc, runlen >> 1);
+				jpc_mqenc_putbit(mqenc, runlen & 1);
+				fp = fvscanstart + frowstep * runlen;
+				dp = dvscanstart + drowstep * runlen;
+				k = vscanlen - runlen;
+				switch (runlen) {
+				case 0:
+					goto clnpass_partial0;
+					break;
+				case 1:
+					goto clnpass_partial1;
+					break;
+				case 2:
+					goto clnpass_partial2;
+					break;
+				case 3:
+					goto clnpass_partial3;
+					break;
+				}
+			} else {
+				runlen = 0;
+				fp = fvscanstart;
+				dp = dvscanstart;
+				k = vscanlen;
+				goto clnpass_full0;
+			}
+			clnpass_step(fp, frowstep, dp, bitpos, one,
+			  orient, nmsedec, mqenc, clnpass_full0:, clnpass_partial0:, vcausalflag);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+			clnpass_step(fp, frowstep, dp, bitpos, one,
+				orient, nmsedec, mqenc, ;, clnpass_partial1:, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+			clnpass_step(fp, frowstep, dp, bitpos, one,
+				orient, nmsedec, mqenc, ;, clnpass_partial2:, 0);
+			if (--k <= 0) {
+				continue;
+			}
+			fp += frowstep;
+			dp += drowstep;
+			clnpass_step(fp, frowstep, dp, bitpos, one,
+				orient, nmsedec, mqenc, ;, clnpass_partial3:, 0);
+		}
+	}
+
+	if (segsymflag) {
+		jpc_mqenc_setcurctx(mqenc, JPC_UCTXNO);
+		jpc_mqenc_putbit(mqenc, 1);
+		jpc_mqenc_putbit(mqenc, 0);
+		jpc_mqenc_putbit(mqenc, 1);
+		jpc_mqenc_putbit(mqenc, 0);
+	}
+
+	if (term) {
+		jpc_mqenc_flush(mqenc, term - 1);
+	}
+
+	return jpc_mqenc_error(mqenc) ? (-1) : 0;
+}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.h b/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.h
new file mode 100644
index 00000000..9ce5ed27
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Tier 1 Encoder
+ *
+ * $Id$
+ */
+
+#ifndef JPC_T1ENC_H
+#define JPC_T1ENC_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jasper/jas_seq.h"
+
+#include "jpc_enc.h"
+#include "jpc_t1cod.h"
+
+/******************************************************************************\
+* Functions.
+\******************************************************************************/
+
+/* Encode all of the code blocks. */
+int jpc_enc_enccblks(jpc_enc_t *enc);
+
+/* Encode a single code block. */
+int jpc_enc_enccblk(jpc_enc_t *enc, jas_stream_t *out, jpc_enc_tcmpt_t *comp,
+  jpc_enc_band_t *band, jpc_enc_cblk_t *cblk);
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_t2cod.c b/converter/other/jpeg2000/libjasper/jpc/jpc_t2cod.c
new file mode 100644
index 00000000..a9ca0dc1
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_t2cod.c
@@ -0,0 +1,733 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Tier-2 Coding Library
+ *
+ * $Id$
+ */
+
+#include "jasper/jas_math.h"
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_math.h"
+
+#include "jpc_cs.h"
+#include "jpc_t2cod.h"
+#include "jpc_math.h"
+
+static int jpc_pi_nextlrcp(jpc_pi_t *pi);
+static int jpc_pi_nextrlcp(jpc_pi_t *pi);
+static int jpc_pi_nextrpcl(jpc_pi_t *pi);
+static int jpc_pi_nextpcrl(jpc_pi_t *pi);
+static int jpc_pi_nextcprl(jpc_pi_t *pi);
+
+int jpc_pi_next(jpc_pi_t *pi)
+{
+	jpc_pchg_t *pchg;
+	int ret;
+
+
+	for (;;) {
+
+		pi->valid = false;
+
+		if (!pi->pchg) {
+			++pi->pchgno;
+			pi->compno = 0;
+			pi->rlvlno = 0;
+			pi->prcno = 0;
+			pi->lyrno = 0;
+			pi->prgvolfirst = true;
+			if (pi->pchgno < jpc_pchglist_numpchgs(pi->pchglist)) {
+				pi->pchg = jpc_pchglist_get(pi->pchglist, pi->pchgno);
+			} else if (pi->pchgno == jpc_pchglist_numpchgs(pi->pchglist)) {
+				pi->pchg = &pi->defaultpchg;
+			} else {
+				return 1;
+			}
+		}
+
+		pchg = pi->pchg;
+		switch (pchg->prgord) {
+		case JPC_COD_LRCPPRG:
+			ret = jpc_pi_nextlrcp(pi);
+			break;
+		case JPC_COD_RLCPPRG:
+			ret = jpc_pi_nextrlcp(pi);
+			break;
+		case JPC_COD_RPCLPRG:
+			ret = jpc_pi_nextrpcl(pi);
+			break;
+		case JPC_COD_PCRLPRG:
+			ret = jpc_pi_nextpcrl(pi);
+			break;
+		case JPC_COD_CPRLPRG:
+			ret = jpc_pi_nextcprl(pi);
+			break;
+		default:
+			ret = -1;
+			break;
+		}
+		if (!ret) {
+			pi->valid = true;
+			++pi->pktno;
+			return 0;
+		}
+		pi->pchg = 0;
+	}
+}
+
+static int jpc_pi_nextlrcp(register jpc_pi_t *pi)
+{
+	jpc_pchg_t *pchg;
+	int *prclyrno;
+
+	pchg = pi->pchg;
+	if (!pi->prgvolfirst) {
+		prclyrno = &pi->pirlvl->prclyrnos[pi->prcno];
+		goto skip;
+	} else {
+		pi->prgvolfirst = false;
+	}
+
+	for (pi->lyrno = 0; pi->lyrno < pi->numlyrs && pi->lyrno <
+	  pchg->lyrnoend; ++pi->lyrno) {
+		for (pi->rlvlno = pchg->rlvlnostart; pi->rlvlno < pi->maxrlvls &&
+		  pi->rlvlno < pchg->rlvlnoend; ++pi->rlvlno) {
+			for (pi->compno = pchg->compnostart, pi->picomp =
+			  &pi->picomps[pi->compno]; pi->compno < pi->numcomps
+			  && pi->compno < pchg->compnoend; ++pi->compno,
+			  ++pi->picomp) {
+				if (pi->rlvlno >= pi->picomp->numrlvls) {
+					continue;
+				}
+				pi->pirlvl = &pi->picomp->pirlvls[pi->rlvlno];
+				for (pi->prcno = 0, prclyrno =
+				  pi->pirlvl->prclyrnos; pi->prcno <
+				  pi->pirlvl->numprcs; ++pi->prcno,
+				  ++prclyrno) {
+					if (pi->lyrno >= *prclyrno) {
+						*prclyrno = pi->lyrno;
+						++(*prclyrno);
+						return 0;
+					}
+skip:
+					;
+				}
+			}
+		}
+	}
+	return 1;
+}
+
+static int jpc_pi_nextrlcp(register jpc_pi_t *pi)
+{
+	jpc_pchg_t *pchg;
+	int *prclyrno;
+
+	pchg = pi->pchg;
+	if (!pi->prgvolfirst) {
+		assert(pi->prcno < pi->pirlvl->numprcs);
+		prclyrno = &pi->pirlvl->prclyrnos[pi->prcno];
+		goto skip;
+	} else {
+		pi->prgvolfirst = 0;
+	}
+
+	for (pi->rlvlno = pchg->rlvlnostart; pi->rlvlno < pi->maxrlvls &&
+	  pi->rlvlno < pchg->rlvlnoend; ++pi->rlvlno) {
+		for (pi->lyrno = 0; pi->lyrno < pi->numlyrs && pi->lyrno <
+		  pchg->lyrnoend; ++pi->lyrno) {
+			for (pi->compno = pchg->compnostart, pi->picomp =
+			  &pi->picomps[pi->compno]; pi->compno < pi->numcomps &&
+			  pi->compno < pchg->compnoend; ++pi->compno, ++pi->picomp) {
+				if (pi->rlvlno >= pi->picomp->numrlvls) {
+					continue;
+				}
+				pi->pirlvl = &pi->picomp->pirlvls[pi->rlvlno];
+				for (pi->prcno = 0, prclyrno = pi->pirlvl->prclyrnos;
+				  pi->prcno < pi->pirlvl->numprcs; ++pi->prcno, ++prclyrno) {
+					if (pi->lyrno >= *prclyrno) {
+						*prclyrno = pi->lyrno;
+						++(*prclyrno);
+						return 0;
+					}
+skip:
+					;
+				}
+			}
+		}
+	}
+	return 1;
+}
+
+static int jpc_pi_nextrpcl(register jpc_pi_t *pi)
+{
+	int rlvlno;
+	jpc_pirlvl_t *pirlvl;
+	jpc_pchg_t *pchg;
+	int prchind;
+	int prcvind;
+	int *prclyrno;
+	int compno;
+	jpc_picomp_t *picomp;
+	int xstep;
+	int ystep;
+	uint_fast32_t r;
+	uint_fast32_t rpx;
+	uint_fast32_t rpy;
+	uint_fast32_t trx0;
+	uint_fast32_t try0;
+
+	pchg = pi->pchg;
+	if (!pi->prgvolfirst) {
+		goto skip;
+	} else {
+		pi->xstep = 0;
+		pi->ystep = 0;
+		for (compno = 0, picomp = pi->picomps; compno < pi->numcomps;
+		  ++compno, ++picomp) {
+			for (rlvlno = 0, pirlvl = picomp->pirlvls; rlvlno <
+			  picomp->numrlvls; ++rlvlno, ++pirlvl) {
+				xstep = picomp->hsamp * (1 << (pirlvl->prcwidthexpn +
+				  picomp->numrlvls - rlvlno - 1));
+				ystep = picomp->vsamp * (1 << (pirlvl->prcheightexpn +
+				  picomp->numrlvls - rlvlno - 1));
+				pi->xstep = (!pi->xstep) ? xstep : JAS_MIN(pi->xstep, xstep);
+				pi->ystep = (!pi->ystep) ? ystep : JAS_MIN(pi->ystep, ystep);
+			}
+		}
+		pi->prgvolfirst = 0;
+	}
+
+	for (pi->rlvlno = pchg->rlvlnostart; pi->rlvlno < pchg->rlvlnoend &&
+	  pi->rlvlno < pi->maxrlvls; ++pi->rlvlno) {
+		for (pi->y = pi->ystart; pi->y < pi->yend; pi->y +=
+		  pi->ystep - (pi->y % pi->ystep)) {
+			for (pi->x = pi->xstart; pi->x < pi->xend; pi->x +=
+			  pi->xstep - (pi->x % pi->xstep)) {
+				for (pi->compno = pchg->compnostart,
+				  pi->picomp = &pi->picomps[pi->compno];
+				  pi->compno < pchg->compnoend && pi->compno <
+				  pi->numcomps; ++pi->compno, ++pi->picomp) {
+					if (pi->rlvlno >= pi->picomp->numrlvls) {
+						continue;
+					}
+					pi->pirlvl = &pi->picomp->pirlvls[pi->rlvlno];
+					if (pi->pirlvl->numprcs == 0) {
+						continue;
+					}
+					r = pi->picomp->numrlvls - 1 - pi->rlvlno;
+					rpx = r + pi->pirlvl->prcwidthexpn;
+					rpy = r + pi->pirlvl->prcheightexpn;
+					trx0 = JPC_CEILDIV(pi->xstart, pi->picomp->hsamp << r);
+					try0 = JPC_CEILDIV(pi->ystart, pi->picomp->vsamp << r);
+					if (((pi->x == pi->xstart && ((trx0 << r) % (1 << rpx)))
+					  || !(pi->x % (1 << rpx))) &&
+					  ((pi->y == pi->ystart && ((try0 << r) % (1 << rpy)))
+					  || !(pi->y % (1 << rpy)))) {
+						prchind = JPC_FLOORDIVPOW2(JPC_CEILDIV(pi->x, pi->picomp->hsamp
+						  << r), pi->pirlvl->prcwidthexpn) - JPC_FLOORDIVPOW2(trx0,
+						  pi->pirlvl->prcwidthexpn);
+						prcvind = JPC_FLOORDIVPOW2(JPC_CEILDIV(pi->y, pi->picomp->vsamp
+						  << r), pi->pirlvl->prcheightexpn) - JPC_FLOORDIVPOW2(try0,
+						  pi->pirlvl->prcheightexpn);
+						pi->prcno = prcvind * pi->pirlvl->numhprcs + prchind;
+
+						assert(pi->prcno < pi->pirlvl->numprcs);
+						for (pi->lyrno = 0; pi->lyrno <
+						  pi->numlyrs && pi->lyrno < pchg->lyrnoend; ++pi->lyrno) {
+							prclyrno = &pi->pirlvl->prclyrnos[pi->prcno];
+							if (pi->lyrno >= *prclyrno) {
+								++(*prclyrno);
+								return 0;
+							}
+skip:
+							;
+						}
+					}
+				}
+			}
+		}
+	}
+	return 1;
+}
+
+static int jpc_pi_nextpcrl(register jpc_pi_t *pi)
+{
+	int rlvlno;
+	jpc_pirlvl_t *pirlvl;
+	jpc_pchg_t *pchg;
+	int prchind;
+	int prcvind;
+	int *prclyrno;
+	int compno;
+	jpc_picomp_t *picomp;
+	int xstep;
+	int ystep;
+	uint_fast32_t trx0;
+	uint_fast32_t try0;
+	uint_fast32_t r;
+	uint_fast32_t rpx;
+	uint_fast32_t rpy;
+
+	pchg = pi->pchg;
+	if (!pi->prgvolfirst) {
+		goto skip;
+	} else {
+		pi->xstep = 0;
+		pi->ystep = 0;
+		for (compno = 0, picomp = pi->picomps; compno < pi->numcomps;
+		  ++compno, ++picomp) {
+			for (rlvlno = 0, pirlvl = picomp->pirlvls; rlvlno <
+			  picomp->numrlvls; ++rlvlno, ++pirlvl) {
+				xstep = picomp->hsamp * (1 <<
+				  (pirlvl->prcwidthexpn + picomp->numrlvls -
+				  rlvlno - 1));
+				ystep = picomp->vsamp * (1 <<
+				  (pirlvl->prcheightexpn + picomp->numrlvls -
+				  rlvlno - 1));
+				pi->xstep = (!pi->xstep) ? xstep :
+				  JAS_MIN(pi->xstep, xstep);
+				pi->ystep = (!pi->ystep) ? ystep :
+				  JAS_MIN(pi->ystep, ystep);
+			}
+		}
+		pi->prgvolfirst = 0;
+	}
+
+	for (pi->y = pi->ystart; pi->y < pi->yend; pi->y += pi->ystep -
+	  (pi->y % pi->ystep)) {
+		for (pi->x = pi->xstart; pi->x < pi->xend; pi->x += pi->xstep -
+		  (pi->x % pi->xstep)) {
+			for (pi->compno = pchg->compnostart, pi->picomp =
+			  &pi->picomps[pi->compno]; pi->compno < pi->numcomps
+			  && pi->compno < pchg->compnoend; ++pi->compno,
+			  ++pi->picomp) {
+				for (pi->rlvlno = pchg->rlvlnostart,
+				  pi->pirlvl = &pi->picomp->pirlvls[pi->rlvlno];
+				  pi->rlvlno < pi->picomp->numrlvls &&
+				  pi->rlvlno < pchg->rlvlnoend; ++pi->rlvlno,
+				  ++pi->pirlvl) {
+					if (pi->pirlvl->numprcs == 0) {
+						continue;
+					}
+					r = pi->picomp->numrlvls - 1 - pi->rlvlno;
+					trx0 = JPC_CEILDIV(pi->xstart, pi->picomp->hsamp << r);
+					try0 = JPC_CEILDIV(pi->ystart, pi->picomp->vsamp << r);
+					rpx = r + pi->pirlvl->prcwidthexpn;
+					rpy = r + pi->pirlvl->prcheightexpn;
+					if (((pi->x == pi->xstart && ((trx0 << r) % (1 << rpx))) ||
+					  !(pi->x % (pi->picomp->hsamp << rpx))) &&
+					  ((pi->y == pi->ystart && ((try0 << r) % (1 << rpy))) ||
+					  !(pi->y % (pi->picomp->vsamp << rpy)))) {
+						prchind = JPC_FLOORDIVPOW2(JPC_CEILDIV(pi->x, pi->picomp->hsamp
+						  << r), pi->pirlvl->prcwidthexpn) - JPC_FLOORDIVPOW2(trx0,
+						  pi->pirlvl->prcwidthexpn);
+						prcvind = JPC_FLOORDIVPOW2(JPC_CEILDIV(pi->y, pi->picomp->vsamp
+						  << r), pi->pirlvl->prcheightexpn) - JPC_FLOORDIVPOW2(try0,
+						  pi->pirlvl->prcheightexpn);
+						pi->prcno = prcvind * pi->pirlvl->numhprcs + prchind;
+						assert(pi->prcno < pi->pirlvl->numprcs);
+						for (pi->lyrno = 0; pi->lyrno < pi->numlyrs &&
+						  pi->lyrno < pchg->lyrnoend; ++pi->lyrno) {
+							prclyrno = &pi->pirlvl->prclyrnos[pi->prcno];
+							if (pi->lyrno >= *prclyrno) {
+								++(*prclyrno);
+								return 0;
+							}
+skip:
+							;
+						}
+					}
+				}
+			}
+		}
+	}
+	return 1;
+}
+
+static int jpc_pi_nextcprl(register jpc_pi_t *pi)
+{
+	int rlvlno;
+	jpc_pirlvl_t *pirlvl;
+	jpc_pchg_t *pchg;
+	int prchind;
+	int prcvind;
+	int *prclyrno;
+	uint_fast32_t trx0;
+	uint_fast32_t try0;
+	uint_fast32_t r;
+	uint_fast32_t rpx;
+	uint_fast32_t rpy;
+
+	pchg = pi->pchg;
+	if (!pi->prgvolfirst) {
+		goto skip;
+	} else {
+		pi->prgvolfirst = 0;
+	}
+
+	for (pi->compno = pchg->compnostart, pi->picomp =
+	  &pi->picomps[pi->compno]; pi->compno < pchg->compnoend; ++pi->compno,
+	  ++pi->picomp) {
+		pirlvl = pi->picomp->pirlvls;
+		pi->xstep = pi->picomp->hsamp * (1 << (pirlvl->prcwidthexpn +
+		  pi->picomp->numrlvls - 1));
+		pi->ystep = pi->picomp->vsamp * (1 << (pirlvl->prcheightexpn +
+		  pi->picomp->numrlvls - 1));
+		for (rlvlno = 1, pirlvl = &pi->picomp->pirlvls[1];
+		  rlvlno < pi->picomp->numrlvls; ++rlvlno, ++pirlvl) {
+			pi->xstep = JAS_MIN(pi->xstep, pi->picomp->hsamp * (1 <<
+			  (pirlvl->prcwidthexpn + pi->picomp->numrlvls -
+			  rlvlno - 1)));
+			pi->ystep = JAS_MIN(pi->ystep, pi->picomp->vsamp * (1 <<
+			  (pirlvl->prcheightexpn + pi->picomp->numrlvls -
+			  rlvlno - 1)));
+		}
+		for (pi->y = pi->ystart; pi->y < pi->yend;
+		  pi->y += pi->ystep - (pi->y % pi->ystep)) {
+			for (pi->x = pi->xstart; pi->x < pi->xend;
+			  pi->x += pi->xstep - (pi->x % pi->xstep)) {
+				for (pi->rlvlno = pchg->rlvlnostart,
+				  pi->pirlvl = &pi->picomp->pirlvls[pi->rlvlno];
+				  pi->rlvlno < pi->picomp->numrlvls && pi->rlvlno <
+				  pchg->rlvlnoend; ++pi->rlvlno, ++pi->pirlvl) {
+					if (pi->pirlvl->numprcs == 0) {
+						continue;
+					}
+					r = pi->picomp->numrlvls - 1 - pi->rlvlno;
+					trx0 = JPC_CEILDIV(pi->xstart, pi->picomp->hsamp << r);
+					try0 = JPC_CEILDIV(pi->ystart, pi->picomp->vsamp << r);
+					rpx = r + pi->pirlvl->prcwidthexpn;
+					rpy = r + pi->pirlvl->prcheightexpn;
+					if (((pi->x == pi->xstart && ((trx0 << r) % (1 << rpx))) ||
+					  !(pi->x % (pi->picomp->hsamp << rpx))) &&
+					  ((pi->y == pi->ystart && ((try0 << r) % (1 << rpy))) ||
+					  !(pi->y % (pi->picomp->vsamp << rpy)))) {
+						prchind = JPC_FLOORDIVPOW2(JPC_CEILDIV(pi->x, pi->picomp->hsamp
+						  << r), pi->pirlvl->prcwidthexpn) - JPC_FLOORDIVPOW2(trx0,
+						  pi->pirlvl->prcwidthexpn);
+						prcvind = JPC_FLOORDIVPOW2(JPC_CEILDIV(pi->y, pi->picomp->vsamp
+						  << r), pi->pirlvl->prcheightexpn) - JPC_FLOORDIVPOW2(try0,
+						  pi->pirlvl->prcheightexpn);
+						pi->prcno = prcvind *
+						  pi->pirlvl->numhprcs +
+						  prchind;
+						assert(pi->prcno <
+						  pi->pirlvl->numprcs);
+						for (pi->lyrno = 0; pi->lyrno <
+						  pi->numlyrs && pi->lyrno < pchg->lyrnoend; ++pi->lyrno) {
+							prclyrno = &pi->pirlvl->prclyrnos[pi->prcno];
+							if (pi->lyrno >= *prclyrno) {
+								++(*prclyrno);
+								return 0;
+							}
+skip:
+							;
+						}
+					}
+				}
+			}
+		}
+	}
+	return 1;
+}
+
+static void pirlvl_destroy(jpc_pirlvl_t *rlvl)
+{
+	if (rlvl->prclyrnos) {
+		jas_free(rlvl->prclyrnos);
+	}
+}
+
+static void jpc_picomp_destroy(jpc_picomp_t *picomp)
+{
+	int rlvlno;
+	jpc_pirlvl_t *pirlvl;
+	if (picomp->pirlvls) {
+		for (rlvlno = 0, pirlvl = picomp->pirlvls; rlvlno <
+		  picomp->numrlvls; ++rlvlno, ++pirlvl) {
+			pirlvl_destroy(pirlvl);
+		}
+		jas_free(picomp->pirlvls);
+	}
+}
+
+void jpc_pi_destroy(jpc_pi_t *pi)
+{
+	jpc_picomp_t *picomp;
+	int compno;
+	if (pi->picomps) {
+		for (compno = 0, picomp = pi->picomps; compno < pi->numcomps;
+		  ++compno, ++picomp) {
+			jpc_picomp_destroy(picomp);
+		}
+		jas_free(pi->picomps);
+	}
+	if (pi->pchglist) {
+		jpc_pchglist_destroy(pi->pchglist);
+	}
+	jas_free(pi);
+}
+
+jpc_pi_t *jpc_pi_create0(void)
+{
+	jpc_pi_t *pi;
+	if (!(pi = jas_malloc(sizeof(jpc_pi_t)))) {
+		return 0;
+	}
+	pi->picomps = 0;
+	pi->pchgno = 0;
+	if (!(pi->pchglist = jpc_pchglist_create())) {
+		jas_free(pi);
+		return 0;
+	}
+	return pi;
+}
+
+int jpc_pi_addpchg(jpc_pi_t *pi, jpc_pocpchg_t *pchg)
+{
+	return jpc_pchglist_insert(pi->pchglist, -1, pchg);
+}
+
+jpc_pchglist_t *jpc_pchglist_create(void)
+{
+	jpc_pchglist_t *pchglist;
+	if (!(pchglist = jas_malloc(sizeof(jpc_pchglist_t)))) {
+		return 0;
+	}
+	pchglist->numpchgs = 0;
+	pchglist->maxpchgs = 0;
+	pchglist->pchgs = 0;
+	return pchglist;
+}
+
+int jpc_pchglist_insert(jpc_pchglist_t *pchglist, int pchgno, jpc_pchg_t *pchg)
+{
+	int i;
+	int newmaxpchgs;
+	jpc_pchg_t **newpchgs;
+	if (pchgno < 0) {
+		pchgno = pchglist->numpchgs;
+	}
+	if (pchglist->numpchgs >= pchglist->maxpchgs) {
+		newmaxpchgs = pchglist->maxpchgs + 128;
+		if (!(newpchgs = jas_realloc(pchglist->pchgs, newmaxpchgs * sizeof(jpc_pchg_t *)))) {
+			return -1;
+		}
+		pchglist->maxpchgs = newmaxpchgs;
+		pchglist->pchgs = newpchgs;
+	}
+	for (i = pchglist->numpchgs; i > pchgno; --i) {
+		pchglist->pchgs[i] = pchglist->pchgs[i - 1];
+	}
+	pchglist->pchgs[pchgno] = pchg;
+	++pchglist->numpchgs;
+	return 0;
+}
+
+jpc_pchg_t *jpc_pchglist_remove(jpc_pchglist_t *pchglist, int pchgno)
+{
+	int i;
+	jpc_pchg_t *pchg;
+	assert(pchgno < pchglist->numpchgs);
+	pchg = pchglist->pchgs[pchgno];
+	for (i = pchgno + 1; i < pchglist->numpchgs; ++i) {
+		pchglist->pchgs[i - 1] = pchglist->pchgs[i];
+	}
+	--pchglist->numpchgs;
+	return pchg;
+}
+
+jpc_pchg_t *jpc_pchg_copy(jpc_pchg_t *pchg)
+{
+	jpc_pchg_t *newpchg;
+	if (!(newpchg = jas_malloc(sizeof(jpc_pchg_t)))) {
+		return 0;
+	}
+	*newpchg = *pchg;
+	return newpchg;
+}
+
+jpc_pchglist_t *jpc_pchglist_copy(jpc_pchglist_t *pchglist)
+{
+	jpc_pchglist_t *newpchglist;
+	jpc_pchg_t *newpchg;
+	int pchgno;
+	if (!(newpchglist = jpc_pchglist_create())) {
+		return 0;
+	}
+	for (pchgno = 0; pchgno < pchglist->numpchgs; ++pchgno) {
+		if (!(newpchg = jpc_pchg_copy(pchglist->pchgs[pchgno])) ||
+		  jpc_pchglist_insert(newpchglist, -1, newpchg)) {
+			jpc_pchglist_destroy(newpchglist);
+			return 0;
+		}
+	}
+	return newpchglist;
+}
+
+void jpc_pchglist_destroy(jpc_pchglist_t *pchglist)
+{
+	int pchgno;
+	if (pchglist->pchgs) {
+		for (pchgno = 0; pchgno < pchglist->numpchgs; ++pchgno) {
+			jpc_pchg_destroy(pchglist->pchgs[pchgno]);
+		}
+		jas_free(pchglist->pchgs);
+	}
+	jas_free(pchglist);
+}
+
+void jpc_pchg_destroy(jpc_pchg_t *pchg)
+{
+	jas_free(pchg);
+}
+
+jpc_pchg_t *jpc_pchglist_get(jpc_pchglist_t *pchglist, int pchgno)
+{
+	return pchglist->pchgs[pchgno];
+}
+
+int jpc_pchglist_numpchgs(jpc_pchglist_t *pchglist)
+{
+	return pchglist->numpchgs;
+}
+
+int jpc_pi_init(jpc_pi_t *pi)
+{
+	int compno;
+	int rlvlno;
+	int prcno;
+	jpc_picomp_t *picomp;
+	jpc_pirlvl_t *pirlvl;
+	int *prclyrno;
+
+	pi->prgvolfirst = 0;
+	pi->valid = 0;
+	pi->pktno = -1;
+	pi->pchgno = -1;
+	pi->pchg = 0;
+
+	for (compno = 0, picomp = pi->picomps; compno < pi->numcomps;
+	  ++compno, ++picomp) {
+		for (rlvlno = 0, pirlvl = picomp->pirlvls; rlvlno <
+		  picomp->numrlvls; ++rlvlno, ++pirlvl) {
+			for (prcno = 0, prclyrno = pirlvl->prclyrnos;
+			  prcno < pirlvl->numprcs; ++prcno, ++prclyrno) {
+				*prclyrno = 0;
+			}
+		}
+	}
+	return 0;
+}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_t2cod.h b/converter/other/jpeg2000/libjasper/jpc/jpc_t2cod.h
new file mode 100644
index 00000000..05f41b9e
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_t2cod.h
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Tier-2 Coding Library
+ *
+ * $Id$
+ */
+
+#ifndef JPC_T2COD_H
+#define	JPC_T2COD_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jpc_cs.h"
+
+/******************************************************************************\
+* Types.
+\******************************************************************************/
+
+/* Progression change list. */
+
+typedef struct {
+
+	/* The number of progression changes. */
+	int numpchgs;
+
+	/* The maximum number of progression changes that can be accomodated
+	  without growing the progression change array. */
+	int maxpchgs;
+
+	/* The progression changes. */
+	jpc_pchg_t **pchgs;
+
+} jpc_pchglist_t;
+
+/* Packet iterator per-resolution-level information. */
+
+typedef struct {
+
+	/* The number of precincts. */
+	int numprcs;
+
+	/* The last layer processed for each precinct. */
+	int *prclyrnos;
+
+	/* The precinct width exponent. */
+	int prcwidthexpn;
+
+	/* The precinct height exponent. */
+	int prcheightexpn;
+
+	/* The number of precincts spanning the resolution level in the horizontal
+	  direction. */
+	int numhprcs;
+
+} jpc_pirlvl_t;
+
+/* Packet iterator per-component information. */
+
+typedef struct {
+
+	/* The number of resolution levels. */
+	int numrlvls;
+
+	/* The per-resolution-level information. */
+	jpc_pirlvl_t *pirlvls;
+
+	/* The horizontal sampling period. */
+	int hsamp;
+
+	/* The vertical sampling period. */
+	int vsamp;
+
+} jpc_picomp_t;
+
+/* Packet iterator class. */
+
+typedef struct {
+
+	/* The number of layers. */
+	int numlyrs;
+
+	/* The number of resolution levels. */
+	int maxrlvls;
+
+	/* The number of components. */
+	int numcomps;
+
+	/* The per-component information. */
+	jpc_picomp_t *picomps;
+
+	/* The current component. */
+	jpc_picomp_t *picomp;
+
+	/* The current resolution level. */
+	jpc_pirlvl_t *pirlvl;
+
+	/* The number of the current component. */
+	int compno;
+
+	/* The number of the current resolution level. */
+	int rlvlno;
+
+	/* The number of the current precinct. */
+	int prcno;
+
+	/* The number of the current layer. */
+	int lyrno;
+
+	/* The x-coordinate of the current position. */
+	int x;
+
+	/* The y-coordinate of the current position. */
+	int y;
+
+	/* The horizontal step size. */
+	int xstep;
+
+	/* The vertical step size. */
+	int ystep;
+
+	/* The x-coordinate of the top-left corner of the tile on the reference
+	  grid. */
+	int xstart;
+
+	/* The y-coordinate of the top-left corner of the tile on the reference
+	  grid. */
+	int ystart;
+
+	/* The x-coordinate of the bottom-right corner of the tile on the
+	  reference grid (plus one). */
+	int xend;
+
+	/* The y-coordinate of the bottom-right corner of the tile on the
+	  reference grid (plus one). */
+	int yend;
+
+	/* The current progression change. */
+	jpc_pchg_t *pchg;
+
+	/* The progression change list. */
+	jpc_pchglist_t *pchglist;
+
+	/* The progression to use in the absense of explicit specification. */
+	jpc_pchg_t defaultpchg;
+
+	/* The current progression change number. */
+	int pchgno;
+
+	/* Is this the first time in the current progression volume? */
+	bool prgvolfirst;
+
+	/* Is the current iterator value valid? */
+	bool valid;
+
+	/* The current packet number. */
+	int pktno;
+
+} jpc_pi_t;
+
+/******************************************************************************\
+* Functions/macros for packet iterators.
+\******************************************************************************/
+
+/* Create a packet iterator. */
+jpc_pi_t *jpc_pi_create0(void);
+
+/* Destroy a packet iterator. */
+void jpc_pi_destroy(jpc_pi_t *pi);
+
+/* Add a progression change to a packet iterator. */
+int jpc_pi_addpchg(jpc_pi_t *pi, jpc_pocpchg_t *pchg);
+
+/* Prepare a packet iterator for iteration. */
+int jpc_pi_init(jpc_pi_t *pi);
+
+/* Set the iterator to the first packet. */
+int jpc_pi_begin(jpc_pi_t *pi);
+
+/* Proceed to the next packet in sequence. */
+int jpc_pi_next(jpc_pi_t *pi);
+
+/* Get the index of the current packet. */
+#define	jpc_pi_getind(pi)	((pi)->pktno)
+
+/* Get the component number of the current packet. */
+#define jpc_pi_cmptno(pi)	(assert(pi->valid), (pi)->compno)
+
+/* Get the resolution level of the current packet. */
+#define jpc_pi_rlvlno(pi)	(assert(pi->valid), (pi)->rlvlno)
+
+/* Get the layer number of the current packet. */
+#define jpc_pi_lyrno(pi)	(assert(pi->valid), (pi)->lyrno)
+
+/* Get the precinct number of the current packet. */
+#define jpc_pi_prcno(pi)	(assert(pi->valid), (pi)->prcno)
+
+/* Get the progression order for the current packet. */
+#define jpc_pi_prg(pi)	(assert(pi->valid), (pi)->pchg->prgord)
+
+/******************************************************************************\
+* Functions/macros for progression change lists.
+\******************************************************************************/
+
+/* Create a progression change list. */
+jpc_pchglist_t *jpc_pchglist_create(void);
+
+/* Destroy a progression change list. */
+void jpc_pchglist_destroy(jpc_pchglist_t *pchglist);
+
+/* Insert a new element into a progression change list. */
+int jpc_pchglist_insert(jpc_pchglist_t *pchglist, int pchgno, jpc_pchg_t *pchg);
+
+/* Remove an element from a progression change list. */
+jpc_pchg_t *jpc_pchglist_remove(jpc_pchglist_t *pchglist, int pchgno);
+
+/* Get an element from a progression change list. */
+jpc_pchg_t *jpc_pchglist_get(jpc_pchglist_t *pchglist, int pchgno);
+
+/* Copy a progression change list. */
+jpc_pchglist_t *jpc_pchglist_copy(jpc_pchglist_t *pchglist);
+
+/* Get the number of elements in a progression change list. */
+int jpc_pchglist_numpchgs(jpc_pchglist_t *pchglist);
+
+/******************************************************************************\
+* Functions/macros for progression changes.
+\******************************************************************************/
+
+/* Destroy a progression change. */
+void jpc_pchg_destroy(jpc_pchg_t *pchg);
+
+/* Copy a progression change. */
+jpc_pchg_t *jpc_pchg_copy(jpc_pchg_t *pchg);
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_t2dec.c b/converter/other/jpeg2000/libjasper/jpc/jpc_t2dec.c
new file mode 100644
index 00000000..9aaaa55c
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_t2dec.c
@@ -0,0 +1,626 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Tier 2 Decoder
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "jasper/jas_types.h"
+#include "jasper/jas_fix.h"
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_math.h"
+#include "jasper/jas_stream.h"
+#include "jasper/jas_debug.h"
+
+#include "jpc_bs.h"
+#include "jpc_dec.h"
+#include "jpc_cs.h"
+#include "jpc_mqdec.h"
+#include "jpc_t2dec.h"
+#include "jpc_t1cod.h"
+#include "jpc_math.h"
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+long jpc_dec_lookahead(jas_stream_t *in);
+static int jpc_getcommacode(jpc_bitstream_t *in);
+static int jpc_getnumnewpasses(jpc_bitstream_t *in);
+static int jpc_dec_decodepkt(jpc_dec_t *dec, jas_stream_t *pkthdrstream, jas_stream_t *in, int compno, int lvlno,
+  int prcno, int lyrno);
+
+/******************************************************************************\
+* Code.
+\******************************************************************************/
+
+static int jpc_getcommacode(jpc_bitstream_t *in)
+{
+	int n;
+	int v;
+
+	n = 0;
+	for (;;) {
+		if ((v = jpc_bitstream_getbit(in)) < 0) {
+			return -1;
+		}
+		if (jpc_bitstream_eof(in)) {
+			return -1;
+		}
+		if (!v) {
+			break;
+		}
+		++n;
+	}
+
+	return n;
+}
+
+static int jpc_getnumnewpasses(jpc_bitstream_t *in)
+{
+	int n;
+
+	if ((n = jpc_bitstream_getbit(in)) > 0) {
+		if ((n = jpc_bitstream_getbit(in)) > 0) {
+			if ((n = jpc_bitstream_getbits(in, 2)) == 3) {
+				if ((n = jpc_bitstream_getbits(in, 5)) == 31) {
+					if ((n = jpc_bitstream_getbits(in, 7)) >= 0) {
+						n += 36 + 1;
+					}
+				} else if (n >= 0) {
+					n += 5 + 1;
+				}
+			} else if (n >= 0) {
+				n += 2 + 1;
+			}
+		} else if (!n) {
+			n += 2;
+		}
+	} else if (!n) {
+		++n;
+	}
+
+	return n;
+}
+
+static int jpc_dec_decodepkt(jpc_dec_t *dec, jas_stream_t *pkthdrstream, jas_stream_t *in, int compno, int rlvlno,
+  int prcno, int lyrno)
+{
+	jpc_bitstream_t *inb;
+	jpc_dec_tcomp_t *tcomp;
+	jpc_dec_rlvl_t *rlvl;
+	jpc_dec_band_t *band;
+	jpc_dec_cblk_t *cblk;
+	int n;
+	int m;
+	int i;
+	jpc_tagtreenode_t *leaf;
+	int included;
+	int ret;
+	int numnewpasses;
+	jpc_dec_seg_t *seg;
+	int len;
+	int present;
+	int savenumnewpasses;
+	int mycounter;
+	jpc_ms_t *ms;
+	jpc_dec_tile_t *tile;
+	jpc_dec_ccp_t *ccp;
+	jpc_dec_cp_t *cp;
+	int bandno;
+	jpc_dec_prc_t *prc;
+	int usedcblkcnt;
+	int cblkno;
+	uint_fast32_t bodylen;
+	bool discard;
+	int passno;
+	int maxpasses;
+	int hdrlen;
+	int hdroffstart;
+	int hdroffend;
+
+	discard = (lyrno >= dec->maxlyrs);
+
+	tile = dec->curtile;
+	cp = tile->cp;
+	ccp = &cp->ccps[compno];
+
+	/*
+	 * Decode the packet header.
+	 */
+
+	/* Decode the SOP marker segment if present. */
+	if (cp->csty & JPC_COD_SOP) {
+		if (jpc_dec_lookahead(in) == JPC_MS_SOP) {
+			if (!(ms = jpc_getms(in, dec->cstate))) {
+				return -1;
+			}
+			if (jpc_ms_gettype(ms) != JPC_MS_SOP) {
+				jpc_ms_destroy(ms);
+				fprintf(stderr, "missing SOP marker segment\n");
+				return -1;
+			}
+			jpc_ms_destroy(ms);
+		}
+	}
+
+hdroffstart = jas_stream_getrwcount(pkthdrstream);
+
+	if (!(inb = jpc_bitstream_sopen(pkthdrstream, "r"))) {
+		return -1;
+	}
+
+	if ((present = jpc_bitstream_getbit(inb)) < 0) {
+		return 1;
+	}
+	JAS_DBGLOG(10, ("\n", present));
+	JAS_DBGLOG(10, ("present=%d ", present));
+
+	/* Is the packet non-empty? */
+	if (present) {
+		/* The packet is non-empty. */
+		tcomp = &tile->tcomps[compno];
+		rlvl = &tcomp->rlvls[rlvlno];
+		bodylen = 0;
+		for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
+		  ++bandno, ++band) {
+			if (!band->data) {
+				continue;
+			}
+			prc = &band->prcs[prcno];
+			if (!prc->cblks) {
+				continue;
+			}
+			usedcblkcnt = 0;
+			for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks;
+			  ++cblkno, ++cblk) {
+				++usedcblkcnt;
+				if (!cblk->numpasses) {
+					leaf = jpc_tagtree_getleaf(prc->incltagtree, usedcblkcnt - 1);
+					if ((included = jpc_tagtree_decode(prc->incltagtree, leaf, lyrno + 1, inb)) < 0) {
+						return -1;
+					}
+				} else {
+					if ((included = jpc_bitstream_getbit(inb)) < 0) {
+						return -1;
+					}
+				}
+				JAS_DBGLOG(10, ("\n"));
+				JAS_DBGLOG(10, ("included=%d ", included));
+				if (!included) {
+					continue;
+				}
+				if (!cblk->numpasses) {
+					i = 1;
+					leaf = jpc_tagtree_getleaf(prc->numimsbstagtree, usedcblkcnt - 1);
+					for (;;) {
+						if ((ret = jpc_tagtree_decode(prc->numimsbstagtree, leaf, i, inb)) < 0) {
+							return -1;
+						}
+						if (ret) {
+							break;
+						}
+						++i;
+					}
+					cblk->numimsbs = i - 1;
+					cblk->firstpassno = cblk->numimsbs * 3;
+				}
+				if ((numnewpasses = jpc_getnumnewpasses(inb)) < 0) {
+					return -1;
+				}
+				JAS_DBGLOG(10, ("numnewpasses=%d ", numnewpasses));
+				seg = cblk->curseg;
+				savenumnewpasses = numnewpasses;
+				mycounter = 0;
+				if (numnewpasses > 0) {
+					if ((m = jpc_getcommacode(inb)) < 0) {
+						return -1;
+					}
+					cblk->numlenbits += m;
+					JAS_DBGLOG(10, ("increment=%d ", m));
+					while (numnewpasses > 0) {
+						passno = cblk->firstpassno + cblk->numpasses + mycounter;
+	/* XXX - the maxpasses is not set precisely but this doesn't matter... */
+						maxpasses = JPC_SEGPASSCNT(passno, cblk->firstpassno, 10000, (ccp->cblkctx & JPC_COX_LAZY) != 0, (ccp->cblkctx & JPC_COX_TERMALL) != 0);
+						if (!discard && !seg) {
+							if (!(seg = jpc_seg_alloc())) {
+								return -1;
+							}
+							jpc_seglist_insert(&cblk->segs, cblk->segs.tail, seg);
+							if (!cblk->curseg) {
+								cblk->curseg = seg;
+							}
+							seg->passno = passno;
+							seg->type = JPC_SEGTYPE(seg->passno, cblk->firstpassno, (ccp->cblkctx & JPC_COX_LAZY) != 0);
+							seg->maxpasses = maxpasses;
+						}
+						n = JAS_MIN(numnewpasses, maxpasses);
+						mycounter += n;
+						numnewpasses -= n;
+						if ((len = jpc_bitstream_getbits(inb, cblk->numlenbits + jpc_floorlog2(n))) < 0) {
+							return -1;
+						}
+						JAS_DBGLOG(10, ("len=%d ", len));
+						if (!discard) {
+							seg->lyrno = lyrno;
+							seg->numpasses += n;
+							seg->cnt = len;
+							seg = seg->next;
+						}
+						bodylen += len;
+					}
+				}
+				cblk->numpasses += savenumnewpasses;
+			}
+		}
+
+		jpc_bitstream_inalign(inb, 0, 0);
+
+	} else {
+		if (jpc_bitstream_inalign(inb, 0x7f, 0)) {
+			fprintf(stderr, "alignment failed\n");
+			return -1;
+		}
+	}
+	jpc_bitstream_close(inb);
+
+	hdroffend = jas_stream_getrwcount(pkthdrstream);
+	hdrlen = hdroffend - hdroffstart;
+	if (jas_getdbglevel() >= 5) {
+		fprintf(stderr, "hdrlen=%lu bodylen=%lu \n", (unsigned long) hdrlen,
+		  (unsigned long) bodylen);
+	}
+
+	if (cp->csty & JPC_COD_EPH) {
+		if (jpc_dec_lookahead(pkthdrstream) == JPC_MS_EPH) {
+			if (!(ms = jpc_getms(pkthdrstream, dec->cstate))) {
+				fprintf(stderr, "cannot get (EPH) marker segment\n");
+				return -1;
+			}
+			if (jpc_ms_gettype(ms) != JPC_MS_EPH) {
+				jpc_ms_destroy(ms);
+				fprintf(stderr, "missing EPH marker segment\n");
+				return -1;
+			}
+			jpc_ms_destroy(ms);
+		}
+	}
+
+	/* decode the packet body. */
+
+	if (jas_getdbglevel() >= 1) {
+		fprintf(stderr, "packet body offset=%06ld\n", (long) jas_stream_getrwcount(in));
+	}
+
+	if (!discard) {
+		tcomp = &tile->tcomps[compno];
+		rlvl = &tcomp->rlvls[rlvlno];
+		for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
+		  ++bandno, ++band) {
+			if (!band->data) {
+				continue;
+			}
+			prc = &band->prcs[prcno];
+			if (!prc->cblks) {
+				continue;
+			}
+			for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks;
+			  ++cblkno, ++cblk) {
+				seg = cblk->curseg;
+				while (seg) {
+					if (!seg->stream) {
+						if (!(seg->stream = jas_stream_memopen(0, 0))) {
+							return -1;
+						}
+					}
+#if 0
+fprintf(stderr, "lyrno=%02d, compno=%02d, lvlno=%02d, prcno=%02d, bandno=%02d, cblkno=%02d, passno=%02d numpasses=%02d cnt=%d numbps=%d, numimsbs=%d\n", lyrno, compno, rlvlno, prcno, band - rlvl->bands, cblk - prc->cblks, seg->passno, seg->numpasses, seg->cnt, band->numbps, cblk->numimsbs);
+#endif
+					if (seg->cnt > 0) {
+						if (jpc_getdata(in, seg->stream, seg->cnt) < 0) {
+							return -1;
+						}
+						seg->cnt = 0;
+					}
+					if (seg->numpasses >= seg->maxpasses) {
+						cblk->curseg = seg->next;
+					}
+					seg = seg->next;
+				}
+			}
+		}
+	} else {
+		if (jas_stream_gobble(in, bodylen) != bodylen) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/********************************************************************************************/
+/********************************************************************************************/
+
+int jpc_dec_decodepkts(jpc_dec_t *dec, jas_stream_t *pkthdrstream, jas_stream_t *in)
+{
+	jpc_dec_tile_t *tile;
+	jpc_pi_t *pi;
+	int ret;
+
+	tile = dec->curtile;
+	pi = tile->pi;
+	for (;;) {
+if (!tile->pkthdrstream || jas_stream_peekc(tile->pkthdrstream) == EOF) {
+		switch (jpc_dec_lookahead(in)) {
+		case JPC_MS_EOC:
+		case JPC_MS_SOT:
+			return 0;
+			break;
+		case JPC_MS_SOP:
+		case JPC_MS_EPH:
+		case 0:
+			break;
+		default:
+			return -1;
+			break;
+		}
+}
+		if ((ret = jpc_pi_next(pi))) {
+			return ret;
+		}
+if (dec->maxpkts >= 0 && dec->numpkts >= dec->maxpkts) {
+	fprintf(stderr, "warning: stopping decode prematurely as requested\n");
+	return 0;
+}
+		if (jas_getdbglevel() >= 1) {
+			fprintf(stderr, "packet offset=%08ld prg=%d cmptno=%02d "
+			  "rlvlno=%02d prcno=%03d lyrno=%02d\n", (long)
+			  jas_stream_getrwcount(in), jpc_pi_prg(pi), jpc_pi_cmptno(pi),
+			  jpc_pi_rlvlno(pi), jpc_pi_prcno(pi), jpc_pi_lyrno(pi));
+		}
+		if (jpc_dec_decodepkt(dec, pkthdrstream, in, jpc_pi_cmptno(pi), jpc_pi_rlvlno(pi),
+		  jpc_pi_prcno(pi), jpc_pi_lyrno(pi))) {
+			return -1;
+		}
+++dec->numpkts;
+	}
+
+	return 0;
+}
+
+jpc_pi_t *jpc_dec_pi_create(jpc_dec_t *dec, jpc_dec_tile_t *tile)
+{
+	jpc_pi_t *pi;
+	int compno;
+	jpc_picomp_t *picomp;
+	jpc_pirlvl_t *pirlvl;
+	jpc_dec_tcomp_t *tcomp;
+	int rlvlno;
+	jpc_dec_rlvl_t *rlvl;
+	int prcno;
+	int *prclyrno;
+	jpc_dec_cmpt_t *cmpt;
+
+	if (!(pi = jpc_pi_create0())) {
+		return 0;
+	}
+	pi->numcomps = dec->numcomps;
+	if (!(pi->picomps = jas_malloc(pi->numcomps * sizeof(jpc_picomp_t)))) {
+		jpc_pi_destroy(pi);
+		return 0;
+	}
+	for (compno = 0, picomp = pi->picomps; compno < pi->numcomps; ++compno,
+	  ++picomp) {
+		picomp->pirlvls = 0;
+	}
+
+	for (compno = 0, tcomp = tile->tcomps, picomp = pi->picomps;
+	  compno < pi->numcomps; ++compno, ++tcomp, ++picomp) {
+		picomp->numrlvls = tcomp->numrlvls;
+		if (!(picomp->pirlvls = jas_malloc(picomp->numrlvls *
+		  sizeof(jpc_pirlvl_t)))) {
+			jpc_pi_destroy(pi);
+			return 0;
+		}
+		for (rlvlno = 0, pirlvl = picomp->pirlvls; rlvlno <
+		  picomp->numrlvls; ++rlvlno, ++pirlvl) {
+			pirlvl->prclyrnos = 0;
+		}
+		for (rlvlno = 0, pirlvl = picomp->pirlvls, rlvl = tcomp->rlvls;
+		  rlvlno < picomp->numrlvls; ++rlvlno, ++pirlvl, ++rlvl) {
+/* XXX sizeof(long) should be sizeof different type */
+			pirlvl->numprcs = rlvl->numprcs;
+			if (!(pirlvl->prclyrnos = jas_malloc(pirlvl->numprcs *
+			  sizeof(long)))) {
+				jpc_pi_destroy(pi);
+				return 0;
+			}
+		}
+	}
+
+	pi->maxrlvls = 0;
+	for (compno = 0, tcomp = tile->tcomps, picomp = pi->picomps, cmpt =
+	  dec->cmpts; compno < pi->numcomps; ++compno, ++tcomp, ++picomp,
+	  ++cmpt) {
+		picomp->hsamp = cmpt->hstep;
+		picomp->vsamp = cmpt->vstep;
+		for (rlvlno = 0, pirlvl = picomp->pirlvls, rlvl = tcomp->rlvls;
+		  rlvlno < picomp->numrlvls; ++rlvlno, ++pirlvl, ++rlvl) {
+			pirlvl->prcwidthexpn = rlvl->prcwidthexpn;
+			pirlvl->prcheightexpn = rlvl->prcheightexpn;
+			for (prcno = 0, prclyrno = pirlvl->prclyrnos;
+			  prcno < pirlvl->numprcs; ++prcno, ++prclyrno) {
+				*prclyrno = 0;
+			}
+			pirlvl->numhprcs = rlvl->numhprcs;
+		}
+		if (pi->maxrlvls < tcomp->numrlvls) {
+			pi->maxrlvls = tcomp->numrlvls;
+		}
+	}
+
+	pi->numlyrs = tile->cp->numlyrs;
+	pi->xstart = tile->xstart;
+	pi->ystart = tile->ystart;
+	pi->xend = tile->xend;
+	pi->yend = tile->yend;
+
+	pi->picomp = 0;
+	pi->pirlvl = 0;
+	pi->x = 0;
+	pi->y = 0;
+	pi->compno = 0;
+	pi->rlvlno = 0;
+	pi->prcno = 0;
+	pi->lyrno = 0;
+	pi->xstep = 0;
+	pi->ystep = 0;
+
+	pi->pchgno = -1;
+
+	pi->defaultpchg.prgord = tile->cp->prgord;
+	pi->defaultpchg.compnostart = 0;
+	pi->defaultpchg.compnoend = pi->numcomps;
+	pi->defaultpchg.rlvlnostart = 0;
+	pi->defaultpchg.rlvlnoend = pi->maxrlvls;
+	pi->defaultpchg.lyrnoend = pi->numlyrs;
+	pi->pchg = 0;
+
+	pi->valid = 0;
+
+	return pi;
+}
+
+long jpc_dec_lookahead(jas_stream_t *in)
+{
+	uint_fast16_t x;
+	if (jpc_getuint16(in, &x)) {
+		return -1;
+	}
+	if (jas_stream_ungetc(in, x & 0xff) == EOF ||
+	  jas_stream_ungetc(in, x >> 8) == EOF) {
+		return -1;
+	}
+	if (x >= JPC_MS_INMIN && x <= JPC_MS_INMAX) {
+		return x;
+	}
+	return 0;
+}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_t2dec.h b/converter/other/jpeg2000/libjasper/jpc/jpc_t2dec.h
new file mode 100644
index 00000000..923d7c1a
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_t2dec.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Tier 2 Decoder
+ *
+ * $Id$
+ */
+
+#ifndef JPC_T2DEC_H
+#define JPC_T2DEC_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jasper/jas_fix.h"
+#include "jasper/jas_stream.h"
+
+#include "jpc_bs.h"
+#include "jpc_dec.h"
+#include "jpc_mqdec.h"
+
+/******************************************************************************\
+* Functions.
+\******************************************************************************/
+
+/* Decode the packets for a tile-part. */
+int jpc_dec_decodepkts(jpc_dec_t *dec, jas_stream_t *pkthdrstream,
+  jas_stream_t *in);
+
+/* Create a packet iterator for the decoder. */
+jpc_pi_t *jpc_dec_pi_create(jpc_dec_t *dec, jpc_dec_tile_t *tile);
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_t2enc.c b/converter/other/jpeg2000/libjasper/jpc/jpc_t2enc.c
new file mode 100644
index 00000000..1444dc7b
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_t2enc.c
@@ -0,0 +1,704 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Tier 2 Encoder
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "jasper/jas_fix.h"
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_math.h"
+#include "jasper/jas_debug.h"
+
+#include "jpc_flt.h"
+#include "jpc_t2enc.h"
+#include "jpc_t2cod.h"
+#include "jpc_tagtree.h"
+#include "jpc_enc.h"
+#include "jpc_math.h"
+
+/******************************************************************************\
+* Code.
+\******************************************************************************/
+
+static int jpc_putcommacode(jpc_bitstream_t *out, int n)
+{
+	assert(n >= 0);
+
+	while (--n >= 0) {
+		if (jpc_bitstream_putbit(out, 1) == EOF) {
+			return -1;
+		}
+	}
+	if (jpc_bitstream_putbit(out, 0) == EOF) {
+		return -1;
+	}
+	return 0;
+}
+
+static int jpc_putnumnewpasses(jpc_bitstream_t *out, int n)
+{
+	int ret;
+
+	if (n <= 0) {
+		return -1;
+	} else if (n == 1) {
+		ret = jpc_bitstream_putbit(out, 0);
+	} else if (n == 2) {
+		ret = jpc_bitstream_putbits(out, 2, 2);
+	} else if (n <= 5) {
+		ret = jpc_bitstream_putbits(out, 4, 0xc | (n - 3));
+	} else if (n <= 36) {
+		ret = jpc_bitstream_putbits(out, 9, 0x1e0 | (n - 6));
+	} else if (n <= 164) {
+		ret = jpc_bitstream_putbits(out, 16, 0xff80 | (n - 37));
+	} else {
+		/* The standard has no provision for encoding a larger value.
+		In practice, however, it is highly unlikely that this
+		limitation will ever be encountered. */
+		return -1;
+	}
+
+	return (ret != EOF) ? 0 : (-1);
+}
+
+int jpc_enc_encpkts(jpc_enc_t *enc, jas_stream_t *out)
+{
+	jpc_enc_tile_t *tile;
+	jpc_pi_t *pi;
+
+	tile = enc->curtile;
+
+	jpc_init_t2state(enc, 0);
+	pi = tile->pi;
+	jpc_pi_init(pi);
+
+	if (!jpc_pi_next(pi)) {
+		for (;;) {
+			if (jpc_enc_encpkt(enc, out, jpc_pi_cmptno(pi), jpc_pi_rlvlno(pi),
+			  jpc_pi_prcno(pi), jpc_pi_lyrno(pi))) {
+				return -1;
+			}
+			if (jpc_pi_next(pi)) {
+				break;
+			}
+		}
+	}
+	
+	return 0;
+}
+
+int jpc_enc_encpkt(jpc_enc_t *enc, jas_stream_t *out, int compno, int lvlno, int prcno, int lyrno)
+{
+	jpc_enc_tcmpt_t *comp;
+	jpc_enc_rlvl_t *lvl;
+	jpc_enc_band_t *band;
+	jpc_enc_band_t *endbands;
+	jpc_enc_cblk_t *cblk;
+	jpc_enc_cblk_t *endcblks;
+	jpc_bitstream_t *outb;
+	jpc_enc_pass_t *pass;
+	jpc_enc_pass_t *startpass;
+	jpc_enc_pass_t *lastpass;
+	jpc_enc_pass_t *endpass;
+	jpc_enc_pass_t *endpasses;
+	int i;
+	int included;
+	int ret;
+	jpc_tagtreenode_t *leaf;
+	int n;
+	int t1;
+	int t2;
+	int adjust;
+	int maxadjust;
+	int datalen;
+	int numnewpasses;
+	int passcount;
+	jpc_enc_tile_t *tile;
+	jpc_enc_prc_t *prc;
+	jpc_enc_cp_t *cp;
+	jpc_ms_t *ms;
+
+	tile = enc->curtile;
+	cp = enc->cp;
+
+	if (cp->tcp.csty & JPC_COD_SOP) {
+		if (!(ms = jpc_ms_create(JPC_MS_SOP))) {
+			return -1;
+		}
+		ms->parms.sop.seqno = jpc_pi_getind(tile->pi);
+		if (jpc_putms(out, enc->cstate, ms)) {
+			return -1;
+		}
+		jpc_ms_destroy(ms);
+	}
+
+	outb = jpc_bitstream_sopen(out, "w+");
+	assert(outb);
+
+	if (jpc_bitstream_putbit(outb, 1) == EOF) {
+		return -1;
+	}
+	JAS_DBGLOG(10, ("\n"));
+	JAS_DBGLOG(10, ("present. "));
+
+	comp = &tile->tcmpts[compno];
+	lvl = &comp->rlvls[lvlno];
+	endbands = &lvl->bands[lvl->numbands];
+	for (band = lvl->bands; band != endbands; ++band) {
+		if (!band->data) {
+			continue;
+		}
+		prc = &band->prcs[prcno];
+		if (!prc->cblks) {
+			continue;
+		}
+
+		endcblks = &prc->cblks[prc->numcblks];
+		for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
+			if (!lyrno) {
+				leaf = jpc_tagtree_getleaf(prc->nlibtree, cblk - prc->cblks);
+				jpc_tagtree_setvalue(prc->nlibtree, leaf, cblk->numimsbs);
+			}
+			pass = cblk->curpass;
+			included = (pass && pass->lyrno == lyrno);
+			if (included && (!cblk->numencpasses)) {
+				assert(pass->lyrno == lyrno);
+				leaf = jpc_tagtree_getleaf(prc->incltree,
+				  cblk - prc->cblks);
+				jpc_tagtree_setvalue(prc->incltree, leaf, pass->lyrno);
+			}
+		}
+
+		endcblks = &prc->cblks[prc->numcblks];
+		for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
+			pass = cblk->curpass;
+			included = (pass && pass->lyrno == lyrno);
+			if (!cblk->numencpasses) {
+				leaf = jpc_tagtree_getleaf(prc->incltree,
+				  cblk - prc->cblks);
+				if (jpc_tagtree_encode(prc->incltree, leaf, lyrno
+				  + 1, outb) < 0) {
+					return -1;
+				}
+			} else {
+				if (jpc_bitstream_putbit(outb, included) == EOF) {
+					return -1;
+				}
+			}
+			JAS_DBGLOG(10, ("included=%d ", included));
+			if (!included) {
+				continue;
+			}
+			if (!cblk->numencpasses) {
+				i = 1;
+				leaf = jpc_tagtree_getleaf(prc->nlibtree, cblk - prc->cblks);
+				for (;;) {
+					if ((ret = jpc_tagtree_encode(prc->nlibtree, leaf, i, outb)) < 0) {
+						return -1;
+					}
+					if (ret) {
+						break;
+					}
+					++i;
+				}
+				assert(leaf->known_ && i == leaf->value_ + 1);
+			}
+
+			endpasses = &cblk->passes[cblk->numpasses];
+			startpass = pass;
+			endpass = startpass;
+			while (endpass != endpasses && endpass->lyrno == lyrno){
+				++endpass;
+			}
+			numnewpasses = endpass - startpass;
+			if (jpc_putnumnewpasses(outb, numnewpasses)) {
+				return -1;
+			}
+			JAS_DBGLOG(10, ("numnewpasses=%d ", numnewpasses));
+
+			lastpass = endpass - 1;
+			n = startpass->start;
+			passcount = 1;
+			maxadjust = 0;
+			for (pass = startpass; pass != endpass; ++pass) {
+				if (pass->term || pass == lastpass) {
+					datalen = pass->end - n;
+					t1 = jpc_firstone(datalen) + 1;
+					t2 = cblk->numlenbits + jpc_floorlog2(passcount);
+					adjust = JAS_MAX(t1 - t2, 0);
+					maxadjust = JAS_MAX(adjust, maxadjust);
+					n += datalen;
+					passcount = 1;
+				} else {
+					++passcount;
+				}
+			}
+			if (jpc_putcommacode(outb, maxadjust)) {
+				return -1;
+			}
+			cblk->numlenbits += maxadjust;
+
+			lastpass = endpass - 1;
+			n = startpass->start;
+			passcount = 1;
+			for (pass = startpass; pass != endpass; ++pass) {
+				if (pass->term || pass == lastpass) {
+					datalen = pass->end - n;
+assert(jpc_firstone(datalen) < cblk->numlenbits + jpc_floorlog2(passcount));
+					if (jpc_bitstream_putbits(outb, cblk->numlenbits + jpc_floorlog2(passcount), datalen) == EOF) {
+						return -1;
+					}
+					n += datalen;
+					passcount = 1;
+				} else {
+					++passcount;
+				}
+			}
+		}
+	}
+
+	jpc_bitstream_outalign(outb, 0);
+	jpc_bitstream_close(outb);
+
+	if (cp->tcp.csty & JPC_COD_EPH) {
+		if (!(ms = jpc_ms_create(JPC_MS_EPH))) {
+			return -1;
+		}
+		jpc_putms(out, enc->cstate, ms);
+		jpc_ms_destroy(ms);
+	}
+
+	comp = &tile->tcmpts[compno];
+	lvl = &comp->rlvls[lvlno];
+	endbands = &lvl->bands[lvl->numbands];
+	for (band = lvl->bands; band != endbands; ++band) {
+		if (!band->data) {
+			continue;
+		}
+		prc = &band->prcs[prcno];
+		if (!prc->cblks) {
+			continue;
+		}
+		endcblks = &prc->cblks[prc->numcblks];
+		for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
+			pass = cblk->curpass;
+
+			if (!pass) {
+				continue;
+			}
+			if (pass->lyrno != lyrno) {
+				assert(pass->lyrno < 0 || pass->lyrno > lyrno);
+				continue;
+			}
+
+			endpasses = &cblk->passes[cblk->numpasses];
+			startpass = pass;
+			endpass = startpass;
+			while (endpass != endpasses && endpass->lyrno == lyrno){
+				++endpass;
+			}
+			lastpass = endpass - 1;
+			numnewpasses = endpass - startpass;
+
+			jas_stream_seek(cblk->stream, startpass->start, SEEK_SET);
+			assert(jas_stream_tell(cblk->stream) == startpass->start);
+			if (jas_stream_copy(out, cblk->stream, lastpass->end - startpass->start)) {
+				return -1;
+			}
+			cblk->curpass = (endpass != endpasses) ? endpass : 0;
+			cblk->numencpasses += numnewpasses;
+
+		}
+	}
+
+	return 0;
+}
+
+void jpc_save_t2state(jpc_enc_t *enc)
+{
+/* stream pos in embedded T1 stream may be wrong since not saved/restored! */
+
+	jpc_enc_tcmpt_t *comp;
+	jpc_enc_tcmpt_t *endcomps;
+	jpc_enc_rlvl_t *lvl;
+	jpc_enc_rlvl_t *endlvls;
+	jpc_enc_band_t *band;
+	jpc_enc_band_t *endbands;
+	jpc_enc_cblk_t *cblk;
+	jpc_enc_cblk_t *endcblks;
+	jpc_enc_tile_t *tile;
+	uint_fast32_t prcno;
+	jpc_enc_prc_t *prc;
+
+	tile = enc->curtile;
+
+	endcomps = &tile->tcmpts[tile->numtcmpts];
+	for (comp = tile->tcmpts; comp != endcomps; ++comp) {
+		endlvls = &comp->rlvls[comp->numrlvls];
+		for (lvl = comp->rlvls; lvl != endlvls; ++lvl) {
+			if (!lvl->bands) {
+				continue;
+			}
+			endbands = &lvl->bands[lvl->numbands];
+			for (band = lvl->bands; band != endbands; ++band) {
+				if (!band->data) {
+					continue;
+				}
+				for (prcno = 0, prc = band->prcs; prcno < lvl->numprcs; ++prcno, ++prc) {
+					if (!prc->cblks) {
+						continue;
+					}
+					jpc_tagtree_copy(prc->savincltree, prc->incltree);
+					jpc_tagtree_copy(prc->savnlibtree, prc->nlibtree);
+					endcblks = &prc->cblks[prc->numcblks];
+					for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
+						cblk->savedcurpass = cblk->curpass;
+						cblk->savednumencpasses = cblk->numencpasses;
+						cblk->savednumlenbits = cblk->numlenbits;
+					}
+				}
+			}
+		}
+	}
+
+}
+
+void jpc_restore_t2state(jpc_enc_t *enc)
+{
+
+	jpc_enc_tcmpt_t *comp;
+	jpc_enc_tcmpt_t *endcomps;
+	jpc_enc_rlvl_t *lvl;
+	jpc_enc_rlvl_t *endlvls;
+	jpc_enc_band_t *band;
+	jpc_enc_band_t *endbands;
+	jpc_enc_cblk_t *cblk;
+	jpc_enc_cblk_t *endcblks;
+	jpc_enc_tile_t *tile;
+	uint_fast32_t prcno;
+	jpc_enc_prc_t *prc;
+
+	tile = enc->curtile;
+
+	endcomps = &tile->tcmpts[tile->numtcmpts];
+	for (comp = tile->tcmpts; comp != endcomps; ++comp) {
+		endlvls = &comp->rlvls[comp->numrlvls];
+		for (lvl = comp->rlvls; lvl != endlvls; ++lvl) {
+			if (!lvl->bands) {
+				continue;
+			}
+			endbands = &lvl->bands[lvl->numbands];
+			for (band = lvl->bands; band != endbands; ++band) {
+				if (!band->data) {
+					continue;
+				}
+				for (prcno = 0, prc = band->prcs; prcno < lvl->numprcs; ++prcno, ++prc) {
+					if (!prc->cblks) {
+						continue;
+					}
+					jpc_tagtree_copy(prc->incltree, prc->savincltree);
+					jpc_tagtree_copy(prc->nlibtree, prc->savnlibtree);
+					endcblks = &prc->cblks[prc->numcblks];
+					for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
+						cblk->curpass = cblk->savedcurpass;
+						cblk->numencpasses = cblk->savednumencpasses;
+						cblk->numlenbits = cblk->savednumlenbits;
+					}
+				}
+			}
+		}
+	}
+}
+
+void jpc_init_t2state(jpc_enc_t *enc, int raflag)
+{
+/* It is assumed that band->numbps and cblk->numbps precomputed */
+
+	jpc_enc_tcmpt_t *comp;
+	jpc_enc_tcmpt_t *endcomps;
+	jpc_enc_rlvl_t *lvl;
+	jpc_enc_rlvl_t *endlvls;
+	jpc_enc_band_t *band;
+	jpc_enc_band_t *endbands;
+	jpc_enc_cblk_t *cblk;
+	jpc_enc_cblk_t *endcblks;
+	jpc_enc_pass_t *pass;
+	jpc_enc_pass_t *endpasses;
+	jpc_tagtreenode_t *leaf;
+	jpc_enc_tile_t *tile;
+	uint_fast32_t prcno;
+	jpc_enc_prc_t *prc;
+
+	tile = enc->curtile;
+
+	endcomps = &tile->tcmpts[tile->numtcmpts];
+	for (comp = tile->tcmpts; comp != endcomps; ++comp) {
+		endlvls = &comp->rlvls[comp->numrlvls];
+		for (lvl = comp->rlvls; lvl != endlvls; ++lvl) {
+			if (!lvl->bands) {
+				continue;
+			}
+			endbands = &lvl->bands[lvl->numbands];
+			for (band = lvl->bands; band != endbands; ++band) {
+				if (!band->data) {
+					continue;
+				}
+				for (prcno = 0, prc = band->prcs; prcno < lvl->numprcs; ++prcno, ++prc) {
+					if (!prc->cblks) {
+						continue;
+					}
+					jpc_tagtree_reset(prc->incltree);
+					jpc_tagtree_reset(prc->nlibtree);
+					endcblks = &prc->cblks[prc->numcblks];
+					for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
+						if (jas_stream_rewind(cblk->stream)) {
+							assert(0);
+						}
+						cblk->curpass = (cblk->numpasses > 0) ? cblk->passes : 0;
+						cblk->numencpasses = 0;
+						cblk->numlenbits = 3;
+						cblk->numimsbs = band->numbps - cblk->numbps;
+						assert(cblk->numimsbs >= 0);
+						leaf = jpc_tagtree_getleaf(prc->nlibtree, cblk - prc->cblks);
+						jpc_tagtree_setvalue(prc->nlibtree, leaf, cblk->numimsbs);
+
+						if (raflag) {
+							endpasses = &cblk->passes[cblk->numpasses];
+							for (pass = cblk->passes; pass != endpasses; ++pass) {
+								pass->lyrno = -1;
+								pass->lyrno = 0;
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+}
+
+jpc_pi_t *jpc_enc_pi_create(jpc_enc_cp_t *cp, jpc_enc_tile_t *tile)
+{
+	jpc_pi_t *pi;
+	int compno;
+	jpc_picomp_t *picomp;
+	jpc_pirlvl_t *pirlvl;
+	jpc_enc_tcmpt_t *tcomp;
+	int rlvlno;
+	jpc_enc_rlvl_t *rlvl;
+	int prcno;
+	int *prclyrno;
+
+	if (!(pi = jpc_pi_create0())) {
+		return 0;
+	}
+	pi->pktno = -1;
+	pi->numcomps = cp->numcmpts;
+	if (!(pi->picomps = jas_malloc(pi->numcomps * sizeof(jpc_picomp_t)))) {
+		jpc_pi_destroy(pi);
+		return 0;
+	}
+	for (compno = 0, picomp = pi->picomps; compno < pi->numcomps; ++compno,
+	  ++picomp) {
+		picomp->pirlvls = 0;
+	}
+
+	for (compno = 0, tcomp = tile->tcmpts, picomp = pi->picomps;
+	  compno < pi->numcomps; ++compno, ++tcomp, ++picomp) {
+		picomp->numrlvls = tcomp->numrlvls;
+		if (!(picomp->pirlvls = jas_malloc(picomp->numrlvls *
+		  sizeof(jpc_pirlvl_t)))) {
+			jpc_pi_destroy(pi);
+			return 0;
+		}
+		for (rlvlno = 0, pirlvl = picomp->pirlvls; rlvlno <
+		  picomp->numrlvls; ++rlvlno, ++pirlvl) {
+			pirlvl->prclyrnos = 0;
+		}
+		for (rlvlno = 0, pirlvl = picomp->pirlvls, rlvl = tcomp->rlvls;
+		  rlvlno < picomp->numrlvls; ++rlvlno, ++pirlvl, ++rlvl) {
+/* XXX sizeof(long) should be sizeof different type */
+			pirlvl->numprcs = rlvl->numprcs;
+			if (rlvl->numprcs) {
+				if (!(pirlvl->prclyrnos = jas_malloc(pirlvl->numprcs *
+				  sizeof(long)))) {
+					jpc_pi_destroy(pi);
+					return 0;
+				}
+			} else {
+				pirlvl->prclyrnos = 0;
+			}
+		}
+	}
+
+	pi->maxrlvls = 0;
+	for (compno = 0, tcomp = tile->tcmpts, picomp = pi->picomps;
+	  compno < pi->numcomps; ++compno, ++tcomp, ++picomp) {
+		picomp->hsamp = cp->ccps[compno].sampgrdstepx;
+		picomp->vsamp = cp->ccps[compno].sampgrdstepy;
+		for (rlvlno = 0, pirlvl = picomp->pirlvls, rlvl = tcomp->rlvls;
+		  rlvlno < picomp->numrlvls; ++rlvlno, ++pirlvl, ++rlvl) {
+			pirlvl->prcwidthexpn = rlvl->prcwidthexpn;
+			pirlvl->prcheightexpn = rlvl->prcheightexpn;
+			for (prcno = 0, prclyrno = pirlvl->prclyrnos;
+			  prcno < pirlvl->numprcs; ++prcno, ++prclyrno) {
+				*prclyrno = 0;
+			}
+			pirlvl->numhprcs = rlvl->numhprcs;
+		}
+		if (pi->maxrlvls < tcomp->numrlvls) {
+			pi->maxrlvls = tcomp->numrlvls;
+		}
+	}
+
+	pi->numlyrs = tile->numlyrs;
+	pi->xstart = tile->tlx;
+	pi->ystart = tile->tly;
+	pi->xend = tile->brx;
+	pi->yend = tile->bry;
+
+	pi->picomp = 0;
+	pi->pirlvl = 0;
+	pi->x = 0;
+	pi->y = 0;
+	pi->compno = 0;
+	pi->rlvlno = 0;
+	pi->prcno = 0;
+	pi->lyrno = 0;
+	pi->xstep = 0;
+	pi->ystep = 0;
+
+	pi->pchgno = -1;
+
+	pi->defaultpchg.prgord = tile->prg;
+	pi->defaultpchg.compnostart = 0;
+	pi->defaultpchg.compnoend = pi->numcomps;
+	pi->defaultpchg.rlvlnostart = 0;
+	pi->defaultpchg.rlvlnoend = pi->maxrlvls;
+	pi->defaultpchg.lyrnoend = pi->numlyrs;
+	pi->pchg = 0;
+
+	pi->valid = 0;
+
+	return pi;
+}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_t2enc.h b/converter/other/jpeg2000/libjasper/jpc/jpc_t2enc.h
new file mode 100644
index 00000000..e97f8c06
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_t2enc.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Tier 2 Encoder
+ *
+ * $Id$
+ */
+
+#ifndef JPC_T2ENC_H
+#define JPC_T2ENC_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "jpc_enc.h"
+
+/******************************************************************************\
+* Functions.
+\******************************************************************************/
+
+/* Encode the packets for a tile. */
+int jpc_enc_encpkts(jpc_enc_t *enc, jas_stream_t *out);
+
+/* Encode the specified packet. */
+int jpc_enc_encpkt(jpc_enc_t *enc, jas_stream_t *out, int compno, int lvlno,
+  int prcno, int lyrno);
+
+/* Save the tier-2 coding state. */
+void jpc_save_t2state(jpc_enc_t *enc);
+
+/* Restore the tier-2 coding state. */
+void jpc_restore_t2state(jpc_enc_t *enc);
+
+/* Initialize the tier-2 coding state. */
+void jpc_init_t2state(jpc_enc_t *enc, int raflag);
+
+/* Create a packet iterator for the encoder. */
+jpc_pi_t *jpc_enc_pi_create(jpc_enc_cp_t *cp, jpc_enc_tile_t *tile);
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_tagtree.c b/converter/other/jpeg2000/libjasper/jpc/jpc_tagtree.c
new file mode 100644
index 00000000..2b13fcf1
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_tagtree.c
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Tag Tree Library
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <limits.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "jasper/jas_malloc.h"
+
+#include "jpc_tagtree.h"
+
+/******************************************************************************\
+* Code for creating and destroying tag trees.
+\******************************************************************************/
+
+static jpc_tagtree_t *jpc_tagtree_alloc(void)
+{
+	jpc_tagtree_t *tree;
+
+	if (!(tree = jas_malloc(sizeof(jpc_tagtree_t)))) {
+		return 0;
+	}
+	tree->numleafsh_ = 0;
+	tree->numleafsv_ = 0;
+	tree->numnodes_ = 0;
+	tree->nodes_ = 0;
+
+	return tree;
+}
+
+/* Create a tag tree. */
+
+jpc_tagtree_t *jpc_tagtree_create(int numleafsh, int numleafsv)
+{
+	int nplh[JPC_TAGTREE_MAXDEPTH];
+	int nplv[JPC_TAGTREE_MAXDEPTH];
+	jpc_tagtreenode_t *node;
+	jpc_tagtreenode_t *parentnode;
+	jpc_tagtreenode_t *parentnode0;
+	jpc_tagtree_t *tree;
+	int i;
+	int j;
+	int k;
+	int numlvls;
+	int n;
+
+	assert(numleafsh > 0 && numleafsv > 0);
+
+	if (!(tree = jpc_tagtree_alloc())) {
+		return 0;
+	}
+	tree->numleafsh_ = numleafsh;
+	tree->numleafsv_ = numleafsv;
+
+	numlvls = 0;
+	nplh[0] = numleafsh;
+	nplv[0] = numleafsv;
+	do {
+		n = nplh[numlvls] * nplv[numlvls];
+		nplh[numlvls + 1] = (nplh[numlvls] + 1) / 2;
+		nplv[numlvls + 1] = (nplv[numlvls] + 1) / 2;
+		tree->numnodes_ += n;
+		++numlvls;
+	} while (n > 1);
+
+	if (!(tree->nodes_ = jas_malloc(tree->numnodes_ * sizeof(jpc_tagtreenode_t)))) {
+		return 0;
+	}
+
+	/* Initialize the parent links for all nodes in the tree. */
+
+	node = tree->nodes_;
+	parentnode = &tree->nodes_[tree->numleafsh_ * tree->numleafsv_];
+	parentnode0 = parentnode;
+
+	for (i = 0; i < numlvls - 1; ++i) {
+		for (j = 0; j < nplv[i]; ++j) {
+			k = nplh[i];
+			while (--k >= 0) {
+				node->parent_ = parentnode;
+				++node;
+				if (--k >= 0) {
+					node->parent_ = parentnode;
+					++node;
+				}
+				++parentnode;
+			}
+			if ((j & 1) || j == nplv[i] - 1) {
+				parentnode0 = parentnode;
+			} else {
+				parentnode = parentnode0;
+				parentnode0 += nplh[i];
+			}
+		}
+	}
+	node->parent_ = 0;
+
+	/* Initialize the data values to something sane. */
+
+	jpc_tagtree_reset(tree);
+
+	return tree;
+}
+
+/* Destroy a tag tree. */
+
+void jpc_tagtree_destroy(jpc_tagtree_t *tree)
+{
+	if (tree->nodes_) {
+		jas_free(tree->nodes_);
+	}
+	jas_free(tree);
+}
+
+/******************************************************************************\
+* Code.
+\******************************************************************************/
+
+/* Copy state information from one tag tree to another. */
+
+void jpc_tagtree_copy(jpc_tagtree_t *dsttree, jpc_tagtree_t *srctree)
+{
+	int n;
+	jpc_tagtreenode_t *srcnode;
+	jpc_tagtreenode_t *dstnode;
+
+	/* The two tag trees must have similar sizes. */
+	assert(srctree->numleafsh_ == dsttree->numleafsh_ &&
+	  srctree->numleafsv_ == dsttree->numleafsv_);
+
+	n = srctree->numnodes_;
+	srcnode = srctree->nodes_;
+	dstnode = dsttree->nodes_;
+	while (--n >= 0) {
+		dstnode->value_ = srcnode->value_;
+		dstnode->low_ = srcnode->low_;
+		dstnode->known_ = srcnode->known_;
+		++dstnode;
+		++srcnode;
+	}
+}
+
+/* Reset all of the state information associated with a tag tree. */
+
+void jpc_tagtree_reset(jpc_tagtree_t *tree)
+{
+	int n;
+	jpc_tagtreenode_t *node;
+
+	n = tree->numnodes_;
+	node = tree->nodes_;
+
+	while (--n >= 0) {
+		node->value_ = INT_MAX;
+		node->low_ = 0;
+		node->known_ = 0;
+		++node;
+	}
+}
+
+/* Set the value associated with the specified leaf node, updating
+the other nodes as necessary. */
+
+void jpc_tagtree_setvalue(jpc_tagtree_t *tree, jpc_tagtreenode_t *leaf, int value)
+{
+	jpc_tagtreenode_t *node;
+
+	assert(value >= 0);
+
+	node = leaf;
+	while (node && node->value_ > value) {
+		node->value_ = value;
+		node = node->parent_;
+	}
+}
+
+/* Get a particular leaf node. */
+
+jpc_tagtreenode_t *jpc_tagtree_getleaf(jpc_tagtree_t *tree, int n)
+{
+	return &tree->nodes_[n];
+}
+
+/* Invoke the tag tree encoding procedure. */
+
+int jpc_tagtree_encode(jpc_tagtree_t *tree, jpc_tagtreenode_t *leaf, int threshold,
+  jpc_bitstream_t *out)
+{
+	jpc_tagtreenode_t *stk[JPC_TAGTREE_MAXDEPTH - 1];
+	jpc_tagtreenode_t **stkptr;
+	jpc_tagtreenode_t *node;
+	int low;
+
+	assert(leaf);
+	assert(threshold >= 0);
+
+	/* Traverse to the root of the tree, recording the path taken. */
+	stkptr = stk;
+	node = leaf;
+	while (node->parent_) {
+		*stkptr++ = node;
+		node = node->parent_;
+	}
+
+	low = 0;
+	for (;;) {
+		if (low > node->low_) {
+			/* Deferred propagation of the lower bound downward in
+			  the tree. */
+			node->low_ = low;
+		} else {
+			low = node->low_;
+		}
+
+		while (low < threshold) {
+			if (low >= node->value_) {
+				if (!node->known_) {
+					if (jpc_bitstream_putbit(out, 1) == EOF) {
+						return -1;
+					}
+					node->known_ = 1;
+				}
+				break;
+			}
+			if (jpc_bitstream_putbit(out, 0) == EOF) {
+				return -1;
+			}
+			++low;
+		}
+		node->low_ = low;
+		if (stkptr == stk) {
+			break;
+		}
+		node = *--stkptr;
+
+	}
+	return (leaf->low_ < threshold) ? 1 : 0;
+
+}
+
+/* Invoke the tag tree decoding procedure. */
+
+int jpc_tagtree_decode(jpc_tagtree_t *tree, jpc_tagtreenode_t *leaf, int threshold,
+  jpc_bitstream_t *in)
+{
+	jpc_tagtreenode_t *stk[JPC_TAGTREE_MAXDEPTH - 1];
+	jpc_tagtreenode_t **stkptr;
+	jpc_tagtreenode_t *node;
+	int low;
+	int ret;
+
+	assert(threshold >= 0);
+
+	/* Traverse to the root of the tree, recording the path taken. */
+	stkptr = stk;
+	node = leaf;
+	while (node->parent_) {
+		*stkptr++ = node;
+		node = node->parent_;
+	}
+
+	low = 0;
+	for (;;) {
+		if (low > node->low_) {
+			node->low_ = low;
+		} else {
+			low = node->low_;
+		}
+		while (low < threshold && low < node->value_) {
+			if ((ret = jpc_bitstream_getbit(in)) < 0) {
+				return -1;
+			}
+			if (ret) {
+				node->value_ = low;
+			} else {
+				++low;
+			}
+		}
+		node->low_ = low;
+		if (stkptr == stk) {
+			break;
+		}
+		node = *--stkptr;
+	}
+
+	return (node->value_ < threshold) ? 1 : 0;
+}
+
+/******************************************************************************\
+* Code for debugging.
+\******************************************************************************/
+
+void jpc_tagtree_dump(jpc_tagtree_t *tree, FILE *out)
+{
+	jpc_tagtreenode_t *node;
+	int n;
+
+	node = tree->nodes_;
+	n = tree->numnodes_;
+	while (--n >= 0) {
+		fprintf(out, "node %p, parent %p, value %d, lower %d, known %d\n",
+		  (void *) node, (void *) node->parent_, node->value_, node->low_,
+		  node->known_);
+		++node;
+	}
+}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_tagtree.h b/converter/other/jpeg2000/libjasper/jpc/jpc_tagtree.h
new file mode 100644
index 00000000..1892b13f
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_tagtree.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Tag Tree Library
+ *
+ * $Id$
+ */
+
+#ifndef JPC_TAGTREE_H
+#define JPC_TAGTREE_H
+
+/******************************************************************************\
+* Includes
+\******************************************************************************/
+
+#include <limits.h>
+#include <stdio.h>
+
+#include "jpc_bs.h"
+
+/******************************************************************************\
+* Constants
+\******************************************************************************/
+
+/* The maximum allowable depth for a tag tree. */
+#define JPC_TAGTREE_MAXDEPTH	32
+
+/******************************************************************************\
+* Types
+\******************************************************************************/
+
+/*
+ * Tag tree node.
+ */
+
+typedef struct jpc_tagtreenode_ {
+
+	/* The parent of this node. */
+	struct jpc_tagtreenode_ *parent_;
+
+	/* The value associated with this node. */
+	int value_;
+
+	/* The lower bound on the value associated with this node. */
+	int low_;
+
+	/* A flag indicating if the value is known exactly. */
+	int known_;
+
+} jpc_tagtreenode_t;
+
+/*
+ * Tag tree.
+ */
+
+typedef struct {
+
+	/* The number of leaves in the horizontal direction. */
+	int numleafsh_;
+
+	/* The number of leaves in the vertical direction. */
+	int numleafsv_;
+
+	/* The total number of nodes in the tree. */
+	int numnodes_;
+
+	/* The nodes. */
+	jpc_tagtreenode_t *nodes_;
+
+} jpc_tagtree_t;
+
+/******************************************************************************\
+* Functions.
+\******************************************************************************/
+
+/* Create a tag tree. */
+jpc_tagtree_t *jpc_tagtree_create(int numleafsh, int numleafsv);
+
+/* Destroy a tag tree. */
+void jpc_tagtree_destroy(jpc_tagtree_t *tree);
+
+/* Copy data from one tag tree to another. */
+void jpc_tagtree_copy(jpc_tagtree_t *dsttree, jpc_tagtree_t *srctree);
+
+/* Reset the tag tree state. */
+void jpc_tagtree_reset(jpc_tagtree_t *tree);
+
+/* Set the value associated with a particular leaf node of a tag tree. */
+void jpc_tagtree_setvalue(jpc_tagtree_t *tree, jpc_tagtreenode_t *leaf,
+  int value);
+
+/* Get a pointer to a particular leaf node. */
+jpc_tagtreenode_t *jpc_tagtree_getleaf(jpc_tagtree_t *tree, int n);
+
+/* Invoke the tag tree decoding procedure. */
+int jpc_tagtree_decode(jpc_tagtree_t *tree, jpc_tagtreenode_t *leaf,
+  int threshold, jpc_bitstream_t *in);
+
+/* Invoke the tag tree encoding procedure. */
+int jpc_tagtree_encode(jpc_tagtree_t *tree, jpc_tagtreenode_t *leaf,
+  int threshold, jpc_bitstream_t *out);
+
+/* Dump a tag tree (for debugging purposes). */
+void jpc_tagtree_dump(jpc_tagtree_t *tree, FILE *out);
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_tsfb.c b/converter/other/jpeg2000/libjasper/jpc/jpc_tsfb.c
new file mode 100644
index 00000000..f7d66b57
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_tsfb.c
@@ -0,0 +1,662 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Tree-Structured Filter Bank (TSFB) Library
+ *
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include <assert.h>
+
+#include "jasper/jas_malloc.h"
+#include "jasper/jas_seq.h"
+
+#include "jpc_tsfb.h"
+#include "jpc_cod.h"
+#include "jpc_cs.h"
+#include "jpc_util.h"
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+#define	bandnotovind(tsfbnode, x)	((x) / (tsfbnode)->numhchans)
+#define	bandnotohind(tsfbnode, x)	((x) % (tsfbnode)->numhchans)
+
+static jpc_tsfb_t *jpc_tsfb_create(void);
+static jpc_tsfbnode_t *jpc_tsfbnode_create(void);
+static void jpc_tsfbnode_destroy(jpc_tsfbnode_t *node);
+static void jpc_tsfbnode_synthesize(jpc_tsfbnode_t *node, int flags, jas_seq2d_t *x);
+static void jpc_tsfbnode_analyze(jpc_tsfbnode_t *node, int flags, jas_seq2d_t *x);
+static void qmfb2d_getbands(jpc_qmfb1d_t *hqmfb, jpc_qmfb1d_t *vqmfb,
+  uint_fast32_t xstart, uint_fast32_t ystart, uint_fast32_t xend,
+  uint_fast32_t yend, int maxbands, int *numbandsptr, jpc_tsfbnodeband_t *bands);
+static void jpc_tsfbnode_getbandstree(jpc_tsfbnode_t *node, uint_fast32_t posxstart,
+  uint_fast32_t posystart, uint_fast32_t xstart, uint_fast32_t ystart,
+  uint_fast32_t xend, uint_fast32_t yend, jpc_tsfb_band_t **bands);
+static int jpc_tsfbnode_findchild(jpc_tsfbnode_t *parnode, jpc_tsfbnode_t *cldnode);
+static int jpc_tsfbnode_getequivfilters(jpc_tsfbnode_t *tsfbnode, int cldind,
+  int width, int height, jas_seq_t **vfilter, jas_seq_t **hfilter);
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+jpc_tsfb_t *jpc_tsfb_wavelet(jpc_qmfb1d_t *hqmfb, jpc_qmfb1d_t *vqmfb, int numdlvls)
+{
+	jpc_tsfb_t *tsfb;
+	int dlvlno;
+	jpc_tsfbnode_t *curnode;
+	jpc_tsfbnode_t *prevnode;
+	int childno;
+	if (!(tsfb = jpc_tsfb_create())) {
+		return 0;
+	}
+	prevnode = 0;
+	for (dlvlno = 0; dlvlno < numdlvls; ++dlvlno) {
+		if (!(curnode = jpc_tsfbnode_create())) {
+			jpc_tsfb_destroy(tsfb);
+			return 0;
+		}
+		if (prevnode) {
+			prevnode->children[0] = curnode;
+			++prevnode->numchildren;
+			curnode->parent = prevnode;
+		} else {
+			tsfb->root = curnode;
+			curnode->parent = 0;
+		}
+		if (hqmfb) {
+			curnode->numhchans = jpc_qmfb1d_getnumchans(hqmfb);
+			if (!(curnode->hqmfb = jpc_qmfb1d_copy(hqmfb))) {
+				jpc_tsfb_destroy(tsfb);
+				return 0;
+			}
+		} else {
+			curnode->hqmfb = 0;
+			curnode->numhchans = 1;
+		}
+		if (vqmfb) {
+			curnode->numvchans = jpc_qmfb1d_getnumchans(vqmfb);
+			if (!(curnode->vqmfb = jpc_qmfb1d_copy(vqmfb))) {
+				jpc_tsfb_destroy(tsfb);
+				return 0;
+			}
+		} else {
+			curnode->vqmfb = 0;
+			curnode->numvchans = 1;
+		}
+		curnode->maxchildren = curnode->numhchans * curnode->numvchans;
+		for (childno = 0; childno < curnode->maxchildren;
+		  ++childno) {
+			curnode->children[childno] = 0;
+		}
+		prevnode = curnode;
+	}
+	return tsfb;
+}
+
+static jpc_tsfb_t *jpc_tsfb_create()
+{
+	jpc_tsfb_t *tsfb;
+	if (!(tsfb = jas_malloc(sizeof(jpc_tsfb_t)))) {
+		return 0;
+	}
+	tsfb->root = 0;
+	return tsfb;
+}
+
+void jpc_tsfb_destroy(jpc_tsfb_t *tsfb)
+{
+	if (tsfb->root) {
+		jpc_tsfbnode_destroy(tsfb->root);
+	}
+	jas_free(tsfb);
+}
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+void jpc_tsfb_analyze(jpc_tsfb_t *tsfb, int flags, jas_seq2d_t *x)
+{
+	if (tsfb->root) {
+		jpc_tsfbnode_analyze(tsfb->root, flags, x);
+	}
+}
+
+static void jpc_tsfbnode_analyze(jpc_tsfbnode_t *node, int flags, jas_seq2d_t *x)
+{
+	jpc_tsfbnodeband_t nodebands[JPC_TSFB_MAXBANDSPERNODE];
+	int numbands;
+	jas_seq2d_t *y;
+	int bandno;
+	jpc_tsfbnodeband_t *band;
+
+	if (node->vqmfb) {
+		jpc_qmfb1d_analyze(node->vqmfb, flags | JPC_QMFB1D_VERT, x);
+	}
+	if (node->hqmfb) {
+		jpc_qmfb1d_analyze(node->hqmfb, flags, x);
+	}
+	if (node->numchildren > 0) {
+		qmfb2d_getbands(node->hqmfb, node->vqmfb, jas_seq2d_xstart(x),
+		  jas_seq2d_ystart(x), jas_seq2d_xend(x), jas_seq2d_yend(x),
+		  JPC_TSFB_MAXBANDSPERNODE, &numbands, nodebands);
+		y = jas_seq2d_create(0, 0, 0, 0);
+		assert(y);
+		for (bandno = 0, band = nodebands; bandno < numbands; ++bandno, ++band) {
+			if (node->children[bandno]) {
+				if (band->xstart != band->xend && band->ystart != band->yend) {
+					jas_seq2d_bindsub(y, x, band->locxstart, band->locystart,
+					  band->locxend, band->locyend);
+					jas_seq2d_setshift(y, band->xstart, band->ystart);
+					jpc_tsfbnode_analyze(node->children[bandno], flags, y);
+				}
+			}
+		}
+		jas_matrix_destroy(y);
+	}
+}
+
+void jpc_tsfb_synthesize(jpc_tsfb_t *tsfb, int flags, jas_seq2d_t *x)
+{
+	if (tsfb->root) {
+		jpc_tsfbnode_synthesize(tsfb->root, flags, x);
+	}
+}
+
+static void jpc_tsfbnode_synthesize(jpc_tsfbnode_t *node, int flags, jas_seq2d_t *x)
+{
+	jpc_tsfbnodeband_t nodebands[JPC_TSFB_MAXBANDSPERNODE];
+	int numbands;
+	jas_seq2d_t *y;
+	int bandno;
+	jpc_tsfbnodeband_t *band;
+
+	if (node->numchildren > 0) {
+		qmfb2d_getbands(node->hqmfb, node->vqmfb, jas_seq2d_xstart(x),
+		  jas_seq2d_ystart(x), jas_seq2d_xend(x), jas_seq2d_yend(x),
+		  JPC_TSFB_MAXBANDSPERNODE, &numbands, nodebands);
+		y = jas_seq2d_create(0, 0, 0, 0);
+		for (bandno = 0, band = nodebands; bandno < numbands; ++bandno, ++band) {
+			if (node->children[bandno]) {
+				if (band->xstart != band->xend && band->ystart != band->yend) {
+					jas_seq2d_bindsub(y, x, band->locxstart, band->locystart,
+					  band->locxend, band->locyend);
+					jas_seq2d_setshift(y, band->xstart, band->ystart);
+					jpc_tsfbnode_synthesize(node->children[bandno], flags, y);
+				}
+			}
+		}
+		jas_seq2d_destroy(y);
+	}
+	if (node->hqmfb) {
+		jpc_qmfb1d_synthesize(node->hqmfb, flags, x);
+	}
+	if (node->vqmfb) {
+		jpc_qmfb1d_synthesize(node->vqmfb, flags | JPC_QMFB1D_VERT, x);
+	}
+}
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+
+int jpc_tsfb_getbands(jpc_tsfb_t *tsfb, uint_fast32_t xstart, uint_fast32_t ystart,
+  uint_fast32_t xend, uint_fast32_t yend, jpc_tsfb_band_t *bands)
+{
+	jpc_tsfb_band_t *savbands;
+	savbands = bands;
+	if (!tsfb->root) {
+		bands[0].xstart = xstart;
+		bands[0].ystart = ystart;
+		bands[0].xend = xend;
+		bands[0].yend = yend;
+		bands[0].locxstart = xstart;
+		bands[0].locystart = ystart;
+		bands[0].locxend = xend;
+		bands[0].locyend = yend;
+		bands[0].orient = JPC_TSFB_LL;
+		bands[0].synenergywt = JPC_FIX_ONE;
+		++bands;
+	} else {
+		jpc_tsfbnode_getbandstree(tsfb->root, xstart, ystart,
+		  xstart, ystart, xend, yend, &bands);
+	}
+	return bands - savbands;
+}
+
+static void jpc_tsfbnode_getbandstree(jpc_tsfbnode_t *node, uint_fast32_t posxstart,
+  uint_fast32_t posystart, uint_fast32_t xstart, uint_fast32_t ystart,
+  uint_fast32_t xend, uint_fast32_t yend, jpc_tsfb_band_t **bands)
+{
+	jpc_tsfbnodeband_t nodebands[JPC_TSFB_MAXBANDSPERNODE];
+	jpc_tsfbnodeband_t *nodeband;
+	int nodebandno;
+	int numnodebands;
+	jpc_tsfb_band_t *band;
+	jas_seq_t *hfilter;
+	jas_seq_t *vfilter;
+
+	qmfb2d_getbands(node->hqmfb, node->vqmfb, xstart, ystart, xend, yend,
+	  JPC_TSFB_MAXBANDSPERNODE, &numnodebands, nodebands);
+	if (node->numchildren > 0) {
+		for (nodebandno = 0, nodeband = nodebands;
+		  nodebandno < numnodebands; ++nodebandno, ++nodeband) {
+			if (node->children[nodebandno]) {
+				jpc_tsfbnode_getbandstree(node->children[
+				  nodebandno], posxstart +
+				  nodeband->locxstart - xstart, posystart +
+				  nodeband->locystart - ystart, nodeband->xstart,
+				  nodeband->ystart, nodeband->xend,
+				  nodeband->yend, bands);
+
+			}
+		}
+	}
+assert(numnodebands == 4 || numnodebands == 3);
+	for (nodebandno = 0, nodeband = nodebands; nodebandno < numnodebands;
+	  ++nodebandno, ++nodeband) {
+		if (!node->children[nodebandno]) {
+			band = *bands;
+			band->xstart = nodeband->xstart;
+			band->ystart = nodeband->ystart;
+			band->xend = nodeband->xend;
+			band->yend = nodeband->yend;
+			band->locxstart = posxstart + nodeband->locxstart -
+			  xstart;
+			band->locystart = posystart + nodeband->locystart -
+			  ystart;
+			band->locxend = band->locxstart + band->xend -
+			  band->xstart;
+			band->locyend = band->locystart + band->yend -
+			  band->ystart;
+			if (numnodebands == 4) {
+				switch (nodebandno) {
+				case 0:
+					band->orient = JPC_TSFB_LL;
+					break;
+				case 1:
+					band->orient = JPC_TSFB_HL;
+					break;
+				case 2:
+					band->orient = JPC_TSFB_LH;
+					break;
+				case 3:
+					band->orient = JPC_TSFB_HH;
+					break;
+				default:
+					abort();
+					break;
+				}
+			} else {
+				switch (nodebandno) {
+				case 0:
+					band->orient = JPC_TSFB_HL;
+					break;
+				case 1:
+					band->orient = JPC_TSFB_LH;
+					break;
+				case 2:
+					band->orient = JPC_TSFB_HH;
+					break;
+				default:
+					abort();
+					break;
+				}
+			}
+			jpc_tsfbnode_getequivfilters(node, nodebandno, band->xend - band->xstart, band->yend - band->ystart, &hfilter, &vfilter);
+			band->synenergywt = jpc_fix_mul(jpc_seq_norm(hfilter),
+			  jpc_seq_norm(vfilter));
+			jas_seq_destroy(hfilter);
+			jas_seq_destroy(vfilter);
+			++(*bands);
+		}
+	}
+}
+
+/******************************************************************************\
+*
+\******************************************************************************/
+
+static jpc_tsfbnode_t *jpc_tsfbnode_create()
+{
+	jpc_tsfbnode_t *node;
+	if (!(node = jas_malloc(sizeof(jpc_tsfbnode_t)))) {
+		return 0;
+	}
+	node->numhchans = 0;
+	node->numvchans = 0;
+	node->numchildren = 0;
+	node->maxchildren = 0;
+	node->hqmfb = 0;
+	node->vqmfb = 0;
+	node->parent = 0;
+	return node;
+}
+
+static void jpc_tsfbnode_destroy(jpc_tsfbnode_t *node)
+{
+	jpc_tsfbnode_t **child;
+	int childno;
+	for (childno = 0, child = node->children; childno < node->maxchildren;
+	  ++childno, ++child) {
+		if (*child) {
+			jpc_tsfbnode_destroy(*child);
+		}
+	}
+	if (node->hqmfb) {
+		jpc_qmfb1d_destroy(node->hqmfb);
+	}
+	if (node->vqmfb) {
+		jpc_qmfb1d_destroy(node->vqmfb);
+	}
+	jas_free(node);
+}
+
+
+
+
+
+
+
+
+static void qmfb2d_getbands(jpc_qmfb1d_t *hqmfb, jpc_qmfb1d_t *vqmfb,
+  uint_fast32_t xstart, uint_fast32_t ystart, uint_fast32_t xend,
+  uint_fast32_t yend, int maxbands, int *numbandsptr, jpc_tsfbnodeband_t *bands)
+{
+	jpc_qmfb1dband_t hbands[JPC_QMFB1D_MAXCHANS];
+	jpc_qmfb1dband_t vbands[JPC_QMFB1D_MAXCHANS];
+	int numhbands;
+	int numvbands;
+	int numbands;
+	int bandno;
+	int hbandno;
+	int vbandno;
+	jpc_tsfbnodeband_t *band;
+
+	if (hqmfb) {
+		jpc_qmfb1d_getbands(hqmfb, 0, xstart, ystart, xend, yend,
+		  JPC_QMFB1D_MAXCHANS, &numhbands, hbands);
+	} else {
+		numhbands = 1;
+		hbands[0].start = xstart;
+		hbands[0].end = xend;
+		hbands[0].locstart = xstart;
+		hbands[0].locend = xend;
+	}
+	if (vqmfb) {
+		jpc_qmfb1d_getbands(vqmfb, JPC_QMFB1D_VERT, xstart, ystart, xend,
+		  yend, JPC_QMFB1D_MAXCHANS, &numvbands, vbands);
+	} else {
+		numvbands = 1;
+		vbands[0].start = ystart;
+		vbands[0].end = yend;
+		vbands[0].locstart = ystart;
+		vbands[0].locend = yend;
+	}
+	numbands = numhbands * numvbands;
+	*numbandsptr = numbands;
+	for (bandno = 0, band = bands; bandno < numbands; ++bandno, ++band) {
+		hbandno = bandno % numhbands;
+		vbandno = bandno / numhbands;
+		band->xstart = hbands[hbandno].start;
+		band->ystart = vbands[vbandno].start;
+		band->xend = hbands[hbandno].end;
+		band->yend = vbands[vbandno].end;
+		band->locxstart = hbands[hbandno].locstart;
+		band->locystart = vbands[vbandno].locstart;
+		band->locxend = hbands[hbandno].locend;
+		band->locyend = vbands[vbandno].locend;
+		assert(band->xstart <= band->xend &&
+		  band->ystart <= band->yend);
+		if (band->xstart == band->xend) {
+			band->yend = band->ystart;
+			band->locyend = band->locystart;
+		} else if (band->ystart == band->yend) {
+			band->xend = band->xstart;
+			band->locxend = band->locxstart;
+		}
+	}
+}
+
+static int jpc_tsfbnode_getequivfilters(jpc_tsfbnode_t *tsfbnode, int cldind,
+  int width, int height, jas_seq_t **hfilter, jas_seq_t **vfilter)
+{
+	jas_seq_t *hseq;
+	jas_seq_t *vseq;
+	jpc_tsfbnode_t *node;
+	jas_seq2d_t *hfilters[JPC_QMFB1D_MAXCHANS];
+	jas_seq2d_t *vfilters[JPC_QMFB1D_MAXCHANS];
+	int numhchans;
+	int numvchans;
+	jas_seq_t *tmpseq;
+
+	hseq = 0;
+	vseq = 0;
+
+	if (!(hseq = jas_seq_create(0, 1))) {
+		goto error;
+	}
+	jas_seq_set(hseq, 0, jpc_inttofix(1));
+	if (!(vseq = jas_seq_create(0, 1))) {
+		goto error;
+	}
+	jas_seq_set(vseq, 0, jpc_inttofix(1));
+
+	node = tsfbnode;
+	while (node) {
+		if (node->hqmfb) {
+			numhchans = jpc_qmfb1d_getnumchans(node->hqmfb);
+			if (jpc_qmfb1d_getsynfilters(node->hqmfb, width, hfilters)) {
+				goto error;
+			}
+			if (!(tmpseq = jpc_seq_upsample(hseq, numhchans))) {
+				goto error;
+			}
+			jas_seq_destroy(hseq);
+			hseq = tmpseq;
+			if (!(tmpseq = jpc_seq_conv(hseq, hfilters[bandnotohind(node, cldind)]))) {
+				goto error;
+			}
+			jas_seq_destroy(hfilters[0]);
+			jas_seq_destroy(hfilters[1]);
+			jas_seq_destroy(hseq);
+			hseq = tmpseq;
+		}
+		if (node->vqmfb) {
+			numvchans = jpc_qmfb1d_getnumchans(node->vqmfb);
+			if (jpc_qmfb1d_getsynfilters(node->vqmfb, height, vfilters)) {
+				abort();
+			}
+			if (!(tmpseq = jpc_seq_upsample(vseq, numvchans))) {
+				goto error;
+			}
+			jas_seq_destroy(vseq);
+			vseq = tmpseq;
+			if (!(tmpseq = jpc_seq_conv(vseq, vfilters[bandnotovind(node, cldind)]))) {
+				goto error;
+			}
+			jas_seq_destroy(vfilters[0]);
+			jas_seq_destroy(vfilters[1]);
+			jas_seq_destroy(vseq);
+			vseq = tmpseq;
+		}
+		if (node->parent) {
+			cldind = jpc_tsfbnode_findchild(node->parent, node);
+		}
+		node = node->parent;
+	}
+
+	*hfilter = hseq;
+	*vfilter = vseq;
+
+	return 0;
+
+error:
+	if (hseq) {
+		jas_seq_destroy(hseq);
+	}
+	if (vseq) {
+		jas_seq_destroy(vseq);
+	}
+	return -1;
+
+}
+
+static int jpc_tsfbnode_findchild(jpc_tsfbnode_t *parnode, jpc_tsfbnode_t *cldnode)
+{
+	int i;
+
+	for (i = 0; i < parnode->maxchildren; i++) {
+		if (parnode->children[i] == cldnode)
+			return i;
+	}
+	assert(0);
+	return -1;
+}
+
+jpc_tsfb_t *jpc_cod_gettsfb(int qmfbid, int numlevels)
+{
+	jpc_tsfb_t *tsfb;
+
+	switch (qmfbid) {
+	case JPC_COX_RFT:
+		qmfbid = JPC_QMFB1D_FT;
+		break;
+	case JPC_COX_INS:
+		qmfbid = JPC_QMFB1D_NS;
+		break;
+	default:
+		assert(0);
+		qmfbid = 10;
+		break;
+	}
+
+{
+	jpc_qmfb1d_t *hqmfb;
+	hqmfb = jpc_qmfb1d_make(qmfbid);
+	assert(hqmfb);
+	tsfb = jpc_tsfb_wavelet(hqmfb, hqmfb, numlevels);
+	assert(tsfb);
+	jpc_qmfb1d_destroy(hqmfb);
+}
+
+	return tsfb;
+}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_tsfb.h b/converter/other/jpeg2000/libjasper/jpc/jpc_tsfb.h
new file mode 100644
index 00000000..8670c22e
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_tsfb.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Tree-Structured Filter Bank (TSFB) Library
+ *
+ * $Id$
+ */
+
+#ifndef JPC_TSFB_H
+#define JPC_TSFB_H
+
+/******************************************************************************\
+* Includes.
+\******************************************************************************/
+
+#include "jasper/jas_seq.h"
+
+#include "jpc_fix.h"
+#include "jpc_qmfb.h"
+
+/******************************************************************************\
+* Constants.
+\******************************************************************************/
+
+#define	JPC_TSFB_MAXBANDS	(JPC_TSFB_MAXDEPTH * 3 + 1)
+#define	JPC_TSFB_MAXDEPTH	32
+#define	JPC_TSFB_RITIMODE	JPC_QMFB1D_RITIMODE
+
+#define	JPC_TSFB_MAXBANDSPERNODE	(JPC_QMFB1D_MAXCHANS * JPC_QMFB1D_MAXCHANS)
+
+#define	JPC_TSFB_PRUNEVERT	0x01
+#define	JPC_TSFB_PRUNEHORZ	0x02
+
+#define JPC_TSFB_LL	0
+#define JPC_TSFB_LH	1
+#define JPC_TSFB_HL	2
+#define JPC_TSFB_HH	3
+
+/******************************************************************************\
+* Types.
+\******************************************************************************/
+
+typedef struct {
+
+	int xstart;
+	int ystart;
+	int xend;
+	int yend;
+	int locxstart;
+	int locystart;
+	int locxend;
+	int locyend;
+
+} jpc_tsfbnodeband_t;
+
+typedef struct jpc_tsfbnode_s {
+
+	int numhchans;
+	int numvchans;
+	jpc_qmfb1d_t *hqmfb;
+	jpc_qmfb1d_t *vqmfb;
+	int maxchildren;
+	int numchildren;
+	struct jpc_tsfbnode_s *children[JPC_TSFB_MAXBANDSPERNODE];
+	struct jpc_tsfbnode_s *parent;
+
+} jpc_tsfbnode_t;
+
+typedef struct {
+	jpc_tsfbnode_t *root;
+} jpc_tsfb_t;
+
+typedef struct {
+	int xstart;
+	int ystart;
+	int xend;
+	int yend;
+	int orient;
+	int locxstart;
+	int locystart;
+	int locxend;
+	int locyend;
+	jpc_fix_t synenergywt;
+} jpc_tsfb_band_t;
+
+/******************************************************************************\
+* Functions.
+\******************************************************************************/
+
+/* Create a TSFB. */
+jpc_tsfb_t *jpc_cod_gettsfb(int qmfbid, int numlevels);
+
+/* Create a wavelet-type TSFB with the specified horizontal and vertical
+  QMFBs. */
+jpc_tsfb_t *jpc_tsfb_wavelet(jpc_qmfb1d_t *hqmfb, jpc_qmfb1d_t *vqmfb,
+  int numdlvls);
+
+/* Destroy a TSFB. */
+void jpc_tsfb_destroy(jpc_tsfb_t *tsfb);
+
+/* Perform analysis. */
+void jpc_tsfb_analyze(jpc_tsfb_t *tsfb, int flags, jas_seq2d_t *x);
+
+/* Perform synthesis. */
+void jpc_tsfb_synthesize(jpc_tsfb_t *tsfb, int flags, jas_seq2d_t *x);
+
+/* Get band information for a TSFB. */
+int jpc_tsfb_getbands(jpc_tsfb_t *tsfb, uint_fast32_t xstart,
+  uint_fast32_t ystart, uint_fast32_t xend, uint_fast32_t yend,
+  jpc_tsfb_band_t *bands);
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_util.c b/converter/other/jpeg2000/libjasper/jpc/jpc_util.c
new file mode 100644
index 00000000..82f4b285
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_util.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * $Id$
+ */
+
+/******************************************************************************\
+* Includes
+\******************************************************************************/
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "jasper/jas_math.h"
+#include "jasper/jas_malloc.h"
+
+#include "jpc_fix.h"
+#include "jpc_cs.h"
+#include "jpc_flt.h"
+#include "jpc_util.h"
+
+/******************************************************************************\
+* Miscellaneous Functions
+\******************************************************************************/
+
+int jpc_atoaf(const char *s, int *numvalues, double **values)
+{
+	static char delim[] = ", \t\n";
+	char buf[4096];
+	int n;
+	double *vs;
+	char *cp;
+
+	strncpy(buf, s, sizeof(buf));
+	buf[sizeof(buf) - 1] = '\0';
+	n = 0;
+	if ((cp = strtok(buf, delim))) {
+		++n;
+		while ((cp = strtok(0, delim))) {
+			if (cp != '\0') {
+				++n;
+			}
+		}
+	}
+
+	if (n) {
+		if (!(vs = jas_malloc(n * sizeof(double)))) {
+			return -1;
+		}
+
+		strncpy(buf, s, sizeof(buf));
+		buf[sizeof(buf) - 1] = '\0';
+		n = 0;
+		if ((cp = strtok(buf, delim))) {
+			vs[n] = atof(cp);
+			++n;
+			while ((cp = strtok(0, delim))) {
+				if (cp != '\0') {
+					vs[n] = atof(cp);
+					++n;
+				}
+			}
+		}
+	} else {
+		vs = 0;
+	}
+
+	*numvalues = n;
+	*values = vs;
+
+	return 0;
+}
+
+jas_seq_t *jpc_seq_upsample(jas_seq_t *x, int m)
+{
+	jas_seq_t *z;
+	int i;
+
+	if (!(z = jas_seq_create(jas_seq_start(x) * m, (jas_seq_end(x) - 1) * m + 1)))
+		return 0;
+	for (i = jas_seq_start(z); i < jas_seq_end(z); i++) {
+		*jas_seq_getref(z, i) = (!JAS_MOD(i, m)) ? jas_seq_get(x, i / m) :
+		  jpc_inttofix(0);
+	}
+
+	return z;
+}
+
+jpc_fix_t jpc_seq_norm(jas_seq_t *x)
+{
+	jpc_fix_t s;
+	int i;
+
+	s = jpc_inttofix(0);
+	for (i = jas_seq_start(x); i < jas_seq_end(x); i++) {
+		s = jpc_fix_add(s, jpc_fix_mul(jas_seq_get(x, i), jas_seq_get(x, i)));
+	}
+
+	return jpc_dbltofix(sqrt(jpc_fixtodbl(s)));
+}
+
+jas_seq_t *jpc_seq_conv(jas_seq_t *x, jas_seq_t *y)
+{
+	int i;
+	int j;
+	int k;
+	jas_seq_t *z;
+	jpc_fix_t s;
+	jpc_fix_t v;
+
+	z = jas_seq_create(jas_seq_start(x) + jas_seq_start(y),
+	  jas_seq_end(x) + jas_seq_end(y) - 1);
+	assert(z);
+	for (i = jas_seq_start(z); i < jas_seq_end(z); i++) {
+		s = jpc_inttofix(0);
+		for (j = jas_seq_start(y); j < jas_seq_end(y); j++) {
+			k = i - j;
+			if (k < jas_seq_start(x) || k >= jas_seq_end(x)) {
+				v = JPC_FIX_ZERO;
+			} else {
+				v = jas_seq_get(x, k);
+			}
+			s = jpc_fix_add(s, jpc_fix_mul(jas_seq_get(y, j), v));
+		}
+		*jas_seq_getref(z, i) = s;
+	}
+
+	return z;
+}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_util.h b/converter/other/jpeg2000/libjasper/jpc/jpc_util.h
new file mode 100644
index 00000000..c23fc33b
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_util.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+#ifndef JPC_UTIL_H
+#define JPC_UTIL_H
+
+/* Parse a comma separated list of real numbers into an array of doubles. */
+int jpc_atoaf(const char *s, int *numvalues, double **values);
+
+/* Upsample a sequence. */
+jas_seq_t *jpc_seq_upsample(jas_seq_t *seq, int n);
+
+/* Convolve two sequences. */
+jas_seq_t *jpc_seq_conv(jas_seq_t *seq0, jas_seq_t *seq1);
+
+/* Compute the norm of a sequence. */
+jpc_fix_t jpc_seq_norm(jas_seq_t *x);
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jpc/partlist b/converter/other/jpeg2000/libjasper/jpc/partlist
new file mode 100644
index 00000000..05c36ff9
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/jpc/partlist
@@ -0,0 +1 @@
+/mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_bs.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_cs.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_dec.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_enc.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_math.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_mct.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_mqdec.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_mqenc.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_tagtree.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_t1cod.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_t1dec.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_tsfb.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_t2cod.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_t2dec.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_t2enc.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_util.o
diff --git a/converter/other/jpeg2000/libjasper/partlist b/converter/other/jpeg2000/libjasper/partlist
new file mode 100644
index 00000000..4a088cee
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/partlist
@@ -0,0 +1,4 @@
+/mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_debug.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_getopt.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_image.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_init.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_malloc.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_seq.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_stream.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_string.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_tvp.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/base/jas_version.o
+/mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jp2/jp2_cod.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jp2/jp2_dec.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jp2/jp2_enc.o
+/mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_bs.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_cs.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_dec.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_enc.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_math.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_mct.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_mqdec.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_mqenc.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_tagtree.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_t1cod.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_t1dec.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_tsfb.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_t2cod.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_t2dec.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_t2enc.o /mp/hdb4d/home/bryanh/netpbm/converter/other/jpeg2000/libjasper/jpc/jpc_util.o
+
diff --git a/converter/other/jpeg2000/libjasper_compat.h b/converter/other/jpeg2000/libjasper_compat.h
new file mode 100644
index 00000000..401144a3
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper_compat.h
@@ -0,0 +1,24 @@
+/* Here's some stuff to create backward compatibility with older Jasper
+   libraries.  Unfortunately, new versions of the Jasper library are not
+   backward compatible with old applications.
+*/
+/* The color space thing got more complex between Version 1.600 and
+   1.701.  For example, it now allows for multiple kinds of RGB, whereas
+   in 1.600 RGB meant SRGB.  As part of that change, names changed
+   from "colorspace" to "clrspc".
+*/
+#if defined(jas_image_setcolorspace)
+/* Old style color space */
+#define jas_image_setclrspc jas_image_setcolorspace
+#define JAS_CLRSPC_GENRGB JAS_IMAGE_CS_RGB
+#define JAS_CLRSPC_GENGRAY JAS_IMAGE_CS_GRAY
+#define JAS_CLRSPC_UNKNOWN JAS_IMAGE_CS_UNKNOWN
+
+#define jas_clrspc_fam(clrspc) (clrspc)
+#define jas_image_clrspc jas_image_colorspace
+
+#define JAS_CLRSPC_FAM_RGB JAS_IMAGE_CS_RGB
+#define JAS_CLRSPC_FAM_GRAY JAS_IMAGE_CS_GRAY
+#define JAS_CLRSPC_FAM_UNKNOWN JAS_IMAGE_CS_UNKNOWN
+
+#endif
diff --git a/converter/other/jpeg2000/pamtojpeg2k.c b/converter/other/jpeg2000/pamtojpeg2k.c
new file mode 100644
index 00000000..76da46d2
--- /dev/null
+++ b/converter/other/jpeg2000/pamtojpeg2k.c
@@ -0,0 +1,522 @@
+/*****************************************************************************
+                              pamtojpeg2k
+******************************************************************************
+
+  Convert a PNM image to JPEG-2000 code stream image
+
+  By Bryan Henderson, San Jose CA  2002.10.26
+
+*****************************************************************************/
+
+#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 "pam.h"
+#include "shhopt.h"
+#include "nstring.h"
+#include "mallocvar.h"
+
+#include <jasper/jasper.h>
+#include "libjasper_compat.h"
+
+
+enum compmode {COMPMODE_INTEGER, COMPMODE_REAL};
+
+enum progression {PROG_LRCP, PROG_RLCP, PROG_RPCL, PROG_PCRL, PROG_CPRL};
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    char * inputFilename;
+    unsigned int imgareatlx;
+    unsigned int imgareatly;
+    unsigned int tilegrdtlx;
+    unsigned int tilegrdtly;
+    unsigned int tilewidth;
+    unsigned int tileheight;
+    unsigned int prcwidth;
+    unsigned int prcheight;
+    unsigned int cblkwidth;
+    unsigned int cblkheight;
+    enum compmode compmode;
+    float        compressionRatio;
+    char *       ilyrrates;
+    enum progression progression;
+    unsigned int numrlvls;
+    unsigned int numgbits;
+    unsigned int nomct;
+    unsigned int sop;
+    unsigned int eph;
+    unsigned int lazy;
+    unsigned int termall;
+    unsigned int segsym;
+    unsigned int vcausal;
+    unsigned int pterm;
+    unsigned int resetprob;
+    unsigned int debuglevel;  /* Jasper library debug level */
+    unsigned int verbose;
+};
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   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;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int imgareatlxSpec, imgareatlySpec;
+    unsigned int tilegrdtlxSpec, tilegrdtlySpec;
+    unsigned int tilewidthSpec, tileheightSpec;
+    unsigned int prcwidthSpec, prcheightSpec;
+    unsigned int cblkwidthSpec, cblkheightSpec;
+    unsigned int modeSpec, compressionSpec, ilyrratesSpec;
+    unsigned int progressionSpec, numrlvlsSpec, numgbitsSpec;
+    unsigned int debuglevelSpec;
+
+    char * progressionOpt;
+    char * modeOpt;
+
+    unsigned int option_def_index;
+    
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "imgareatlx",   OPT_UINT,   &cmdlineP->imgareatlx,
+            &imgareatlxSpec,       0);
+    OPTENT3(0, "imgareatly",   OPT_UINT,   &cmdlineP->imgareatly,
+            &imgareatlySpec,       0);
+    OPTENT3(0, "tilegrdtlx",   OPT_UINT,   &cmdlineP->tilegrdtlx,
+            &tilegrdtlxSpec,       0);
+    OPTENT3(0, "tilegrdtly",   OPT_UINT,   &cmdlineP->tilegrdtly,
+            &tilegrdtlySpec,       0);
+    OPTENT3(0, "tilewidth",    OPT_UINT,   &cmdlineP->tilewidth,
+            &tilewidthSpec,        0);
+    OPTENT3(0, "tileheight",   OPT_UINT,   &cmdlineP->tileheight,
+            &tileheightSpec,       0);
+    OPTENT3(0, "prcwidth",     OPT_UINT,   &cmdlineP->prcwidth,
+            &prcwidthSpec,       0);
+    OPTENT3(0, "prcheight",    OPT_UINT,   &cmdlineP->prcheight,
+            &prcheightSpec,      0);
+    OPTENT3(0, "cblkwidth",    OPT_UINT,   &cmdlineP->cblkwidth,
+            &cblkwidthSpec,      0);
+    OPTENT3(0, "cblkheight",   OPT_UINT,   &cmdlineP->cblkheight,
+            &cblkheightSpec,     0);
+    OPTENT3(0, "mode",         OPT_STRING, &modeOpt,
+            &modeSpec,           0);
+    OPTENT3(0, "compression",  OPT_FLOAT,  &cmdlineP->compressionRatio,
+            &compressionSpec,    0);
+    OPTENT3(0, "ilyrrates",    OPT_STRING, &cmdlineP->ilyrrates,
+            &ilyrratesSpec,      0);
+    OPTENT3(0, "progression",  OPT_STRING, &progressionOpt,
+            &progressionSpec,    0);
+    OPTENT3(0, "numrlvls",     OPT_UINT,   &cmdlineP->numrlvls,
+            &numrlvlsSpec,       0);
+    OPTENT3(0, "numgbits",     OPT_UINT,   &cmdlineP->numgbits,
+            &numgbitsSpec,       0);
+    OPTENT3(0, "nomct",        OPT_FLAG,   NULL, 
+            &cmdlineP->nomct,    0);
+    OPTENT3(0, "sop",          OPT_FLAG,   NULL, 
+            &cmdlineP->sop,      0);
+    OPTENT3(0, "eph",          OPT_FLAG,   NULL, 
+            &cmdlineP->eph,      0);
+    OPTENT3(0, "lazy",         OPT_FLAG,   NULL, 
+            &cmdlineP->lazy,     0);
+    OPTENT3(0, "termall",      OPT_FLAG,   NULL, 
+            &cmdlineP->termall,  0);
+    OPTENT3(0, "segsym",       OPT_FLAG,   NULL, 
+            &cmdlineP->segsym,    0);
+    OPTENT3(0, "vcausal",      OPT_FLAG,   NULL, 
+            &cmdlineP->vcausal,   0);
+    OPTENT3(0, "pterm",        OPT_FLAG,   NULL, 
+            &cmdlineP->pterm,     0);
+    OPTENT3(0, "resetprob",    OPT_FLAG,   NULL, 
+            &cmdlineP->resetprob, 0);
+    OPTENT3(0, "verbose",      OPT_FLAG,   NULL, 
+            &cmdlineP->verbose,   0);
+    OPTENT3(0, "debuglevel",   OPT_UINT,   &cmdlineP->debuglevel,
+            &debuglevelSpec,      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 (!imgareatlxSpec)
+        cmdlineP->imgareatlx = 0;
+    if (!imgareatlySpec)
+        cmdlineP->imgareatly = 0;
+    if (!tilegrdtlxSpec)
+        cmdlineP->tilegrdtlx = 0;
+    if (!tilegrdtlySpec)
+        cmdlineP->tilegrdtly = 0;
+    if (!tilewidthSpec)
+        cmdlineP->tilewidth = 0;
+    if (!tileheightSpec)
+        cmdlineP->tileheight = 0;
+    if (!prcwidthSpec)
+        cmdlineP->prcwidth = 32768;
+    if (!prcheightSpec)
+        cmdlineP->prcheight = 32768;
+    if (!cblkwidthSpec)
+        cmdlineP->cblkwidth = 64;
+    if (!cblkheightSpec)
+        cmdlineP->cblkheight = 64;
+    if (modeSpec) {
+        if (strcmp(modeOpt, "integer") == 0 || strcmp(modeOpt, "int") == 0)
+            cmdlineP->compmode = COMPMODE_INTEGER;
+        else if (strcmp(modeOpt, "real") == 0)
+            cmdlineP->compmode = COMPMODE_REAL;
+        else
+            pm_error("Invalid value for 'mode' option: '%s'.  "
+                     "valid values are 'INTEGER' and 'REAL'", modeOpt);
+    } else
+        cmdlineP->compmode = COMPMODE_INTEGER;
+    if (compressionSpec) {
+        if (cmdlineP->compressionRatio < 1.0)
+            pm_error("Compression ratio less than 1 does not make sense.");
+    } else
+        cmdlineP->compressionRatio = 1.0;
+    if (!ilyrratesSpec)
+        cmdlineP->ilyrrates = (char*) "";
+    if (progressionSpec) {
+        if (strcmp(progressionOpt, "lrcp") == 0)
+            cmdlineP->progression = PROG_LRCP;
+        if (strcmp(progressionOpt, "rlcp") == 0)
+            cmdlineP->progression = PROG_RLCP;
+        if (strcmp(progressionOpt, "rpcl") == 0)
+            cmdlineP->progression = PROG_RPCL;
+        if (strcmp(progressionOpt, "pcrl") == 0)
+            cmdlineP->progression = PROG_PCRL;
+        if (strcmp(progressionOpt, "cprl") == 0)
+            cmdlineP->progression = PROG_CPRL;
+        else
+            pm_error("Invalid value for -progression: '%s'.  "
+                     "Valid values are lrcp, rlcp, rpcl, pcrl, and cprl.",
+                     progressionOpt);
+    } else
+        cmdlineP->progression = PROG_LRCP;
+    if (!numrlvlsSpec)
+        cmdlineP->numrlvls = 6;
+    if (!numgbitsSpec)
+        cmdlineP->numgbits = 2;
+    if (!debuglevelSpec)
+        cmdlineP->debuglevel = 0;
+
+    if (argc - 1 == 0)
+        cmdlineP->inputFilename = strdup("-");  /* he wants stdin */
+    else if (argc - 1 == 1)
+        cmdlineP->inputFilename = strdup(argv[1]);
+    else 
+        pm_error("Too many arguments.  The only argument accepted\n"
+                 "is the input file specification");
+
+}
+
+
+
+static void
+createJasperRaster(struct pam *  const inpamP, 
+                   jas_image_t * const jasperP) {
+
+    jas_matrix_t ** matrix;  /* malloc'ed */
+        /* matrix[X] is the data for Plane X of the current row */
+    unsigned int plane;
+    unsigned int row;
+    tuple * tuplerow;
+    bool oddMaxval;
+    sample jasperMaxval;
+
+    MALLOCARRAY_NOFAIL(matrix, inpamP->depth);
+
+    for (plane = 0; plane < inpamP->depth; ++plane) {
+        matrix[plane] = jas_matrix_create(1, inpamP->width);
+
+        if (matrix[plane] == NULL)
+            pm_error("Unable to create matrix for plane %u.  "
+                     "jas_matrix_create() failed.", plane);
+    }   
+    tuplerow = pnm_allocpamrow(inpamP);
+
+    jasperMaxval = pm_bitstomaxval(pm_maxvaltobits(inpamP->maxval));
+    oddMaxval = jasperMaxval != inpamP->maxval;
+
+    for (row = 0; row < inpamP->height; ++row) {
+        unsigned int col;
+
+        pnm_readpamrow(inpamP, tuplerow);
+
+        for (col = 0; col < inpamP->width; ++col) {
+            unsigned int plane;
+            for (plane = 0; plane < inpamP->depth; ++plane) {
+                unsigned int jasperSample;
+
+                if (oddMaxval)
+                    jasperSample = tuplerow[col][plane] * 
+                        jasperMaxval / inpamP->maxval;
+                else
+                    jasperSample = tuplerow[col][plane];
+
+                jas_matrix_set(matrix[plane], 0, col, jasperSample);
+            }
+        }
+        { 
+            unsigned int plane;
+
+            for (plane = 0; plane < inpamP->depth; ++plane) {
+                int rc;
+                rc = jas_image_writecmpt(jasperP, plane, 0, row, 
+                                         inpamP->width, 1,
+                                         matrix[plane]);
+                if (rc != 0)
+                    pm_error("jas_image_writecmpt() of plane %u failed.", 
+                             plane);
+            }
+        }
+    }
+
+    pnm_freepamrow(tuplerow);
+    for (plane = 0; plane < inpamP->depth; ++plane)
+        jas_matrix_destroy(matrix[plane]);
+    
+    free(matrix);
+}
+
+
+
+static void
+createJasperImage(struct pam *   const inpamP, 
+                  jas_image_t ** const jasperPP) {
+
+	jas_image_cmptparm_t * cmptparms;
+    unsigned int plane;
+
+    MALLOCARRAY_NOFAIL(cmptparms, inpamP->depth);
+
+    for (plane = 0; plane < inpamP->depth; ++plane) {
+        cmptparms[plane].tlx = 0;
+        cmptparms[plane].tly = 0;
+        cmptparms[plane].hstep = 1;
+        cmptparms[plane].vstep = 1;
+        cmptparms[plane].width = inpamP->width;
+        cmptparms[plane].height = inpamP->height;
+        cmptparms[plane].prec = pm_maxvaltobits(inpamP->maxval);
+        cmptparms[plane].sgnd = 0;
+    }
+    *jasperPP = 
+        jas_image_create(inpamP->depth, cmptparms, JAS_CLRSPC_UNKNOWN);
+    if (*jasperPP == NULL)
+        pm_error("Unable to create jasper image structure.  "
+                 "jas_image_create() failed.");
+
+    free(cmptparms);
+}
+
+
+
+static void
+convertToJasperImage(struct pam *   const inpamP,
+                     jas_image_t ** const jasperPP) {
+
+    jas_image_t * jasperP;
+
+    createJasperImage(inpamP, &jasperP);
+
+    if (strncmp(inpamP->tuple_type, "RGB", 3) == 0) {
+        if (inpamP->depth < 3)
+            pm_error("Input tuple type is RGB*, but depth is only %d.  "
+                     "It should be at least 3.", inpamP->depth);
+        else {
+            jas_image_setclrspc(jasperP, JAS_CLRSPC_GENRGB);
+            jas_image_setcmpttype(jasperP, 0,
+                                  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_R));
+            jas_image_setcmpttype(jasperP, 1,
+                                  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_G));
+            jas_image_setcmpttype(jasperP, 2,
+                                  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_B));
+        }
+    } else {
+        if (strncmp(inpamP->tuple_type, "GRAYSCALE", 9 == 0) ||
+            strncmp(inpamP->tuple_type, "BLACKANDWHITE", 13) == 0) {
+            jas_image_setclrspc(jasperP, JAS_CLRSPC_GENGRAY);
+            jas_image_setcmpttype(jasperP, 0,
+                                  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_GRAY_Y));
+        }
+    }
+
+    createJasperRaster(inpamP, jasperP);
+
+    *jasperPP = jasperP;
+}
+
+
+
+static void
+writeJpc(jas_image_t *      const jasperP, 
+         struct cmdlineInfo const cmdline,
+         FILE *             const ofP) {
+
+    jas_stream_t * outStreamP;
+    const char * options;
+    const char * ilyrratesOpt;
+    const char * prgValue;
+    char rateOpt[20+1];
+
+    /* Note: ilyrrates is a hack because we're too lazy to properly parse
+       command line options to get the information and then compose
+       a proper input to Jasper.  So the user can screw things up by 
+       specifying garbage for the -ilyrrates option 
+    */
+    if (strlen(cmdline.ilyrrates) > 0)
+        asprintfN(&ilyrratesOpt, "ilyrrates=%s", cmdline.ilyrrates);
+    else
+        ilyrratesOpt = strdup("");
+
+    switch(cmdline.progression) {
+    case PROG_LRCP: prgValue = "lrcp"; break;
+    case PROG_RLCP: prgValue = "rlcp"; break;
+    case PROG_RPCL: prgValue = "rcpc"; break;
+    case PROG_PCRL: prgValue = "pcrl"; break;
+    case PROG_CPRL: prgValue = "cprl"; break;
+    }
+
+    /* Note that asprintfN() doesn't understand %f, but sprintf() does */
+
+    sprintf(rateOpt, "%1.9f", 1.0/cmdline.compressionRatio);
+
+    asprintfN(&options, 
+              "imgareatlx=%u "
+              "imgareatly=%u "
+              "tilegrdtlx=%u "
+              "tilegrdtly=%u "
+              "tilewidth=%u "
+              "tileheight=%u "
+              "prcwidth=%u "
+              "prcheight=%u "
+              "cblkwidth=%u "
+              "cblkheight=%u "
+              "mode=%s "
+              "rate=%s "
+              "%s "
+              "prg=%s "
+              "numrlvls=%u "
+              "numgbits=%u "
+              "%s %s %s %s %s %s %s %s %s",
+
+              cmdline.imgareatlx,
+              cmdline.imgareatly,
+              cmdline.tilegrdtlx,
+              cmdline.tilegrdtlx,
+              cmdline.tilewidth,
+              cmdline.tileheight,
+              cmdline.prcwidth,
+              cmdline.prcheight,
+              cmdline.cblkwidth,
+              cmdline.cblkheight,
+              cmdline.compmode == COMPMODE_INTEGER ? "int" : "real",
+              rateOpt,
+              ilyrratesOpt,
+              prgValue,
+              cmdline.numrlvls,
+              cmdline.numgbits,
+              cmdline.nomct     ? "nomct"     : "",
+              cmdline.sop       ? "sop"       : "",
+              cmdline.eph       ? "eph"       : "",
+              cmdline.lazy      ? "lazy"      : "",
+              cmdline.termall   ? "termall"   : "",
+              cmdline.segsym    ? "segsym"    : "",
+              cmdline.vcausal   ? "vcausal"   : "",
+              cmdline.pterm     ? "pterm"     : "",
+              cmdline.resetprob ? "resetprob" : ""
+        );
+    strfree(ilyrratesOpt);
+
+
+    /* Open the output image file (Standard Output) */
+    outStreamP = jas_stream_fdopen(fileno(ofP), "w+b");
+    if (outStreamP == NULL)
+        pm_error("Unable to open output stream.  jas_stream_fdopen() "
+                 "failed");
+
+    {
+        int rc;
+
+        if (cmdline.verbose)
+            pm_message("Using Jasper to encode to 'jpc' format with options "
+                       "'%s'", options);
+
+        rc = jas_image_encode(jasperP, outStreamP, 
+                              jas_image_strtofmt((char*)"jpc"), 
+                              (char*)options);
+        if (rc != 0)
+            pm_error("jas_image_encode() failed to encode the JPEG 2000 "
+                     "image.  Rc=%d", rc);
+    }
+	jas_stream_flush(outStreamP);
+
+    {
+        int rc;
+
+        rc = jas_stream_close(outStreamP);
+            
+        if (rc != 0)
+            pm_error("Failed to close output stream, "
+                     "jas_stream_close() rc = %d", rc);
+    }                     
+
+	jas_image_clearfmts();
+
+    strfree(options);
+}
+
+
+
+int
+main(int argc, char **argv)
+{
+    struct cmdlineInfo cmdline;
+    FILE *ifP;
+    struct pam inpam;
+    jas_image_t * jasperP;
+
+    pnm_init(&argc, argv);
+    
+    parseCommandLine(argc, argv, &cmdline);
+    
+    { 
+        int rc;
+        
+        rc = jas_init();
+        if ( rc != 0 )
+            pm_error("Failed to initialize Jasper library.  "
+                     "jas_init() returns rc %d", rc );
+    }
+    
+    jas_setdbglevel(cmdline.debuglevel);
+    
+    ifP = pm_openr(cmdline.inputFilename);
+    
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+    
+    convertToJasperImage(&inpam, &jasperP);
+    
+    writeJpc(jasperP, cmdline, stdout);
+    
+	jas_image_destroy(jasperP);
+
+    pm_close(ifP);
+
+    pm_close(stdout);
+    
+    return 0;
+}
diff --git a/converter/other/jpegdatasource.c b/converter/other/jpegdatasource.c
new file mode 100644
index 00000000..5c1070e4
--- /dev/null
+++ b/converter/other/jpegdatasource.c
@@ -0,0 +1,183 @@
+/*****************************************************************************
+                           jpegdatasource.c
+******************************************************************************
+   This is the data source manager that Jpegtopnm supplies to the JPEG
+   library.  The Jpeg library uses this object to get JPEG input.
+
+   This data source manager is the same as the Jpeg library's built in
+   "stdio" one, except that it looks ahead and one can query it to see
+   if there is any data in the stream that the Jpeg library hasn't seen
+   yet.  Thus, you can use it in a program that reads multiple JPEG 
+   images and know when to stop.  You can also nicely handle completely
+   empty input files more gracefully than just crying input error.
+
+   This data source manager does 4K fread() reads and passes 4K buffers 
+   to the Jpeg library.  It reads one 4K block ahead, so there is up to
+   8K of image buffered at any time.
+
+   By Bryan Henderson, San Jose CA 2002.10.13
+*****************************************************************************/
+
+#include <ctype.h>	   
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+/* Note: jpeglib.h prerequires stdlib.h and ctype.h.  It should include them
+   itself, but doesn't.
+*/
+#include <jpeglib.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "pnm.h"
+
+#include "jpegdatasource.h"
+
+#define BUFFER_SIZE 4096
+
+struct sourceManager {
+    /* The following structure _must_ be the first thing in this structure,
+       because the methods below assume the address of the struct
+       jpeg_source_mgr is the same as the address of the struct sourceManager.
+       The former address is the only information the Jpeg library gives
+       these methods to tell them on what source manager object they are
+       supposed to operate.
+    */
+    struct jpeg_source_mgr jpegSourceMgr;
+    FILE * ifP;
+    JOCTET * currentBuffer;
+    JOCTET * nextBuffer;
+    unsigned int bytesInNextBuffer;
+    JOCTET buffer1[BUFFER_SIZE];
+    JOCTET buffer2[BUFFER_SIZE];
+};
+
+
+static void
+dsInitSource(j_decompress_ptr const cinfoP) {
+/*----------------------------------------------------------------------------
+   This is the init_source method for the data source manager the Jpeg
+   library uses.
+-----------------------------------------------------------------------------*/
+    /* The object was created ready to go and remains ready from one image
+       to the next.  No per-image initialization is necessary.
+    */
+}
+
+
+
+static boolean
+dsFillInputBuffer(j_decompress_ptr const cinfoP) {
+/*----------------------------------------------------------------------------
+   This is the fill_input_buffer method for the data source manager the Jpeg
+   library uses.
+-----------------------------------------------------------------------------*/
+    struct sourceManager * const srcP = (struct sourceManager *) cinfoP->src;
+
+    if (srcP->bytesInNextBuffer == 0) 
+        pm_error("End-of-file encountered in the middle of JPEG image.");
+    else {
+        /* Rotate the buffers */
+        srcP->jpegSourceMgr.next_input_byte = srcP->nextBuffer;
+        srcP->jpegSourceMgr.bytes_in_buffer = srcP->bytesInNextBuffer;
+        {
+            JOCTET * tmp;
+            tmp = srcP->nextBuffer;
+            srcP->nextBuffer = srcP->currentBuffer;
+            srcP->currentBuffer = tmp;
+        }
+
+        /* Fill the new 'next' buffer */
+        srcP->bytesInNextBuffer = 
+            fread(srcP->nextBuffer, 1, BUFFER_SIZE, srcP->ifP);
+    }
+    return TRUE;
+}
+
+
+
+static void
+dsSkipInputData(j_decompress_ptr const cinfoP, long const num_bytes) {
+/*----------------------------------------------------------------------------
+   This is the skip_input_data method for the data source manager the Jpeg
+   library uses.
+-----------------------------------------------------------------------------*/
+    struct jpeg_source_mgr * const jpegSourceMgrP = cinfoP->src;
+
+    long i;
+
+    for (i = 0; i < num_bytes; ++i) {
+        if (jpegSourceMgrP->bytes_in_buffer == 0)
+            dsFillInputBuffer(cinfoP);
+        ++jpegSourceMgrP->next_input_byte;
+        --jpegSourceMgrP->bytes_in_buffer;
+    }
+}
+
+
+
+static void
+dsTermSource(j_decompress_ptr const cinfoP) {
+/*----------------------------------------------------------------------------
+   This is the term_source method for the data source manager the Jpeg
+   library uses.
+-----------------------------------------------------------------------------*/
+    /* We couldn't care less that the Jpeg library is done reading an
+       image.  The source manager object remains active and ready for the
+       Jpeg library to read the next image.
+    */
+}
+
+
+
+bool
+dsDataLeft(struct sourceManager * const srcP) {
+
+    return((srcP->jpegSourceMgr.bytes_in_buffer > 0 ||
+            srcP->bytesInNextBuffer > 0));
+}
+
+
+
+struct sourceManager * 
+dsCreateSource(const char * const fileName) {
+
+    struct sourceManager * srcP;
+
+    MALLOCVAR(srcP);
+    if (srcP == NULL)
+        pm_error("Unable to get memory for the Jpeg library source manager.");
+
+    srcP->ifP = pm_openr(fileName);
+
+    srcP->jpegSourceMgr.init_source = dsInitSource;
+    srcP->jpegSourceMgr.fill_input_buffer = dsFillInputBuffer;
+    srcP->jpegSourceMgr.skip_input_data = dsSkipInputData;
+    srcP->jpegSourceMgr.resync_to_restart = jpeg_resync_to_restart;
+    srcP->jpegSourceMgr.term_source = dsTermSource;
+    
+    srcP->currentBuffer = srcP->buffer1;
+    srcP->nextBuffer = srcP->buffer2;
+    srcP->jpegSourceMgr.bytes_in_buffer = 
+        fread(srcP->currentBuffer, 1, BUFFER_SIZE, srcP->ifP);
+    srcP->jpegSourceMgr.next_input_byte = srcP->currentBuffer;
+    srcP->bytesInNextBuffer = 
+        fread(srcP->nextBuffer, 1, BUFFER_SIZE, srcP->ifP);
+
+    return srcP;
+}
+
+void
+dsDestroySource(struct sourceManager * const srcP) {
+    
+    pm_close(srcP->ifP);
+    free(srcP);
+
+}
+
+
+
+struct jpeg_source_mgr *
+dsJpegSourceMgr(struct sourceManager * const srcP) {
+    return &srcP->jpegSourceMgr;
+}
diff --git a/converter/other/jpegdatasource.h b/converter/other/jpegdatasource.h
new file mode 100644
index 00000000..07f17389
--- /dev/null
+++ b/converter/other/jpegdatasource.h
@@ -0,0 +1,18 @@
+#ifndef JPEGDATASOURCE_H_INCLUDED
+#define JPEGDATASOURCE_H_INCLUDED
+
+#include "pm_c_util.h"
+
+struct sourceManager * 
+dsCreateSource(const char * const fileName);
+
+void
+dsDestroySource(struct sourceManager * const srcP);
+
+bool
+dsDataLeft(struct sourceManager * const srcP);
+
+struct jpeg_source_mgr *
+dsJpegSourceMgr(struct sourceManager * const srcP);
+
+#endif
diff --git a/converter/other/jpegtopnm.c b/converter/other/jpegtopnm.c
new file mode 100644
index 00000000..60ae7e42
--- /dev/null
+++ b/converter/other/jpegtopnm.c
@@ -0,0 +1,970 @@
+/*****************************************************************************
+                                jpegtopnm
+******************************************************************************
+  This program is part of the Netpbm package.
+
+  This program converts from the JFIF format, which is based on JPEG, to
+  the fundamental ppm or pgm format (depending on whether the JFIF 
+  image is gray scale or color).
+
+  This program is by Bryan Henderson on 2000.03.20, but is derived
+  with permission from the program djpeg, which is in the Independent
+  Jpeg Group's JPEG library package.  Under the terms of that permission,
+  redistribution of this software is restricted as described in the 
+  file README.JPEG.
+
+  Copyright (C) 1991-1998, Thomas G. Lane.
+
+  TODO:
+
+    For CMYK and YCCK JPEG input, optionally produce a 4-deep PAM
+    output containing CMYK values.  Define a tupletype for this.
+    Extend pamtoppm to convert this to ppm using the standard
+    transformation.
+
+    See if additional decompressor options effects signficant speedup.
+    grayscale output of color image, downscaling, color quantization, and
+    dithering are possibilities.  Djpeg's man page says they make it faster.
+
+  IMPLEMENTATION NOTE - PRECISION
+
+    There are two versions of the JPEG library.  One handles only JPEG
+    files with 8 bit samples; the other handles only 12 bit files.
+    This program may be compiled and linked against either, and run
+    dynamically linked to either at runtime independently.  It uses
+    the precision information from the file header.  Note that when
+    the input has 12 bit precision, this program generates PPM files
+    with two-byte samples, but when the input has 8 bit precision, it
+    generates PPM files with one-byte samples.  One should use
+    Pnmdepth to reduce precision to 8 bits if one-byte-sample output
+    is essential.
+
+  IMPLEMENTATION NOTE - EXIF
+
+    See http://exif.org.  See the programs Exifdump
+    (http://topo.math.u-psud.fr/~bousch/exifdump.py) and Jhead
+    (http://www.sentex.net/~mwandel/jhead).
+
+    
+*****************************************************************************/
+
+#define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include <ctype.h>		/* to declare isprint() */
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+/* Note: jpeglib.h prerequires stdlib.h and ctype.h.  It should include them
+   itself, but doesn't.
+*/
+#include <jpeglib.h>
+#include "pnm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "exif.h"
+#include "jpegdatasource.h"
+
+#define EXIT_WARNING 2  /* Goes with EXIT_FAILURE, EXIT_SUCCESS in stdlib.h */
+
+enum inklevel {NORMAL, ADOBE, GUESS};
+   /* This describes image samples that represent ink levels.  NORMAL
+      means 0 is no ink; ADOBE means 0 is maximum ink.  GUESS means we
+      don't know what 0 means, so we have to guess from information in 
+      the image.
+      */
+
+enum colorspace {
+    /* These are the color spaces in which we can get pixels from the
+       JPEG decompressor.  We include only those that are possible
+       given our particular inputs to the decompressor.  The
+       decompressor is theoretically capable of other, e.g. YCCK.
+       Unlike the JPEG library, this type distinguishes between the
+       Adobe and non-Adobe style of CMYK samples.  
+    */
+    GRAYSCALE_COLORSPACE,
+    RGB_COLORSPACE, 
+    CMYK_NORMAL_COLORSPACE, 
+    CMYK_ADOBE_COLORSPACE
+    };
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    char *input_filespec;
+    char *exif_filespec;
+        /* Filespec in which to save EXIF information.  NULL means don't
+           save.  "-" means standard output
+        */
+    unsigned int verbose;
+    unsigned int nosmooth;
+    J_DCT_METHOD dct_method;
+    long int max_memory_to_use;
+    unsigned int trace_level;
+    enum inklevel inklevel;
+    unsigned int comments;
+    unsigned int dumpexif;
+    unsigned int multiple;
+};
+
+
+static bool displayComments;
+    /* User wants comments from the JPEG to be displayed */
+
+static void 
+interpret_maxmemory (bool         const maxmemorySpec,
+                     const char * const maxmemory, 
+                     long int *   const max_memory_to_use_p) { 
+/*----------------------------------------------------------------------------
+   Interpret the "maxmemory" command line option.
+-----------------------------------------------------------------------------*/
+    long int lval;
+    char ch;
+    
+    if (!maxmemorySpec) {
+        *max_memory_to_use_p = -1;  /* unspecified */
+    } else if (sscanf(maxmemory, "%ld%c", &lval, &ch) < 1) {
+        pm_error("Invalid value for --maxmemory option: '%s'.", maxmemory);
+    } else {
+        if (ch == 'm' || ch == 'M') lval *= 1000L;
+        *max_memory_to_use_p = lval * 1000L;
+    }
+}
+
+
+static void
+interpret_adobe(const int adobe, const int notadobe, 
+                enum inklevel * const inklevel_p) {
+/*----------------------------------------------------------------------------
+   Interpret the adobe/notadobe command line options
+-----------------------------------------------------------------------------*/
+    if (adobe && notadobe)
+        pm_error("You cannot specify both -adobe and -notadobe options.");
+    else {
+        if (adobe)
+            *inklevel_p = ADOBE;
+        else if (notadobe)
+            *inklevel_p = NORMAL;
+        else 
+            *inklevel_p = GUESS;
+    }
+}
+
+
+
+static void
+parse_command_line(const 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!
+
+   On the other hand, unlike other option processing functions, we do
+   not change argv at all.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    int i;  /* local loop variable */
+
+    char *maxmemory;
+    char *dctval;
+    unsigned int adobe, notadobe;
+
+    unsigned int tracelevelSpec, exifSpec, dctvalSpec, maxmemorySpec;
+    unsigned int option_def_index;
+
+    int argc_parse;       /* argc, except we modify it as we parse */
+    char ** argv_parse;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+    MALLOCARRAY_NOFAIL(argv_parse, argc);
+    
+    /* argv, except we modify it as we parse */
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "verbose",     OPT_FLAG,   NULL, &cmdlineP->verbose,       0);
+    OPTENT3(0, "dct",         OPT_STRING, &dctval,
+            &dctvalSpec, 0);
+    OPTENT3(0, "maxmemory",   OPT_STRING, &maxmemory,
+            &maxmemorySpec, 0); 
+    OPTENT3(0, "nosmooth",    OPT_FLAG,   NULL, &cmdlineP->nosmooth,      0);
+    OPTENT3(0, "tracelevel",  OPT_UINT,   &cmdlineP->trace_level,   
+            &tracelevelSpec, 0);
+    OPTENT3(0, "adobe",       OPT_FLAG,   NULL, &adobe,                   0);
+    OPTENT3(0, "notadobe",    OPT_FLAG,   NULL, &notadobe,                0);
+    OPTENT3(0, "comments",    OPT_FLAG,   NULL, &cmdlineP->comments,      0);
+    OPTENT3(0, "exif",        OPT_STRING, &cmdlineP->exif_filespec, 
+            &exifSpec, 0);
+    OPTENT3(0, "dumpexif",    OPT_FLAG,   NULL, &cmdlineP->dumpexif,      0);
+    OPTENT3(0, "multiple",    OPT_FLAG,   NULL, &cmdlineP->multiple,      0);
+
+    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 */
+
+    /* Make private copy of arguments for optParseOptions to corrupt */
+    argc_parse = argc;
+    for (i=0; i < argc; i++) argv_parse[i] = argv[i];
+
+    optParseOptions3( &argc_parse, argv_parse, opt, sizeof(opt), 0);
+        /* Uses and sets argc_parse, argv_parse, 
+           and some of *cmdlineP and others. */
+
+    if (!tracelevelSpec)
+        cmdlineP->trace_level = 0;
+
+    if (!exifSpec)
+        cmdlineP->exif_filespec = NULL;
+
+    if (argc_parse - 1 == 0)
+        cmdlineP->input_filespec = strdup("-");  /* he wants stdin */
+    else if (argc_parse - 1 == 1)
+        cmdlineP->input_filespec = strdup(argv_parse[1]);
+    else 
+        pm_error("Too many arguments.  The only argument accepted "
+                 "is the input file specification");
+
+    if (!dctvalSpec)
+        cmdlineP->dct_method = JDCT_DEFAULT;
+    else {
+        if (STREQ(dctval, "int"))
+            cmdlineP->dct_method = JDCT_ISLOW;
+        else if (STREQ(dctval, "fast"))
+            cmdlineP->dct_method = JDCT_IFAST;
+        else if (STREQ(dctval, "float"))
+            cmdlineP->dct_method = JDCT_FLOAT;
+        else pm_error("Invalid value for the --dct option: '%s'.", dctval);
+    }
+
+    interpret_maxmemory(maxmemorySpec, maxmemory, 
+                        &cmdlineP->max_memory_to_use);
+
+    interpret_adobe(adobe, notadobe, &cmdlineP->inklevel);
+
+    free(argv_parse);
+}
+
+
+/*
+ * Marker processor for COM and interesting APPn markers.
+ * This replaces the library's built-in processor, which just skips the marker.
+ * We want to print out the marker as text, to the extent possible.
+ * Note this code relies on a non-suspending data source.
+ */
+
+#if 0
+static unsigned int
+jpeg_getc (j_decompress_ptr cinfo)
+/* Read next byte */
+{
+  struct jpeg_source_mgr * datasrc = cinfo->src;
+
+  if (datasrc->bytes_in_buffer == 0) {
+      if (! (*datasrc->fill_input_buffer) (cinfo)) 
+          pm_error("Can't suspend here.");
+  }
+  datasrc->bytes_in_buffer--;
+  return GETJOCTET(*datasrc->next_input_byte++);
+}
+
+
+static boolean
+print_text_marker (j_decompress_ptr cinfo) {
+/*----------------------------------------------------------------------------
+   This is a routine that you can register with the Jpeg decompressor
+   with e.g.
+
+     jpeg_set_marker_processor(cinfoP, JPEG_APP0 + app_type, 
+                               print_text_marker);
+
+  The decompressor then calls it when it encounters a miscellaneous marker
+  of the specified type (e.g. APP1).  At that time, the jpeg input stream
+  is positioned to the marker contents -- first 2 bytes of length information,
+  MSB first, where the length includes those two bytes, then the data.
+  
+  We just get and print the contents of the marker.
+
+  This routine is no longer used; it is kept as an example in case we want
+  to use it in the future.  Instead, we use jpeg_save_markers() and have
+  the Jpeg library store all the markers in memory for our later access.
+-----------------------------------------------------------------------------*/
+    const boolean traceit = (cinfo->err->trace_level >= 1);
+    const boolean display_value = 
+        traceit || (cinfo->unread_marker == JPEG_COM && displayComments);
+    
+    INT32 length;
+    unsigned int ch;
+    unsigned int lastch = 0;
+    
+    length = jpeg_getc(cinfo) << 8;
+    length += jpeg_getc(cinfo);
+    length -= 2;			/* discount the length word itself */
+
+    if (traceit) {
+        if (cinfo->unread_marker == JPEG_COM)
+            fprintf(stderr, "Comment, length %ld:\n", (long) length);
+        else			/* assume it is an APPn otherwise */
+            fprintf(stderr, "APP%d, length %ld:\n",
+                    cinfo->unread_marker - JPEG_APP0, (long) length);
+    }
+    
+    if (cinfo->unread_marker == JPEG_COM && displayComments)
+        fprintf(stderr, "COMMENT: ");
+    
+    while (--length >= 0) {
+        ch = jpeg_getc(cinfo);
+        if (display_value) {
+            /* Emit the character in a readable form.
+             * Nonprintables are converted to \nnn form,
+             * while \ is converted to \\.
+             * Newlines in CR, CR/LF, or LF form will be printed as one 
+             * newline.
+             */
+            if (ch == '\r') {
+              fprintf(stderr, "\n");
+            } else if (ch == '\n') {
+                if (lastch != '\r')
+                    fprintf(stderr, "\n");
+            } else if (ch == '\\') {
+                fprintf(stderr, "\\\\");
+            } else if (isprint(ch)) {
+                putc(ch, stderr);
+            } else {
+                fprintf(stderr, "\\%03o", ch);
+            }
+          lastch = ch;
+        }
+    }
+    
+    if (display_value)
+        fprintf(stderr, "\n");
+    
+    return TRUE;
+}
+#endif
+
+
+
+static void
+print_marker(struct jpeg_marker_struct const marker) {
+
+    if (marker.original_length != marker.data_length) {
+        /* This should be impossible, because we asked for up to 65535
+           bytes, and the jpeg spec doesn't allow anything bigger than that.
+        */
+        pm_message("INTERNAL ERROR: %d of %d bytes of marker were "
+                   "saved.", marker.data_length, marker.original_length);
+    }
+
+    {
+        int i;
+        JOCTET lastch;
+
+        lastch = 0;
+        for (i = 0; i < marker.data_length; i++) {
+            /* Emit the character in a readable form.
+             * Nonprintables are converted to \nnn form,
+             * while \ is converted to \\.
+             * Newlines in CR, CR/LF, or LF form will be printed as one 
+             * newline.
+             */
+            if (marker.data[i] == '\r') {
+                fprintf(stderr, "\n");
+            } else if (marker.data[i] == '\n') {
+                if (lastch != '\r')
+                    fprintf(stderr, "\n");
+            } else if (marker.data[i] == '\\') {
+                fprintf(stderr, "\\\\");
+            } else if (isprint(marker.data[i])) {
+                putc(marker.data[i], stderr);
+            } else {
+                fprintf(stderr, "\\%03o", marker.data[i]);
+            }
+            lastch = marker.data[i];
+        }
+        fprintf(stderr, "\n");
+    }
+}
+
+
+typedef struct rgb {unsigned int r; unsigned int g; unsigned int b;} rgb_type;
+
+
+static rgb_type *
+read_rgb(JSAMPLE *ptr, const enum colorspace color_space, 
+         const unsigned int maxval) {
+/*----------------------------------------------------------------------------
+  Return the RGB triple corresponding to the color of the JPEG pixel at
+  'ptr', which is in color space 'color_space'.  
+
+  Assume 'maxval' is the maximum sample value in the input pixel, and also
+  use it for the maximum sample value in the return value.
+-----------------------------------------------------------------------------*/
+    static rgb_type rgb;  /* Our return value */
+
+    switch (color_space) {
+    case RGB_COLORSPACE: {
+        rgb.r = GETJSAMPLE(*(ptr+0));
+        rgb.g = GETJSAMPLE(*(ptr+1)); 
+        rgb.b = GETJSAMPLE(*(ptr+2)); 
+    }
+        break;
+    case CMYK_NORMAL_COLORSPACE: {
+        const int c = GETJSAMPLE(*(ptr+0));
+        const int m = GETJSAMPLE(*(ptr+1));
+        const int y = GETJSAMPLE(*(ptr+2));
+        const int k = GETJSAMPLE(*(ptr+3));
+
+        /* I swapped m and y below, because they looked wrong.
+           -Bryan 2000.08.20
+           */
+        rgb.r = ((maxval-k)*(maxval-c))/maxval;
+        rgb.g = ((maxval-k)*(maxval-m))/maxval;
+        rgb.b = ((maxval-k)*(maxval-y))/maxval;
+    }
+        break;
+    case CMYK_ADOBE_COLORSPACE: {
+        const int c = GETJSAMPLE(*(ptr+0));
+        const int m = GETJSAMPLE(*(ptr+1));
+        const int y = GETJSAMPLE(*(ptr+2));
+        const int k = GETJSAMPLE(*(ptr+3));
+
+        rgb.r = (k*c)/maxval;
+        rgb.g = (k*m)/maxval;
+        rgb.b = (k*y)/maxval;
+    }
+        break;
+    default:
+        pm_error("Internal error: unknown color space %d passed to "
+                 "read_rgb().", (int) color_space);
+    }
+    return(&rgb);
+}
+
+
+
+/* pnmbuffer is declared global because it would be improper to pass a
+   pointer to it as input to copy_pixel_row(), since it isn't
+   logically a parameter of the operation, but rather is private to
+   copy_pixel_row().  But it would be impractical to allocate and free
+   the storage with every call to copy_pixel_row().
+*/
+static xel *pnmbuffer;      /* Output buffer.  Input to pnm_writepnmrow() */
+
+static void
+copy_pixel_row(const JSAMPROW jpegbuffer, const int width, 
+               const unsigned int samples_per_pixel, 
+               const enum colorspace color_space,
+               const unsigned int maxval,
+               FILE * const output_file, const int output_type) {
+  JSAMPLE *ptr;
+  unsigned int output_cursor;     /* Cursor into output buffer 'pnmbuffer' */
+
+  ptr = jpegbuffer;  /* Start at beginning of input row */
+
+  for (output_cursor = 0; output_cursor < width; output_cursor++) {
+      xel current_pixel;
+      if (samples_per_pixel >= 3) {
+          const rgb_type * const rgb_p = read_rgb(ptr, color_space, maxval);
+          PPM_ASSIGN(current_pixel, rgb_p->r, rgb_p->g, rgb_p->b);
+      } else {
+          PNM_ASSIGN1(current_pixel, GETJSAMPLE(*ptr));
+      }
+      ptr += samples_per_pixel;  /* move to next pixel of input */
+      pnmbuffer[output_cursor] = current_pixel;
+  }
+  pnm_writepnmrow(output_file, pnmbuffer, width,
+                  maxval, output_type, FALSE);
+}
+
+
+
+static void
+set_color_spaces(const J_COLOR_SPACE jpeg_color_space,
+                 int * const output_type_p,
+                 J_COLOR_SPACE * const out_color_space_p) {
+/*----------------------------------------------------------------------------
+   Decide what type of output (PPM or PGM) we shall generate and what 
+   color space we must request from the JPEG decompressor, based on the
+   color space of the input JPEG image, 'jpeg_color_space'.
+
+   Write to stderr a message telling which type we picked.
+
+   Exit the process with EXIT_FAILURE completion code and a message to
+   stderr if the input color space is beyond our capability.
+-----------------------------------------------------------------------------*/
+    /* Note that the JPEG decompressor is not capable of translating
+       CMYK or YCCK to RGB, but can translate YCCK to CMYK.
+    */
+
+    switch (jpeg_color_space) {
+    case JCS_UNKNOWN:
+        pm_error("Input JPEG image has 'unknown' color space "
+                 "(JCS_UNKNOWN).\n"
+                 "We cannot interpret this image.");
+        break;
+    case JCS_GRAYSCALE:
+        *output_type_p = PGM_TYPE;
+        *out_color_space_p = JCS_GRAYSCALE;
+        break;
+    case JCS_RGB:
+        *output_type_p = PPM_TYPE;
+        *out_color_space_p = JCS_RGB;
+        break;
+    case JCS_YCbCr:
+        *output_type_p = PPM_TYPE;
+        *out_color_space_p = JCS_RGB;
+        /* Design note:  We found this YCbCr->RGB conversion increases
+           user mode CPU time by 2.5%.  2002.10.12
+        */
+        break;
+    case JCS_CMYK:
+        *output_type_p = PPM_TYPE;
+        *out_color_space_p = JCS_CMYK;
+        break;
+    case JCS_YCCK:
+        *output_type_p = PPM_TYPE;
+        *out_color_space_p = JCS_CMYK;
+        break;
+    default:
+        pm_error("Internal error: unknown color space code %d passed "
+                 "to set_color_spaces().", jpeg_color_space);
+    }
+    pm_message("WRITING %s FILE", 
+               *output_type_p == PPM_TYPE ? "PPM" : "PGM");
+}
+
+
+
+static const char *
+colorspace_name(const J_COLOR_SPACE jpeg_color_space) {
+
+    const char *retval;
+
+    switch(jpeg_color_space) {
+    case JCS_UNKNOWN: retval = "JCS_UNKNOWN"; break;
+    case JCS_GRAYSCALE: retval= "JCS_GRAYSCALE"; break;
+    case JCS_RGB: retval = "JCS_RGB"; break;
+    case JCS_YCbCr: retval = "JCS_YCbCr"; break;
+    case JCS_CMYK: retval = "JCS_CMYK"; break;
+    case JCS_YCCK: retval = "JCS_YCCK"; break;
+    default: retval = "invalid"; break;
+    };
+    return(retval);
+}
+
+
+
+static void
+print_verbose_info_about_header(struct jpeg_decompress_struct const cinfo){
+
+    struct jpeg_marker_struct * markerP;
+
+    pm_message("input color space is %d (%s)\n", 
+               cinfo.jpeg_color_space, 
+               colorspace_name(cinfo.jpeg_color_space));
+
+    /* Note that raw information about marker, including marker type code,
+       was already printed by the jpeg library, due to the jpeg library
+       trace level >= 1.  Our job is to interpret it a little bit.
+    */
+    if (cinfo.marker_list)
+        pm_message("Miscellaneous markers (excluding APP0, APP12) "
+                   "in header:");
+    else
+        pm_message("No miscellaneous markers (excluding APP0, APP12) "
+                   "in header");
+    for (markerP = cinfo.marker_list; markerP; markerP = markerP->next) {
+        if (markerP->marker == JPEG_COM)
+            pm_message("Comment marker (COM):");
+        else if (markerP->marker >= JPEG_APP0 && 
+                 markerP->marker <= JPEG_APP0+15)
+            pm_message("Miscellaneous marker type APP%d:", 
+                       markerP->marker - JPEG_APP0);
+        else
+            pm_message("Miscellaneous marker of unknown type (0x%X):",
+                       markerP->marker);
+        
+        print_marker(*markerP);
+    }
+}
+
+
+
+static void
+beginJpegInput(struct jpeg_decompress_struct * const cinfoP,
+               const boolean verbose, 
+               const J_DCT_METHOD dct_method, 
+               const int max_memory_to_use, 
+               const boolean nosmooth) {
+/*----------------------------------------------------------------------------
+   Read the JPEG header, create decompressor object (and
+   allocate memory for it), set up decompressor.
+-----------------------------------------------------------------------------*/
+    /* Read file header, set default decompression parameters */
+    jpeg_read_header(cinfoP, TRUE);
+
+    cinfoP->dct_method = dct_method;
+    if (max_memory_to_use != -1)
+        cinfoP->mem->max_memory_to_use = max_memory_to_use;
+    if (nosmooth)
+        cinfoP->do_fancy_upsampling = FALSE;
+    
+}
+
+
+
+static void
+print_comments(struct jpeg_decompress_struct const cinfo) {
+    
+    struct jpeg_marker_struct * markerP;
+
+    for (markerP = cinfo.marker_list;
+         markerP; markerP = markerP->next) 
+        if (markerP->marker == JPEG_COM) {
+            pm_message("COMMENT:");
+            print_marker(*markerP);
+        }
+}
+
+
+
+static void
+print_exif_info(struct jpeg_marker_struct const marker) {
+/*----------------------------------------------------------------------------
+   Dump as informational messages the contents of the Jpeg miscellaneous
+   marker 'marker', assuming it is an Exif header.
+-----------------------------------------------------------------------------*/
+    ImageInfo_t imageInfo;
+    const char * error;
+
+    assert(marker.data_length >= 6);
+
+    process_EXIF(marker.data+6, marker.data_length-6, 
+                 &imageInfo, FALSE, &error);
+
+    if (error) {
+        pm_message("EXIF header is invalid.  %s", error);
+        strfree(error);
+    } else
+        ShowImageInfo(&imageInfo);
+}
+
+
+
+static boolean
+is_exif(struct jpeg_marker_struct const marker) {
+/*----------------------------------------------------------------------------
+   Return true iff the JPEG miscellaneous marker 'marker' is an Exif 
+   header.
+-----------------------------------------------------------------------------*/
+    boolean retval;
+    
+    if (marker.marker == JPEG_APP0+1) {
+        if (marker.data_length >=6 && memcmp(marker.data, "Exif", 4) == 0)
+            retval = TRUE;
+        else retval = FALSE;
+    }
+    else retval = FALSE;
+
+    return retval;
+}
+
+
+
+static void
+dump_exif(struct jpeg_decompress_struct const cinfo) {
+/*----------------------------------------------------------------------------
+   Dump as informational messages the contents of all EXIF headers in
+   the image, interpreted.  An EXIF header is an APP1 marker.
+-----------------------------------------------------------------------------*/
+    struct jpeg_marker_struct * markerP;
+    boolean found_one;
+
+    found_one = FALSE;  /* initial value */
+
+    for (markerP = cinfo.marker_list;
+         markerP; markerP = markerP->next) 
+        if (is_exif(*markerP)) {
+            pm_message("EXIF INFO:");
+            print_exif_info(*markerP);
+            found_one = TRUE;
+        }
+    if (!found_one)
+        pm_message("No EXIF info in image.");
+}
+
+
+
+static void
+save_exif(struct jpeg_decompress_struct const cinfo, 
+          const char *                  const exif_filespec) {
+/*----------------------------------------------------------------------------
+  Write the contents of the first Exif header in the image into the
+  file with filespec 'exif_filespec'.  Start with the two byte length
+  field.  If 'exif_filespec' is "-", write to standard output.
+
+  If there is no Exif header in the image, write just zero, as a two
+  byte pure binary integer.
+-----------------------------------------------------------------------------*/
+    FILE * exif_file;
+    struct jpeg_marker_struct * markerP;
+
+    exif_file = pm_openw(exif_filespec);
+
+    for (markerP = cinfo.marker_list; 
+         markerP && !is_exif(*markerP);
+         markerP = markerP->next);
+
+    if (markerP) {
+        pm_writebigshort(exif_file, markerP->data_length+2);
+        if (ferror(exif_file))
+            pm_error("Write of Exif header to %s failed on first byte.",
+                     exif_filespec);
+        else {
+            int rc;
+
+            rc = fwrite(markerP->data, 1, markerP->data_length, exif_file);
+            if (rc != markerP->data_length)
+                pm_error("Write of Exif header to '%s' failed.  Wrote "
+                         "length successfully, but then failed after "
+                         "%d characters of data.", exif_filespec, rc);
+        }
+    } else {
+        /* There is no Exif header in the image */
+        pm_writebigshort(exif_file, 0);
+        if (ferror(exif_file))
+            pm_error("Write of Exif header file '%s' failed.", exif_filespec);
+    }
+    pm_close(exif_file);
+}
+
+
+
+static void
+tellDetails(struct jpeg_decompress_struct const cinfo,
+            xelval                        const maxval,
+            int                           const output_type) {
+
+    print_verbose_info_about_header(cinfo);
+
+    pm_message("Input image data precision = %d bits", 
+               cinfo.data_precision);
+    pm_message("Output file will have format %c%c "
+               "with max sample value of %d.", 
+               (char) (output_type/256), (char) (output_type % 256),
+               maxval);
+}  
+
+
+
+static enum colorspace
+computeColorSpace(struct jpeg_decompress_struct * const cinfoP,
+                  enum inklevel                   const inklevel) {
+    
+    enum colorspace colorSpace;
+
+    if (cinfoP->out_color_space == JCS_GRAYSCALE)
+        colorSpace = GRAYSCALE_COLORSPACE;
+    else if (cinfoP->out_color_space == JCS_RGB)
+        colorSpace = RGB_COLORSPACE;
+    else if (cinfoP->out_color_space == JCS_CMYK) {
+        switch (inklevel) {
+        case ADOBE:
+            colorSpace = CMYK_ADOBE_COLORSPACE; break;
+        case NORMAL:
+            colorSpace = CMYK_NORMAL_COLORSPACE; break;
+        case GUESS:
+            colorSpace = CMYK_ADOBE_COLORSPACE; break;
+        }
+    } else
+        pm_error("Internal error: unacceptable output color space from "
+                 "JPEG decompressor.");
+
+    return colorSpace;
+}
+
+
+
+static void
+convertImage(FILE *                          const ofP, 
+             struct cmdlineInfo              const cmdline,
+             struct jpeg_decompress_struct * const cinfoP) {
+
+    int output_type;
+        /* The type of output file, PGM or PPM.  Value is either PPM_TYPE
+           or PGM_TYPE, which conveniently also pass as format values
+           PPM_FORMAT and PGM_FORMAT.
+        */
+    JSAMPROW jpegbuffer;  /* Input buffer.  Filled by jpeg_scanlines() */
+    unsigned int maxval;  
+        /* The maximum value of a sample (color component), both in the input
+           and the output.
+        */
+    enum colorspace color_space;
+        /* The color space of the pixels coming out of the JPEG decompressor */
+
+    beginJpegInput(cinfoP, cmdline.verbose, 
+                   cmdline.dct_method, 
+                   cmdline.max_memory_to_use, cmdline.nosmooth);
+                   
+    set_color_spaces(cinfoP->jpeg_color_space, &output_type, 
+                     &cinfoP->out_color_space);
+
+    maxval = (1 << cinfoP->data_precision) - 1;
+
+    if (cmdline.verbose) 
+        tellDetails(*cinfoP, maxval, output_type);
+
+    /* Calculate output image dimensions so we can allocate space */
+    jpeg_calc_output_dimensions(cinfoP);
+
+    jpegbuffer = ((*cinfoP->mem->alloc_sarray)
+                  ((j_common_ptr) cinfoP, JPOOL_IMAGE,
+                   cinfoP->output_width * cinfoP->output_components, 
+                   (JDIMENSION) 1)
+        )[0];
+
+    /* Start decompressor */
+    jpeg_start_decompress(cinfoP);
+
+    if (ofP)
+        /* Write pnm output header */
+        pnm_writepnminit(ofP, cinfoP->output_width, cinfoP->output_height,
+                         maxval, output_type, FALSE);
+
+    pnmbuffer = pnm_allocrow(cinfoP->output_width);
+    
+    color_space = computeColorSpace(cinfoP, cmdline.inklevel);
+
+    /* Process data */
+    while (cinfoP->output_scanline < cinfoP->output_height) {
+        jpeg_read_scanlines(cinfoP, &jpegbuffer, 1);
+        if (ofP)
+            copy_pixel_row(jpegbuffer, cinfoP->output_width, 
+                           cinfoP->out_color_components,
+                           color_space, maxval, ofP, output_type);
+    }
+
+    if (cmdline.comments)
+        print_comments(*cinfoP);
+    if (cmdline.dumpexif)
+        dump_exif(*cinfoP);
+    if (cmdline.exif_filespec)
+        save_exif(*cinfoP, cmdline.exif_filespec);
+
+    pnm_freerow(pnmbuffer);
+
+    /* Finish decompression and release decompressor memory. */
+    jpeg_finish_decompress(cinfoP);
+}
+
+
+
+
+static void
+saveMarkers(struct jpeg_decompress_struct * const cinfoP) {
+
+    unsigned int app_type;
+    /* Get all the miscellaneous markers (COM and APPn) saved for our
+       later access.
+    */
+    jpeg_save_markers(cinfoP, JPEG_COM, 65535);
+    for (app_type = 0; app_type <= 15; ++app_type) {
+        if (app_type == 0 || app_type == 14) {
+            /* The jpeg library uses APP0 and APP14 internally (see
+               libjpeg.doc), so we don't mess with those.
+            */
+        } else
+            jpeg_save_markers(cinfoP, JPEG_APP0 + app_type, 65535);
+    }
+}
+
+
+
+static void
+convertImages(FILE *                          const ofP,
+              struct cmdlineInfo              const cmdline,
+              struct jpeg_decompress_struct * const cinfoP,
+              struct sourceManager *          const sourceManagerP) {
+              
+    if (cmdline.multiple) {
+        unsigned int imageSequence;
+        for (imageSequence = 0; dsDataLeft(sourceManagerP); ++imageSequence) {
+            if (cmdline.verbose)
+                pm_message("Reading Image %u", imageSequence);
+            convertImage(ofP, cmdline, cinfoP);
+        }
+    } else {
+        if (dsDataLeft(sourceManagerP))
+            convertImage(ofP, cmdline, cinfoP);
+        else
+            pm_error("Input stream is empty");
+    }
+}
+
+
+
+int
+main(int argc, char **argv) {
+    FILE * ofP;
+    struct cmdlineInfo cmdline;
+    struct jpeg_decompress_struct cinfo;
+    struct jpeg_error_mgr jerr;
+    struct sourceManager * sourceManagerP;
+
+    pnm_init(&argc, argv);
+
+    parse_command_line(argc, argv, &cmdline);
+
+    if (cmdline.exif_filespec && STREQ(cmdline.exif_filespec, "-"))
+        /* He's got exif going to stdout, so there can be no image output */
+        ofP = NULL;
+    else
+        ofP = stdout;
+
+    displayComments = cmdline.comments;
+
+    /* Initialize the JPEG decompression object with default error handling. */
+    cinfo.err = jpeg_std_error(&jerr);
+    jpeg_create_decompress(&cinfo);
+
+    if (cmdline.trace_level == 0 && cmdline.verbose) 
+        cinfo.err->trace_level = 1;
+    else 
+        cinfo.err->trace_level = cmdline.trace_level;
+    
+    saveMarkers(&cinfo);
+
+    sourceManagerP = dsCreateSource(cmdline.input_filespec);
+
+    cinfo.src = dsJpegSourceMgr(sourceManagerP);
+
+    convertImages(ofP, cmdline, &cinfo, sourceManagerP);
+
+    jpeg_destroy_decompress(&cinfo);
+
+    if (ofP) {
+        int rc;
+        rc = fclose(ofP);
+        if (rc == EOF) 
+            pm_error("Error writing output file.  Errno = %s (%d).",
+                     strerror(errno), errno);
+    }
+
+    dsDestroySource(sourceManagerP);
+
+    free(cmdline.input_filespec);
+  
+    exit(jerr.num_warnings > 0 ? EXIT_WARNING : EXIT_SUCCESS);
+}
+
diff --git a/converter/other/pamrgbatopng.c b/converter/other/pamrgbatopng.c
new file mode 100644
index 00000000..7babf9f9
--- /dev/null
+++ b/converter/other/pamrgbatopng.c
@@ -0,0 +1,150 @@
+#include <png.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+
+#include "pam.h"
+#include "mallocvar.h"
+
+struct cmdlineInfo {
+    const char * inputFileName;
+};
+
+
+
+static void
+processCommandLine(int                  const argc,
+                   char *               const 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.  "
+                     "The only argument is the input file name.");
+    }
+}
+
+
+
+static void
+convertPamToPng(const struct pam * const pamP,
+                const tuple *      const tuplerow,
+                png_byte *         const pngRow) {
+    
+    unsigned int col;
+    
+    for (col = 0; col < pamP->width; ++col) {
+        unsigned int plane;
+        
+        for (plane = 0; plane < 4; ++plane)
+            pngRow[4 * col + plane] = tuplerow[col][plane];
+    }
+}
+
+
+
+static void
+writeRaster(const struct pam * const pamP,
+            png_struct *       const pngP) {
+    
+    tuple * tupleRow;
+    png_byte * pngRow;
+    
+    tupleRow = pnm_allocpamrow(pamP);
+    MALLOCARRAY(pngRow, pamP->width * 4);
+
+    if (pngRow == NULL)
+        pm_error("Unable to allocate space for PNG pixel row.");
+    else {
+        unsigned int row;
+        for (row = 0; row < pamP->height; ++row) {
+            pnm_readpamrow(pamP, tupleRow);
+            
+            convertPamToPng(pamP, tupleRow, pngRow);
+            
+            png_write_row(pngP, pngRow);
+        }
+        free(pngRow);
+    }
+    pnm_freepamrow(tupleRow);
+}
+
+
+
+static void
+pngErrorHandler(png_struct * const pngP,
+                const char * const message) {
+
+    pm_error("Error generating PNG image.  libpng says: %s", message);
+}
+
+
+
+static void
+writePng(const struct pam * const pamP,
+         FILE *             const ofP) {
+
+    png_struct * pngP;
+    png_info * infoP;
+
+    pngP = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+    if (!pngP)
+        pm_error("Could not allocate png struct.");
+
+    png_set_error_fn(pngP, NULL, &pngErrorHandler, NULL);
+
+    infoP = png_create_info_struct(pngP);
+    if (!infoP)
+        pm_error("Could not allocate PNG info structure");
+    else {
+        infoP->width      = pamP->width;
+        infoP->height     = pamP->height;
+        infoP->bit_depth  = 8;
+        infoP->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+        
+        png_init_io(pngP, ofP);
+
+        png_write_info(pngP, infoP);
+        
+        writeRaster(pamP, pngP);
+
+        png_write_end(pngP, infoP);
+        
+        png_destroy_write_struct(&pngP, &infoP);
+    }
+}
+    
+
+
+int
+main(int argc, char * argv[]) {
+
+    FILE * ifP;
+    struct cmdlineInfo cmdline;
+    struct pam pam;
+
+    pnm_init(&argc, argv);
+
+    processCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
+    
+    if (pam.depth < 4)
+        pm_error("PAM must have depth at least 4 (red, green, blue, alpha).  "
+                 "This one has depth %u", pam.depth);
+        
+    if (pam.maxval != 255)
+        pm_error("PAM must have maxval 255.  This one has %lu", pam.maxval);
+
+    writePng(&pam, stdout);
+
+    pm_close(ifP);
+
+    return 0;
+}
diff --git a/converter/other/pamtodjvurle.c b/converter/other/pamtodjvurle.c
new file mode 100644
index 00000000..ae35e81d
--- /dev/null
+++ b/converter/other/pamtodjvurle.c
@@ -0,0 +1,293 @@
+/*****************************************************************************
+                               pamtodjvurle
+******************************************************************************
+  This program converts a PAM image to DjVu Color RLE format.
+  
+  By Bryan Henderson, San Jose, CA April 2004.
+
+  Contributed to the public domain by its author.
+
+  This work is inspired by Ppmtodjvurle, written by Scott Pakin
+  <scott+pbm@pakin.org> in March 2004.  Bryan took the requirements of
+  the DjVu Color RLE format and the technique for generating the format
+  (but not code) from that program.
+
+*****************************************************************************/
+#include <stdio.h>
+#include <assert.h>
+
+#include "pam.h"
+#include "pammap.h"
+#include "shhopt.h"
+
+
+struct cmdlineInfo {
+    const char * inputFilespec;
+    const char * transparent;
+    unsigned int showcolormap;
+};
+
+
+
+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 = malloc( 100*sizeof( optEntry ) );
+    /* Instructions to optParseOptions3 on how to parse our options. */
+    optStruct3 opt;
+  
+    unsigned int option_def_index;
+    unsigned int transparentSpec;
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "transparent",   OPT_STRING, &cmdlineP->transparent, 
+            &transparentSpec,        0);
+    OPTENT3(0, "showcolormap",  OPT_FLAG, NULL,
+            &cmdlineP->showcolormap,        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 (!transparentSpec)
+        cmdlineP->transparent = "white";
+
+    /* Get the program parameters */
+
+    if (argc-1 >= 1)
+        cmdlineP->inputFilespec = argv[1];
+    else
+        cmdlineP->inputFilespec = "-";
+    
+    if (argc-1 > 1)
+        pm_error("Program takes at most one argument:  the file name.  "
+                 "You specified %d", argc-1);
+}
+
+
+
+static void
+computeColorMap(struct pam *   const pamP,
+                tuple **       const tupleArray,
+                unsigned int * const numColorsP,
+                tupletable *   const colormapP,
+                tuplehash *    const colorhashP,
+                bool           const show) {
+
+    unsigned int numColors;
+    tupletable colormap;
+
+    colormap = pnm_computetuplefreqtable(pamP, tupleArray, 0, &numColors);
+    if (numColors > 0xFF0)
+        pm_error("too many colors; "
+                 "use pnmquant to reduce to no more than %u colors", 0xFF0);
+    
+    if (show) {
+        unsigned int colorIndex;
+        fprintf(stderr, "Color map:\n");
+        fprintf(stderr, "    Index Color\n");
+        for (colorIndex = 0; colorIndex < numColors; ++colorIndex) {
+            unsigned int plane;
+            fprintf(stderr, "    %5u   ", colorIndex);
+            for (plane = 0; plane < pamP->depth; ++plane)
+                fprintf(stderr, "%3lu ", colormap[colorIndex]->tuple[plane]);
+            fprintf(stderr, "\n");
+        }
+    }
+
+    *colorhashP = pnm_computetupletablehash(pamP, colormap, numColors);
+
+    *numColorsP = numColors;
+    *colormapP  = colormap;
+}
+
+
+
+static void
+makeDjvurleHeader(FILE *       const ofP,
+                  struct pam * const pamP,
+                  unsigned int const numColors,
+                  tupletable   const colormap) {
+    
+    unsigned int colorIndex;
+
+    fprintf(ofP, "R6\n");
+    fprintf(ofP, "%d %d %d\n", pamP->width, pamP->height, numColors);
+
+    for (colorIndex = 0; colorIndex < numColors; ++colorIndex) {
+        sample red, grn, blu;
+
+        if (pamP->depth >= 3) {
+            red = colormap[colorIndex]->tuple[PAM_RED_PLANE];
+            grn = colormap[colorIndex]->tuple[PAM_GRN_PLANE];
+            blu = colormap[colorIndex]->tuple[PAM_BLU_PLANE];
+        } else
+            red = grn = blu = colormap[colorIndex]->tuple[0];
+        
+        fputc(pnm_scalesample(red, pamP->maxval, 255), ofP);
+        fputc(pnm_scalesample(grn, pamP->maxval, 255), ofP);
+        fputc(pnm_scalesample(blu, pamP->maxval, 255), ofP);
+    }
+}
+
+
+
+static bool
+colorEqual(tuple        comparand,
+           unsigned int comparandDepth,
+           tuple        comparator) {
+
+    /* comparator has depth 3 */
+
+    if (comparandDepth >= 3)
+        return (comparand[0] == comparator[0] &&
+                comparand[1] == comparator[1] &&
+                comparand[2] == comparator[2]);
+    else
+        return (comparand[0] == comparator[0] &&
+                comparand[0] == comparator[1] &&
+                comparand[0] == comparator[2]);
+}
+
+
+
+static void 
+writeRleRun(FILE *       const ofP, 
+            struct pam * const pamP,
+            tuple        const color, 
+            int          const count,
+            tuplehash    const colorhash,
+            tuple        const transcolor) {
+/*----------------------------------------------------------------------------
+  Write one DjVu Color RLE run to the file 'ofP'.  The run is 
+  'count' pixels of color 'color', using the color index given by
+  'colorhash' and assuming 'transcolor' is the transparent color.
+  
+  'transcolor' is a 3-deep tuple with the same maxval as the image.
+-----------------------------------------------------------------------------*/
+    uint32n rlevalue;         /* RLE-encoded color/valuex */
+    int index;
+
+    if (count > 0) {
+        if (colorEqual(color, pamP->depth, transcolor))
+            index = 0xFFF;
+        else {
+            int found;
+            pnm_lookuptuple(pamP, colorhash, color, &found, &index);
+            assert(found);
+        }
+        rlevalue = (index << 20) | count;
+      
+        pm_writebiglong(ofP, rlevalue);
+    }
+}
+
+
+
+static void
+writeDjvurleRow(FILE *       const ofP,
+                struct pam * const pamP,
+                tuple *      const tupleRow,
+                tuplehash    const colorhash,
+                tuple        const transcolor) {
+
+    unsigned int col;
+    unsigned int runlength;
+    tuple prevpixel;        /* Previous pixel seen */
+
+    prevpixel = tupleRow[0];
+    runlength = 0;
+    
+    for (col = 0; col < pamP->width; ++col) {
+        tuple const newpixel = tupleRow[col];      /* Current pixel color */
+        
+        if (pnm_tupleequal(pamP, newpixel, prevpixel))
+            /* This is a continuation of the current run */
+            ++runlength;
+          else {
+              /* The run is over.  Write it out and start a run of the next
+                 color.
+              */
+              writeRleRun(ofP, pamP, prevpixel, runlength, 
+                          colorhash, transcolor);
+              runlength = 1;
+              prevpixel = newpixel;
+          }
+        if (runlength >= (1<<20)-1) {
+            /* Can't make the run any longer.  Write it out and start a
+               new run.
+            */
+            writeRleRun(ofP, pamP, prevpixel, runlength, 
+                        colorhash, transcolor);
+            runlength = 1;
+        }
+    }
+    /* Write the last run we started */
+    writeRleRun(ofP, pamP, prevpixel, runlength, colorhash, transcolor);
+}
+
+
+
+int 
+main(int argc, char *argv[]) {
+
+    FILE * const rlefile = stdout;
+
+    struct cmdlineInfo cmdline;
+    FILE *ifP;                 /* Input (Netpbm) file */
+    struct pam pam;            /* Description of the image */
+    tuple ** tupleArray;       /* The image raster */
+    tupletable colormap;       /* List of all of the colors used */
+    unsigned int numColors;    /* Number of unique colors in the color map */
+    tuplehash colorhash; 
+        /* Mapping from color to index into colormap[] */
+    tuple transcolor;
+        /* Color that should be considered transparent */
+
+    pnm_init (&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    tupleArray = pnm_readpam(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
+
+    transcolor = pnm_parsecolor(cmdline.transparent, pam.maxval);
+    
+    computeColorMap(&pam, tupleArray, &numColors, &colormap, &colorhash,
+                    cmdline.showcolormap);
+    
+    makeDjvurleHeader(rlefile, &pam, numColors, colormap);
+
+    /* Write the raster */
+
+    {
+        unsigned int row;
+        for (row = 0; row < pam.height; ++row)
+            writeDjvurleRow(rlefile, &pam, tupleArray[row], colorhash, 
+                            transcolor);
+    }
+    /* Clean up */
+    
+    pnm_freepamarray(tupleArray, &pam);
+    pnm_freetupletable(&pam, colormap);
+    pnm_destroytuplehash(colorhash);
+    pnm_freepamtuple(transcolor);
+    pm_close(ifP);
+
+    return 0;
+}
diff --git a/converter/other/pamtofits.c b/converter/other/pamtofits.c
new file mode 100644
index 00000000..d0552a5c
--- /dev/null
+++ b/converter/other/pamtofits.c
@@ -0,0 +1,304 @@
+/* pnmtofits.c - read a PNM image and produce a FITS file
+**
+** Copyright (C) 1989 by Wilson H. Bent (whb@hoh-2.att.com).
+**
+** 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 Alberto Accomazzi (alberto@cfa.harvard.edu), Dec 1, 1992.
+**
+** Added PPM input capability; the program is renamed pnmtofits.
+** This program produces files with NAXIS = 2 if input file is in PBM
+** or PGM format, and NAXIS = 3, NAXIS3 = 3 if input file is a PPM file.
+** Data is written out as either 8 bits/pixel or 16 bits/pixel integers,
+** depending on the value of maxval in the input file.
+** Flags -max, -min can be used to set DATAMAX, DATAMIN, BSCALE and BZERO
+** in the FITS header, but do not cause the data to be rescaled.
+*/
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "nstring.h"
+#include "pam.h"
+
+struct cmdlineInfo {
+    const char * inputFileName;
+    unsigned int maxSpec;
+    double max;
+    double min;
+};
+
+
+
+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 minSpec;
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "min",     OPT_FLOAT,
+            &cmdlineP->min,  &minSpec,                              0);
+    OPTENT3(0, "max",     OPT_FLOAT,
+            &cmdlineP->max,  &cmdlineP->maxSpec,                    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 */
+
+    /* Set some defaults the lazy way (using multiple setting of variables) */
+
+    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!minSpec)
+        cmdlineP->min = 0.0;
+
+    if (cmdlineP->maxSpec) {
+        if (cmdlineP->max <= cmdlineP->min)
+            pm_error("-max must be greater than min (%f).  You specified %f",
+                     cmdlineP->min, cmdlineP->max);
+    }
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else {
+        cmdlineP->inputFileName = argv[1];
+        
+        if (argc-1 > 1)
+            pm_error("Too many arguments (%u).  The only non-option argument "
+                     "is the input file name.", argc-1);
+    }
+}
+
+
+
+
+static void
+writeHeaderCard(const char * const s) {
+/*----------------------------------------------------------------------------
+   Write the string 's', padded with spaces to 80 characters.
+-----------------------------------------------------------------------------*/
+    const char * card;
+
+    asprintfN(&card, "%-80.80s", s);
+
+    fwrite(card, sizeof(card[0]), 80, stdout);
+
+    strfree(card);
+}
+
+
+
+static void
+padToMultipleOf36Cards(unsigned int const nCardsAlreadyWritten) {
+
+    /* pad with blanks cards to multiple of 36 cards */
+
+    unsigned int const npadCard = 36 - (nCardsAlreadyWritten % 36);
+    unsigned int i;
+    
+    for (i = 0; i < npadCard; ++i)
+        writeHeaderCard("");
+}
+
+
+
+static void
+writeFitsHeader(int    const bitpix,
+                int    const planes,
+                int    const cols,
+                int    const rows,
+                double const bscale,
+                double const fitsBzero,
+                double const datamax,
+                double const datamin) {
+
+    char buffer[80+1];
+    unsigned int cardsWritten;
+                
+    cardsWritten = 0;  /* initial value */
+
+    sprintf(buffer, "%-20.20s%10.10s", "SIMPLE  =", "T");
+    writeHeaderCard(buffer);
+    ++cardsWritten;
+
+    sprintf(buffer, "%-20.20s%10d", "BITPIX  =", bitpix);
+    writeHeaderCard(buffer);
+    ++cardsWritten;
+
+    sprintf(buffer, "%-20.20s%10d", "NAXIS   =", (planes == 3) ? 3 : 2);
+    writeHeaderCard(buffer);
+    ++cardsWritten;
+
+    sprintf(buffer, "%-20.20s%10d", "NAXIS1  =", cols);
+    writeHeaderCard(buffer);
+    ++cardsWritten;
+
+    sprintf(buffer, "%-20.20s%10d", "NAXIS2  =", rows);
+    writeHeaderCard(buffer);
+    ++cardsWritten;
+
+    if (planes == 3) {
+        sprintf(buffer, "%-20.20s%10d", "NAXIS3  =", 3);
+        writeHeaderCard(buffer);
+        ++cardsWritten;
+    }
+
+    sprintf(buffer, "%-18.18s%12.5E", "BSCALE  =", bscale);
+    writeHeaderCard(buffer);
+    ++cardsWritten;
+    
+    sprintf(buffer, "%-18.18s%12.5E", "BZERO   =", fitsBzero);
+    writeHeaderCard(buffer);
+    ++cardsWritten;
+
+    sprintf(buffer, "%-18.18s%12.5E", "DATAMAX =", datamax);
+    writeHeaderCard(buffer);
+    ++cardsWritten;
+
+    sprintf(buffer, "%-18.18s%12.5E", "DATAMIN =", datamin);
+    writeHeaderCard(buffer);
+    ++cardsWritten;
+
+    writeHeaderCard("HISTORY Created by pnmtofits.");
+    ++cardsWritten;
+
+    writeHeaderCard("END");
+    ++cardsWritten;
+
+    padToMultipleOf36Cards(cardsWritten);
+}
+
+
+
+static void
+writeRaster(struct pam * const pamP,
+            tuple **     const tuples,
+            unsigned int const bitpix,
+            int          const offset) {
+
+    unsigned int plane;
+
+    for (plane = 0; plane < pamP->depth; ++plane) {
+        unsigned int row;
+        for (row = 0; row < pamP->height; ++row) {
+            unsigned int col;
+            for (col = 0; col < pamP->width; ++col) {
+                if (bitpix == 16) {
+                    /* 16 bit FITS samples are signed integers */
+                    int const fitsSample =
+                        (int)tuples[row][col][plane] - offset;
+                    pm_writebigshort(stdout, (short)fitsSample);
+                } else
+                    /* 8 bit FITS samples are unsigned integers */
+                    putchar(tuples[row][col][plane] - offset);
+            }
+        }
+    }
+    {
+        /* pad raster to 36 cards with nulls */
+        unsigned int const bytesWritten =
+            pamP->height * pamP->width * pamP->depth * bitpix/8;
+        unsigned int const npad = (36*80) - (bytesWritten % (36*80));
+        unsigned int i;
+
+        for (i = 0; i < npad; ++i)
+            putchar('\0');
+    }
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    tuple ** tuples;
+    struct pam pam;
+    unsigned int bitpix;
+    double datamin, datamax, bscale, fitsBzero;
+    int pnmSampleOffsetFromFits;
+        /* This is what you add to a FITS sample (raster) value in order
+           to get the PNM sample value which it represents.  Note that in
+           the default case, that PNM sample value is also the FITS "physical"
+           value, but user options can change that.
+        */
+    
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    tuples = pnm_readpam(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
+
+    datamin = cmdline.min;
+
+    if (cmdline.maxSpec)
+        datamax = cmdline.max;
+    else {
+        if (pam.maxval <= cmdline.min)
+            pm_error("You must specify -max greater than -min (%f).  "
+                     "max defaults to the maxval, which is %u",
+                     cmdline.min, (unsigned)pam.maxval);
+        datamax = pam.maxval;
+    }
+
+    assert(datamax > datamin);
+
+    bscale = (datamax - datamin) / pam.maxval;
+    
+    if (pam.maxval > 255) {
+        bitpix = 16;
+        /* Because 16 bit FITS samples are signed, we have to do a 2**15
+           offset to get any possible unsigned 16 bit PNM sample into a FITS
+           sample.
+        */
+        fitsBzero = 1 << 15;
+        pnmSampleOffsetFromFits = 1 << 15;
+    } else {
+        bitpix = 8;
+        fitsBzero = datamin;
+        /* Both 8 bit FITS samples and PNM samples are unsigned 8 bits, so
+           we make them identical.
+        */
+        pnmSampleOffsetFromFits = 0;
+    }
+
+    fitsBzero = datamin + pnmSampleOffsetFromFits;
+    pm_close(ifP);
+
+    writeFitsHeader(bitpix, pam.depth, pam.width, pam.height,
+                    bscale, fitsBzero, datamax, datamin);
+
+    writeRaster(&pam, tuples, bitpix, pnmSampleOffsetFromFits);
+
+    return 0;
+}
diff --git a/converter/other/pamtohdiff.c b/converter/other/pamtohdiff.c
new file mode 100644
index 00000000..0e1ff00f
--- /dev/null
+++ b/converter/other/pamtohdiff.c
@@ -0,0 +1,142 @@
+/******************************************************************************
+                                 pamtohdiff
+*******************************************************************************
+  This program creates a PAM output which a horizontal difference image of
+  the input PAM.  The samples in each row are a number to be added to to
+  the previous output row to create the next output row (and the first
+  output row is simply the same as the input row).
+
+  Because these samples must be positive and negative and PAM samples are
+  unsigned, we bias each PAM sample by the input maxval and make the output
+  maxval twice the input maxval.
+
+  By Bryan Henderson, San Jose, CA 2002.04.15.
+
+******************************************************************************/
+#include <string.h>
+#include <stdio.h>
+
+#include "pam.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;  /* Filespecs of input files */
+    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 = 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. */
+
+    if (argc-1 < 1)
+        cmdlineP->inputFilespec = "-";
+    else if (argc-1 == 1)
+        cmdlineP->inputFilespec = argv[1];
+    else
+        pm_error("Too many arguments.");
+}
+
+
+
+int 
+main(int argc, char *argv[]) {
+    FILE *ifP;
+    struct cmdlineInfo cmdline;
+    struct pam inpam, outpam;
+    unsigned int row;
+    tuple * inrow;
+    tuple * outrow;
+    tuple * prevrow;
+
+    pnm_init( &argc, argv );
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    outpam = inpam;
+    outpam.file = stdout;
+    outpam.format = PAM_FORMAT;
+    strcpy(outpam.tuple_type, "hdiff");
+
+    pnm_writepaminit(&outpam);
+
+    inrow = pnm_allocpamrow(&inpam);
+    outrow = pnm_allocpamrow(&outpam);
+    prevrow = pnm_allocpamrow(&inpam);
+
+    pnm_setpamrow(&inpam, prevrow, 0);
+
+    /* All arithmetic in this operation and in the reverse operation
+       (to recover the image) is done modulus the maxval+1 (the hdiff
+       PAM and the image have the same maxval) in order to produce
+       legal PAM samples (which must be in the range 0..maxval).  This
+       might seem to throw away information, but it doesn't.  Example:
+       maxval is 99.  Intensity goes from 90 in Row 0 to 10 in Row 1.
+       The difference is -80.  -80 mod 100 is 20, so 20 goes in the
+       hdiff output.  On reconstructing the image, the interpreter
+       knows the "20" can't be +20, because that would create the
+       sample 90 + 20 = 110, and violate maxval.  So it must be -80.
+       Modulus arithmetic by the interpreter effectively makes that
+       decision.  
+    */
+
+
+    /* The bias is just to make it easier to look at the output visually.
+       If you display the values as intensities, and your differences are
+       all +/- half of maxval, you can see positive transitions as bright
+       spots and negative transitions as dark spots.
+    */
+    
+    {
+        unsigned int const bias = outpam.maxval/2;
+        for (row = 0; row < inpam.height; ++row) {
+            unsigned int col;
+            pnm_readpamrow(&inpam, inrow);
+            for (col = 0; col < inpam.width; ++col) {
+            unsigned int plane;
+            for (plane = 0; plane < inpam.depth; ++plane) {
+                
+                sample const sampleValue = inrow[col][plane];
+                int const difference = sampleValue - prevrow[col][plane];
+                outrow[col][plane] = (difference + bias) % (outpam.maxval+1);
+                prevrow[col][plane] = sampleValue;
+            }
+        }
+            pnm_writepamrow(&outpam, outrow);
+        }
+    }
+    pnm_freepamrow(prevrow);
+    pnm_freepamrow(outrow);
+    pnm_freepamrow(inrow);
+
+    exit(0);
+}
+
diff --git a/converter/other/pamtohtmltbl.c b/converter/other/pamtohtmltbl.c
new file mode 100644
index 00000000..293badcb
--- /dev/null
+++ b/converter/other/pamtohtmltbl.c
@@ -0,0 +1,284 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pam.h"
+#include "shhopt.h"
+#include "mallocvar.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;  /* '-' if stdin */
+    const char *transparent;  /* NULL if none */
+    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;
+
+    unsigned int transparentSpec;
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "verbose",     OPT_FLAG,   NULL,                  
+            &cmdlineP->verbose,       0 );
+    OPTENT3(0, "transparent", OPT_STRING, &cmdlineP->transparent,
+            &transparentSpec,  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 (!transparentSpec)
+        cmdlineP->transparent = NULL;
+
+    if (argc-1 < 1)
+        cmdlineP->inputFilespec = "-";
+    else if (argc-1 == 1)
+        cmdlineP->inputFilespec = argv[1];
+    else
+        pm_error("Too many arguments.  Program takes at most one argument: "
+                 "input file name");
+}
+
+
+
+
+static void
+pripix(struct pam * const pamP,
+       tuple        const color, 
+       unsigned int const rectWidth, 
+       unsigned int const rectHeight, 
+       tuple        const transparentColor) {
+
+    if (rectWidth > 0 && rectHeight > 0) {
+        printf("<TD VALIGN=CENTER ALIGN=CENTER");
+
+        if (rectWidth != 1) 
+            printf(" COLSPAN=%d", rectWidth);
+        if (rectHeight != 1) 
+            printf(" ROWSPAN=%d", rectHeight);
+
+        if (transparentColor && 
+            !pnm_tupleequal(pamP, color,transparentColor)) {
+            /* No BGCOLOR attribute */
+        } else {
+            tuple const colorff = pnm_allocpamtuple(pamP);
+
+            unsigned int r, g, b;
+
+            pnm_scaletuple(pamP, colorff, color, 0xff);
+
+            if (pamP->depth < 3)
+                r = g = b = colorff[0];
+            else {
+                r = color[PAM_RED_PLANE];
+                g = color[PAM_GRN_PLANE];
+                b = color[PAM_BLU_PLANE];
+            }
+            printf(" BGCOLOR=#%02X%02X%02X", r, g, b);
+        }
+        printf(">");
+        printf("<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0>"
+               "<TR><TD></TD></TR></TABLE>");
+        printf("</TD>\n");
+    }
+}
+
+
+
+static void
+findSameColorRectangle(struct pam *   const pamP,
+                       tuple **       const tuples,
+                       unsigned int   const row,
+                       unsigned int   const col,
+                       unsigned int * const rectWidthP,
+                       unsigned int * const rectHeightP) {
+/*----------------------------------------------------------------------------
+   Find the largest rectangle, in the image described by 'pam' and 
+   'tuples', of uniform color, whose upper left corner is at (row, col).
+
+   Return the width and height of that rectangle as *rectWidthP
+   and *rectHeightP.
+-----------------------------------------------------------------------------*/
+    tuple const rectangleColor = tuples[row][col];
+
+    unsigned int i;
+    unsigned int mx, my;
+    unsigned int cnx, cny;
+
+    mx=0; my=0;
+    cnx = pamP->width - col; cny = pamP->height - row;
+
+    for (i=0; (!mx)||(!my); i++) {
+        int j;
+        /*fprintf(stderr,"\n[%d]",i);*/
+        for (j=0; j<=i; j++) {
+            if (!my) {
+                if (i>=cny) 
+                    my=cny;
+                else
+                    if (((!mx) || (j<mx)) && (j < cnx)) {
+                        /*fprintf(stderr,"%d/%d ",j,i);*/
+                        if (!pnm_tupleequal(pamP, tuples[row+i][col+j],
+                                            rectangleColor)) 
+                            my = i;
+                    }
+            }
+            if (!mx) {
+                if (i>=cnx) 
+                    mx=cnx;
+                else
+                    if (((!my) || (j<my)) && (j < cny)) {
+                        /*fprintf(stderr,"%d/%d ",i,j);*/
+                        if (!pnm_tupleequal(pamP, tuples[row+j][col+i],
+                                            rectangleColor)) 
+                            mx = i;
+                    }
+            }
+        }
+    }
+    *rectWidthP  = mx;
+    *rectHeightP = my;
+}
+
+
+
+static bool **
+allocOutputtedArray(unsigned int const width, unsigned int const height) {
+
+    bool ** outputted;
+    unsigned int row;
+
+    MALLOCARRAY(outputted, height);
+    if (outputted == NULL)
+        pm_error("Unable to allocate space for 'outputted' array");
+
+    for (row = 0; row < height; ++row) {
+        MALLOCARRAY(outputted[row], width);
+        if (outputted[row] == NULL)
+            pm_error("Unable to allocate space for 'outputted' array");
+    }
+    return outputted;
+}
+
+
+
+static void
+freeOutputtedArray(bool ** const outputted, unsigned int const height) {
+
+    unsigned int row;
+
+    for (row = 0; row < height; ++row)
+        free(outputted[row]);
+}
+
+
+
+static void
+markOutputted(bool ** const outputted,
+              unsigned int const upperLeftCol,
+              unsigned int const upperLeftRow,
+              unsigned int const width,
+              unsigned int const height) {
+
+    unsigned int const lowerRightCol = upperLeftCol + width;
+    unsigned int const lowerRightRow = upperLeftRow + height;
+    unsigned int row;
+    
+    for (row = upperLeftRow; row < lowerRightRow; ++row) {
+        unsigned int col;
+        for (col = upperLeftCol; col < lowerRightCol; ++col) 
+            outputted[row][col] = TRUE;
+    }
+}
+
+
+
+int
+main(int argc, char **argv) {
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    struct pam inpam;
+    tuple ** tuples;
+    int row;
+    unsigned int rectWidth, rectHeight;
+    bool ** outputted;
+    tuple transparentColor;
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    tuples = pnm_readpam(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    if (cmdline.transparent) {
+        pixel transcolor;
+        transcolor = ppm_parsecolor(cmdline.transparent, inpam.maxval);
+        transparentColor = pnm_allocpamtuple(&inpam);
+        transparentColor[PAM_RED_PLANE] = PPM_GETR(transcolor);
+        transparentColor[PAM_GRN_PLANE] = PPM_GETG(transcolor);
+        transparentColor[PAM_BLU_PLANE] = PPM_GETB(transcolor);
+    } else
+        transparentColor = NULL;
+
+    outputted = allocOutputtedArray(inpam.width, inpam.height);
+
+    printf("<TABLE WIDTH=%d HEIGHT=%d BORDER=0 CELLSPACING=0 CELLPADDING=0>\n",
+           inpam.width, inpam.height);
+    for (row = 0; row < inpam.height; ++row) {
+        int col;
+        printf("<TR>\n");
+        pripix(&inpam, tuples[row][0], 1, 1, transparentColor); 
+        markOutputted(outputted, 0, row, 1, 1);
+
+        for (col = 1; col < inpam.width; ++col) {
+            if (!outputted[row][col]) {
+                findSameColorRectangle(&inpam, tuples, row, col, 
+                                       &rectWidth, &rectHeight);
+                if (cmdline.verbose)
+                    pm_message("[%d/%d] [%d/%d]",
+                               col, row, rectWidth, rectHeight);
+                pripix(&inpam, tuples[row][col], rectWidth, rectHeight, 
+                       transparentColor);
+                markOutputted(outputted, col, row, rectWidth, rectHeight);
+            }
+        }
+        printf("</TR>\n");
+    }
+    printf("</TABLE>\n");
+
+    if (transparentColor)
+        pnm_freepamtuple(transparentColor);
+    pnm_freepamarray(tuples, &inpam);
+    freeOutputtedArray(outputted, inpam.height);
+
+    exit(0);
+}
diff --git a/converter/other/pamtopfm.c b/converter/other/pamtopfm.c
new file mode 100644
index 00000000..f8fdc96b
--- /dev/null
+++ b/converter/other/pamtopfm.c
@@ -0,0 +1,304 @@
+/*****************************************************************************
+                                  pamtopfm
+******************************************************************************
+  This program converts a PAM image to PFM (Portable Float Map).
+  
+  By Bryan Henderson, San Jose, CA April 2004.
+
+  Contributed to the public domain by its author.
+
+*****************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <assert.h>
+
+#include "pam.h"
+#include "pm_gamma.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+
+enum endian {ENDIAN_BIG, ENDIAN_LITTLE};
+
+struct cmdlineInfo {
+    const char * inputFilespec;
+    unsigned int verbose;
+    enum endian endian;
+    float scale;
+};
+
+
+
+static enum endian machineEndianness;
+
+
+
+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 = malloc( 100*sizeof( optEntry ) );
+    /* Instructions to optParseOptions3 on how to parse our options. */
+    optStruct3 opt;
+  
+    unsigned int option_def_index;
+    char * endianOpt;
+    unsigned int endianSpec, scaleSpec;
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "endian",   OPT_STRING, &endianOpt, &endianSpec,        0);
+    OPTENT3(0, "scale",    OPT_FLOAT,  &cmdlineP->scale, &scaleSpec,   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 (endianSpec) {
+        if (streq(endianOpt, "big"))
+            cmdlineP->endian = ENDIAN_BIG;
+        else if (streq(endianOpt, "little"))
+            cmdlineP->endian = ENDIAN_LITTLE;
+        else
+            pm_error("Invalid value '%s' for -endian.  "
+                     "Must be 'big' or 'little'.", endianOpt);
+    } else
+        cmdlineP->endian = machineEndianness;
+
+    if (!scaleSpec) {
+        cmdlineP->scale = 1.0;
+    }
+    if (cmdlineP->scale == 0.0)
+        pm_error("Scale factor cannot be zero");
+
+    /* Get the program parameters */
+
+    if (argc-1 >= 1)
+        cmdlineP->inputFilespec = argv[1];
+    else
+        cmdlineP->inputFilespec = "-";
+    
+    if (argc-1 > 1)
+        pm_error("Program takes at most one argument:  the file name.  "
+                 "You specified %d", argc-1);
+}
+
+
+
+static enum endian
+thisMachineEndianness(void) {
+/*----------------------------------------------------------------------------
+   Endianness is a component of the format in which a machine represents
+   a number in memory or a register.  It is the only component of the format
+   that varies among typical machines.
+
+   Big endianness is the natural format.  In this format, if an integer is
+   4 bytes, to be stored at memory address 100-103, the most significant 
+   byte goes at 100, the next most significant at 101, and the least
+   significant byte at 103.  This is natural because it matches the way
+   humans read and write numbers.  I.e. 258 is stored as 0x00000102.
+
+   Little endian is extremely common because it is used by IA32.  In the
+   example above, the least significant byte goes first, so 258 would be
+   stored as 0x02010000.
+
+   You can extend this concept to floating point numbers, even though the
+   bytes of a floating point number differ by more than significance.
+-----------------------------------------------------------------------------*/
+    short const testNumber = 0x0001;
+
+    unsigned char * const storedNumber = (unsigned char *)&testNumber;
+    enum endian endianness;
+    
+    if (storedNumber[0] == 0x01)
+        endianness = ENDIAN_LITTLE;
+    else
+        endianness = ENDIAN_BIG;
+
+    return endianness;
+}
+
+
+
+typedef struct {
+    unsigned char bytes[4];
+} pfmSample;
+
+
+
+static void
+floatToPfmSample(float       const input,
+                 pfmSample *       outputP,
+                 enum endian const pfmEndianness) {
+/*----------------------------------------------------------------------------
+   Type converter
+-----------------------------------------------------------------------------*/
+    if (machineEndianness == pfmEndianness) {
+        *(float *)outputP->bytes = input;
+    } else {
+        unsigned char reversed[sizeof(pfmSample)];
+        unsigned int i, j;
+
+        *(float *)reversed = input;
+        
+        for (i = 0, j = sizeof(pfmSample)-1; 
+             i < sizeof(pfmSample); 
+             ++i, --j)
+            
+            outputP->bytes[i] = reversed[j];
+    }
+}
+
+
+
+struct pfmHeader {
+    unsigned int width;
+    unsigned int height;
+    bool color;
+    float scaleFactor;
+    enum endian endian;
+};
+
+
+static void
+writePfmHeader(FILE *           const ofP,
+               struct pfmHeader const pfmHeader) {
+
+    const char * const magic = pfmHeader.color ? "PF" : "Pf";
+    float const scaleFactorEndian = 
+        pfmHeader.endian == ENDIAN_BIG ? 
+            pfmHeader.scaleFactor :
+            - pfmHeader.scaleFactor;
+
+    fprintf(ofP, "%s\n",    magic);
+    fprintf(ofP, "%u %u\n", pfmHeader.width, pfmHeader.height);
+    fprintf(ofP, "%f\n",    scaleFactorEndian);
+}
+
+
+
+static void
+writePfmRow(struct pam * const pamP,
+            FILE *       const ofP,
+            unsigned int const pfmRow,
+            unsigned int const pfmSamplesPerRow,
+            tuplen **    const tuplenArray,
+            enum endian  const endian,
+            float        const scaleFactor,
+            pfmSample *  const pfmRowBuffer) {
+
+    int const row = pamP->height - pfmRow - 1;
+    tuplen * const tuplenRow = tuplenArray[row];
+
+    int col;
+    int pfmCursor;
+    int rc;
+
+    pfmCursor = 0;  /* initial value */
+
+    for (col = 0; col < pamP->width; ++col) {
+        /* The order of planes (R, G, B) is the same in PFM as in PAM. */
+        unsigned int plane;
+        for (plane = 0; plane < pamP->depth; ++plane) {
+            pfmSample val;
+            floatToPfmSample(tuplenRow[col][plane] * scaleFactor, 
+                             &val, endian);
+            pfmRowBuffer[pfmCursor++] = val;
+        }
+    }
+    assert(pfmCursor == pfmSamplesPerRow);
+
+    rc = fwrite(pfmRowBuffer, sizeof(pfmSample), pfmSamplesPerRow, ofP);
+    if (rc != pfmSamplesPerRow)
+        pm_error("Unable to write to output file in the middle of row %d", 
+                 pfmRow);
+
+
+}
+
+
+
+static struct pfmHeader
+makePfmHeader(const struct pam * const pamP,
+              float              const scaleFactor,
+              enum endian        const endian) {
+    
+    struct pfmHeader pfmHeader;
+    
+    pfmHeader.width  = pamP->width;
+    pfmHeader.height = pamP->height;
+
+    if (strncmp(pamP->tuple_type, "RGB", 3) == 0)
+        pfmHeader.color = TRUE;
+    else if (strncmp(pamP->tuple_type, "GRAYSCALE", 9) == 0)
+        pfmHeader.color = FALSE;
+    else if (strncmp(pamP->tuple_type, "BLACKANDWHITE", 13) == 0)
+        pfmHeader.color = FALSE;
+    else
+        pm_error("Invalid PAM input.  Tuple type is '%s'.  "
+                 "We understand only RGB* and GRAYSCALE*", pamP->tuple_type);
+
+    pfmHeader.scaleFactor = scaleFactor;
+    pfmHeader.endian = endian;
+        
+    return pfmHeader;
+}
+
+
+int
+main(int argc, char **argv ) {
+
+    struct cmdlineInfo cmdline;
+    FILE* ifP;
+    struct pam pam;
+    pfmSample * pfmRowBuffer;
+    unsigned int pfmSamplesPerRow;
+    unsigned int pfmRow;
+    tuplen ** tuplenArray;
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    machineEndianness = thisMachineEndianness();
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    tuplenArray = pnm_readpamn(ifP, &pam, sizeof(pam));
+
+    writePfmHeader(stdout, 
+                   makePfmHeader(&pam, cmdline.scale, cmdline.endian));
+
+    pfmSamplesPerRow = pam.width * pam.depth;
+    
+    MALLOCARRAY_NOFAIL(pfmRowBuffer, pfmSamplesPerRow);
+
+    /* PFMs are upside down like BMPs */
+    for (pfmRow = 0; pfmRow < pam.height; ++pfmRow)
+        writePfmRow(&pam, stdout, pfmRow, pfmSamplesPerRow,
+                    tuplenArray, cmdline.endian, cmdline.scale,
+                    pfmRowBuffer);
+
+    pnm_freepamarrayn(tuplenArray, &pam);
+    free(pfmRowBuffer);
+    
+    pm_close(stdout);
+    pm_close(pam.file);
+
+    return 0;
+}
diff --git a/converter/other/pamtopnm.c b/converter/other/pamtopnm.c
new file mode 100644
index 00000000..cc1164da
--- /dev/null
+++ b/converter/other/pamtopnm.c
@@ -0,0 +1,155 @@
+/*----------------------------------------------------------------------------
+                               pamtopnm
+------------------------------------------------------------------------------
+  Part of the Netpbm package.
+
+  Convert PAM images to PBM, PGM, or PPM (i.e. PNM)
+
+  By Bryan Henderson, San Jose CA 2000.08.05
+
+  Contributed to the public domain by its author 2000.08.05.
+-----------------------------------------------------------------------------*/
+
+#include <string.h>
+
+#include "pam.h"
+#include "shhopt.h"
+#include "mallocvar.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;  /* Filespecs of input files */
+    unsigned int assume;    /* -assume option */
+};
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo *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;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0,   "assume",     OPT_FLAG,   NULL, &cmdlineP->assume,         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->inputFilespec = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
+    else
+        cmdlineP->inputFilespec = argv[1];
+}
+
+
+
+static void
+validateTupleType(struct pam const inpam, 
+                  int        const assumeTupleType) {
+/*----------------------------------------------------------------------------
+   Make sure the image has a tuple type we know how to convert to PNM.
+
+   We're quite liberal, trying to accomodate all sorts of future
+   twists on the formats.  If the tuple type _starts with_
+   BLACKANDWHITE, GRAYSCALE, or RGB, and has at least as many planes
+   as we'd need to convert to PBM, PGM, or PPM, respectively, we
+   accept it.  We thus accomodate variations on these formats that add
+   planes and add to the right end of the tuple type to explain them.
+
+   If Callers specified 'assumeTupleType', we're even more liberal.
+-----------------------------------------------------------------------------*/
+    if (assumeTupleType) {
+        /* User says tuple type is appropriate regardless of tuple_type. */
+    } else {
+        if (inpam.depth >= 1 && 
+            strncmp(inpam.tuple_type, "BLACKANDWHITE", 
+                    sizeof("BLACKANDWHITE")-1) == 0) {
+            /* It's a PBMable image */
+        } else if (inpam.depth >= 1 && 
+                   strncmp(inpam.tuple_type, "GRAYSCALE",
+                           sizeof("GRAYSCALE")-1) == 0) {
+            /* It's a PGMable image */
+        } else if (inpam.depth >= 3 &&
+                   strncmp(inpam.tuple_type, "RGB", sizeof("RGB")-1) == 0) {
+            /* It's a PPMable image */
+        } else 
+            pm_error("PAM image does not have a depth and tuple_type "
+                     "consistent with a PNM image."
+                     "According to its "
+                     "header, depth is %d and tuple_type is '%s'.  "
+                     "Use the -assume option to convert anyway.",
+                     inpam.depth, inpam.tuple_type);
+    }
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE* ifP;
+    struct pam inpam;   /* Input PAM image */
+    struct pam outpam;  /* Output PNM image */
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    validateTupleType(inpam, cmdline.assume);
+
+    outpam = inpam;
+    outpam.file = stdout;
+    
+    if (inpam.depth < 3) {
+        outpam.depth = 1;
+        if (inpam.maxval == 1)
+            outpam.format = PBM_FORMAT;
+        else 
+            outpam.format = PGM_FORMAT;
+    } else {
+        outpam.depth = 3;
+        outpam.format = PPM_FORMAT;
+    }
+
+    pnm_writepaminit(&outpam);
+
+    {
+        tuple *tuplerow;
+        
+        tuplerow = pnm_allocpamrow(&inpam);      
+        { 
+            int row;
+            
+            for (row = 0; row < inpam.height; row++) {
+                pnm_readpamrow(&inpam, tuplerow);
+                pnm_writepamrow(&outpam, tuplerow);
+            }
+        }
+        pnm_freepamrow(tuplerow);        
+    }
+    return 0;
+}
diff --git a/converter/other/pamtosvg/Makefile b/converter/other/pamtosvg/Makefile
new file mode 100644
index 00000000..7f9c3e30
--- /dev/null
+++ b/converter/other/pamtosvg/Makefile
@@ -0,0 +1,55 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/other/pamtosvg
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+BINARIES = pamtosvg
+
+PAMTOSVG_OBJECTS = \
+	pamtosvg.o \
+	output-svg.o \
+	fit.o \
+	spline.o \
+	curve.o \
+	vector.o \
+	epsilon-equal.o \
+	autotrace.o \
+	pxl-outline.o \
+	bitmap.o \
+	thin-image.o \
+	logreport.o \
+	exception.o \
+	image-proc.o \
+
+MERGE_OBJECTS = \
+	pamtosvg.o2 \
+	output-svg.o \
+	fit.o \
+	spline.o \
+	curve.o \
+	vector.o \
+	epsilon-equal.o \
+	autotrace.o \
+	pxl-outline.o \
+	bitmap.o \
+	thin-image.o \
+	logreport.o \
+	exception.o \
+	image-proc.o \
+
+OBJECTS = $(PAMTOSVG_OBJECTS)
+
+MERGEBINARIES = $(BINARIES)
+
+all: $(BINARIES)
+
+include $(SRCDIR)/Makefile.common
+
+pamtosvg: $(PAMTOSVG_OBJECTS) $(NETPBMLIB) $(LIBOPT)
+	$(LD) $(LDFLAGS) -o $@ $(PAMTOSVG_OBJECTS) \
+	  $(shell $(LIBOPT) $(NETPBMLIB)) \
+	  $(MATHLIB) $(LDLIBS) $(RPATH) $(LADD)
diff --git a/converter/other/pamtosvg/README b/converter/other/pamtosvg/README
new file mode 100644
index 00000000..a06b71a5
--- /dev/null
+++ b/converter/other/pamtosvg/README
@@ -0,0 +1,109 @@
+The core of this program is derived from Martin Weber's
+(martweb@gmx.net) Autotrace.  Bryan Henderson adapted it to Netpbm in 
+February 2006.
+
+Much of the Autotrace code has been rewritten to be easier to read and match
+Netpbm coding style.
+
+Pieces of Autotrace that duplicate other Netpbm programs or just don't fit
+the Netpbm philosophy have been removed.  In particular, Autotrace has the
+ability to take formats other than Netpbm formats for input, to despeckle
+the image before tracing, and to quantize colors before tracing.  Pamtosvg
+has none of that.
+
+Pamtosvg uses libnetpbm to read the input image, process the command line,
+manage memory, and do several other minor things.  Autotrace has its own
+code for those things.
+
+Autotrace uses a shmaltzy trick to deal with open outlines.  There is a
+piece of the program that divides an outline at its corners and then creates
+a curve for the pieces between every two corners.  For an open outline,
+there are segments of the outline at each end that are not between two
+corners.  One piece of code finds the corners, and another computes the
+curves between them.  
+
+In order to use the closed outline code for the open outline case,
+Autotrace includes a dummy corner at the start of the outline in the
+list of corners.  The curve-finding code then has a special case for
+the segment after the last corner.  Pamtosvg uses a cleaner approach.
+The corner list is an actual list of corners -- there is no dummy
+corner.  The curve-finding code has two special cases for open
+outlines -- one for the segment before the first corner, and one for
+the segment after the last corner.
+
+
+STRATEGY
+--------
+
+Autotrace is capable of many more vector graphics output formats
+besides SVG.  The basic curve tracing is the same for all; it just has
+output formatting modules.  Netpbm ought to be able to generate those
+formats as well.  SVG is just the beginning.
+
+Of course, having a program that generates multiple output formats based
+on command line options is not the Netpbm way to approach it.  A pure
+traditional Netpbm approach would be to have a converter between PAM
+and each of the vector graphics formats.
+
+That isn't practical because of the information that gets lost when you
+convert from vector to raster form, and also because 99% of the logic
+in converting between PAM and a vector graphics format is the same for
+all the vector graphics formats.
+
+Therefore, the strategy is to adopt a single vector graphics format
+and use it as the common intermediate format.  Right now, it looks
+like SVG would be a good choice for that.  However, it may turn out to
+be better to create a vector graphics format specially for Netpbm.
+The reason for that is that an intermediate Netpbm format has a rather
+different requirement from all these other formats -- it isn't meant
+to be transmitted or stored, but it is meant to be easy for a
+programmer to work with.
+
+We need a program to go the other way -- to convert from SVG to PAM.
+I.e. a curve drawing program.  The Ppmdraw program does this kind of
+curve drawing (in fact, a Ppmdraw script can be considered a vector
+graphics format).  Ppmdraw itself might not be adaptable, but the
+library routines it uses are probably all we need to convert SVG to
+PAM.
+
+The Pamtosvg code should be reworked to use a libnetpbm tuple array
+instead of its at_bitmap_type for the raster.
+
+Nobody has any plans to do any of this work.  I document the strategy
+only so that if someone decides to do some work, he can go in the
+right direction.
+
+
+COPYRIGHT CONSIDERATIONS
+------------------------
+
+Bryan took the code and has distributed it under a copyright license
+granted to him, as a member of the public, by the authors.  That
+license is the GNU Lesser Public License Version 2.1.  All the authors
+offer that same license for Pamtosvg to the public.  A copy of it is
+in the Netpbm doc/ directory.
+
+
+CREDITS
+-------
+
+Autotrace's source code listed the following as contributors to it:
+
+Martin Weber <martweb@gmx.net>
+Bernhard Herzog (Postscript, svg and sk export filter) 
+Ian MacPhedran (xfig export filter) 
+Martin Kroeker (bugfixes) 
+Tobias Polzin (bugfixes) 
+Kevin O'Gorman (Shockwave support) 
+MenTaLguY (png import filter)
+Peter Cucka (bugfixes)
+Enrico Persiani (emf export) 
+Johannes Schindelin (Magick import filter)
+Masatake YAMATO (library, help with cvs)
+Steffen Politzky (dxf export)
+David A. Bartold (part of despeckle)
+Han-Wen Nienhuys (rpm-spec file)
+R. P. C. Rodgers (man page)
+Allen Barnett (improved emf export)
+Andrew Elia (dr2d export filter)
+Ralf Stubner (bugfixes about pstoedit usage)
diff --git a/converter/other/pamtosvg/autotrace.c b/converter/other/pamtosvg/autotrace.c
new file mode 100644
index 00000000..e9902669
--- /dev/null
+++ b/converter/other/pamtosvg/autotrace.c
@@ -0,0 +1,220 @@
+/* autotrace.c --- Autotrace API
+
+  Copyright (C) 2000, 2001, 2002 Martin Weber
+
+  The author can be contacted at <martweb@gmx.net>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "mallocvar.h"
+
+#include "autotrace.h"
+#include "exception.h"
+
+#include "fit.h"
+#include "bitmap.h"
+#include "spline.h"
+
+#include "image-header.h"
+#include "image-proc.h"
+#include "thin-image.h"
+
+
+#define AT_DEFAULT_DPI 72
+
+at_fitting_opts_type *
+at_fitting_opts_new(void)
+{
+  at_fitting_opts_type * opts;
+  MALLOCVAR_NOFAIL(opts);
+  return opts;
+}
+
+at_fitting_opts_type *
+at_fitting_opts_copy (at_fitting_opts_type * original)
+{
+  at_fitting_opts_type * new_opts;
+  if (original == NULL)
+    return NULL;
+
+  new_opts = at_fitting_opts_new ();
+  *new_opts = *original;
+  new_opts->backgroundSpec = original->backgroundSpec;
+  new_opts->background_color = original->background_color;
+  return new_opts;
+}
+
+void 
+at_fitting_opts_free(at_fitting_opts_type * opts)
+{
+  free(opts);
+}
+
+at_output_opts_type *
+at_output_opts_new(void)
+{
+  at_output_opts_type * opts;
+  MALLOCVAR_NOFAIL(opts);
+  opts->dpi          = AT_DEFAULT_DPI;
+  return opts;
+}
+
+at_output_opts_type *
+at_output_opts_copy(at_output_opts_type * original)
+{
+  at_output_opts_type * opts =  at_output_opts_new();
+  *opts = *original;
+  return opts;
+}
+
+void
+at_output_opts_free(at_output_opts_type * opts)
+{
+  free(opts);
+}
+
+/* at_splines_new_full modifies its 'bitmap' argument
+   when it does the thin_image thing.
+*/
+at_spline_list_array_type * 
+at_splines_new_full(at_bitmap_type *       const bitmap,
+                    at_fitting_opts_type * const opts,
+                    at_msg_func                  msg_func, 
+                    void *                 const msg_data,
+                    at_progress_func             notify_progress,
+                    void *                 const progress_data,
+                    at_testcancel_func           test_cancel,
+                    void *                 const testcancel_data) {
+
+    at_spline_list_array_type * retval;
+    image_header_type image_header;
+    pixel_outline_list_type pixelOutlineList;
+    at_exception_type exp;
+    distance_map_type distanceMap;
+    bool haveDistMap;
+
+    exp = at_exception_new(msg_func, msg_data);
+
+    image_header.width  = at_bitmap_get_width(bitmap);
+    image_header.height = at_bitmap_get_height(bitmap);
+
+    if (opts->centerline) {
+        if (opts->preserve_width) {
+            /* Preserve line width prior to thinning. */
+            bool const paddedTrue = true;
+            distanceMap = new_distance_map(*bitmap, 255, paddedTrue, &exp);
+            haveDistMap = true;
+        } else
+            haveDistMap = false;
+        thin_image(bitmap, opts->backgroundSpec, opts->background_color, &exp);
+    } else
+        haveDistMap = false;
+
+    if (at_exception_got_fatal(&exp))
+        retval = NULL;
+    else {
+        if (opts->centerline) {
+            pixel background_color;
+
+            if (opts->backgroundSpec) 
+                background_color = opts->background_color;
+            else
+                PPM_ASSIGN(background_color, 255, 255, 255);
+            
+            pixelOutlineList =
+                find_centerline_pixels(*bitmap, background_color, 
+                                       notify_progress, progress_data,
+                                       test_cancel, testcancel_data, &exp);
+        } else
+            pixelOutlineList =
+                find_outline_pixels(*bitmap,
+                                    opts->backgroundSpec,
+                                    opts->background_color, 
+                                    notify_progress, progress_data,
+                                    test_cancel, testcancel_data, &exp);
+
+        if (at_exception_got_fatal(&exp) ||
+            (test_cancel && test_cancel(testcancel_data)))
+            retval = NULL;
+        else {
+            at_spline_list_array_type * splinesP;
+        
+            MALLOCVAR_NOFAIL(splinesP); 
+            fit_outlines_to_splines(pixelOutlineList, opts,
+                                    haveDistMap ? &distanceMap : NULL,
+                                    image_header.width,
+                                    image_header.height,
+                                    &exp,
+                                    notify_progress, progress_data,
+                                    test_cancel, testcancel_data,
+                                    splinesP);
+
+            if (at_exception_got_fatal(&exp) ||
+                (test_cancel && test_cancel(testcancel_data)))
+                retval = NULL;
+            else {
+                if (notify_progress)
+                    notify_progress(1.0, progress_data);
+
+                retval = splinesP;
+            }
+            free_pixel_outline_list(&pixelOutlineList);
+        }
+        if (haveDistMap)
+            free_distance_map(&distanceMap);
+    }
+    return retval;
+}
+
+
+
+void 
+at_splines_write(at_output_write_func                  outputWriter,
+                 FILE *                          const writeto,
+                 at_output_opts_type *           const optsArg,
+                 at_spline_list_array_type *     const splinesP,
+                 at_msg_func                           msgFunc,
+                 void *                          const msgData) {
+
+    at_output_opts_type * optsP;
+    bool newOpts;
+    int llx, lly, urx, ury;
+    llx = 0;
+    lly = 0;
+    urx = splinesP->width;
+    ury = splinesP->height;
+    
+    if (optsArg == NULL) {
+        newOpts = true;
+        optsP   = at_output_opts_new();
+    } else {
+        newOpts = false;
+        optsP   = optsArg;
+    }
+    (*outputWriter)(writeto, "DUMMYFILENAME",
+                    llx, lly, urx, ury, optsP, *splinesP,
+                    msgFunc, msgData);
+    if (newOpts)
+        at_output_opts_free(optsP);
+}
+
+
+
+void 
+at_splines_free(at_spline_list_array_type * const splines) {
+
+    free_spline_list_array(splines);
+    free(splines);
+}
diff --git a/converter/other/pamtosvg/autotrace.h b/converter/other/pamtosvg/autotrace.h
new file mode 100644
index 00000000..2ac81a08
--- /dev/null
+++ b/converter/other/pamtosvg/autotrace.h
@@ -0,0 +1,258 @@
+/* autotrace.h --- Autotrace API
+
+  Copyright (C) 2000, 2001, 2002 Martin Weber
+
+  The author can be contacted at <martweb@gmx.net>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef AUTOTRACE_H
+#define AUTOTRACE_H
+
+#include <stdio.h>
+
+#include "point.h"
+#include "pm_c_util.h"
+#include "ppm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* ===================================================================== *
+ * Typedefs
+ * ===================================================================== */
+
+typedef struct _at_fitting_opts_type at_fitting_opts_type;
+typedef struct _at_input_opts_type   at_input_opts_type;
+typedef struct _at_output_opts_type  at_output_opts_type;
+typedef struct _at_bitmap_type at_bitmap_type;
+typedef struct _at_spline_type at_spline_type;
+typedef struct _at_spline_list_type at_spline_list_type;
+typedef struct _at_spline_list_array_type at_spline_list_array_type;
+
+/* Third degree is the highest we deal with.  */
+typedef enum _at_polynomial_degree
+{
+  AT_LINEARTYPE = 1, 
+  AT_QUADRATICTYPE = 2, 
+  AT_CUBICTYPE = 3, 
+  AT_PARALLELELLIPSETYPE = 4,
+  AT_ELLIPSETYPE = 5, 
+  AT_CIRCLETYPE = 6 
+  /* not the real number of points to define a
+     circle but to distinguish between a cubic spline */
+} at_polynomial_degree;
+
+/* A Bezier spline can be represented as four points in the real plane:
+   a starting point, ending point, and two control points.  The
+   curve always lies in the convex hull defined by the four points.  It
+   is also convenient to save the divergence of the spline from the
+   straight line defined by the endpoints.  */
+struct _at_spline_type
+{
+  float_coord v[4];	/* The control points.  */
+  at_polynomial_degree degree;
+  float linearity;
+};
+
+/* Each outline in a character is typically represented by many
+   splines.  So, here is a list structure for that:  */
+struct _at_spline_list_type
+{
+  at_spline_type *data;
+  unsigned length;
+  bool clockwise;
+  pixel color;
+  bool open;
+};
+
+/* Each character is in general made up of many outlines. So here is one
+   more list structure.  */
+struct _at_spline_list_array_type
+{
+  at_spline_list_type *data;
+  unsigned length;
+
+  /* splines bbox */
+  unsigned short height, width;
+  
+  /* the values for following members are inherited from 
+     at_fitting_opts_type */
+  bool backgroundSpec;
+  pixel background_color;
+  bool centerline;
+  bool preserve_width;
+  float width_weight_factor;
+
+};
+
+
+/* Fitting option.
+   With using at_fitting_opts_doc macro, the description of 
+   each option could be get. e.g. at_fitting_opts_doc(background_color) */
+struct _at_fitting_opts_type {
+    bool backgroundSpec;
+    pixel background_color;
+    float corner_always_threshold;
+    unsigned corner_surround;
+    float corner_threshold;
+    float error_threshold;
+    unsigned filter_iterations;
+    float line_reversion_threshold;
+    float line_threshold;
+    bool remove_adjacent_corners;
+    unsigned tangent_surround;
+    bool centerline;
+    bool preserve_width;
+    float width_weight_factor;
+};
+
+struct _at_output_opts_type
+{
+  int dpi;			/* DPI is used only in MIF output.*/
+};
+
+struct _at_bitmap_type
+{
+  unsigned short height;
+  unsigned short width;
+  unsigned char *bitmap;
+  unsigned int np;
+};
+
+typedef enum _at_msg_type
+{
+  AT_MSG_FATAL = 1,
+  AT_MSG_WARNING
+} at_msg_type;
+
+typedef
+void (* at_msg_func) (const char * const msg,
+                      at_msg_type  const msg_type,
+                      void *       const client_data);
+
+typedef 
+int (*at_output_write_func) (FILE *                          const file,
+                             const char *                    const name,
+                             int                             const llx,
+                             int                             const lly, 
+                             int                             const urx,
+                             int                             const ury,
+                             at_output_opts_type *           const opts,
+                             at_spline_list_array_type       const shape,
+                             at_msg_func                           msg_func, 
+                             void *                          const msg_data);
+
+/*
+ * Progress handler typedefs
+ * 0.0 <= percentage <= 1.0
+ */
+typedef
+void (*at_progress_func) (float const percentage,
+                           void *  const client_data);
+
+/*
+ * Test cancel
+ * Return true if auto-tracing should be stopped.
+ */
+typedef
+bool (*at_testcancel_func) (void * const client_data);
+
+/* ===================================================================== *
+ * Functions
+ * ===================================================================== */
+
+/* --------------------------------------------------------------------- *
+ * Fitting option related
+ *
+ * TODO: internal data access, copy
+ * --------------------------------------------------------------------- */
+at_fitting_opts_type * at_fitting_opts_new(void);
+at_fitting_opts_type * at_fitting_opts_copy (at_fitting_opts_type * original); 
+void at_fitting_opts_free(at_fitting_opts_type * opts);
+
+/* TODO: Gettextize */
+#define at_fitting_opts_doc(opt) _(at_doc__##opt)
+
+/* --------------------------------------------------------------------- *
+ * Output option related
+ *
+ * TODO: internal data access
+ * --------------------------------------------------------------------- */
+at_output_opts_type * at_output_opts_new(void);
+at_output_opts_type * at_output_opts_copy(at_output_opts_type * original);
+void at_output_opts_free(at_output_opts_type * opts);
+
+/* --------------------------------------------------------------------- *
+ * Spline related
+ *
+ * TODO: internal data access
+ * --------------------------------------------------------------------- */
+/* at_splines_new_full
+
+   args:
+
+   NOTIFY_PROGRESS is called repeatedly inside at_splines_new_full
+   to notify the progress of the execution. This might be useful for 
+   interactive applications. NOTIFY_PROGRESS is called following 
+   format:
+
+   NOTIFY_PROGRESS (percentage, progress_data);
+
+   test_cancel is called repeatedly inside at_splines_new_full
+   to test whether the execution is canceled or not.
+   If test_cancel returns TRUE, execution of at_splines_new_full
+   is stopped as soon as possible and returns NULL. If test_cancel 
+   returns FALSE, nothing happens. test_cancel  is called following
+   format:
+
+   TEST_CANCEL (testcancel_data);
+   
+   NULL is valid value for notify_progress and/or test_cancel if 
+   you don't need to know the progress of the execution and/or 
+   cancel the execution */ 
+
+at_spline_list_array_type * 
+at_splines_new_full(at_bitmap_type *       const bitmap,
+                    at_fitting_opts_type * const opts,
+                    at_msg_func                  msg_func, 
+                    void *                 const msg_data,
+                    at_progress_func             notify_progress,
+                    void *                 const progress_data,
+                    at_testcancel_func           test_cancel,
+                    void *                 const testcancel_data);
+
+void 
+at_splines_write(at_output_write_func                  output_writer,
+                 FILE *                          const writeto,
+                 at_output_opts_type *           const opts,
+                 at_spline_list_array_type *     const splines,
+                 at_msg_func                           msg_func,
+                 void *                          const msg_data);
+
+void
+at_splines_free(at_spline_list_array_type * const splines);
+
+
+/* --------------------------------------------------------------------- *
+ * Misc
+ * --------------------------------------------------------------------- */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/converter/other/pamtosvg/bitmap.c b/converter/other/pamtosvg/bitmap.c
new file mode 100644
index 00000000..1a00e748
--- /dev/null
+++ b/converter/other/pamtosvg/bitmap.c
@@ -0,0 +1,116 @@
+/* bitmap.c: operations on bitmaps. */
+
+#include <string.h>
+
+#include "mallocvar.h"
+
+#include "bitmap.h"
+
+at_bitmap_type *
+at_bitmap_new(unsigned short width,
+              unsigned short height,
+              unsigned int planes) {
+
+    at_bitmap_type * bitmap;
+
+    MALLOCVAR_NOFAIL(bitmap); 
+
+    *bitmap = at_bitmap_init(NULL, width, height, planes);
+
+    return bitmap;
+}
+
+
+
+at_bitmap_type *
+at_bitmap_copy(at_bitmap_type * src)
+{
+    at_bitmap_type * dist;
+    unsigned short width, height, planes;
+
+    width  = at_bitmap_get_width(src);
+    height = at_bitmap_get_height(src);
+    planes = at_bitmap_get_planes(src);
+    
+    dist = at_bitmap_new(width, height, planes);
+    memcpy(dist->bitmap, 
+           src->bitmap, 
+           width * height * planes * sizeof(unsigned char));
+    return dist;
+}
+
+
+
+at_bitmap_type
+at_bitmap_init(unsigned char * area,
+               unsigned short width,
+               unsigned short height,
+               unsigned int planes) {
+
+    at_bitmap_type bitmap;
+    
+    if (area)
+        bitmap.bitmap = area;
+    else {
+        if (width * height == 0)
+            bitmap.bitmap = NULL;
+        else {
+            MALLOCARRAY(bitmap.bitmap, width * height * planes);
+            if (bitmap.bitmap == NULL)
+                pm_error("Unable to allocate %u x %u x %u bitmap array",
+                         width, height, planes);
+            bzero(bitmap.bitmap,
+                  width * height * planes * sizeof(unsigned char));
+        }
+    }
+    
+    bitmap.width  = width;
+    bitmap.height = height;
+    bitmap.np     =  planes;
+
+    return bitmap;  
+}
+
+void 
+at_bitmap_free (at_bitmap_type * bitmap)
+{
+    free_bitmap (bitmap);
+    free(bitmap);
+}
+
+unsigned short
+at_bitmap_get_width (at_bitmap_type * bitmap)
+{
+    return bitmap->width;
+}
+
+unsigned short
+at_bitmap_get_height (at_bitmap_type * bitmap)
+{
+    return bitmap->height;
+}
+
+unsigned short
+at_bitmap_get_planes (at_bitmap_type * bitmap)
+{
+    return bitmap->np;
+}
+
+
+
+bitmap_type
+new_bitmap (unsigned short width, unsigned short height)
+{
+    return at_bitmap_init(NULL,width,height,1);
+}
+
+/* Free the storage that is allocated for a bitmap.  On the other hand,
+   the bitmap might not have any storage allocated for it if it is zero
+   in either dimension; in that case, don't free it.  */
+
+void
+free_bitmap (bitmap_type *b)
+{
+    if (b->bitmap != NULL)
+        free (b->bitmap);
+}
diff --git a/converter/other/pamtosvg/bitmap.h b/converter/other/pamtosvg/bitmap.h
new file mode 100644
index 00000000..7334f138
--- /dev/null
+++ b/converter/other/pamtosvg/bitmap.h
@@ -0,0 +1,53 @@
+/* bitmap.h: definition for a bitmap type.  No packing is done by
+   default; each pixel is represented by an entire byte.  Among other
+   things, this means the type can be used for both grayscale and binary
+   images. */
+
+#ifndef BITMAP_H
+#define BITMAP_H
+
+#include "autotrace.h"
+#include <stdio.h>
+
+/* at_ prefix removed version */
+typedef at_bitmap_type bitmap_type;
+#define BITMAP_PLANES(b)          AT_BITMAP_PLANES(b)
+#define BITMAP_BITS(b)            AT_BITMAP_BITS(b)  
+#define BITMAP_WIDTH(b)           AT_BITMAP_WIDTH(b)  
+#define BITMAP_HEIGHT(b)          AT_BITMAP_HEIGHT(b) 
+
+/* This is the pixel at [ROW,COL].  */
+#define BITMAP_PIXEL(b, row, col)					\
+  ((b).bitmap + ((row) * (b).width + (col)) * (b).np)
+
+#define BITMAP_VALID_PIXEL(b, row, col)					\
+   	((row) < (b).height && (col) < (b).width)
+
+/* Allocate storage for the bits, set them all to white, and return an
+   initialized structure.  */
+extern bitmap_type new_bitmap (unsigned short width, unsigned short height);
+
+/* Free that storage.  */
+extern void free_bitmap (bitmap_type *);
+
+
+at_bitmap_type * at_bitmap_new(unsigned short width,
+			       unsigned short height,
+			       unsigned int planes);
+at_bitmap_type * at_bitmap_copy(at_bitmap_type * src);
+
+/* We have to export functions that supports internal datum 
+   access. Such functions might be useful for 
+   at_bitmap_new user. */
+unsigned short at_bitmap_get_width (at_bitmap_type * bitmap);
+unsigned short at_bitmap_get_height (at_bitmap_type * bitmap);
+unsigned short at_bitmap_get_planes (at_bitmap_type * bitmap);
+void at_bitmap_free (at_bitmap_type * bitmap);
+
+at_bitmap_type
+at_bitmap_init(unsigned char * area,
+	       unsigned short width,
+	       unsigned short height,
+	       unsigned int planes);
+
+#endif /* not BITMAP_H */
diff --git a/converter/other/pamtosvg/curve.c b/converter/other/pamtosvg/curve.c
new file mode 100644
index 00000000..cc8aeb59
--- /dev/null
+++ b/converter/other/pamtosvg/curve.c
@@ -0,0 +1,314 @@
+/* curve.c: operations on the lists of pixels and lists of curves.
+
+   The code was partially derived from limn.
+
+   Copyright (C) 1992 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "mallocvar.h"
+
+#include "logreport.h"
+#include "curve.h"
+
+
+static float_coord
+int_to_real_coord(pm_pixelcoord const int_coord) {
+/*----------------------------------------------------------------------------
+  Turn an integer point into a real one.
+-----------------------------------------------------------------------------*/
+    float_coord real_coord;
+
+    real_coord.x = int_coord.col;
+    real_coord.y = int_coord.row;
+    real_coord.z = 0.0;
+    
+    return real_coord;
+}
+
+
+
+/* Return an entirely empty curve.  */
+
+curve_type
+new_curve (void)
+{
+  curve_type curve;
+  MALLOCVAR_NOFAIL(curve);
+  curve->point_list = NULL;
+  CURVE_LENGTH (curve) = 0;
+  CURVE_CYCLIC (curve) = false;
+  CURVE_START_TANGENT (curve) = CURVE_END_TANGENT (curve) = NULL;
+  PREVIOUS_CURVE (curve) = NEXT_CURVE (curve) = NULL;
+
+  return curve;
+}
+
+
+/* Don't copy the points or tangents, but copy everything else.  */
+
+curve_type
+copy_most_of_curve (curve_type old_curve)
+{
+  curve_type curve = new_curve ();
+
+  CURVE_CYCLIC (curve) = CURVE_CYCLIC (old_curve);
+  PREVIOUS_CURVE (curve) = PREVIOUS_CURVE (old_curve);
+  NEXT_CURVE (curve) = NEXT_CURVE (old_curve);
+
+  return curve;
+}
+
+
+/* The length of CURVE will be zero if we ended up not being able to fit
+   it (which in turn implies a problem elsewhere in the program, but at
+   any rate, we shouldn't try here to free the nonexistent curve).  */
+
+void
+free_curve (curve_type curve)
+{
+  if (CURVE_LENGTH (curve) > 0)
+    free (curve->point_list);
+  if (CURVE_START_TANGENT (curve))
+    free (CURVE_START_TANGENT (curve));
+  if (CURVE_END_TANGENT (curve))
+    free (CURVE_END_TANGENT (curve));
+}
+
+
+void
+append_point(curve_type  const curve,
+             float_coord const coord) {
+
+    CURVE_LENGTH(curve)++;
+    REALLOCARRAY_NOFAIL(curve->point_list, CURVE_LENGTH(curve));
+    LAST_CURVE_POINT(curve) = coord;
+    /* The t value does not need to be set.  */
+}
+
+
+void
+append_pixel(curve_type    const curve,
+             pm_pixelcoord const coord) {
+
+    append_point(curve, int_to_real_coord(coord));
+}
+
+
+/* Print a curve in human-readable form.  It turns out we never care
+   about most of the points on the curve, and so it is pointless to
+   print them all out umpteen times.  What matters is that we have some
+   from the end and some from the beginning.  */
+
+#define NUM_TO_PRINT 3
+
+#define LOG_CURVE_POINT(c, p, print_t)					\
+  do									\
+    {									\
+      LOG2 ("(%.3f,%.3f)", CURVE_POINT (c, p).x, CURVE_POINT (c, p).y);	\
+      if (print_t)							\
+        LOG1 ("/%.2f", CURVE_T (c, p));					\
+    }									\
+  while (0)
+
+void
+log_curve (curve_type curve, bool print_t)
+{
+  unsigned this_point;
+
+  if (!log_file) return;
+
+  LOG1 ("curve id = %lx:\n", (unsigned long) curve);
+  LOG1 ("  length = %u.\n", CURVE_LENGTH (curve));
+  if (CURVE_CYCLIC (curve))
+    LOG ("  cyclic.\n");
+
+  /* It should suffice to check just one of the tangents for being null
+     -- either they both should be, or neither should be.  */
+  if (CURVE_START_TANGENT (curve) != NULL)
+    LOG4 ("  tangents = (%.3f,%.3f) & (%.3f,%.3f).\n",
+          CURVE_START_TANGENT (curve)->dx, CURVE_START_TANGENT (curve)->dy,
+          CURVE_END_TANGENT (curve)->dx, CURVE_END_TANGENT (curve)->dy);
+
+  LOG ("  ");
+
+  /* If the curve is short enough, don't use ellipses.  */
+  if (CURVE_LENGTH (curve) <= NUM_TO_PRINT * 2)
+    {
+      for (this_point = 0; this_point < CURVE_LENGTH (curve); this_point++)
+        {
+          LOG_CURVE_POINT (curve, this_point, print_t);
+          LOG (" ");
+
+          if (this_point != CURVE_LENGTH (curve) - 1
+              && (this_point + 1) % NUM_TO_PRINT == 0)
+            LOG ("\n  ");
+        }
+    }
+  else
+    {
+      for (this_point = 0;
+           this_point < NUM_TO_PRINT && this_point < CURVE_LENGTH (curve);
+           this_point++)
+        {
+          LOG_CURVE_POINT (curve, this_point, print_t);
+          LOG (" ");
+        }
+
+      LOG ("...\n   ...");
+
+      for (this_point = CURVE_LENGTH (curve) - NUM_TO_PRINT;
+           this_point < CURVE_LENGTH (curve);
+           this_point++)
+        {
+          LOG (" ");
+          LOG_CURVE_POINT (curve, this_point, print_t);
+        }
+    }
+
+  LOG (".\n");
+}
+
+
+/* Like `log_curve', but write the whole thing.  */
+
+void
+log_entire_curve (curve_type curve)
+{
+  unsigned this_point;
+
+  if (!log_file) return;
+
+  LOG1 ("curve id = %lx:\n", (unsigned long) curve);
+  LOG1 ("  length = %u.\n", CURVE_LENGTH (curve));
+  if (CURVE_CYCLIC (curve))
+    LOG ("  cyclic.\n");
+
+  /* It should suffice to check just one of the tangents for being null
+     -- either they both should be, or neither should be.  */
+  if (CURVE_START_TANGENT (curve) != NULL)
+    LOG4 ("  tangents = (%.3f,%.3f) & (%.3f,%.3f).\n",
+          CURVE_START_TANGENT (curve)->dx, CURVE_START_TANGENT (curve)->dy,
+          CURVE_END_TANGENT (curve)->dx, CURVE_END_TANGENT (curve)->dy);
+
+  LOG (" ");
+
+  for (this_point = 0; this_point < CURVE_LENGTH (curve); this_point++)
+    {
+      LOG (" ");
+      LOG_CURVE_POINT (curve, this_point, true);
+      /* Compiler warning `Condition is always true' can be ignored */
+    }
+
+  LOG (".\n");
+}
+
+
+/* Return an initialized but empty curve list.  */
+
+curve_list_type
+new_curve_list (void)
+{
+  curve_list_type curve_list;
+
+  curve_list.length = 0;
+  curve_list.data = NULL;
+
+  return curve_list;
+}
+
+
+/* Free a curve list and all the curves it contains.  */
+
+void
+free_curve_list(curve_list_type * const curve_list) {
+
+  unsigned this_curve;
+
+  for (this_curve = 0; this_curve < curve_list->length; this_curve++)
+    {
+      free_curve (curve_list->data[this_curve]);
+      free (curve_list->data[this_curve]);
+    }
+
+  /* If the character was empty, it won't have any curves.  */
+  if (curve_list->data != NULL)
+    free (curve_list->data);
+}
+
+
+/* Add an element to a curve list.  */
+
+void
+append_curve (curve_list_type *curve_list, curve_type curve)
+{
+  curve_list->length++;
+  REALLOCARRAY_NOFAIL(curve_list->data, curve_list->length);
+  curve_list->data[curve_list->length - 1] = curve; }
+
+
+/* Return an initialized but empty curve list array.  */
+
+curve_list_array_type
+new_curve_list_array (void)
+{
+  curve_list_array_type curve_list_array;
+
+  CURVE_LIST_ARRAY_LENGTH (curve_list_array) = 0;
+  curve_list_array.data = NULL;
+
+  return curve_list_array;
+}
+
+
+/* Free a curve list array and all the curve lists it contains.  */
+
+void
+free_curve_list_array(const curve_list_array_type * const curve_list_array,
+                      at_progress_func                    notify_progress, 
+                      void *                        const client_data) {
+
+  unsigned this_list;
+
+  for (this_list = 0; this_list < CURVE_LIST_ARRAY_LENGTH(*curve_list_array);
+       this_list++) {
+      if (notify_progress)
+          notify_progress(((float)this_list)/
+                          (CURVE_LIST_ARRAY_LENGTH(*curve_list_array) *
+                           (float)3.0)+(float)0.666 ,
+                          client_data);
+      free_curve_list(&CURVE_LIST_ARRAY_ELT (*curve_list_array, this_list));
+  }
+  
+  /* If the character was empty, it won't have any curves.  */
+  if (curve_list_array->data != NULL)
+      free(curve_list_array->data);
+}
+
+
+/* Add an element to a curve list array.  */
+
+void
+append_curve_list(curve_list_array_type * const curve_list_array,
+                  curve_list_type         const curve_list) {
+
+  CURVE_LIST_ARRAY_LENGTH (*curve_list_array)++;
+  REALLOCARRAY_NOFAIL(curve_list_array->data,
+                      CURVE_LIST_ARRAY_LENGTH(*curve_list_array));
+  LAST_CURVE_LIST_ARRAY_ELT (*curve_list_array) = curve_list;
+}
+
+
+
diff --git a/converter/other/pamtosvg/curve.h b/converter/other/pamtosvg/curve.h
new file mode 100644
index 00000000..db3cc682
--- /dev/null
+++ b/converter/other/pamtosvg/curve.h
@@ -0,0 +1,160 @@
+/* curve.h: data structures for the conversion from pixels to splines. */
+
+#ifndef CURVE_H
+#define CURVE_H
+
+#include "autotrace.h"
+#include "point.h"
+#include "vector.h"
+
+/* We are simultaneously manipulating two different representations of
+   the same outline: one based on (x,y) positions in the plane, and one
+   based on parametric splines.  (We are trying to match the latter to
+   the former.)  Although the original (x,y)'s are pixel positions,
+   i.e., integers, after filtering they are reals.  */
+
+typedef struct {
+    float_coord coord;
+    float       t;
+} point_type;
+
+
+
+struct curve {
+/*----------------------------------------------------------------------------
+  An ordered list of contiguous points in the raster, with no corners
+  in it.  I.e. something that could reasonably be fit to a spline.
+-----------------------------------------------------------------------------*/
+    point_type *   point_list;
+    unsigned       length;
+    bool           cyclic;
+    vector_type *  start_tangent;
+    vector_type *  end_tangent;
+    struct curve * previous;
+    struct curve * next;
+};
+
+typedef struct curve * curve_type;
+
+/* Get at the coordinates and the t values.  */
+#define CURVE_POINT(c, n) ((c)->point_list[n].coord)
+#define LAST_CURVE_POINT(c) ((c)->point_list[(c)->length-1].coord)
+#define CURVE_T(c, n) ((c)->point_list[n].t)
+#define LAST_CURVE_T(c) ((c)->point_list[(c)->length-1].t)
+
+/* This is the length of `point_list'.  */
+#define CURVE_LENGTH(c)  ((c)->length)
+
+/* A curve is ``cyclic'' if it didn't have any corners, after all, so
+   the last point is adjacent to the first.  */
+#define CURVE_CYCLIC(c)  ((c)->cyclic)
+
+/* If the curve is cyclic, the next and previous points should wrap
+   around; otherwise, if we get to the end, we return CURVE_LENGTH and
+   -1, respectively.  */
+#define CURVE_NEXT(c, n)						\
+  ((n) + 1 >= CURVE_LENGTH (c)						\
+  ? CURVE_CYCLIC (c) ? ((n) + 1) % CURVE_LENGTH (c) : CURVE_LENGTH (c)	\
+  : (n) + 1)
+#define CURVE_PREV(c, n)						\
+  ((signed int) (n) - 1 < 0							\
+  ? CURVE_CYCLIC (c) ? (signed int) CURVE_LENGTH (c) + (signed int) (n) - 1 : -1\
+  : (signed int) (n) - 1)
+
+/* The tangents at the endpoints are computed using the neighboring curves.  */
+#define CURVE_START_TANGENT(c) ((c)->start_tangent)
+#define CURVE_END_TANGENT(c) ((c)->end_tangent)
+#define PREVIOUS_CURVE(c) ((c)->previous)
+#define NEXT_CURVE(c) ((c)->next)
+
+
+/* Return an entirely empty curve.  */
+extern curve_type new_curve (void);
+
+/* Return a curve the same as C, except without any points.  */
+extern curve_type copy_most_of_curve (curve_type c);
+
+/* Free the memory C uses.  */
+extern void free_curve (curve_type c);
+
+/* Append the point P to the end of C's list.  */
+void
+append_pixel(curve_type    const c,
+             pm_pixelcoord const p);
+
+/* Like `append_pixel', for a point in real coordinates.  */
+extern void append_point (curve_type c, float_coord p);
+
+/* Write some or all, respectively, of the curve C in human-readable
+   form to the log file, if logging is enabled.  */
+extern void log_curve (curve_type c, bool print_t);
+extern void log_entire_curve (curve_type c);
+
+/* Display the curve C online, if displaying is enabled.  */
+extern void display_curve (curve_type);
+
+
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   An ordered list of contiguous curves of a particular color.
+-----------------------------------------------------------------------------*/
+    curve_type * data;
+    unsigned     length;
+    bool         clockwise;
+    pixel        color;
+    bool         open;
+        /* The curve list does not form a closed shape;  i.e. the last
+           curve doesn't end where the first one starts.
+        */
+} curve_list_type;
+
+/* Number of curves in the list.  */
+#define CURVE_LIST_LENGTH(c_l)  ((c_l).length)
+#define CURVE_LIST_EMPTY(c_l) ((c_l).length == 0)
+
+/* Access the individual curves.  */
+#define CURVE_LIST_ELT(c_l, n) ((c_l).data[n])
+#define LAST_CURVE_LIST_ELT(c_l) ((c_l).data[CURVE_LIST_LENGTH (c_l) - 1])
+
+/* Says whether the outline that this curve list represents moves
+   clockwise or counterclockwise.  */
+#define CURVE_LIST_CLOCKWISE(c_l) ((c_l).clockwise)
+
+
+extern curve_list_type new_curve_list (void);
+
+void
+free_curve_list(curve_list_type * const curve_list);
+
+extern void append_curve (curve_list_type *, curve_type);
+
+/* And a character is a list of outlines.  I named this
+   `curve_list_array_type' because `curve_list_list_type' seemed pretty
+   monstrous.  */
+typedef struct
+{
+  curve_list_type *data;
+  unsigned length;
+} curve_list_array_type;
+
+/* Turns out we can use the same definitions for lists of lists as for
+   just lists.  But we define the usual names, just in case.  */
+#define CURVE_LIST_ARRAY_LENGTH CURVE_LIST_LENGTH
+#define CURVE_LIST_ARRAY_ELT CURVE_LIST_ELT
+#define LAST_CURVE_LIST_ARRAY_ELT LAST_CURVE_LIST_ELT
+
+curve_list_array_type
+new_curve_list_array(void);
+
+void
+free_curve_list_array(const curve_list_array_type * const curve_list_array,
+                      at_progress_func                    notify_progress, 
+                      void *                        const client_data);
+
+void
+append_curve_list(curve_list_array_type * const curve_list_array,
+                  curve_list_type         const curve_list);
+
+#endif
+
diff --git a/converter/other/pamtosvg/epsilon-equal.c b/converter/other/pamtosvg/epsilon-equal.c
new file mode 100644
index 00000000..46d630c5
--- /dev/null
+++ b/converter/other/pamtosvg/epsilon-equal.c
@@ -0,0 +1,19 @@
+/* epsilon-equal.c: define a error resist compare. */
+
+#include <math.h>
+
+#include "epsilon-equal.h"
+
+/* Numerical errors sometimes make a floating point number just slightly
+   larger or smaller than its true value.  When it matters, we need to
+   compare with some tolerance, REAL_EPSILON, defined in kbase.h.  */
+
+bool
+epsilon_equal(float const v1,
+              float const v2) {
+
+    return
+        v1 == v2		       /* Usually they'll be exactly equal, anyway.  */
+        || fabs(v1 - v2) <= REAL_EPSILON;
+}
+
diff --git a/converter/other/pamtosvg/epsilon-equal.h b/converter/other/pamtosvg/epsilon-equal.h
new file mode 100644
index 00000000..fa0437cd
--- /dev/null
+++ b/converter/other/pamtosvg/epsilon-equal.h
@@ -0,0 +1,20 @@
+/* epsilon-equal.h: define an error resist compare. */
+
+#ifndef EPSILON_EQUAL_H
+#define EPSILON_EQUAL_H
+
+#include "pm_c_util.h"
+
+/* Says whether V1 and V2 are within REAL_EPSILON of each other.
+   Fixed-point arithmetic would be better, to guarantee machine
+   independence, but it's so much more painful to work with.  The value
+   here is smaller than can be represented in either a `fix_word' or a
+   `scaled_num', so more precision than this will be lost when we
+   output, anyway.  */
+bool epsilon_equal(float const v1,
+                   float const v2);
+
+#define REAL_EPSILON 0.00001
+
+#endif /* not EPSILON_EQUAL_H */
+
diff --git a/converter/other/pamtosvg/exception.c b/converter/other/pamtosvg/exception.c
new file mode 100644
index 00000000..43761936
--- /dev/null
+++ b/converter/other/pamtosvg/exception.c
@@ -0,0 +1,55 @@
+
+#include "exception.h"
+
+at_exception_type
+at_exception_new(at_msg_func       client_func,
+                 void *      const client_data) {
+
+    at_exception_type e;
+
+    e.msg_type = 0;
+    e.client_func = client_func;
+    e.client_data = client_data;
+    
+    return e;
+}
+
+
+
+bool
+at_exception_got_fatal(at_exception_type * const exception) {
+
+    return (exception->msg_type == AT_MSG_FATAL);
+}
+
+
+
+void
+at_exception_fatal(at_exception_type * const exception,
+                   const char *        const message) {
+
+    if (exception) {
+        exception->msg_type = AT_MSG_FATAL;
+        if (exception->client_func) {
+            exception->client_func(message, 
+                                   AT_MSG_FATAL,
+                                   exception->client_data);
+        }
+    }
+}
+
+
+
+void
+at_exception_warning(at_exception_type * const exception,
+                     const char *        const message) {
+
+    if (exception) {
+        exception->msg_type = AT_MSG_WARNING;
+        if (exception->client_func) {
+            exception->client_func(message, 
+                                   AT_MSG_WARNING,
+                                   exception->client_data);
+        }
+    }
+}
diff --git a/converter/other/pamtosvg/exception.h b/converter/other/pamtosvg/exception.h
new file mode 100644
index 00000000..113f65e6
--- /dev/null
+++ b/converter/other/pamtosvg/exception.h
@@ -0,0 +1,43 @@
+/* exception.h: facility to handle error in autotrace */
+
+#ifndef AT_EXCEPTION_H
+#define AT_EXCEPTION_H 
+
+#include "autotrace.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Protocol:
+   If a function raises a FATAL(including propagation), 
+   the function must release resources allocated by the 
+   function itself.
+*/
+typedef struct _at_exception_type at_exception_type;
+struct _at_exception_type {
+    at_msg_type msg_type;
+    at_msg_func client_func;
+    void **     client_data;
+};
+
+at_exception_type
+at_exception_new(at_msg_func       client_func,
+                 void *      const client_data);
+
+bool
+at_exception_got_fatal(at_exception_type * const exception);
+
+void
+at_exception_fatal(at_exception_type * const exception,
+                   const char *        const message);
+
+void
+at_exception_warning(at_exception_type * const exception,
+                     const char *        const message);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/converter/other/pamtosvg/fit.c b/converter/other/pamtosvg/fit.c
new file mode 100644
index 00000000..bcda033f
--- /dev/null
+++ b/converter/other/pamtosvg/fit.c
@@ -0,0 +1,1923 @@
+/* fit.c: turn a bitmap representation of a curve into a list of splines.
+    Some of the ideas, but not the code, comes from the Phoenix thesis.
+   See README for the reference.
+
+   The code was partially derived from limn.
+
+   Copyright (C) 1992 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <math.h>
+#include <limits.h>
+#include <float.h>
+#include <string.h>
+#include <assert.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+
+#include "autotrace.h"
+#include "fit.h"
+#include "message.h"
+#include "logreport.h"
+#include "spline.h"
+#include "vector.h"
+#include "curve.h"
+#include "pxl-outline.h"
+#include "epsilon-equal.h"
+
+#define CUBE(x) ((x) * (x) * (x))
+
+/* We need to manipulate lists of array indices.  */
+
+typedef struct index_list
+{
+  unsigned *data;
+  unsigned length;
+} index_list_type;
+
+/* The usual accessor macros.  */
+#define GET_INDEX(i_l, n)  ((i_l).data[n])
+#define INDEX_LIST_LENGTH(i_l)  ((i_l).length)
+#define GET_LAST_INDEX(i_l)  ((i_l).data[INDEX_LIST_LENGTH (i_l) - 1])
+
+static void append_index (index_list_type *, unsigned);
+static void free_index_list (index_list_type *);
+static index_list_type new_index_list (void);
+static void remove_adjacent_corners (index_list_type *, unsigned, bool,
+                     at_exception_type * exception);
+static void filter (curve_type, fitting_opts_type *);
+static void find_vectors
+  (unsigned, pixel_outline_type, vector_type *, vector_type *, unsigned);
+static float find_error (curve_type, spline_type, unsigned *,
+               at_exception_type * exception);
+static vector_type find_half_tangent (curve_type, bool start, unsigned *, unsigned);
+static void find_tangent (curve_type, bool, bool, unsigned);
+static void remove_knee_points (curve_type, bool);
+static void set_initial_parameter_values (curve_type);
+static float distance (float_coord, float_coord);
+
+
+static pm_pixelcoord
+real_to_int_coord(float_coord const real_coord) {
+/*----------------------------------------------------------------------------
+  Turn an real point into a integer one.
+-----------------------------------------------------------------------------*/
+
+    pm_pixelcoord int_coord;
+
+    int_coord.col = ROUND(real_coord.x);
+    int_coord.row = ROUND(real_coord.y);
+    
+    return int_coord;
+}
+
+
+static void
+appendCorner(index_list_type *  const cornerListP,
+             unsigned int       const pixelSeq,
+             pixel_outline_type const outline,
+             float              const angle,
+             char               const logType) {
+
+    pm_pixelcoord const coord = O_COORDINATE(outline, pixelSeq);
+
+    append_index(cornerListP, pixelSeq);
+    LOG4(" (%d,%d)%c%.3f", coord.col, coord.row, logType, angle);
+}
+
+
+
+static void
+lookAheadForBetterCorner(pixel_outline_type  const outline,
+                         unsigned int        const basePixelSeq,
+                         float               const baseCornerAngle,
+                         unsigned int        const cornerSurround,
+                         unsigned int        const cornerAlwaysThreshold,
+                         unsigned int *      const highestExaminedP,
+                         float *             const bestCornerAngleP,
+                         unsigned int *      const bestCornerIndexP,
+                         index_list_type *   const equallyGoodListP,
+                         index_list_type *   const cornerListP,
+                         at_exception_type * const exceptionP) {
+/*----------------------------------------------------------------------------
+   'basePixelSeq' is the sequence position within 'outline' of a pixel
+   that has a sufficiently small angle (to wit 'baseCornerAngle') to
+   be a corner.  We look ahead in 'outline' for an even better one.
+   We'll look up to 'cornerSurround' pixels ahead.
+
+   We return the pixel sequence of the best corner we find (which could
+   be the base) as *bestCornerIndexP.  Its angle is *bestCornerAngleP.
+
+   We return as *highestExaminedP the pixel sequence of the last pixel
+   we examined in our search (Caller can use this information to avoid
+   examining them again).
+
+   And we have this really dirty side effect: If we encounter any
+   corner whose angle is less than 'cornerAlwaysThreshold', we add
+   that to the list *cornerListP along the way.
+-----------------------------------------------------------------------------*/
+    float bestCornerAngle;
+    unsigned bestCornerIndex;
+    index_list_type equallyGoodList;
+    unsigned int q;
+    unsigned int i;
+
+    bestCornerIndex = basePixelSeq;     /* initial assumption */
+    bestCornerAngle = baseCornerAngle;    /* initial assumption */
+    
+    equallyGoodList = new_index_list();
+    
+    q = basePixelSeq;
+    i = basePixelSeq + 1;  /* Start with the next pixel */
+    
+    while (i < bestCornerIndex + cornerSurround &&
+           i < O_LENGTH(outline) &&
+           !at_exception_got_fatal(exceptionP)) {
+
+        vector_type inVector, outVector;
+        float cornerAngle;
+        
+        /* Check the angle.  */
+
+        q = i % O_LENGTH(outline);
+        find_vectors(q, outline, &inVector, &outVector, cornerSurround);
+        cornerAngle = Vangle(inVector, outVector, exceptionP);
+        if (!at_exception_got_fatal(exceptionP)) {
+            /* Perhaps the angle is sufficiently small that we want to
+               consider this a corner, even if it's not the best
+               (unless we've already wrapped around in the search, in
+               which case we have already added the corner, and we
+               don't want to add it again).
+            */
+            if (cornerAngle <= cornerAlwaysThreshold && q >= basePixelSeq)
+                appendCorner(cornerListP, q, outline, cornerAngle, '\\');
+
+            if (epsilon_equal(cornerAngle, bestCornerAngle))
+                append_index(&equallyGoodList, q);
+            else if (cornerAngle < bestCornerAngle) {
+                bestCornerAngle = cornerAngle;
+                /* We want to check `cornerSurround' pixels beyond the
+                   new best corner.
+                */
+                i = bestCornerIndex = q;
+                free_index_list(&equallyGoodList);
+                equallyGoodList = new_index_list();
+            }
+            ++i;
+        }
+    }
+    *bestCornerAngleP = bestCornerAngle;
+    *bestCornerIndexP = bestCornerIndex;
+    *equallyGoodListP = equallyGoodList;
+    *highestExaminedP = q;
+}
+
+
+
+static void
+establishCornerSearchLimits(pixel_outline_type  const outline,
+                            fitting_opts_type * const fittingOptsP,
+                            unsigned int *      const firstP,
+                            unsigned int *      const lastP) {
+/*----------------------------------------------------------------------------
+   Determine where in the outline 'outline' we should look for corners.
+-----------------------------------------------------------------------------*/
+    assert(O_LENGTH(outline) >= 1);
+    assert(O_LENGTH(outline) - 1 >= fittingOptsP->corner_surround);
+
+    *firstP = 0;
+    *lastP  = O_LENGTH(outline) - 1;
+    if (outline.open) {
+        *firstP += fittingOptsP->corner_surround;
+        *lastP  -= fittingOptsP->corner_surround;
+    }
+}
+
+
+
+static void
+removeAdjacent(index_list_type *   const cornerListP,
+               pixel_outline_type  const outline,
+               fitting_opts_type * const fittingOptsP,
+               at_exception_type * const exception) {
+               
+    /* We never want two corners next to each other, since the
+       only way to fit such a ``curve'' would be with a straight
+       line, which usually interrupts the continuity dreadfully.
+    */
+
+    if (INDEX_LIST_LENGTH(*cornerListP) > 0)
+        remove_adjacent_corners(
+            cornerListP,
+            O_LENGTH(outline) - (outline.open ? 2 : 1),
+            fittingOptsP->remove_adjacent_corners,
+            exception);
+}
+
+
+
+static index_list_type
+find_corners(pixel_outline_type  const outline,
+             fitting_opts_type * const fittingOptsP,
+             at_exception_type * const exceptionP) {
+
+    /* We consider a point to be a corner if (1) the angle defined by
+       the `corner_surround' points coming into it and going out from
+       it is less than `corner_threshold' degrees, and no point within
+       `corner_surround' points has a smaller angle; or (2) the angle
+       is less than `corner_always_threshold' degrees.
+    */
+    unsigned int p;
+    unsigned int firstPixelSeq, lastPixelSeq;
+    index_list_type cornerList;
+
+    cornerList = new_index_list();
+
+    if (O_LENGTH(outline) <= fittingOptsP->corner_surround * 2 + 1)
+        return cornerList;
+
+    establishCornerSearchLimits(outline, fittingOptsP,
+                                &firstPixelSeq, &lastPixelSeq);
+    
+    /* Consider each pixel on the outline in turn.  */
+    for (p = firstPixelSeq; p <= lastPixelSeq;) {
+        vector_type inVector, outVector;
+        float cornerAngle;
+
+        /* Check if the angle is small enough.  */
+        find_vectors(p, outline, &inVector, &outVector,
+                     fittingOptsP->corner_surround);
+        cornerAngle = Vangle(inVector, outVector, exceptionP);
+        if (at_exception_got_fatal(exceptionP))
+            goto cleanup;
+
+        if (fabs(cornerAngle) <= fittingOptsP->corner_threshold) {
+            /* We want to keep looking, instead of just appending the
+               first pixel we find with a small enough angle, since there
+               might be another corner within `corner_surround' pixels, with
+               a smaller angle.  If that is the case, we want that one.
+
+               If we come across a corner that is just as good as the
+               best one, we should make it a corner, too.  This
+               happens, for example, at the points on the `W' in some
+               typefaces, where the "points" are flat.
+            */
+            float bestCornerAngle;
+            unsigned bestCornerIndex;
+            index_list_type equallyGoodList;
+            unsigned int q;
+
+            if (cornerAngle <= fittingOptsP->corner_always_threshold)
+                /* The angle is sufficiently small that we want to
+                   consider this a corner, even if it's not the best.
+                */
+                appendCorner(&cornerList, p, outline, cornerAngle, '\\');
+
+            lookAheadForBetterCorner(outline, p, cornerAngle,
+                                     fittingOptsP->corner_surround,
+                                     fittingOptsP->corner_always_threshold,
+                                     &q,
+                                     &bestCornerAngle, &bestCornerIndex,
+                                     &equallyGoodList,
+                                     &cornerList,
+                                     exceptionP);
+
+            if (at_exception_got_fatal(exceptionP))
+                goto cleanup;
+
+            /* `q' is the index of the last point lookAhead checked.
+               He added the corner if `bestCornerAngle' is less than
+               `corner_always_threshold'.  If we've wrapped around, we
+               added the corner on the first pass.  Otherwise, we add
+               the corner now.
+            */
+            if (bestCornerAngle > fittingOptsP->corner_always_threshold
+                && bestCornerIndex >= p) {
+
+                unsigned int j;
+                
+                appendCorner(&cornerList, bestCornerIndex,
+                             outline, bestCornerAngle, '/');
+                
+                for (j = 0; j < INDEX_LIST_LENGTH (equallyGoodList); ++j)
+                    appendCorner(&cornerList, GET_INDEX(equallyGoodList, j),
+                                 outline, bestCornerAngle, '@');
+            }
+            free_index_list(&equallyGoodList);
+
+            /* If we wrapped around in our search, we're done;
+               otherwise, we move on to the pixel after the highest
+               one we just checked.
+            */
+            p = (q < p) ? O_LENGTH(outline) : q + 1;
+        } else
+            ++p;
+    }
+    removeAdjacent(&cornerList, outline, fittingOptsP, exceptionP);
+
+cleanup:  
+    return cornerList;
+}
+
+
+
+static void
+makeOutlineOneCurve(pixel_outline_type const outline,
+                    curve_list_type *  const curveListP) {
+/*----------------------------------------------------------------------------
+   Add to *curveListP a single curve that represents the outline 'outline'.
+-----------------------------------------------------------------------------*/
+    curve_type curve;
+    unsigned int pixelSeq;
+
+    curve = new_curve();
+    
+    for (pixelSeq = 0; pixelSeq < O_LENGTH(outline); ++pixelSeq)
+        append_pixel(curve, O_COORDINATE(outline, pixelSeq));
+    
+    if (curveListP->open)
+        CURVE_CYCLIC(curve) = false;
+    else
+        CURVE_CYCLIC(curve) = true;
+    
+    /* Make it a one-curve cycle */
+    NEXT_CURVE(curve)     = curve;
+    PREVIOUS_CURVE(curve) = curve;
+
+    append_curve(curveListP, curve);
+}
+
+
+
+static void
+addCurveStartingAtCorner(pixel_outline_type const outline,
+                         index_list_type    const cornerList,
+                         unsigned int       const cornerSeq,
+                         curve_list_type *  const curveListP) {
+
+    unsigned int const cornerPixelSeq = GET_INDEX(cornerList, cornerSeq);
+    
+    unsigned int lastPixelSeq;
+    curve_type curve;
+    unsigned int pixelSeq;
+    
+    if (cornerSeq + 1 >= cornerList.length)
+        /* No more corners, so we go through the end of the outline. */
+        lastPixelSeq = O_LENGTH(outline) - 1;
+    else
+        /* Go through the next corner */
+        lastPixelSeq = GET_INDEX(cornerList, cornerSeq + 1);
+    
+    curve = new_curve();
+
+    for (pixelSeq = cornerPixelSeq; pixelSeq <= lastPixelSeq; ++pixelSeq)
+        append_pixel(curve, O_COORDINATE(outline, pixelSeq));
+    
+    {
+        /* Add curve to end of chain */
+        if (!CURVE_LIST_EMPTY(*curveListP)) {
+            curve_type const previousCurve = LAST_CURVE_LIST_ELT(*curveListP);
+            NEXT_CURVE(previousCurve) = curve;
+            PREVIOUS_CURVE(curve)     = previousCurve;
+        }
+    }
+    append_curve(curveListP, curve);
+}
+
+
+
+static void
+divideOutlineWithCorners(pixel_outline_type const outline,
+                         index_list_type    const cornerList,
+                         curve_list_type *  const curveListP) {
+/*----------------------------------------------------------------------------
+   Divide the outline 'outline' into curves at the corner points
+   'cornerList' and add each curve to *curveListP.
+
+   Each curve contains the corners at each end.
+
+   The last curve is special.  It consists of the pixels (inclusive)
+   between the last corner and the end of the outline, and the
+   beginning of the outline and the first corner.
+
+   We link the curves in a chain.  If the outline (and therefore the
+   curve list) is closed, the chain is a cycle of all the curves.  If
+   it is open, the chain is a linear chain of all the curves except
+   the last one (the one that goes from the last corner to the first
+   corner).
+
+   Assume there is at least one corner.
+-----------------------------------------------------------------------------*/
+    unsigned int const firstCurveSeq = CURVE_LIST_LENGTH(*curveListP);
+        /* Index in curve list of the first curve we add */
+    unsigned int cornerSeq;
+
+    assert(cornerList.length > 0);
+
+    if (outline.open) {
+        /* Start with a curve that contains the point up to the first
+           corner
+        */
+        curve_type curve;
+        unsigned int pixelSeq;
+        
+        curve = new_curve();
+
+        for (pixelSeq = 0; pixelSeq <= GET_INDEX(cornerList, 0); ++pixelSeq)
+            append_pixel(curve, O_COORDINATE(outline, pixelSeq));
+
+        append_curve(curveListP, curve);
+    } else
+        /* We'll pick up the pixels before the first corner at the end */
+
+    /* Add to the list a curve that starts at each corner and goes
+       through the following corner, or the end of the outline if
+       there is no following corner.  Do it in order of the corners.
+    */
+    for (cornerSeq = 0; cornerSeq < cornerList.length; ++cornerSeq)
+        addCurveStartingAtCorner(outline, cornerList, cornerSeq, curveListP);
+
+    if (!outline.open) {
+        /* Come around to the start of the curve list -- add the pixels
+           before the first corner to the last curve, and chain the last
+           curve to the first one.
+        */
+        curve_type const firstCurve =
+            CURVE_LIST_ELT(*curveListP, firstCurveSeq);
+        curve_type const lastCurve  =
+            LAST_CURVE_LIST_ELT(*curveListP);
+
+        unsigned int pixelSeq;
+
+        for (pixelSeq = 0; pixelSeq <= GET_INDEX(cornerList, 0); ++pixelSeq)
+            append_pixel(lastCurve, O_COORDINATE(outline, pixelSeq));
+
+        NEXT_CURVE(lastCurve)      = firstCurve;
+        PREVIOUS_CURVE(firstCurve) = lastCurve;
+    }
+}
+
+
+
+static curve_list_array_type
+split_at_corners(pixel_outline_list_type const pixel_list,
+                 fitting_opts_type *     const fitting_opts,
+                 at_exception_type *     const exception) {
+/*----------------------------------------------------------------------------
+   Find the corners in PIXEL_LIST, the list of points.  (Presumably we
+   can't fit a single spline around a corner.)  The general strategy
+   is to look through all the points, remembering which we want to
+   consider corners.  Then go through that list, producing the
+   curve_list.  This is dictated by the fact that PIXEL_LIST does not
+   necessarily start on a corner---it just starts at the character's
+   first outline pixel, going left-to-right, top-to-bottom.  But we
+   want all our splines to start and end on real corners.
+
+   For example, consider the top of a capital `C' (this is in cmss20):
+                     x
+                     ***********
+                  ******************
+
+   PIXEL_LIST will start at the pixel below the `x'.  If we considered
+   this pixel a corner, we would wind up matching a very small segment
+   from there to the end of the line, probably as a straight line, which
+   is certainly not what we want.
+
+   PIXEL_LIST has one element for each closed outline on the character.
+   To preserve this information, we return an array of curve_lists, one
+   element (which in turn consists of several curves, one between each
+   pair of corners) for each element in PIXEL_LIST.
+-----------------------------------------------------------------------------*/
+    unsigned outlineSeq;
+    curve_list_array_type curve_array;
+
+    curve_array = new_curve_list_array();
+
+    LOG("\nFinding corners:\n");
+
+    for (outlineSeq = 0;
+         outlineSeq < O_LIST_LENGTH(pixel_list);
+         ++outlineSeq) {
+
+        pixel_outline_type const outline =
+            O_LIST_OUTLINE(pixel_list, outlineSeq);
+
+        index_list_type corner_list;
+        curve_list_type curve_list;
+
+        curve_list = new_curve_list();
+
+        CURVE_LIST_CLOCKWISE(curve_list) = O_CLOCKWISE(outline);
+        curve_list.color = outline.color;
+        curve_list.open  = outline.open;
+
+        LOG1("#%u:", outlineSeq);
+        
+        /* If the outline does not have enough points, we can't do
+           anything.  The endpoints of the outlines are automatically
+           corners.  We need at least `corner_surround' more pixels on
+           either side of a point before it is conceivable that we might
+           want another corner.
+        */
+        if (O_LENGTH(outline) > fitting_opts->corner_surround * 2 + 2)
+            corner_list = find_corners(outline, fitting_opts, exception);
+
+        else {
+            int const surround = (O_LENGTH(outline) - 3) / 2;
+            if (surround >= 2) {
+                unsigned int const save_corner_surround =
+                    fitting_opts->corner_surround;
+                fitting_opts->corner_surround = surround;
+                corner_list = find_corners(outline, fitting_opts, exception);
+                fitting_opts->corner_surround = save_corner_surround;
+            } else {
+                corner_list.length = 0;
+                corner_list.data = NULL;
+            }
+        }
+
+        if (corner_list.length == 0)
+            /* No corners.  Use all of the pixel outline as the one curve. */
+            makeOutlineOneCurve(outline, &curve_list);
+        else
+            divideOutlineWithCorners(outline, corner_list, &curve_list);
+        
+        LOG1(" [%u].\n", corner_list.length);
+        free_index_list(&corner_list);
+
+        /* And now add the just-completed curve list to the array.  */
+        append_curve_list(&curve_array, curve_list);
+    }
+
+    return curve_array;
+}
+
+
+
+static void
+removeKnees(curve_list_type const curveList) {
+/*----------------------------------------------------------------------------
+  Remove the extraneous ``knee'' points before filtering.  Since the
+  corners have already been found, we don't need to worry about
+  removing a point that should be a corner.
+-----------------------------------------------------------------------------*/
+    unsigned int curveSeq;
+    
+    LOG("\nRemoving knees:\n");
+    for (curveSeq = 0; curveSeq < curveList.length; ++curveSeq) {
+        LOG1("#%u:", curveSeq);
+        remove_knee_points(CURVE_LIST_ELT(curveList, curveSeq),
+                           CURVE_LIST_CLOCKWISE(curveList));
+    }
+}
+    
+
+
+static void
+computePointWeights(curve_list_type     const curveList,
+                    fitting_opts_type * const fittingOptsP,
+                    distance_map_type * const distP) {
+
+    unsigned int const height = distP->height;
+
+    unsigned int curveSeq;
+    
+    for (curveSeq = 0; curveSeq < curveList.length; ++curveSeq) {
+        unsigned pointSeq;
+        curve_type const curve = CURVE_LIST_ELT(curveList, curveSeq);
+        for (pointSeq = 0; pointSeq < CURVE_LENGTH(curve); ++pointSeq) {
+            float_coord * const coordP = &CURVE_POINT(curve, pointSeq);
+            unsigned int x = coordP->x;
+            unsigned int y = height - (unsigned int)coordP->y - 1;
+            
+            float width, w;
+
+            /* Each (x, y) is a point on the skeleton of the curve, which
+               might be offset from the true centerline, where the width
+               is maximal.  Therefore, use as the local line width the
+               maximum distance over the neighborhood of (x, y). 
+            */
+            width = distP->d[y][x];  /* initial value */
+            if (y - 1 >= 0) {
+                if ((w = distP->d[y-1][x]) > width)
+                    width = w;
+                if (x - 1 >= 0) {
+                    if ((w = distP->d[y][x-1]) > width)
+                        width = w;
+                    if ((w = distP->d[y-1][x-1]) > width)
+                        width = w;
+                }
+                if (x + 1 < distP->width) {
+                    if ((w = distP->d[y][x+1]) > width)
+                        width = w;
+                    if ((w = distP->d[y-1][x+1]) > width)
+                        width = w;
+                }
+            }
+            if (y + 1 < height) {
+                if ((w = distP->d[y+1][x]) > width)
+                    width = w;
+                if (x - 1 >= 0 && (w = distP->d[y+1][x-1]) > width)
+                    width = w;
+                if (x + 1 < distP->width && (w = distP->d[y+1][x+1]) > width)
+                    width = w;
+            }
+            coordP->z = width * (fittingOptsP->width_weight_factor);
+        }
+    }
+}
+
+
+
+static void
+filterCurves(curve_list_type     const curveList,
+             fitting_opts_type * const fittingOptsP) {
+             
+    unsigned int curveSeq;
+
+    LOG("\nFiltering curves:\n");
+
+    for (curveSeq = 0; curveSeq < curveList.length; ++curveSeq) {
+        LOG1("#%u: ", curveSeq);
+        filter(CURVE_LIST_ELT(curveList, curveSeq), fittingOptsP);
+    }
+}
+
+
+
+static void
+logSplinesForCurve(unsigned int     const curveSeq,
+                   spline_list_type const curveSplines) {
+
+    unsigned int splineSeq;
+
+    LOG1("Fitted splines for curve #%u:\n", curveSeq);
+    for (splineSeq = 0;
+         splineSeq < SPLINE_LIST_LENGTH(curveSplines);
+         ++splineSeq) {
+        LOG1("  %u: ", splineSeq);
+        if (log_file)
+            print_spline(log_file, SPLINE_LIST_ELT(curveSplines, splineSeq));
+    }
+}     
+       
+
+
+static void
+change_bad_lines(spline_list_type *        const spline_list,
+                 const fitting_opts_type * const fitting_opts) {
+
+/* Unfortunately, we cannot tell in isolation whether a given spline
+   should be changed to a line or not.  That can only be known after the
+   entire curve has been fit to a list of splines.  (The curve is the
+   pixel outline between two corners.)  After subdividing the curve, a
+   line may very well fit a portion of the curve just as well as the
+   spline---but unless a spline is truly close to being a line, it
+   should not be combined with other lines.  */
+
+  unsigned this_spline;
+  bool found_cubic = false;
+  unsigned length = SPLINE_LIST_LENGTH (*spline_list);
+
+  LOG1 ("\nChecking for bad lines (length %u):\n", length);
+
+  /* First see if there are any splines in the fitted shape.  */
+  for (this_spline = 0; this_spline < length; this_spline++)
+    {
+      if (SPLINE_DEGREE (SPLINE_LIST_ELT (*spline_list, this_spline)) ==
+       CUBICTYPE)
+        {
+          found_cubic = true;
+          break;
+        }
+    }
+
+  /* If so, change lines back to splines (we haven't done anything to
+     their control points, so we only have to change the degree) unless
+     the spline is close enough to being a line.  */
+  if (found_cubic)
+    for (this_spline = 0; this_spline < length; this_spline++)
+      {
+        spline_type s = SPLINE_LIST_ELT (*spline_list, this_spline);
+
+        if (SPLINE_DEGREE (s) == LINEARTYPE)
+          {
+            LOG1 ("  #%u: ", this_spline);
+            if (SPLINE_LINEARITY (s) > fitting_opts->line_reversion_threshold)
+              {
+                LOG ("reverted, ");
+                SPLINE_DEGREE (SPLINE_LIST_ELT (*spline_list, this_spline))
+                  = CUBICTYPE;
+              }
+            LOG1 ("linearity %.3f.\n", SPLINE_LINEARITY (s));
+          }
+      }
+    else
+      LOG ("  No lines.\n");
+}
+
+
+
+static bool
+spline_linear_enough(spline_type *             const spline,
+                     curve_type                const curve,
+                     const fitting_opts_type * const fitting_opts) {
+
+/* Supposing that we have accepted the error, another question arises:
+   would we be better off just using a straight line?  */
+
+  float A, B, C;
+  unsigned this_point;
+  float dist = 0.0, start_end_dist, threshold;
+
+  LOG ("Checking linearity:\n");
+
+  A = END_POINT(*spline).x - START_POINT(*spline).x;
+  B = END_POINT(*spline).y - START_POINT(*spline).y;
+  C = END_POINT(*spline).z - START_POINT(*spline).z;
+
+  start_end_dist = (float) (SQR(A) + SQR(B) + SQR(C));
+  LOG1 ("start_end_distance is %.3f.\n", sqrt(start_end_dist));
+
+  LOG3 ("  Line endpoints are (%.3f, %.3f, %.3f) and ", START_POINT(*spline).x, START_POINT(*spline).y, START_POINT(*spline).z);
+  LOG3 ("(%.3f, %.3f, %.3f)\n", END_POINT(*spline).x, END_POINT(*spline).y, END_POINT(*spline).z);
+
+  /* LOG3 ("  Line is %.3fx + %.3fy + %.3f = 0.\n", A, B, C); */
+
+  for (this_point = 0; this_point < CURVE_LENGTH (curve); this_point++)
+    {
+      float a, b, c, w;
+      float t = CURVE_T (curve, this_point);
+      float_coord spline_point = evaluate_spline (*spline, t);
+
+      a = spline_point.x - START_POINT(*spline).x;
+      b = spline_point.y - START_POINT(*spline).y;
+      c = spline_point.z - START_POINT(*spline).z;
+      w = (A*a + B*b + C*c) / start_end_dist;
+
+      dist += (float)sqrt(SQR(a-A*w) + SQR(b-B*w) + SQR(c-C*w));
+    }
+  LOG1 ("  Total distance is %.3f, ", dist);
+
+  dist /= (CURVE_LENGTH (curve) - 1);
+  LOG1 ("which is %.3f normalized.\n", dist);
+
+  /* We want reversion of short curves to splines to be more likely than
+     reversion of long curves, hence the second division by the curve
+     length, for use in `change_bad_lines'.  */
+  SPLINE_LINEARITY (*spline) = dist;
+  LOG1 ("  Final linearity: %.3f.\n", SPLINE_LINEARITY (*spline));
+  if (start_end_dist * (float) 0.5 > fitting_opts->line_threshold)
+    threshold = fitting_opts->line_threshold;
+  else
+    threshold = start_end_dist * (float) 0.5;
+  LOG1 ("threshold is %.3f .\n", threshold);
+  if (dist < threshold)
+    return true;
+  else
+    return false;
+}
+
+
+
+/* Forward declaration for recursion */
+
+static spline_list_type *
+fitCurve(curve_type                const curve,
+         const fitting_opts_type * const fitting_opts,
+         at_exception_type *       const exception);
+
+
+
+static spline_list_type *
+fit_with_line(curve_type const curve) {
+/*----------------------------------------------------------------------------
+  This routine returns the curve fitted to a straight line in a very
+  simple way: make the first and last points on the curve be the
+  endpoints of the line.  This simplicity is justified because we are
+  called only on very short curves.
+-----------------------------------------------------------------------------*/
+    spline_type line;
+
+    LOG("Fitting with straight line:\n");
+
+    SPLINE_DEGREE(line) = LINEARTYPE;
+    START_POINT(line) = CONTROL1(line) = CURVE_POINT(curve, 0);
+    END_POINT(line) = CONTROL2(line) = LAST_CURVE_POINT(curve);
+
+    /* Make sure that this line is never changed to a cubic.  */
+    SPLINE_LINEARITY(line) = 0;
+
+    if (log_file) {
+        LOG("  ");
+        print_spline(log_file, line);
+    }
+
+    return new_spline_list_with_spline(line);
+}
+
+
+
+#define B0(t) CUBE ((float) 1.0 - (t))
+#define B1(t) ((float) 3.0 * (t) * SQR ((float) 1.0 - (t)))
+#define B2(t) ((float) 3.0 * SQR (t) * ((float) 1.0 - (t)))
+#define B3(t) CUBE (t)
+
+static spline_type
+fit_one_spline(curve_type          const curve, 
+               at_exception_type * const exception) {
+/*----------------------------------------------------------------------------
+   Our job here is to find alpha1 (and alpha2), where t1_hat (t2_hat) is
+   the tangent to CURVE at the starting (ending) point, such that:
+
+   control1 = alpha1 * t1_hat + starting point
+   control2 = alpha2 * t2_hat + ending_point
+
+   and the resulting spline (starting_point .. control1 and control2 ..
+   ending_point) minimizes the least-square error from CURVE.
+
+   See pp.57--59 of the Phoenix thesis.
+
+   The B?(t) here corresponds to B_i^3(U_i) there.
+   The Bernshte\u in polynomials of degree n are defined by
+   B_i^n(t) = { n \choose i } t^i (1-t)^{n-i}, i = 0..n
+-----------------------------------------------------------------------------*/
+    /* Since our arrays are zero-based, the `C0' and `C1' here correspond
+       to `C1' and `C2' in the paper. 
+    */
+    float X_C1_det, C0_X_det, C0_C1_det;
+    float alpha1, alpha2;
+    spline_type spline;
+    vector_type start_vector, end_vector;
+    unsigned i;
+    vector_type * A;
+    vector_type t1_hat;
+    vector_type t2_hat;
+    float C[2][2] = { { 0.0, 0.0 }, { 0.0, 0.0 } };
+    float X[2] = { 0.0, 0.0 };
+
+    t1_hat = *CURVE_START_TANGENT(curve);  /* initial value */
+    t2_hat = *CURVE_END_TANGENT(curve);    /* initial value */
+
+    MALLOCARRAY_NOFAIL(A, CURVE_LENGTH(curve) * 2);
+
+    START_POINT(spline) = CURVE_POINT(curve, 0);
+    END_POINT(spline)   = LAST_CURVE_POINT(curve);
+    start_vector = make_vector(START_POINT(spline));
+    end_vector   = make_vector(END_POINT(spline));
+
+    for (i = 0; i < CURVE_LENGTH(curve); ++i) {
+        A[(i << 1) + 0] = Vmult_scalar(t1_hat, B1(CURVE_T(curve, i)));
+        A[(i << 1) + 1] = Vmult_scalar(t2_hat, B2(CURVE_T(curve, i)));
+    }
+
+    for (i = 0; i < CURVE_LENGTH(curve); ++i) {
+        vector_type temp, temp0, temp1, temp2, temp3;
+        vector_type * Ai = A + (i << 1);
+
+        C[0][0] += Vdot(Ai[0], Ai[0]);
+        C[0][1] += Vdot(Ai[0], Ai[1]);
+        /* C[1][0] = C[0][1] (this is assigned outside the loop)  */
+        C[1][1] += Vdot(Ai[1], Ai[1]);
+
+        /* Now the right-hand side of the equation in the paper.  */
+        temp0 = Vmult_scalar(start_vector, B0(CURVE_T(curve, i)));
+        temp1 = Vmult_scalar(start_vector, B1(CURVE_T(curve, i)));
+        temp2 = Vmult_scalar(end_vector, B2(CURVE_T(curve, i)));
+        temp3 = Vmult_scalar(end_vector, B3(CURVE_T(curve, i)));
+
+        temp = make_vector(
+            Vsubtract_point(CURVE_POINT(curve, i),
+                            Vadd(temp0, Vadd(temp1, Vadd(temp2, temp3)))));
+
+        X[0] += Vdot(temp, Ai[0]);
+        X[1] += Vdot(temp, Ai[1]);
+    }
+    free(A);
+
+    C[1][0] = C[0][1];
+    
+    X_C1_det = X[0] * C[1][1] - X[1] * C[0][1];
+    C0_X_det = C[0][0] * X[1] - C[0][1] * X[0];
+    C0_C1_det = C[0][0] * C[1][1] - C[1][0] * C[0][1];
+    if (C0_C1_det == 0.0) {
+        LOG ("zero determinant of C0*C1");
+        at_exception_fatal(exception, "zero determinant of C0*C1");
+        goto cleanup;
+    }
+
+    alpha1 = X_C1_det / C0_C1_det;
+    alpha2 = C0_X_det / C0_C1_det;
+
+    CONTROL1(spline) = Vadd_point(START_POINT(spline),
+                                  Vmult_scalar(t1_hat, alpha1));
+    CONTROL2(spline) = Vadd_point(END_POINT(spline),
+                                  Vmult_scalar(t2_hat, alpha2));
+    SPLINE_DEGREE(spline) = CUBICTYPE;
+
+cleanup:
+    return spline;
+}
+
+
+
+static void
+logSplineFit(spline_type const spline) {
+
+    if (SPLINE_DEGREE(spline) == LINEARTYPE)
+        LOG("  fitted to line:\n");
+    else
+        LOG("  fitted to spline:\n");
+    
+    if (log_file) {
+        LOG ("    ");
+        print_spline (log_file, spline);
+    }
+}
+
+
+
+static spline_list_type *
+fit_with_least_squares(curve_type                const curve,
+                       const fitting_opts_type * const fitting_opts,
+                       at_exception_type *       const exception) {
+/*----------------------------------------------------------------------------
+  The least squares method is well described in Schneider's thesis.
+  Briefly, we try to fit the entire curve with one spline.  If that
+  fails, we subdivide the curve. 
+-----------------------------------------------------------------------------*/
+    float error;
+    float best_error;
+    spline_type spline;
+    spline_type best_spline;
+    spline_list_type * spline_list;
+    unsigned int worst_point;
+    float previous_error;
+    
+    best_error = FLT_MAX;  /* initial value */
+    previous_error = FLT_MAX;  /* initial value */
+    spline_list = NULL;  /* initial value */
+    worst_point = 0;  /* initial value */
+
+    LOG ("\nFitting with least squares:\n");
+    
+    /* Phoenix reduces the number of points with a ``linear spline
+       technique''.  But for fitting letterforms, that is
+       inappropriate.  We want all the points we can get.
+    */
+    
+    /* It makes no difference whether we first set the `t' values or
+       find the tangents.  This order makes the documentation a little
+       more coherent.
+    */
+
+    LOG("Finding tangents:\n");
+    find_tangent(curve, /* to_start */ true,  /* cross_curve */ false,
+                 fitting_opts->tangent_surround);
+    find_tangent(curve, /* to_start */ false, /* cross_curve */ false,
+                 fitting_opts->tangent_surround);
+
+    set_initial_parameter_values(curve);
+
+    /* Now we loop, subdividing, until CURVE has been fit.  */
+    while (true) {
+        float error;
+
+        spline = fit_one_spline(curve, exception);
+        best_spline = spline;
+        if (at_exception_got_fatal(exception))
+            goto cleanup;
+
+        logSplineFit(spline);
+        
+        if (SPLINE_DEGREE(spline) == LINEARTYPE)
+            break;
+
+        error = find_error(curve, spline, &worst_point, exception);
+        if (error <= previous_error) {
+            best_error  = error;
+            best_spline = spline;
+        }
+        break;
+    }
+
+    if (SPLINE_DEGREE(spline) == LINEARTYPE) {
+        spline_list = new_spline_list_with_spline(spline);
+        LOG1("Accepted error of %.3f.\n", error);
+        return spline_list;
+    }
+
+    /* Go back to the best fit.  */
+    spline = best_spline;
+    error = best_error;
+
+    if (error < fitting_opts->error_threshold && !CURVE_CYCLIC(curve)) {
+        /* The points were fitted with a spline.  We end up here
+           whenever a fit is accepted.  We have one more job: see if
+           the ``curve'' that was fit should really be a straight
+           line.
+        */
+        if (spline_linear_enough(&spline, curve, fitting_opts)) {
+            SPLINE_DEGREE(spline) = LINEARTYPE;
+            LOG("Changed to line.\n");
+        }
+        spline_list = new_spline_list_with_spline(spline);
+        LOG1("Accepted error of %.3f.\n", error);
+    } else {
+        /* We couldn't fit the curve acceptably, so subdivide.  */
+        unsigned subdivision_index;
+        spline_list_type * left_spline_list;
+        spline_list_type * right_spline_list;
+        curve_type left_curve, right_curve;
+
+        left_curve  = new_curve();
+        right_curve = new_curve();
+
+        /* Insert 'left_curve', then 'right_curve' after 'curve' in the list */
+        NEXT_CURVE(right_curve) = NEXT_CURVE(curve);
+        PREVIOUS_CURVE(right_curve) = left_curve;
+        NEXT_CURVE(left_curve) = right_curve;
+        PREVIOUS_CURVE(left_curve) = curve;
+        NEXT_CURVE(curve) = left_curve;
+
+        LOG1("\nSubdividing (error %.3f):\n", error);
+        LOG3("  Original point: (%.3f,%.3f), #%u.\n",
+             CURVE_POINT (curve, worst_point).x,
+             CURVE_POINT (curve, worst_point).y, worst_point);
+        subdivision_index = worst_point;
+        LOG3 ("  Final point: (%.3f,%.3f), #%u.\n",
+              CURVE_POINT (curve, subdivision_index).x,
+              CURVE_POINT (curve, subdivision_index).y, subdivision_index);
+
+        /* The last point of the left-hand curve will also be the first
+           point of the right-hand curve.  */
+        CURVE_LENGTH(left_curve)  = subdivision_index + 1;
+        CURVE_LENGTH(right_curve) = CURVE_LENGTH(curve) - subdivision_index;
+        left_curve->point_list = curve->point_list;
+        right_curve->point_list = curve->point_list + subdivision_index;
+
+        /* We want to use the tangents of the curve which we are
+           subdividing for the start tangent for left_curve and the
+           end tangent for right_curve.
+        */
+        CURVE_START_TANGENT(left_curve) = CURVE_START_TANGENT(curve);
+        CURVE_END_TANGENT(right_curve)  = CURVE_END_TANGENT(curve);
+
+        /* We have to set up the two curves before finding the tangent at
+           the subdivision point.  The tangent at that point must be the
+           same for both curves, or noticeable bumps will occur in the
+           character.  But we want to use information on both sides of the
+           point to compute the tangent, hence cross_curve = true.
+        */
+        find_tangent(left_curve, /* to_start_point: */ false,
+                     /* cross_curve: */ true, fitting_opts->tangent_surround);
+        CURVE_START_TANGENT(right_curve) = CURVE_END_TANGENT(left_curve);
+
+        /* Now that we've set up the curves, we can fit them.  */
+        left_spline_list = fitCurve(left_curve, fitting_opts, exception);
+        if (at_exception_got_fatal(exception))
+            /* TODO: Memory allocated for left_curve and right_curve
+               will leak.*/
+            goto cleanup;
+
+        right_spline_list = fitCurve(right_curve, fitting_opts, exception);
+        /* TODO: Memory allocated for left_curve and right_curve
+           will leak.*/
+        if (at_exception_got_fatal(exception))
+            goto cleanup;
+        
+        /* Neither of the subdivided curves could be fit, so fail.  */
+        if (left_spline_list == NULL && right_spline_list == NULL)
+            return NULL;
+
+        /* Put the two together (or whichever of them exist).  */
+        spline_list = new_spline_list();
+
+        if (left_spline_list == NULL) {
+            LOG1("Could not fit spline to left curve (%lx).\n",
+                 (unsigned long) left_curve);
+            at_exception_warning(exception, "Could not fit left spline list");
+        } else {
+            concat_spline_lists(spline_list, *left_spline_list);
+            free_spline_list(*left_spline_list);
+            free(left_spline_list);
+        }
+        
+        if (right_spline_list == NULL) {
+            LOG1("Could not fit spline to right curve (%lx).\n",
+                 (unsigned long) right_curve);
+            at_exception_warning(exception, "Could not fit right spline list");
+        } else {
+            concat_spline_lists(spline_list, *right_spline_list);
+            free_spline_list(*right_spline_list);
+            free(right_spline_list);
+        }
+        if (CURVE_END_TANGENT(left_curve))
+            free(CURVE_END_TANGENT(left_curve));
+        free(left_curve);
+        free(right_curve);
+    }
+cleanup:
+
+    return spline_list;
+}
+
+
+
+static spline_list_type *
+fitCurve(curve_type                const curve,
+         const fitting_opts_type * const fittingOptsP,
+         at_exception_type *       const exception) {
+/*----------------------------------------------------------------------------
+  Transform a set of locations to a list of splines (the fewer the
+  better).  We are guaranteed that CURVE does not contain any corners.
+  We return NULL if we cannot fit the points at all.
+-----------------------------------------------------------------------------*/
+    spline_list_type * fittedSplinesP;
+
+    if (CURVE_LENGTH(curve) < 2) {
+        LOG("Tried to fit curve with less than two points");
+        at_exception_warning(exception, 
+                             "Tried to fit curve with less than two points");
+        fittedSplinesP = NULL;
+    } else if (CURVE_LENGTH(curve) < 4)
+        fittedSplinesP = fit_with_line(curve);
+    else
+        fittedSplinesP =
+            fit_with_least_squares(curve, fittingOptsP, exception);
+
+    return fittedSplinesP;
+}
+
+
+
+static void
+fitCurves(curve_list_type           const curveList,
+          pixel                     const color,
+          const fitting_opts_type * const fittingOptsP,
+          spline_list_type *        const splinesP,
+          at_exception_type *       const exceptionP) {
+          
+    spline_list_type curveListSplines;
+    unsigned int curveSeq;
+
+    curveListSplines = empty_spline_list();
+    
+    curveListSplines.open      = curveList.open;
+    curveListSplines.clockwise = curveList.clockwise;
+    curveListSplines.color     = color;
+
+    for (curveSeq = 0;
+         curveSeq < curveList.length && !at_exception_got_fatal(exceptionP);
+         ++curveSeq) {
+
+        curve_type const currentCurve = CURVE_LIST_ELT(curveList, curveSeq);
+
+        spline_list_type * curveSplinesP;
+
+        LOG1("\nFitting curve #%u:\n", curveSeq);
+
+        curveSplinesP = fitCurve(currentCurve, fittingOptsP, exceptionP);
+        if (!at_exception_got_fatal(exceptionP)) {
+            if (curveSplinesP == NULL) {
+                LOG1("Could not fit curve #%u", curveSeq);
+                at_exception_warning(exceptionP, "Could not fit curve");
+            } else {
+                logSplinesForCurve(curveSeq, *curveSplinesP);
+                
+                /* After fitting, we may need to change some would-be lines
+                   back to curves, because they are in a list with other
+                   curves.
+                */
+                change_bad_lines(curveSplinesP, fittingOptsP);
+                
+                concat_spline_lists(&curveListSplines, *curveSplinesP);
+                free_spline_list(*curveSplinesP);
+                free(curveSplinesP);
+            }
+        }
+    }
+    if (at_exception_got_fatal(exceptionP))
+        free_spline_list(curveListSplines);
+    else
+        *splinesP = curveListSplines;
+}
+    
+
+
+static void
+logFittedSplines(spline_list_type const curve_list_splines) {
+
+    unsigned int splineSeq;
+
+    LOG("\nFitted splines are:\n");
+    for (splineSeq = 0;
+         splineSeq < SPLINE_LIST_LENGTH(curve_list_splines);
+         ++splineSeq) {
+        LOG1("  %u: ", splineSeq);
+        print_spline(log_file,
+                     SPLINE_LIST_ELT(curve_list_splines, splineSeq));
+    }
+}
+
+
+
+static void
+fitCurveList(curve_list_type     const curveList,
+             fitting_opts_type * const fittingOptsP,
+             distance_map_type * const dist,
+             pixel               const color,
+             spline_list_type *  const splineListP,
+             at_exception_type * const exception) {
+/*----------------------------------------------------------------------------
+  Fit the list of curves CURVE_LIST to a list of splines, and return
+  it.  CURVE_LIST represents a single closed paths, e.g., either the
+  inside or outside outline of an `o'.
+-----------------------------------------------------------------------------*/
+    curve_type curve;
+    spline_list_type curveListSplines;
+
+    removeKnees(curveList);
+
+    if (dist != NULL)
+        computePointWeights(curveList, fittingOptsP, dist);
+    
+    /* We filter all the curves in 'curveList' at once; otherwise, we
+       would look at an unfiltered curve when computing tangents.
+    */
+    filterCurves(curveList, fittingOptsP);
+
+    /* Make the first point in the first curve also be the last point in
+       the last curve, so the fit to the whole curve list will begin and
+       end at the same point.  This may cause slight errors in computing
+       the tangents and t values, but it's worth it for the continuity.
+       Of course we don't want to do this if the two points are already
+       the same, as they are if the curve is cyclic.  (We don't append it
+       earlier, in `split_at_corners', because that confuses the
+       filtering.)  Finally, we can't append the point if the curve is
+       exactly three points long, because we aren't adding any more data,
+       and three points isn't enough to determine a spline.  Therefore,
+       the fitting will fail.
+    */
+    curve = CURVE_LIST_ELT(curveList, 0);
+    if (CURVE_CYCLIC(curve))
+        append_point(curve, CURVE_POINT(curve, 0));
+
+    /* Finally, fit each curve in the list to a list of splines.  */
+
+    fitCurves(curveList, color, fittingOptsP, &curveListSplines, exception);
+    if (!at_exception_got_fatal(exception)) {
+        if (log_file)
+            logFittedSplines(curveListSplines);
+        *splineListP = curveListSplines;
+    }
+}
+
+
+
+static void
+fitCurvesToSplines(curve_list_array_type    const curveArray,
+                   fitting_opts_type *      const fittingOptsP,
+                   distance_map_type *      const dist,
+                   unsigned short           const width,
+                   unsigned short           const height,
+                   at_exception_type *      const exception,
+                   at_progress_func               notifyProgress, 
+                   void *                   const progressData,
+                   at_testcancel_func             testCancel,
+                   void *                   const testcancelData,
+                   spline_list_array_type * const splineListArrayP) {
+    
+    unsigned splineListSeq;
+    bool cancelled;
+    spline_list_array_type splineListArray;
+
+    splineListArray = new_spline_list_array();
+    splineListArray.centerline          = fittingOptsP->centerline;
+    splineListArray.preserve_width      = fittingOptsP->preserve_width;
+    splineListArray.width_weight_factor = fittingOptsP->width_weight_factor;
+    splineListArray.backgroundSpec      = fittingOptsP->backgroundSpec;
+    splineListArray.background_color    = fittingOptsP->background_color;
+    /* Set dummy values. Real value is set in upper context. */
+    splineListArray.width  = width;
+    splineListArray.height = height;
+    
+    for (splineListSeq = 0, cancelled = false;
+         splineListSeq < CURVE_LIST_ARRAY_LENGTH(curveArray) &&
+             !at_exception_got_fatal(exception) && !cancelled;
+         ++splineListSeq) {
+
+        curve_list_type const curveList =
+            CURVE_LIST_ARRAY_ELT(curveArray, splineListSeq);
+      
+        spline_list_type curveSplineList;
+
+        if (notifyProgress)
+            notifyProgress((((float)splineListSeq)/
+                            ((float)CURVE_LIST_ARRAY_LENGTH(curveArray) *
+                             (float)3.0) + (float)0.333),
+                           progressData);
+        if (testCancel && testCancel(testcancelData))
+            cancelled = true;
+
+        LOG1("\nFitting curve list #%u:\n", splineListSeq);
+
+        fitCurveList(curveList, fittingOptsP, dist, curveList.color,
+                     &curveSplineList, exception);
+        if (!at_exception_got_fatal(exception))
+            append_spline_list(&splineListArray, curveSplineList);
+    }
+    *splineListArrayP = splineListArray;
+}
+
+
+
+void
+fit_outlines_to_splines(pixel_outline_list_type  const pixelOutlineList,
+                        fitting_opts_type *      const fittingOptsP,
+                        distance_map_type *      const dist,
+                        unsigned short           const width,
+                        unsigned short           const height,
+                        at_exception_type *      const exception,
+                        at_progress_func               notifyProgress, 
+                        void *                   const progressData,
+                        at_testcancel_func             testCancel,
+                        void *                   const testcancelData,
+                        spline_list_array_type * const splineListArrayP) {
+/*----------------------------------------------------------------------------
+   Transform a list of pixels in the outlines of the original character to
+   a list of spline lists fitted to those pixels.
+-----------------------------------------------------------------------------*/
+    curve_list_array_type const curveListArray =
+        split_at_corners(pixelOutlineList, fittingOptsP, exception);
+
+    fitCurvesToSplines(curveListArray, fittingOptsP, dist, width, height,
+                       exception, notifyProgress, progressData,
+                       testCancel, testcancelData, splineListArrayP);
+
+
+    free_curve_list_array(&curveListArray, notifyProgress, progressData);
+    
+    flush_log_output();
+}
+
+
+
+
+static void
+find_vectors(unsigned int       const test_index,
+             pixel_outline_type const outline,
+             vector_type *      const in,
+             vector_type *      const out,
+             unsigned int       const corner_surround) {
+/*----------------------------------------------------------------------------
+  Return the difference vectors coming in and going out of the outline
+  OUTLINE at the point whose index is TEST_INDEX.  In Phoenix,
+  Schneider looks at a single point on either side of the point we're
+  considering.  That works for him because his points are not touching.
+  But our points *are* touching, and so we have to look at
+  `corner_surround' points on either side, to get a better picture of
+  the outline's shape.
+-----------------------------------------------------------------------------*/
+    int i;
+    unsigned n_done;
+    pm_pixelcoord const candidate = O_COORDINATE(outline, test_index);
+
+    in->dx  = in->dy  = in->dz  = 0.0;
+    out->dx = out->dy = out->dz = 0.0;
+    
+    /* Add up the differences from p of the `corner_surround' points
+       before p.
+    */
+    for (i = O_PREV(outline, test_index), n_done = 0;
+         n_done < corner_surround;
+         i = O_PREV(outline, i), ++n_done)
+        *in = Vadd(*in, IPsubtract(O_COORDINATE(outline, i), candidate));
+    
+    /* And the points after p. */
+    for (i = O_NEXT (outline, test_index), n_done = 0;
+         n_done < corner_surround;
+         i = O_NEXT(outline, i), ++n_done)
+        *out = Vadd(*out, IPsubtract(O_COORDINATE(outline, i), candidate));
+}
+
+
+
+/* Remove adjacent points from the index list LIST.  We do this by first
+   sorting the list and then running through it.  Since these lists are
+   quite short, a straight selection sort (e.g., p.139 of the Art of
+   Computer Programming, vol.3) is good enough.  LAST_INDEX is the index
+   of the last pixel on the outline, i.e., the next one is the first
+   pixel. We need this for checking the adjacency of the last corner.
+
+   We need to do this because the adjacent corners turn into
+   two-pixel-long curves, which can only be fit by straight lines.  */
+
+static void
+remove_adjacent_corners (index_list_type *list, unsigned last_index,
+             bool remove_adj_corners,
+             at_exception_type * exception)
+             
+{
+  unsigned j;
+  unsigned last;
+  index_list_type new_list = new_index_list ();
+
+  for (j = INDEX_LIST_LENGTH (*list) - 1; j > 0; j--)
+    {
+      unsigned search;
+      unsigned temp;
+      /* Find maximal element below `j'.  */
+      unsigned max_index = j;
+
+      for (search = 0; search < j; search++)
+        if (GET_INDEX (*list, search) > GET_INDEX (*list, max_index))
+          max_index = search;
+
+      if (max_index != j)
+        {
+          temp = GET_INDEX (*list, j);
+          GET_INDEX (*list, j) = GET_INDEX (*list, max_index);
+          GET_INDEX (*list, max_index) = temp;
+      
+      /* xx -- really have to sort?  */
+      LOG ("needed exchange");
+      at_exception_warning(exception, "needed exchange");
+        }
+    }
+
+  /* The list is sorted.  Now look for adjacent entries.  Each time
+     through the loop we insert the current entry and, if appropriate,
+     the next entry.  */
+  for (j = 0; j < INDEX_LIST_LENGTH (*list) - 1; j++)
+    {
+      unsigned current = GET_INDEX (*list, j);
+      unsigned next = GET_INDEX (*list, j + 1);
+
+      /* We should never have inserted the same element twice.  */
+      /* assert (current != next); */
+
+      if ((remove_adj_corners) && ((next == current + 1) || (next == current)))
+        j++;
+
+      append_index (&new_list, current);
+    }
+
+  /* Don't append the last element if it is 1) adjacent to the previous
+     one; or 2) adjacent to the very first one.  */
+  last = GET_LAST_INDEX (*list);
+  if (INDEX_LIST_LENGTH (new_list) == 0
+      || !(last == GET_LAST_INDEX (new_list) + 1
+           || (last == last_index && GET_INDEX (*list, 0) == 0)))
+    append_index (&new_list, last);
+
+  free_index_list (list);
+  *list = new_list;
+}
+
+/* A ``knee'' is a point which forms a ``right angle'' with its
+   predecessor and successor.  See the documentation (the `Removing
+   knees' section) for an example and more details.
+
+   The argument CLOCKWISE tells us which direction we're moving.  (We
+   can't figure that information out from just the single segment with
+   which we are given to work.)
+
+   We should never find two consecutive knees.
+
+   Since the first and last points are corners (unless the curve is
+   cyclic), it doesn't make sense to remove those.  */
+
+/* This evaluates to true if the vector V is zero in one direction and
+   nonzero in the other.  */
+#define ONLY_ONE_ZERO(v)                                                \
+  (((v).dx == 0.0 && (v).dy != 0.0) || ((v).dy == 0.0 && (v).dx != 0.0))
+
+/* There are four possible cases for knees, one for each of the four
+   corners of a rectangle; and then the cases differ depending on which
+   direction we are going around the curve.  The tests are listed here
+   in the order of upper left, upper right, lower right, lower left.
+   Perhaps there is some simple pattern to the
+   clockwise/counterclockwise differences, but I don't see one.  */
+#define CLOCKWISE_KNEE(prev_delta, next_delta)                                                  \
+  ((prev_delta.dx == -1.0 && next_delta.dy == 1.0)                                              \
+   || (prev_delta.dy == 1.0 && next_delta.dx == 1.0)                                    \
+   || (prev_delta.dx == 1.0 && next_delta.dy == -1.0)                                   \
+   || (prev_delta.dy == -1.0 && next_delta.dx == -1.0))
+
+#define COUNTERCLOCKWISE_KNEE(prev_delta, next_delta)                                   \
+  ((prev_delta.dy == 1.0 && next_delta.dx == -1.0)                                              \
+   || (prev_delta.dx == 1.0 && next_delta.dy == 1.0)                                    \
+   || (prev_delta.dy == -1.0 && next_delta.dx == 1.0)                                   \
+   || (prev_delta.dx == -1.0 && next_delta.dy == -1.0))
+
+
+
+static void
+remove_knee_points(curve_type const curve,
+                   bool       const clockwise) {
+
+      unsigned const offset = (CURVE_CYCLIC(curve) == true) ? 0 : 1;
+      curve_type const trimmed_curve = copy_most_of_curve(curve);
+
+      pm_pixelcoord previous;
+      unsigned i;
+
+      if (!CURVE_CYCLIC(curve))
+          append_pixel(trimmed_curve,
+                       real_to_int_coord(CURVE_POINT(curve, 0)));
+
+      previous = real_to_int_coord(CURVE_POINT(curve,
+                                               CURVE_PREV(curve, offset)));
+
+      for (i = offset; i < CURVE_LENGTH (curve) - offset; ++i) {
+          pm_pixelcoord const current =
+              real_to_int_coord(CURVE_POINT(curve, i));
+          pm_pixelcoord const next =
+              real_to_int_coord(CURVE_POINT(curve, CURVE_NEXT(curve, i)));
+          vector_type const prev_delta = IPsubtract(previous, current);
+          vector_type const next_delta = IPsubtract(next, current);
+
+          if (ONLY_ONE_ZERO(prev_delta) && ONLY_ONE_ZERO(next_delta)
+              && ((clockwise && CLOCKWISE_KNEE(prev_delta, next_delta))
+                  || (!clockwise
+                      && COUNTERCLOCKWISE_KNEE(prev_delta, next_delta))))
+              LOG2(" (%d,%d)", current.col, current.row);
+          else {
+              previous = current;
+              append_pixel(trimmed_curve, current);
+          }
+      }
+
+      if (!CURVE_CYCLIC(curve))
+          append_pixel(trimmed_curve,
+                       real_to_int_coord(LAST_CURVE_POINT(curve)));
+
+      if (CURVE_LENGTH(trimmed_curve) == CURVE_LENGTH(curve))
+          LOG(" (none)");
+
+      LOG(".\n");
+
+      free_curve(curve);
+      *curve = *trimmed_curve;
+      free(trimmed_curve);      /* free_curve? --- Masatake */
+}
+
+
+
+/* Smooth the curve by adding in neighboring points.  Do this
+   `filter_iterations' times.  But don't change the corners.  */
+
+static void
+filter (curve_type curve, fitting_opts_type *fitting_opts)
+{
+  unsigned iteration, this_point;
+  unsigned offset = (CURVE_CYCLIC (curve) == true) ? 0 : 1;
+  float_coord prev_new_point;
+
+  /* We must have at least three points---the previous one, the current
+     one, and the next one.  But if we don't have at least five, we will
+     probably collapse the curve down onto a single point, which means
+     we won't be able to fit it with a spline.  */
+  if (CURVE_LENGTH (curve) < 5)
+    {
+      LOG1 ("Length is %u, not enough to filter.\n", CURVE_LENGTH (curve));
+      return;
+    }
+
+  prev_new_point.x = FLT_MAX;
+  prev_new_point.y = FLT_MAX;
+  prev_new_point.z = FLT_MAX;
+
+  for (iteration = 0; iteration < fitting_opts->filter_iterations;
+   iteration++)
+    {
+      curve_type newcurve = copy_most_of_curve (curve);
+      bool collapsed = false;
+
+      /* Keep the first point on the curve.  */
+      if (offset)
+        append_point (newcurve, CURVE_POINT (curve, 0));
+
+      for (this_point = offset; this_point < CURVE_LENGTH (curve) - offset;
+           this_point++)
+        {
+          vector_type in, out, sum;
+          float_coord new_point;
+
+          /* Calculate the vectors in and out, computed by looking at n points
+             on either side of this_point. Experimental it was found that 2 is
+             optimal. */
+
+          signed int prev, prevprev; /* have to be signed */
+          unsigned int next, nextnext;
+          float_coord candidate = CURVE_POINT (curve, this_point);
+
+          prev = CURVE_PREV (curve, this_point);
+          prevprev = CURVE_PREV (curve, prev);
+          next = CURVE_NEXT (curve, this_point);
+          nextnext = CURVE_NEXT (curve, next);
+
+          /* Add up the differences from p of the `surround' points
+             before p.  */
+          in.dx = in.dy = in.dz = 0.0;
+
+          in = Vadd (in, Psubtract (CURVE_POINT (curve, prev), candidate));
+          if (prevprev >= 0)
+              in = Vadd (in, Psubtract (CURVE_POINT (curve, prevprev), candidate));
+
+          /* And the points after p.  Don't use more points after p than we
+             ended up with before it.  */
+          out.dx = out.dy = out.dz = 0.0;
+
+          out = Vadd (out, Psubtract (CURVE_POINT (curve, next), candidate));
+          if (nextnext < CURVE_LENGTH (curve))
+              out = Vadd (out, Psubtract (CURVE_POINT (curve, nextnext), candidate));
+
+          /* Start with the old point.  */
+          new_point = candidate;
+          sum = Vadd (in, out);
+          /* We added 2*n+2 points, so we have to divide the sum by 2*n+2 */
+          new_point.x += sum.dx / 6;
+          new_point.y += sum.dy / 6;
+          new_point.z += sum.dz / 6;
+          if (fabs (prev_new_point.x - new_point.x) < 0.3
+              && fabs (prev_new_point.y - new_point.y) < 0.3
+              && fabs (prev_new_point.z - new_point.z) < 0.3)
+            {
+              collapsed = true;
+              break;
+            }
+
+
+          /* Put the newly computed point into a separate curve, so it
+             doesn't affect future computation (on this iteration).  */
+          append_point (newcurve, prev_new_point = new_point);
+        }
+
+      if (collapsed)
+    free_curve (newcurve);
+      else
+    {
+          /* Just as with the first point, we have to keep the last point.  */
+          if (offset)
+        append_point (newcurve, LAST_CURVE_POINT (curve));
+      
+          /* Set the original curve to the newly filtered one, and go again.  */
+          free_curve (curve);
+          *curve = *newcurve;
+    }
+      free (newcurve);
+    }
+
+  log_curve (curve, false);
+}
+
+
+
+/* Find reasonable values for t for each point on CURVE.  The method is
+   called chord-length parameterization, which is described in Plass &
+   Stone.  The basic idea is just to use the distance from one point to
+   the next as the t value, normalized to produce values that increase
+   from zero for the first point to one for the last point.  */
+
+static void
+set_initial_parameter_values (curve_type curve)
+{
+  unsigned p;
+
+  LOG ("\nAssigning initial t values:\n  ");
+
+  CURVE_T (curve, 0) = 0.0;
+
+  for (p = 1; p < CURVE_LENGTH (curve); p++)
+    {
+      float_coord point = CURVE_POINT (curve, p),
+                           previous_p = CURVE_POINT (curve, p - 1);
+      float d = distance (point, previous_p);
+      CURVE_T (curve, p) = CURVE_T (curve, p - 1) + d;
+    }
+
+  assert (LAST_CURVE_T (curve) != 0.0);
+
+  for (p = 1; p < CURVE_LENGTH (curve); p++)
+    CURVE_T (curve, p) = CURVE_T (curve, p) / LAST_CURVE_T (curve);
+
+  log_entire_curve (curve);
+}
+
+/* Find an approximation to the tangent to an endpoint of CURVE (to the
+   first point if TO_START_POINT is true, else the last).  If
+   CROSS_CURVE is true, consider points on the adjacent curve to CURVE.
+
+   It is important to compute an accurate approximation, because the
+   control points that we eventually decide upon to fit the curve will
+   be placed on the half-lines defined by the tangents and
+   endpoints...and we never recompute the tangent after this.  */
+
+static void
+find_tangent (curve_type curve, bool to_start_point, bool cross_curve,
+  unsigned tangent_surround)
+{
+  vector_type tangent;
+  vector_type **curve_tangent = (to_start_point == true) ? &(CURVE_START_TANGENT (curve))
+                                               : &(CURVE_END_TANGENT (curve));
+  unsigned n_points = 0;
+
+  LOG1 ("  tangent to %s: ", (to_start_point == true) ? "start" : "end");
+
+  if (*curve_tangent == NULL)
+    {
+        MALLOCVAR_NOFAIL(*curve_tangent);
+      do
+        {
+          tangent = find_half_tangent (curve, to_start_point, &n_points,
+            tangent_surround);
+
+          if ((cross_curve == true) || (CURVE_CYCLIC (curve) == true))
+            {
+              curve_type adjacent_curve
+                = (to_start_point == true) ? PREVIOUS_CURVE (curve) : NEXT_CURVE (curve);
+              vector_type tangent2
+                = (to_start_point == false) ? find_half_tangent (adjacent_curve, true, &n_points,
+                tangent_surround) : find_half_tangent (adjacent_curve, true, &n_points,
+                tangent_surround);
+
+              LOG3 ("(adjacent curve half tangent (%.3f,%.3f,%.3f)) ",
+                tangent2.dx, tangent2.dy, tangent2.dz);
+              tangent = Vadd (tangent, tangent2);
+            }
+          tangent_surround--;
+
+        }
+      while (tangent.dx == 0.0 && tangent.dy == 0.0);
+
+      assert (n_points > 0);
+      **curve_tangent = Vmult_scalar (tangent, (float)(1.0 / n_points));
+      if ((CURVE_CYCLIC (curve) == true) && CURVE_START_TANGENT (curve))
+          *CURVE_START_TANGENT (curve) = **curve_tangent;
+      if  ((CURVE_CYCLIC (curve) == true) && CURVE_END_TANGENT (curve))
+          *CURVE_END_TANGENT (curve) = **curve_tangent;
+    }
+  else
+    LOG ("(already computed) ");
+
+  LOG3 ("(%.3f,%.3f,%.3f).\n", (*curve_tangent)->dx, (*curve_tangent)->dy, (*curve_tangent)->dz);
+}
+
+/* Find the change in y and change in x for `tangent_surround' (a global)
+   points along CURVE.  Increment N_POINTS by the number of points we
+   actually look at.  */
+
+static vector_type
+find_half_tangent (curve_type c, bool to_start_point, unsigned *n_points,
+  unsigned tangent_surround)
+{
+  unsigned p;
+  int factor = to_start_point ? 1 : -1;
+  unsigned tangent_index = to_start_point ? 0 : c->length - 1;
+  float_coord tangent_point = CURVE_POINT (c, tangent_index);
+  vector_type tangent = { 0.0, 0.0 };
+  unsigned int surround;
+
+  if ((surround = CURVE_LENGTH (c) / 2) > tangent_surround)
+    surround = tangent_surround;
+
+  for (p = 1; p <= surround; p++)
+    {
+      int this_index = p * factor + tangent_index;
+      float_coord this_point;
+
+      if (this_index < 0 || this_index >= (int) c->length)
+        break;
+
+      this_point = CURVE_POINT (c, p * factor + tangent_index);
+
+      /* Perhaps we should weight the tangent from `this_point' by some
+         factor dependent on the distance from the tangent point.  */
+      tangent = Vadd (tangent,
+                      Vmult_scalar (Psubtract (this_point, tangent_point),
+                                    (float) factor));
+      (*n_points)++;
+    }
+
+  return tangent;
+}
+
+/* When this routine is called, we have computed a spline representation
+   for the digitized curve.  The question is, how good is it?  If the
+   fit is very good indeed, we might have an error of zero on each
+   point, and then WORST_POINT becomes irrelevant.  But normally, we
+   return the error at the worst point, and the index of that point in
+   WORST_POINT.  The error computation itself is the Euclidean distance
+   from the original curve CURVE to the fitted spline SPLINE.  */
+
+static float
+find_error (curve_type curve, spline_type spline, unsigned *worst_point,
+        at_exception_type * exception)
+{
+  unsigned this_point;
+  float total_error = 0.0;
+  float worst_error = FLT_MIN;
+
+  *worst_point = CURVE_LENGTH (curve) + 1;   /* A sentinel value.  */
+
+  for (this_point = 0; this_point < CURVE_LENGTH (curve); this_point++)
+    {
+      float_coord curve_point = CURVE_POINT (curve, this_point);
+      float t = CURVE_T (curve, this_point);
+      float_coord spline_point = evaluate_spline (spline, t);
+      float this_error = distance (curve_point, spline_point);
+      if (this_error >= worst_error)
+        {
+         *worst_point = this_point;
+          worst_error = this_error;
+        }
+      total_error += this_error;
+    }
+
+  if (*worst_point == CURVE_LENGTH (curve) + 1)
+    { /* Didn't have any ``worst point''; the error should be zero.  */
+      if (epsilon_equal (total_error, 0.0))
+        LOG ("  Every point fit perfectly.\n");
+      else
+    {
+      LOG("No worst point found; something is wrong");
+      at_exception_warning(exception, "No worst point found; something is wrong");
+    }
+    }
+  else
+    {
+      if (epsilon_equal (total_error, 0.0))
+        LOG ("  Every point fit perfectly.\n");
+      else
+        {
+          LOG5 ("  Worst error (at (%.3f,%.3f,%.3f), point #%u) was %.3f.\n",
+              CURVE_POINT (curve, *worst_point).x,
+              CURVE_POINT (curve, *worst_point).y,
+              CURVE_POINT (curve, *worst_point).z, *worst_point, worst_error);
+          LOG1 ("  Total error was %.3f.\n", total_error);
+          LOG2 ("  Average error (over %u points) was %.3f.\n",
+              CURVE_LENGTH (curve), total_error / CURVE_LENGTH (curve));
+        }
+    }
+
+  return worst_error;
+}
+
+
+/* Lists of array indices (well, that is what we use it for).  */
+
+static index_list_type
+new_index_list (void)
+{
+  index_list_type index_list;
+
+  index_list.data = NULL;
+  INDEX_LIST_LENGTH (index_list) = 0;
+
+  return index_list;
+}
+
+static void
+free_index_list (index_list_type *index_list)
+{
+  if (INDEX_LIST_LENGTH (*index_list) > 0)
+    {
+      free (index_list->data);
+      index_list->data = NULL;
+      INDEX_LIST_LENGTH (*index_list) = 0;
+    }
+}
+
+static void
+append_index (index_list_type *list, unsigned new_index)
+{
+  INDEX_LIST_LENGTH (*list)++;
+  REALLOCARRAY_NOFAIL(list->data, INDEX_LIST_LENGTH(*list));
+  list->data[INDEX_LIST_LENGTH (*list) - 1] = new_index;
+}
+
+
+/* Return the Euclidean distance between P1 and P2.  */
+
+static float
+distance (float_coord p1, float_coord p2)
+{
+  float x = p1.x - p2.x, y = p1.y - p2.y, z = p1.z - p2.z;
+  return (float) sqrt (SQR(x) + SQR(y) + SQR(z));
+}
diff --git a/converter/other/pamtosvg/fit.h b/converter/other/pamtosvg/fit.h
new file mode 100644
index 00000000..529da5c7
--- /dev/null
+++ b/converter/other/pamtosvg/fit.h
@@ -0,0 +1,34 @@
+/* fit.h: convert the pixel representation to splines. */
+
+#ifndef FIT_H_INCLUDED
+#define FIT_H_INCLUDED
+
+#include "autotrace.h"
+#include "image-proc.h"
+#include "pxl-outline.h"
+#include "spline.h"
+#include "exception.h"
+
+/* See fit.c for descriptions of these variables, all of which can be
+   set using options. 
+*/
+typedef at_fitting_opts_type fitting_opts_type;
+
+void
+fit_outlines_to_splines(pixel_outline_list_type  const pixelOutlineList,
+                        fitting_opts_type *      const fittingOptsP,
+                        distance_map_type *      const dist,
+                        unsigned short           const width,
+                        unsigned short           const height,
+                        at_exception_type *      const exception,
+                        at_progress_func               notifyProgress, 
+                        void *                   const progressData,
+                        at_testcancel_func             testCancel,
+                        void *                   const testcancelData,
+                        spline_list_array_type * const splineListArrayP);
+
+/* Get a new set of fitting options */
+fitting_opts_type
+new_fitting_opts(void);
+
+#endif
diff --git a/converter/other/pamtosvg/image-header.h b/converter/other/pamtosvg/image-header.h
new file mode 100644
index 00000000..adcf4771
--- /dev/null
+++ b/converter/other/pamtosvg/image-header.h
@@ -0,0 +1,19 @@
+/* image-header.h: declarations for a generic image header. */
+
+#ifndef IMAGE_HEADER_H
+#define IMAGE_HEADER_H
+
+
+/* The important general information about the image data.
+   See `get_{img,pbm}_header' for the full details of the headers for
+   the particular formats.  */
+typedef struct
+{
+  unsigned short hres, vres;	/* In pixels per inch.  */
+  unsigned short width, height;	/* In bits.  */
+  unsigned short depth;		/* Perhaps the depth?  */
+  unsigned format;		/* (for pbm) Whether packed or not.  */
+} image_header_type;
+
+#endif /* not IMAGE_HEADER_H */
+
diff --git a/converter/other/pamtosvg/image-proc.c b/converter/other/pamtosvg/image-proc.c
new file mode 100644
index 00000000..287e6384
--- /dev/null
+++ b/converter/other/pamtosvg/image-proc.c
@@ -0,0 +1,516 @@
+/* image-proc.c: image processing routines */
+
+#include <assert.h>
+#include <math.h>
+#include <string.h>
+
+#include "mallocvar.h"
+
+#include "message.h"
+
+#include "image-proc.h"
+
+#define BLACK 0
+#define WHITE 0xff
+#ifndef M_SQRT2
+#define M_SQRT2 1.41421356237
+#endif
+
+#if 0
+struct etyp { int t00, t11, t01, t01s; };
+
+
+static bool get_edge(bitmap_type, int y, int x, struct etyp *t);
+static void check(int v1, int v2, int v3, struct etyp *t);
+#endif
+
+
+/* Allocate storage for a new distance map with the same dimensions
+   as BITMAP and initialize it so that pixels in BITMAP with value
+   TARGET_VALUE are at distance zero and all other pixels are at
+   distance infinity.  Then compute the gray-weighted distance from
+   every non-target point to the nearest target point. */
+
+distance_map_type
+new_distance_map(bitmap_type bitmap, unsigned char target_value, bool padded, at_exception_type * exp)
+{
+    signed x, y;
+    float d, min;
+    distance_map_type dist;
+    unsigned char *b = bitmap.bitmap;
+    unsigned w = bitmap.width;
+    unsigned h = bitmap.height;
+    unsigned spp = bitmap.np;
+
+    dist.height = h; dist.width = w;
+    MALLOCARRAY(dist.d, h);
+    MALLOCARRAY(dist.weight, h);
+    if (dist.d == NULL || dist.weight == NULL)
+        pm_error("Unable to get memory for distance map");
+    for (y = 0; y < (signed) h; y++)
+    {
+        MALLOCARRAY(dist.d[y], w);
+        if (dist.d[y] == NULL)
+            pm_error("Unable to get memory for distance map");
+        bzero(dist.d[y], w * sizeof(float));
+        
+        MALLOCARRAY(dist.weight[y], w);
+        if (dist.weight[y] == NULL)
+            pm_error("Unable to get memory for distance map");
+    }
+
+    if (spp == 3)
+    {
+      for (y = 0; y < (signed) h; y++)
+      {
+        for (x = 0; x < (signed) w; x++, b += spp)
+        {
+          int gray; float fgray;
+          gray = (int)LUMINANCE(b[0], b[1], b[2]);
+          dist.d[y][x] = (gray == target_value ? 0.0F : 1.0e10F);
+          fgray = gray * 0.0039215686F;  /* = gray / 255.0F */
+          dist.weight[y][x] = 1.0F - fgray;
+/*        dist.weight[y][x] = 1.0F - (fgray * fgray);*/
+/*        dist.weight[y][x] = (fgray < 0.5F ? 1.0F - fgray : -2.0F * fgray * (fgray - 1.0F));*/
+	    }
+      }
+    }
+    else
+    {
+      for (y = 0; y < (signed) h; y++)
+      {
+        for (x = 0; x < (signed) w; x++, b += spp)
+        {
+          int gray; float fgray;
+          gray = b[0];
+          dist.d[y][x] = (gray == target_value ? 0.0F : 1.0e10F);
+          fgray = gray * 0.0039215686F;  /* = gray / 255.0F */
+          dist.weight[y][x] = 1.0F - fgray;
+/*        dist.weight[y][x] = 1.0F - (fgray * fgray);*/
+/*        dist.weight[y][x] = (fgray < 0.5F ? 1.0F - fgray : -2.0F * fgray * (fgray - 1.0F)); */
+        }
+      }
+    }
+
+    /* If the image is padded then border points are all at most
+       one unit away from the nearest target point. */
+    if (padded)
+    {
+        for (y = 0; y < (signed) h; y++)
+        {
+            if (dist.d[y][0] > dist.weight[y][0])
+                dist.d[y][0] = dist.weight[y][0];
+            if (dist.d[y][w - 1] > dist.weight[y][w - 1])
+                dist.d[y][w - 1] = dist.weight[y][w - 1];
+        }
+        for (x = 0; x < (signed) w; x++)
+        {
+            if (dist.d[0][x] > dist.weight[0][x])
+                dist.d[0][x] = dist.weight[0][x];
+            if (dist.d[h - 1][x] > dist.weight[h - 1][x])
+                dist.d[h - 1][x] = dist.weight[h - 1][x];
+        }
+    }
+
+    /* Scan the image from left to right, top to bottom.
+       Examine the already-visited neighbors of each point (those
+       situated above or to the left of it).  Each neighbor knows
+       the distance to its nearest target point; add to this distance
+       the distance from the central point to the neighbor (either
+       sqrt(2) or one) multiplied by the central point's weight
+       (derived from its gray level).  Replace the distance already
+       stored at the central point if the new distance is smaller. */
+    for (y = 1; y < (signed) h; y++)
+    {
+        for (x = 1; x < (signed) w; x++)
+        {
+            if (dist.d[y][x] == 0.0F) continue;
+
+            min = dist.d[y][x];
+
+            /* upper-left neighbor */
+            d = dist.d[y - 1][x - 1] + (float) M_SQRT2 * dist.weight[y][x];
+            if (d < min) min = dist.d[y][x] = d;
+
+            /* upper neighbor */
+            d = dist.d[y - 1][x] + dist.weight[y][x];
+            if (d < min) min = dist.d[y][x] = d;
+
+            /* left neighbor */
+            d = dist.d[y][x - 1] + dist.weight[y][x];
+            if (d < min) min = dist.d[y][x] = d;
+
+            /* upper-right neighbor (except at the last column) */
+            if (x + 1 < (signed) w)
+            {
+                d = dist.d[y - 1][x + 1] + (float) M_SQRT2 * dist.weight[y][x];
+                if (d < min) min = dist.d[y][x] = d;
+            }
+        }
+    }
+
+    /* Same as above, but now scanning right to left, bottom to top. */
+    for (y = h - 2; y >= 0; y--)
+    {
+        for (x = w - 2; x >= 0; x--)
+        {
+            min = dist.d[y][x];
+
+            /* lower-right neighbor */
+            d = dist.d[y + 1][x + 1] + (float) M_SQRT2 * dist.weight[y][x];
+	        if (d < min) min = dist.d[y][x] = d;
+
+            /* lower neighbor */
+            d = dist.d[y + 1][x] + dist.weight[y][x];
+	        if (d < min) min = dist.d[y][x] = d;
+
+            /* right neighbor */
+            d = dist.d[y][x + 1] + dist.weight[y][x];
+	        if (d < min) min = dist.d[y][x] = d;
+
+            /* lower-left neighbor (except at the first column) */
+            if (x - 1 >= 0)
+            {
+                d = dist.d[y + 1][x - 1] + (float) M_SQRT2 * dist.weight[y][x];
+                if (d < min) min = dist.d[y][x] = d;
+            }
+        }
+    }
+    return dist;
+}
+
+
+/* Free the dynamically-allocated storage associated with a distance map. */
+
+void
+free_distance_map(distance_map_type *dist)
+{
+    unsigned y, h;
+
+    if (!dist) return;
+
+    h = dist->height;
+
+    if (dist->d != NULL)
+    {
+	for (y = 0; y < h; y++)
+	    free(dist->d[y]);
+        free(dist->d);
+    }
+    if (dist->weight != NULL)
+    {
+	for (y = 0; y < h; y++)
+	    free(dist->weight[y]);
+        free(dist->weight);
+    }
+}
+
+
+#if 0
+void
+medial_axis(bitmap_type *bitmap, distance_map_type *dist,
+            bool bgSpec, pixel bg_color)
+{
+    unsigned x, y, test;
+    unsigned w, h;
+    unsigned char *b;
+    float **d, f;
+    pixel bg;
+
+    assert(bitmap != NULL);
+
+    assert(BITMAP_PLANES(*bitmap) == 1);
+
+    b = BITMAP_BITS(*bitmap);
+    assert(b != NULL);
+    assert(dist != NULL);
+    d = dist->d;
+    assert(d != NULL);
+
+    h = BITMAP_HEIGHT(*dist);
+    w = BITMAP_WIDTH(*dist);
+    assert(BITMAP_WIDTH(*bitmap) == w && BITMAP_HEIGHT(*bitmap) == h);
+
+    if (bgSpec)
+        bg = bg_color;
+    else 
+        PPM_ASSIGN(bg, 255, 255, 255);
+
+    f = d[0][0] + 0.5;
+    test = (f < d[1][0]) + (f < d[1][1]) + (f < d[0][1]);
+    if (test > 1) b[0] = PPM_GETR(bg);
+
+    f = d[0][w-1] + 0.5;
+    test = (f < d[1][w-1]) + (f < d[1][w-2]) + (f < d[0][w-2]);
+    if (test > 1) b[w-1] = PPM_GETR(bg);
+
+    for (x = 1; x < w - 1; x++)
+    {
+	    f = d[0][x] + 0.5;
+	    test = (f < d[0][x-1]) + (f < d[0][x+1]) + (f < d[1][x-1])
+	        + (f < d[1][x]) + (f < d[1][x+1]);
+	    if (test > 1) b[x] = PPM_GETR(bg);
+    }
+    b += w;
+
+    for (y = 1; y < h - 1; y++)
+    {
+	    f = d[y][0] + 0.5;
+	    test = (f < d[y-1][0]) + (f < d[y-1][1]) + (f < d[y][1])
+	        + (f < d[y+1][0]) + (f < d[y+1][1]);
+	    if (test > 1) b[0] = PPM_GETR(bg);
+
+	    for (x = 1; x < w - 1; x++)
+		{
+	        f = d[y][x] + 0.5;
+	        test = (f < d[y-1][x-1]) + (f < d[y-1][x]) + (f < d[y-1][x+1])
+		    + (f < d[y][x-1]) + (f < d[y][x+1])
+		    + (f < d[y+1][x-1]) + (f < d[y+1][x]) + (f < d[y+1][x+1]);
+	        if (test > 1) b[x] = PPM_GETR(bg)
+		}
+
+	    f = d[y][w-1] + 0.5;
+	    test = (f < d[y-1][w-1]) + (f < d[y-1][w-2]) + (f < d[y][w-2])
+	        + (f < d[y+1][w-1]) + (f < d[y+1][w-2]);
+	    if (test > 1)
+	        b[w-1] = PPM_GETR(bg);
+
+        b += w;
+    }
+
+    for (x = 1; x < w - 1; x++)
+    {
+	    f = d[h-1][x] + 0.5;
+	    test = (f < d[h-1][x-1]) + (f < d[h-1][x+1])
+	        + (f < d[h-2][x-1]) + (f < d[h-2][x]) + (f < d[h-2][x+1]);
+	    if (test > 1) b[x] = PPM_GETR(bg);
+    }
+
+    f = d[h-1][0] + 0.5;
+    test = (f < d[h-2][0]) + (f < d[h-2][1]) + (f < d[h-1][1]);
+    if (test > 1) b[0] = PPM_GETR(bg);
+
+    f = d[h-1][w-1] + 0.5;
+    test = (f < d[h-2][w-1]) + (f < d[h-2][w-2]) + (f < d[h-1][w-2]);
+    if (test > 1) b[w-1] = PPM_GETR(bg);
+}
+#endif
+
+
+/* Binarize a grayscale or color image. */
+
+void
+binarize(bitmap_type *bitmap)
+{
+    unsigned i, npixels, spp;
+    unsigned char *b;
+
+    assert(bitmap != NULL);
+    assert(bitmap->bitmap != NULL);
+
+    b = bitmap->bitmap;
+    spp = bitmap->np;
+    npixels = bitmap->width * bitmap->height;
+
+    if (spp == 1)
+    {
+	    for (i = 0; i < npixels; i++)
+	        b[i] = (b[i] > GRAY_THRESHOLD ? WHITE : BLACK);
+    }
+    else if (spp == 3)
+    {
+	    unsigned char *rgb = b;
+	    for (i = 0; i < npixels; i++, rgb += 3)
+		{
+	        b[i] = (LUMINANCE(rgb[0], rgb[1], rgb[2]) > GRAY_THRESHOLD
+		        ? WHITE : BLACK);
+		}
+	    REALLOCARRAY_NOFAIL(bitmap->bitmap, npixels);
+	    bitmap->np = 1;
+    }
+    else
+    {
+	    WARNING1("binarize: %u-plane images are not supported", spp);
+    }
+}
+
+
+#if 0
+/* Thin a binary image, replacing the original image with the thinned one. */
+
+bitmap_type
+ip_thin(bitmap_type input_b)
+{
+    unsigned y, x, i;
+    bool k, again;
+    struct etyp t;
+    unsigned w = BITMAP_WIDTH(input_b);
+    unsigned h = BITMAP_HEIGHT(input_b);
+    size_t num_bytes = w * h;
+    bitmap_type b = input_b;
+
+    if (BITMAP_PLANES(input_b) != 1)
+    {
+	    FATAL1("thin: single-plane image required; "
+	        "%u-plane images cannot be thinned", BITMAP_PLANES(input_b));
+	    return b;
+    }
+
+    /* Process and return a copy of the input image. */
+    MALLOCARRAY(b.bitmap, num_bytes);
+    if (b.bitmap == NULL)
+        pm_error("Unable to get memory for bitmap");
+    memcpy(b.bitmap, input_b.bitmap, num_bytes);
+
+    /* Set background pixels to zero, foreground pixels to one. */
+    for (i = 0; i < num_bytes; i++)
+	b.bitmap[i] = (b.bitmap[i] == BLACK ? 1 : 0);
+
+    again = true;
+    while (again)
+    {
+	again = false;
+
+	for (y = 1; y < h - 1; y++)
+	{
+	    for (x = 1; x < w - 1; x++)
+	    {
+		    /* During processing, pixels are used to store edge
+		       type codes, so we can't just test for WHITE or BLACK. */
+		    if (*BITMAP_PIXEL(b, y, x) == 0) continue;
+
+		    k = (!get_edge(b, y, x, &t)
+		        || (get_edge(b, y, x+1, &t) && *BITMAP_PIXEL(b, y-1, x)
+			    && *BITMAP_PIXEL(b, y+1, x))
+		        || (get_edge(b, y+1, x, &t) && *BITMAP_PIXEL(b, y, x-1)
+			    && *BITMAP_PIXEL(b, y, x+1))
+		        || (get_edge(b, y, x+1, &t) && get_edge(b, y+1, x+1, &t)
+			    && get_edge(b, y+1, x, &t)));
+		    if (k) continue;
+
+		    get_edge(b, y, x, &t);
+		    if (t.t01) *BITMAP_PIXEL(b, y, x) |= 4;
+		    *BITMAP_PIXEL(b, y, x) |= 2;
+		    again = true;
+	    }
+	}
+
+	for (y = 0; y < h; y++)
+	    for (x = 0; x < w; x++)
+		    if (*BITMAP_PIXEL(b, y, x) & 02) *BITMAP_PIXEL(b, y, x) = 0;
+
+	for (y = 1; y < h - 1; y++)
+	{
+	    for (x = 1; x < w - 1; x++)
+	    {
+		    if (*BITMAP_PIXEL(b, y, x) == 0) continue;
+
+		    k = (!get_edge(b, y, x, &t)
+		        || ((*BITMAP_PIXEL(b, y, x) & 04) == 0)
+		        || (get_edge(b, y+1, x, &t) && (*BITMAP_PIXEL(b, y, x-1))
+			    && *BITMAP_PIXEL(b, y, x+1))
+		        || (get_edge(b, y, x+1, &t) && *BITMAP_PIXEL(b, y-1, x)
+			    && *BITMAP_PIXEL(b, y+1, x))
+		        || (get_edge(b, y+1, x, &t) && get_edge(b, y, x+1, &t)
+			    && get_edge(b, y+1, x+1, &t)));
+		    if (k) continue;
+
+		    *BITMAP_PIXEL(b, y, x) |= 02;
+		    again = true;
+	    }
+	}
+
+	for (y = 0; y < h; y++)
+	{
+	    for (x = 0; x < w; x++)
+	    {
+		    if (*BITMAP_PIXEL(b, y, x) & 02) *BITMAP_PIXEL(b, y, x) = 0;
+		    else if (*BITMAP_PIXEL(b, y, x) > 0) *BITMAP_PIXEL(b, y, x) = 1;
+	    }
+	}
+    }
+
+    /* Staircase removal; northward bias. */
+    for (y = 1; y < h - 1; y++)
+    {
+	    for (x = 1; x < w - 1; x++)
+		{
+	        if (*BITMAP_PIXEL(b, y, x) == 0) continue;
+
+	        k = !(*BITMAP_PIXEL(b, y-1, x)
+		        && ((*BITMAP_PIXEL(b, y, x+1) && !*BITMAP_PIXEL(b, y-1, x+1)
+		        && !*BITMAP_PIXEL(b, y+1, x-1)
+		        && (!*BITMAP_PIXEL(b, y, x-1) || !*BITMAP_PIXEL(b, y+1, x)))
+		        || (*BITMAP_PIXEL(b, y, x-1) && !*BITMAP_PIXEL(b, y-1, x-1)
+		        && !*BITMAP_PIXEL(b, y+1, x+1) &&
+		        (!*BITMAP_PIXEL(b, y, x+1) || !*BITMAP_PIXEL(b, y+1, x)))));
+	        if (k) continue;
+
+	        *BITMAP_PIXEL(b, y, x) |= 02;
+		}
+    }
+    for (y = 0; y < h; y++)
+    {
+	    for (x = 0; x < w; x++)
+		{
+	        if (*BITMAP_PIXEL(b, y, x) & 02) *BITMAP_PIXEL(b, y, x) = 0;
+	        else if (*BITMAP_PIXEL(b, y, x) > 0) *BITMAP_PIXEL(b, y, x) = 1;
+		}
+    }
+
+    /* Southward bias */
+    for (y = 1; y < h - 1; y++)
+    {
+	    for (x = 1; x < w - 1; x++)
+		{
+	        if (*BITMAP_PIXEL(b, y, x) == 0) continue;
+
+	        k = !(*BITMAP_PIXEL(b, y+1, x)
+		    && ((*BITMAP_PIXEL(b, y, x+1) && !*BITMAP_PIXEL(b, y+1, x+1)
+		    && !*BITMAP_PIXEL(b, y-1, x-1) && (!*BITMAP_PIXEL(b, y, x-1)
+		    || !*BITMAP_PIXEL(b, y-1, x))) || (*BITMAP_PIXEL(b, y, x-1)
+		    && !*BITMAP_PIXEL(b, y+1, x-1) && !*BITMAP_PIXEL(b, y-1, x+1)
+		    && (!*BITMAP_PIXEL(b, y, x+1) || !*BITMAP_PIXEL(b, y-1, x)) )));
+	        if (k) continue;
+
+	        *BITMAP_PIXEL(b, y, x) |= 02;
+		}
+    }
+    for (y = 0; y < h; y++)
+    {
+	    for (x = 0; x < w; x++)
+		{
+	        if (*BITMAP_PIXEL(b, y, x) & 02) *BITMAP_PIXEL(b, y, x) = 0;
+	        else if (*BITMAP_PIXEL(b, y, x) > 0) *BITMAP_PIXEL(b, y, x) = 1;
+		}
+    }
+
+    /* Set background pixels to WHITE, foreground pixels to BLACK. */
+    for (i = 0; i < num_bytes; i++)
+	b.bitmap[i] = (b.bitmap[i] == 0 ? WHITE : BLACK);
+    return b;
+}
+
+
+bool get_edge(bitmap_type b, int y, int x, struct etyp *t)
+{
+    t->t00 = 0; t->t01 = 0; t->t01s = 0; t->t11 = 0;
+    check(*BITMAP_PIXEL(b, y - 1, x - 1), *BITMAP_PIXEL(b, y - 1, x),
+	*BITMAP_PIXEL(b, y - 1, x + 1), t);
+    check(*BITMAP_PIXEL(b, y - 1, x + 1), *BITMAP_PIXEL(b, y, x + 1),
+	*BITMAP_PIXEL(b, y + 1, x + 1), t);
+    check(*BITMAP_PIXEL(b, y + 1, x + 1), *BITMAP_PIXEL(b, y + 1, x),
+	*BITMAP_PIXEL(b, y + 1, x - 1), t);
+    check(*BITMAP_PIXEL(b, y + 1, x - 1), *BITMAP_PIXEL(b, y, x - 1),
+	*BITMAP_PIXEL(b, y - 1, x - 1), t);
+    return *BITMAP_PIXEL(b, y, x) && t->t00 && t->t11 && !t->t01s;
+}
+
+
+void check(int v1, int v2, int v3, struct etyp *t)
+{
+    if (!v2 && (!v1 || !v3)) t->t00 = 1;
+    if (v2 && (v1 || v3)) t->t11 = 1;
+    if ((!v1 && v2) || (!v2 && v3)) { t->t01s = t->t01; t->t01 = 1; }
+}
+#endif
diff --git a/converter/other/pamtosvg/image-proc.h b/converter/other/pamtosvg/image-proc.h
new file mode 100644
index 00000000..a5b86ec1
--- /dev/null
+++ b/converter/other/pamtosvg/image-proc.h
@@ -0,0 +1,42 @@
+/* image-proc.h: image processing routines */
+
+#ifndef IMAGE_PROC_H
+#define IMAGE_PROC_H
+
+#include "bitmap.h"
+#include "exception.h"
+
+
+/* Threshold for binarizing a monochrome image */
+#define GRAY_THRESHOLD 225
+
+/* RGB to grayscale */
+#define LUMINANCE(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11 + 0.5)
+
+
+typedef struct
+{
+  unsigned height, width;
+  float **weight;
+  float **d;
+} distance_map_type;
+
+
+/* Allocate and compute a new distance map. */
+extern distance_map_type new_distance_map(bitmap_type,
+    unsigned char target_value, bool padded,
+					  at_exception_type * exp);
+
+/* Free the dynamically-allocated storage associated with a distance map. */
+extern void free_distance_map(distance_map_type*);
+
+
+/* Binarize a grayscale or color image. */
+extern void binarize(bitmap_type*);
+
+
+/* Thin a binary image, replacing the original image with the thinned one. */
+extern bitmap_type ip_thin(bitmap_type);
+
+#endif /* not IMAGE_PROC_H */
+
diff --git a/converter/other/pamtosvg/logreport.c b/converter/other/pamtosvg/logreport.c
new file mode 100644
index 00000000..7d726584
--- /dev/null
+++ b/converter/other/pamtosvg/logreport.c
@@ -0,0 +1,17 @@
+/* logreport.c: showing information to the user. */
+
+#include "logreport.h"
+#include "message.h"
+
+/* Says whether to output detailed progress reports, i.e., all the data
+   on the fitting, as we run.  (-log)  */
+FILE *log_file = NULL;
+
+
+void
+flush_log_output (void)
+{
+  if (log_file)
+    fflush (log_file);
+}
+
diff --git a/converter/other/pamtosvg/logreport.h b/converter/other/pamtosvg/logreport.h
new file mode 100644
index 00000000..577da8df
--- /dev/null
+++ b/converter/other/pamtosvg/logreport.h
@@ -0,0 +1,28 @@
+/* logreport.h: status reporting routines. */
+
+#ifndef LOGREPORT_H
+#define LOGREPORT_H
+
+#include <stdio.h>
+
+/* The file we write information to.  */
+extern FILE *at_log_file;
+#define log_file at_log_file
+
+extern void flush_log_output (void);
+
+#define LOG(s)								\
+  do { if (log_file) fputs (s, log_file); } while (0)
+#define LOG1(s, e)							\
+  do { if (log_file) fprintf (log_file, s, e); } while (0)
+#define LOG2(s, e1, e2)							\
+  do { if (log_file) fprintf (log_file, s, e1, e2); } while (0)
+#define LOG3(s, e1, e2, e3)						\
+  do { if (log_file) fprintf (log_file, s, e1, e2, e3); } while (0)
+#define LOG4(s, e1, e2, e3, e4)						\
+  do { if (log_file) fprintf (log_file, s, e1, e2, e3, e4); } while (0)
+#define LOG5(s, e1, e2, e3, e4, e5)					\
+  do { if (log_file) fprintf (log_file, s, e1, e2, e3, e4, e5); } while (0)
+
+#endif /* not LOGREPORT_H */
+
diff --git a/converter/other/pamtosvg/message.h b/converter/other/pamtosvg/message.h
new file mode 100644
index 00000000..0a226206
--- /dev/null
+++ b/converter/other/pamtosvg/message.h
@@ -0,0 +1,47 @@
+/* message.h: extend the standard programming environment a little. */
+
+#ifndef MESSAGE_H
+#define MESSAGE_H
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "logreport.h"
+
+/* Define common sorts of messages.  */
+
+/* This should be called only after a system call fails.  */
+#define FATAL_PERROR(s) do { perror (s); exit (errno); } while (0)
+
+
+#define START_FATAL() do { fputs ("fatal: ", stderr); LOG("fatal: ")
+#define END_FATAL() fputs (".\n", stderr); exit (1); } while (0)
+
+#define FATAL(s)							\
+  START_FATAL (); fprintf (stderr, "%s", s); LOG (s); END_FATAL ()
+#define FATAL1(s, e1)							\
+  START_FATAL (); fprintf (stderr, s, e1); LOG1 (s, e1); END_FATAL ()
+#define FATAL2(s, e1, e2)						\
+  START_FATAL (); fprintf (stderr, s, e1, e2); LOG2 (s, e1, e2); END_FATAL ()
+#define FATAL3(s, e1, e2, e3)						\
+  START_FATAL (); fprintf (stderr, s, e1, e2, e3); LOG3 (s, e1, e2, e3); END_FATAL ()
+#define FATAL4(s, e1, e2, e3, e4)					\
+  START_FATAL (); fprintf (stderr, s, e1, e2, e3, e4); LOG4 (s, e1, e2, e3, e4); END_FATAL ()
+
+
+#define START_WARNING() do { fputs ("warning: ", stderr); LOG ("warning: ")
+#define END_WARNING() fputs (".\n", stderr); } while (0)
+
+#define WARNING(s)							\
+  START_WARNING (); fprintf (stderr, "%s", s); LOG (s); END_WARNING ()
+#define WARNING1(s, e1)							\
+  START_WARNING (); fprintf (stderr, s, e1); LOG1 (s, e1); END_WARNING ()
+#define WARNING2(s, e1, e2)						\
+  START_WARNING (); fprintf (stderr, s, e1, e2); LOG2 (s, e1, e2); END_WARNING ()
+#define WARNING3(s, e1, e2, e3)						\
+  START_WARNING (); fprintf (stderr, s, e1, e2, e3); LOG3 (s, e1, e2, e3); END_WARNING ()
+#define WARNING4(s, e1, e2, e3, e4)					\
+  START_WARNING (); fprintf (stderr, s, e1, e2, e3, e4); LOG4 (s, e1, e2, e3, e4); END_WARNING ()
+
+#endif /* not MESSAGE_H */
+
diff --git a/converter/other/pamtosvg/output-svg.c b/converter/other/pamtosvg/output-svg.c
new file mode 100644
index 00000000..53a8d4fd
--- /dev/null
+++ b/converter/other/pamtosvg/output-svg.c
@@ -0,0 +1,130 @@
+/* output-svg.h - output in SVG format
+
+   Copyright (C) 1999, 2000, 2001 Bernhard Herzog
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation; either version 2.1 of
+   the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+   USA. */
+
+#include "spline.h"
+#include "output-svg.h"
+
+
+
+static void
+outSplineList(FILE *           const fileP,
+              spline_list_type const splineList,
+              unsigned int     const height) {
+              
+    unsigned splineSeq;
+        
+    for (splineSeq = 0;
+         splineSeq < SPLINE_LIST_LENGTH(splineList);
+         ++splineSeq) {
+        
+        spline_type const spline = SPLINE_LIST_ELT(splineList, splineSeq);
+        
+        if (SPLINE_DEGREE(spline) == LINEARTYPE) {
+            fprintf(fileP, "L%g %g",
+                    END_POINT(spline).x, height - END_POINT(spline).y);
+        } else
+            fprintf(fileP, "C%g %g %g %g %g %g",
+                    CONTROL1(spline).x, height  - CONTROL1(spline).y,
+                    CONTROL2(spline).x, height  - CONTROL2(spline).y,
+                    END_POINT(spline).x, height - END_POINT(spline).y);
+    }
+}
+
+
+
+static void
+out_splines(FILE *                 const fileP,
+            spline_list_array_type const shape,
+            unsigned int           const height) {
+
+    unsigned listSeq;
+    pixel lastColor;
+    
+    PPM_ASSIGN(lastColor, 0, 0, 0);
+    
+    for (listSeq = 0;
+         listSeq < SPLINE_LIST_ARRAY_LENGTH(shape);
+         ++listSeq) {
+        
+        spline_list_type const splineList =
+            SPLINE_LIST_ARRAY_ELT(shape, listSeq);
+        spline_type const first = SPLINE_LIST_ELT(splineList, 0);
+
+        if (listSeq == 0 || !PPM_EQUAL(splineList.color, lastColor)) {
+            if (listSeq > 0) {
+                /* Close previous <path> element */
+                if (!(shape.centerline || splineList.open))
+                    fputs("z", fileP);
+                fputs("\"/>\n", fileP);
+            }
+            /* Open new <path> element */
+            fprintf(fileP, "<path style=\"%s:#%02x%02x%02x; %s:none;\" d=\"",
+                    (shape.centerline || splineList.open) ? "stroke" : "fill",
+                    PPM_GETR(splineList.color),
+                    PPM_GETG(splineList.color),
+                    PPM_GETB(splineList.color),
+                    (shape.centerline || splineList.open) ? "fill" : "stroke");
+        }
+        fprintf(fileP, "M%g %g",
+                START_POINT(first).x, height - START_POINT(first).y);
+
+        outSplineList(fileP, splineList, height);
+
+        lastColor = splineList.color;
+    }
+
+    if (SPLINE_LIST_ARRAY_LENGTH(shape) > 0) {
+        spline_list_type const lastSplineList =
+            SPLINE_LIST_ARRAY_ELT(shape, SPLINE_LIST_ARRAY_LENGTH(shape)-1);
+
+        if (!(shape.centerline || lastSplineList.open))
+            fputs("z", fileP);
+
+        /* Close last <path> element */
+        fputs("\"/>\n", fileP);
+    }
+}
+
+
+
+int
+output_svg_writer(FILE *                    const fileP,
+                  const char *              const name,
+                  int                       const llx,
+                  int                       const lly,
+                  int                       const urx,
+                  int                       const ury, 
+                  at_output_opts_type *     const opts,
+                  at_spline_list_array_type const shape,
+                  at_msg_func                     msg_func, 
+                  void *                    const msg_data) {
+
+    int const width  = urx - llx;
+    int const height = ury - lly;
+
+    fputs("<?xml version=\"1.0\" standalone=\"yes\"?>\n", fileP);
+
+    fprintf(fileP, "<svg width=\"%d\" height=\"%d\">\n", width, height);
+
+    out_splines(fileP, shape, height);
+
+    fputs("</svg>\n", fileP);
+    
+    return 0;
+}
diff --git a/converter/other/pamtosvg/output-svg.h b/converter/other/pamtosvg/output-svg.h
new file mode 100644
index 00000000..46fc8f8f
--- /dev/null
+++ b/converter/other/pamtosvg/output-svg.h
@@ -0,0 +1,37 @@
+/* output-svg.h - output in SVG format
+
+   Copyright (C) 1999, 2000, 2001 Bernhard Herzog
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation; either version 2.1 of
+   the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+   USA. */
+
+#ifndef OUTPUT_SVG_H
+#define OUTPUT_SVG_H
+
+int
+output_svg_writer(FILE *                    const file,
+                  const char *              const name,
+                  int                       const llx,
+                  int                       const lly,
+                  int                       const urx,
+                  int                       const ury, 
+                  at_output_opts_type *     const opts,
+                  at_spline_list_array_type const shape,
+                  at_msg_func                     msg_func, 
+                  void *                    const msg_data);
+
+
+#endif /* not OUTPUT_SVG_H */
+
diff --git a/converter/other/pamtosvg/pamtosvg.c b/converter/other/pamtosvg/pamtosvg.c
new file mode 100644
index 00000000..31597a4a
--- /dev/null
+++ b/converter/other/pamtosvg/pamtosvg.c
@@ -0,0 +1,395 @@
+/* main.c: main driver for autotrace -- convert bitmaps to splines. */
+
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+
+#include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
+
+#include "autotrace.h"
+#include "message.h"
+#include "logreport.h"
+#include "output-svg.h"
+#include "bitmap.h"
+
+#define dot_printer_max_column 50
+#define dot_printer_char '|'
+
+
+
+static void
+readImageToBitmap(FILE *            const ifP,
+                  at_bitmap_type ** const bitmapPP) {
+
+    at_bitmap_type * bitmapP;
+    struct pam pam;
+    tuple ** tuples;
+    uint row;
+    tuple * row255;
+
+    MALLOCVAR_NOFAIL(bitmapP);
+
+    tuples = pnm_readpam(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
+
+    bitmapP->width  = pam.width;
+    bitmapP->height = pam.height;
+    bitmapP->np     = pam.depth;
+
+    MALLOCARRAY(bitmapP->bitmap, pam.width * pam.height * pam.depth);
+
+    row255 = pnm_allocpamrow(&pam);
+
+    for (row = 0; row < pam.height; ++row) {
+        unsigned int col;
+
+        pnm_scaletuplerow(&pam, row255, tuples[row], 255);
+        
+        for (col = 0; col < pam.width; ++col) {
+            unsigned int plane;
+
+            for (plane = 0; plane < pam.depth; ++plane) {
+                unsigned int const bitmapIndex = 
+                    (row * pam.width + col) * pam.depth + plane;
+                bitmapP->bitmap[bitmapIndex] = row255[col][plane];
+            }
+        }
+    }
+    pnm_freepamrow(row255);
+    pnm_freepamarray(tuples, &pam);
+    
+    *bitmapPP = bitmapP;
+}
+
+
+
+static void
+dotPrinter(float  const percentage,
+           void * const clientData) {
+
+    int * const currentP = (int *)clientData;
+    float const unit     = (float)1.0 / (float)(dot_printer_max_column) ;
+    int   const maximum  = (int)(percentage / unit);
+    
+    while (*currentP < maximum) {
+        fputc(dot_printer_char, stderr);
+        (*currentP)++;
+    }
+}
+
+
+
+static void
+exceptionHandler(const char * const msg,
+                 at_msg_type  const type,
+                 void *       const data) {
+
+    if (type == AT_MSG_FATAL)
+        pm_error("%s", msg);
+    else if (type == AT_MSG_WARNING)
+        pm_message("%s", msg);
+    else
+        exceptionHandler("Wrong type of msg", AT_MSG_FATAL, NULL);
+}
+
+
+
+struct cmdlineInfo {
+    const char * inputFileName;
+    float        align_threshold;
+    unsigned int backgroundSpec;
+    pixel        background_color;
+    unsigned int centerline;
+    float        corner_always_threshold;
+    unsigned int corner_surround;
+    float        corner_threshold;
+    unsigned int dpi;
+    float        error_threshold;
+    unsigned int filter_iterations;
+    float        line_reversion_threshold;
+    float        line_threshold;
+    unsigned int log;
+    unsigned int preserve_width;
+    unsigned int remove_adjacent_corners;
+    unsigned int tangent_surround;
+    unsigned int report_progress;
+    float        width_weight_factor;
+};
+
+
+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;
+
+    const char * background_colorOpt;
+  
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "align-threshold",     OPT_FLOAT,
+            &cmdlineP->align_threshold,  NULL,                              0);
+    OPTENT3(0, "background-color",    OPT_STRING,
+            &background_colorOpt,        &cmdlineP->backgroundSpec,         0);
+    OPTENT3(0, "centerline",          OPT_FLAG,
+            NULL,                        &cmdlineP->centerline,             0);
+    OPTENT3(0, "corner-always-threshold", OPT_FLOAT, 
+            &cmdlineP->corner_always_threshold, NULL,                       0);
+    OPTENT3(0, "corner-surround",     OPT_UINT,
+            &cmdlineP->corner_surround,  NULL,                              0);
+    OPTENT3(0, "corner-threshold",    OPT_FLOAT,
+            &cmdlineP->corner_threshold, NULL,                              0);
+    OPTENT3(0, "dpi",                 OPT_UINT,
+            &cmdlineP->dpi,              NULL,                              0);
+    OPTENT3(0, "error-threshold",     OPT_FLOAT,
+            &cmdlineP->error_threshold,  NULL,                              0);
+    OPTENT3(0, "filter-iterations",   OPT_UINT,
+            &cmdlineP->filter_iterations, NULL,                             0);
+    OPTENT3(0, "line-reversion-threshold", OPT_FLOAT,
+            &cmdlineP->line_reversion_threshold, NULL,                    0);
+    OPTENT3(0, "line-threshold",      OPT_FLOAT,
+            &cmdlineP->line_threshold, NULL,                                0);
+    OPTENT3(0, "log",                 OPT_FLAG,
+            NULL,                         &cmdlineP->log,                   0);
+    OPTENT3(0, "preserve-width",      OPT_FLAG,
+            NULL,                         &cmdlineP->preserve_width,        0);
+    OPTENT3(0, "remove-adjacent-corners", OPT_UINT,
+            NULL,                       &cmdlineP->remove_adjacent_corners, 0);
+    OPTENT3(0, "tangent-surround",    OPT_UINT,    
+            &cmdlineP->tangent_surround, NULL,                              0);
+    OPTENT3(0, "report-progress",     OPT_FLAG,
+            NULL,                       &cmdlineP->report_progress,         0);
+    OPTENT3(0, "width-weight-factor", OPT_FLOAT,    
+            &cmdlineP->width_weight_factor, NULL,                           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 */
+
+    /* Set some defaults the lazy way (using multiple setting of variables) */
+
+    cmdlineP->corner_always_threshold  = 60.0;
+    cmdlineP->corner_surround          = 4;
+    cmdlineP->corner_threshold         = 100.0;
+    cmdlineP->error_threshold          = 2.0;
+    cmdlineP->filter_iterations        = 4;
+    cmdlineP->line_reversion_threshold = 0.01;
+    cmdlineP->line_threshold           = 1.0;
+    cmdlineP->tangent_surround         = 3;
+    cmdlineP->width_weight_factor      = 6.0;
+
+    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (cmdlineP->backgroundSpec)
+        cmdlineP->background_color = ppm_parsecolor(background_colorOpt, 255);
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else {
+        cmdlineP->inputFileName = argv[1];
+        
+        if (argc-1 > 1)
+            pm_error("Too many arguments (%u).  The only non-option argument "
+                     "is the input file name.", argc-1);
+    }
+}
+
+
+
+static void
+fitSplines(at_bitmap_type *             const bitmapP,
+           struct cmdlineInfo           const cmdline,
+           at_msg_func                        exceptionHandler,
+           at_progress_func                   progressFunc,
+           at_spline_list_array_type ** const splinesPP) {
+
+    unsigned int progressStat;
+    at_fitting_opts_type * fittingOptsP;
+
+    progressStat = 0;
+           
+    fittingOptsP = at_fitting_opts_new();
+
+    fittingOptsP->backgroundSpec           = cmdline.backgroundSpec;
+    fittingOptsP->background_color         = cmdline.background_color;
+    fittingOptsP->corner_always_threshold  = cmdline.corner_always_threshold;
+    fittingOptsP->corner_surround          = cmdline.corner_surround;
+    fittingOptsP->corner_threshold         = cmdline.corner_threshold;
+    fittingOptsP->error_threshold          = cmdline.error_threshold;
+    fittingOptsP->filter_iterations        = cmdline.filter_iterations;
+    fittingOptsP->line_reversion_threshold = cmdline.line_reversion_threshold;
+    fittingOptsP->line_threshold           = cmdline.line_threshold;
+    fittingOptsP->remove_adjacent_corners  = cmdline.remove_adjacent_corners;
+    fittingOptsP->tangent_surround         = cmdline.tangent_surround;
+    fittingOptsP->centerline               = cmdline.centerline;
+    fittingOptsP->preserve_width           = cmdline.preserve_width;
+    fittingOptsP->width_weight_factor      = cmdline.width_weight_factor;
+
+    *splinesPP = at_splines_new_full(bitmapP, fittingOptsP,
+                                     exceptionHandler, NULL,
+                                     progressFunc, &progressStat,
+                                     NULL, NULL);
+
+    at_fitting_opts_free(fittingOptsP);
+}
+  
+
+
+static void
+writeSplines(at_spline_list_array_type * const splinesP,
+             struct cmdlineInfo          const cmdline,
+             at_output_write_func              outputWriter,
+             FILE *                      const ofP,
+             at_msg_func                       exceptionHandler) {
+
+    at_output_opts_type * outputOptsP;
+
+    outputOptsP = at_output_opts_new();
+    outputOptsP->dpi = cmdline.dpi;
+    
+    at_splines_write(outputWriter, ofP, outputOptsP,
+                     splinesP, exceptionHandler, NULL);
+
+    at_output_opts_free(outputOptsP);
+}  
+
+
+
+static const char *
+filenameRoot(const char * const filename) {
+/*----------------------------------------------------------------------------
+   Return the root of the filename.  E.g. for /home/bryanh/foo.ppm,
+   return 'foo'.
+-----------------------------------------------------------------------------*/
+    char * buffer;
+    bool foundSlash;
+    unsigned int slashPos;
+    bool foundDot;
+    unsigned int dotPos;
+    unsigned int rootStart, rootEnd;
+    unsigned int i, j;
+
+    for (i = 0, foundSlash = FALSE; i < strlen(filename); ++i) {
+        if (filename[i] == '/') {
+            foundSlash = TRUE;
+            slashPos = i;
+        }
+    }
+
+    if (foundSlash)
+        rootStart = slashPos + 1;
+    else
+        rootStart = 0;
+
+    for (i = rootStart, foundDot = FALSE; i < strlen(filename); ++i) {
+        if (filename[i] == '.') {
+            foundDot = TRUE;
+            dotPos = i;
+        }
+    }
+
+    if (foundDot)
+        rootEnd = dotPos;
+    else
+        rootEnd = strlen(filename);
+
+    MALLOCARRAY(buffer, rootEnd - rootStart + 1);
+    
+    j = 0;
+    for (i = rootStart; i < rootEnd; ++i)
+        buffer[j++] = filename[i];
+
+    buffer[j] = '\0';
+    
+    return buffer;
+}
+
+
+
+static void
+openLogFile(FILE **      const logFileP,
+            const char * const inputRootName) {
+
+    const char * logfileName;
+
+    asprintfN(&logfileName, "%s.log", inputRootName);
+
+    *logFileP = pm_openw(logfileName);
+
+    strfree(logfileName);
+}
+    
+
+
+int
+main(int argc, char * argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    at_bitmap_type * bitmapP;
+    at_spline_list_array_type * splinesP;
+    at_progress_func progressReporter;
+    const char * inputRootName;
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    inputRootName = filenameRoot(cmdline.inputFileName);
+    if (inputRootName == NULL)
+        pm_error("Can't find the root portion of file name '%s'",
+                 cmdline.inputFileName);
+    
+    if (cmdline.log)
+        openLogFile(&log_file, inputRootName);
+
+    readImageToBitmap(ifP, &bitmapP);
+    
+    if (cmdline.report_progress) {
+        progressReporter = dotPrinter;
+        fprintf(stderr, "%-15s", cmdline.inputFileName);
+    } else
+        progressReporter = NULL;
+
+    fitSplines(bitmapP, cmdline, exceptionHandler,
+               progressReporter, &splinesP);
+
+    writeSplines(splinesP, cmdline, output_svg_writer, stdout,
+                 exceptionHandler);
+
+    strfree(inputRootName);
+
+    pm_close(stdout);
+    pm_close(ifP);
+    if (cmdline.log)
+        pm_close(log_file);
+    
+    at_splines_free(splinesP);
+    at_bitmap_free(bitmapP);
+
+    if (cmdline.report_progress)
+        fputs("\n", stderr);
+    
+    return 0;
+}
diff --git a/converter/other/pamtosvg/pamtosvg.test b/converter/other/pamtosvg/pamtosvg.test
new file mode 100644
index 00000000..df3a07d3
--- /dev/null
+++ b/converter/other/pamtosvg/pamtosvg.test
@@ -0,0 +1,6 @@
+# This will print nothing if successful (diff will find no difference)
+ppmmake black 20 20 | ppmdraw -script="line 5 2 15 17" | pamtosvg | \
+  diff testline.svg -
+
+# This will print nothing if successful (diff will find no difference)
+pamtosvg ../../../../testgrid.pbm | diff testgrid.svg -
diff --git a/converter/other/pamtosvg/point.h b/converter/other/pamtosvg/point.h
new file mode 100644
index 00000000..037ec8a0
--- /dev/null
+++ b/converter/other/pamtosvg/point.h
@@ -0,0 +1,8 @@
+#ifndef POINT_H_INCLUDED
+#define POINT_H_INCLUDED
+
+typedef struct {
+  float x, y, z;
+} float_coord;
+
+#endif
diff --git a/converter/other/pamtosvg/pxl-outline.c b/converter/other/pamtosvg/pxl-outline.c
new file mode 100644
index 00000000..68cd0565
--- /dev/null
+++ b/converter/other/pamtosvg/pxl-outline.c
@@ -0,0 +1,1370 @@
+/* pxl-outline.c: find the outlines of a bitmap image; each outline is
+   made up of one or more pixels; and each pixel participates via one
+   or more edges.
+*/
+
+#include <assert.h>
+
+#include "mallocvar.h"
+
+#include "message.h"
+#include "bitmap.h"
+#include "bitmap.h"
+#include "logreport.h"
+#include "pxl-outline.h"
+
+/* We consider each pixel to consist of four edges, and we travel along
+   edges, instead of through pixel centers.  This is necessary for those
+   unfortunate times when a single pixel is on both an inside and an
+   outside outline.
+
+   The numbers chosen here are not arbitrary; the code that figures out
+   which edge to move to depends on particular values.  See the
+   `TRY_PIXEL' macro in `edge.c'.  To emphasize this, I've written in the
+   numbers we need for each edge value.  */
+
+typedef enum
+  {
+    TOP = 1, LEFT = 2, BOTTOM = 3, RIGHT = 0, NO_EDGE = 4
+  } edge_type;
+
+/* This choice is also not arbitrary: starting at the top edge makes the
+   code find outside outlines before inside ones, which is certainly
+   what we want.  */
+#define START_EDGE  top
+
+typedef enum
+  {
+    NORTH = 0, NORTHWEST = 1, WEST = 2, SOUTHWEST = 3, SOUTH = 4,
+    SOUTHEAST = 5, EAST = 6, NORTHEAST = 7
+  } direction_type;
+
+#define NUM_EDGES NO_EDGE
+
+#define COMPUTE_DELTA(axis, dir)                        \
+  ((dir) % 2 != 0                                       \
+    ? COMPUTE_##axis##_DELTA ((dir) - 1)                \
+      + COMPUTE_##axis##_DELTA (((dir) + 1) % 8)        \
+    : COMPUTE_##axis##_DELTA (dir)                      \
+  )
+
+#define COMPUTE_ROW_DELTA(dir)                          \
+  ((dir) == NORTH ? -1 : (dir) == SOUTH ? +1 : 0)
+
+#define COMPUTE_COL_DELTA(dir)                  \
+  ((dir) == WEST ? -1 : (dir) == EAST ? +1 : 0)
+
+static void append_pixel_outline (pixel_outline_list_type *,
+                                  pixel_outline_type);
+static pixel_outline_type new_pixel_outline (void);
+static void free_pixel_outline (pixel_outline_type *);
+static void concat_pixel_outline (pixel_outline_type *,
+                                  const pixel_outline_type*);
+static bool is_marked_edge (edge_type, unsigned short, unsigned short, bitmap_type);
+
+static void
+mark_edge (edge_type e, unsigned short, unsigned short, bitmap_type *);
+
+static bool is_marked_dir(unsigned short, unsigned short, direction_type, bitmap_type);
+static bool is_other_dir_marked(unsigned short, unsigned short, direction_type, bitmap_type);
+static void mark_dir(unsigned short, unsigned short, direction_type, bitmap_type *);
+
+static unsigned
+num_neighbors(unsigned short, unsigned short, bitmap_type);
+
+#define CHECK_FATAL() if (at_exception_got_fatal(exp)) goto cleanup;
+#define RETURN_IF_FATAL() if (at_exception_got_fatal(exp)) return;
+
+
+
+
+static pixel
+getBitmapColor(bitmap_type  const bitmap,
+               unsigned int const row,
+               unsigned int const col) {
+
+    pixel pix;
+    unsigned char *p = BITMAP_PIXEL (bitmap, row, col);
+  
+    if (bitmap.np >= 3)
+        PPM_ASSIGN(pix, p[0], p[1], p[2]);
+    else
+        PPM_ASSIGN(pix, p[0], p[0], p[0]);
+
+    return pix;
+}
+
+
+
+
+static void
+append_outline_pixel(pixel_outline_type * const pixelOutlineP,
+                     pm_pixelcoord        const coord) {
+/*----------------------------------------------------------------------------
+  Add a point to the pixel list.
+-----------------------------------------------------------------------------*/
+
+    O_LENGTH(*pixelOutlineP)++;
+    REALLOCARRAY_NOFAIL(pixelOutlineP->data, O_LENGTH(*pixelOutlineP));
+
+    O_COORDINATE(*pixelOutlineP, O_LENGTH(*pixelOutlineP) - 1) = coord;
+}
+
+
+
+/* We check to see if the edge of the pixel at position ROW and COL
+   is an outline edge */
+
+static bool
+is_outline_edge (edge_type edge, bitmap_type bitmap,
+                 unsigned short row, unsigned short col, pixel color,
+                 at_exception_type * exp)
+{
+  /* If this pixel isn't of the same color, it's not part of the outline. */
+  if (!PPM_EQUAL (getBitmapColor (bitmap, row, col), color))
+    return false;
+
+  switch (edge)
+    {
+    case LEFT:
+      return (bool)
+          (col == 0 ||
+           !PPM_EQUAL (getBitmapColor (bitmap, row, col - 1), color));
+
+    case TOP:
+      return (bool)
+          (row == 0 ||
+           !PPM_EQUAL (getBitmapColor (bitmap, row - 1, col), color));
+
+    case RIGHT:
+        return (bool)
+            (col == bitmap.width - 1
+             || !PPM_EQUAL(getBitmapColor(bitmap, row, col + 1), color));
+
+    case BOTTOM:
+        return (bool)
+            (row == bitmap.height - 1
+             || !PPM_EQUAL(getBitmapColor (bitmap, row + 1, col), color));
+
+    case NO_EDGE:
+    default:
+      LOG1 ("is_outline_edge: Bad edge value(%d)", edge);
+      at_exception_fatal(exp, "is_outline_edge: Bad edge value");
+    }
+
+  return false; /* NOT REACHED */
+}
+
+
+/* Is this really an edge and is it still unmarked? */
+
+static bool
+is_unmarked_outline_edge(unsigned short row,
+                         unsigned short col,
+                         edge_type edge,
+                         bitmap_type bitmap,
+                         bitmap_type marked,
+                         pixel color,
+                         at_exception_type * exp)
+{
+  return
+    (bool)(!is_marked_edge (edge, row, col, marked)
+              && is_outline_edge (edge, bitmap, row, col, color, exp));
+}
+
+
+static bool
+is_valid_dir(unsigned int   const row,
+             unsigned int   const col,
+             direction_type const dir,
+             bitmap_type    const bitmap,
+             bitmap_type    const marked) {
+  
+    return(!is_marked_dir(row, col, dir, marked)
+           && COMPUTE_DELTA(ROW, dir)+row > 0
+           && COMPUTE_DELTA(COL, dir)+col > 0
+           && BITMAP_VALID_PIXEL(bitmap,
+                                 COMPUTE_DELTA(ROW, dir) + row,
+                                 COMPUTE_DELTA(COL, dir) + col)
+           && PPM_EQUAL(getBitmapColor(bitmap,
+                                       COMPUTE_DELTA(ROW, dir) + row,
+                                       COMPUTE_DELTA(COL, dir) + col),
+                        getBitmapColor(bitmap, row, col)));
+}
+
+
+
+static bool
+next_unmarked_pixel(unsigned int *   const row,
+                    unsigned int *   const col,
+                    direction_type * const dir,
+                    bitmap_type      const bitmap,
+                    bitmap_type *    const marked) {
+
+    unsigned int   const orig_row = *row;
+    unsigned int   const orig_col = *col;
+    direction_type const orig_dir = *dir;
+
+    direction_type test_dir;
+    pixel color;
+
+    test_dir = *dir;  /* initial value */
+    color = getBitmapColor(bitmap, *row, *col);
+
+    do {
+        if (is_valid_dir(orig_row, orig_col, test_dir, bitmap, *marked)) {
+            *row = orig_row + COMPUTE_DELTA(ROW, test_dir);
+            *col = orig_col + COMPUTE_DELTA(COL, test_dir);
+            *dir = test_dir;
+            break;
+        }
+
+        if (orig_dir == test_dir)
+            test_dir = (orig_dir + 2) % 8;
+        else if ((orig_dir + 2) % 8 == test_dir)
+            test_dir = (orig_dir + 6) % 8;
+        else if ((orig_dir + 6) % 8 == test_dir)
+            test_dir = (orig_dir + 1) % 8;
+        else if ((orig_dir + 1) % 8 == test_dir)
+            test_dir = (orig_dir + 7) % 8;
+        else if ((orig_dir + 7) % 8 == test_dir)
+            test_dir = (orig_dir + 3) % 8;
+        else if ((orig_dir + 3) % 8 == test_dir)
+            test_dir = (orig_dir + 5) % 8;
+        else if ((orig_dir + 5) % 8 == test_dir)
+            break;
+    } while (1);
+
+    if ((*row != orig_row || *col != orig_col) && 
+        (!(is_other_dir_marked(orig_row, orig_col, test_dir, *marked) &&
+           is_other_dir_marked(orig_row + COMPUTE_DELTA(ROW, test_dir),
+                               orig_col + COMPUTE_DELTA(COL, test_dir),
+                               test_dir,*marked))))
+        return true;
+    else
+        return false;
+}
+
+
+
+static pixel_outline_type
+find_one_centerline(bitmap_type    const bitmap,
+                    direction_type const original_dir,
+                    unsigned int   const original_row,
+                    unsigned int   const original_col,
+                    bitmap_type *  const marked) {
+
+    direction_type search_dir;
+    unsigned int row, col;
+    pixel_outline_type outline;
+
+    outline = new_pixel_outline();
+    outline.open  = false;
+    outline.color = getBitmapColor(bitmap, row, col);
+
+    /* Add the starting pixel to the output list, changing from bitmap
+       to Cartesian coordinates and specifying the left edge so that
+       the coordinates won't be adjusted.
+    */
+    {
+        pm_pixelcoord pos;
+        pos.col = col; pos.row = bitmap.height - row - 1;
+        LOG2(" (%d,%d)", pos.col, pos.row);
+        append_outline_pixel(&outline, pos);
+    }
+    search_dir = original_dir;  /* initial value */
+    row = original_row;         /* initial value */
+    col = original_col;         /* initial values */
+
+    for ( ; ; ) {
+        unsigned int const prev_row = row;
+        unsigned int const prev_col = col;
+
+        /* If there is no adjacent, unmarked pixel, we can't proceed
+           any further, so return an open outline.
+        */
+        if (!next_unmarked_pixel(&row, &col, &search_dir, bitmap, marked)) {
+            outline.open = true;
+            break;
+        }
+
+        /* If we've moved to a new pixel, mark all edges of the previous
+           pixel so that it won't be revisited.
+        */
+        if (!(prev_row == original_row && prev_col == original_col))
+            mark_dir(prev_row, prev_col, search_dir, marked);
+        mark_dir(row, col, (search_dir + 4) % 8, marked);
+
+        /* If we've returned to the starting pixel, we're done. */
+        if (row == original_row && col == original_col)
+            break;
+
+        
+        {
+            /* Add the new pixel to the output list. */
+            pm_pixelcoord pos;
+            pos.col = col; pos.row = bitmap.height - row - 1;
+            LOG2(" (%d,%d)", pos.col, pos.row);
+            append_outline_pixel(&outline, pos);
+        }
+    }
+    mark_dir(original_row, original_col, original_dir, marked);
+
+    return outline;
+}
+
+
+
+static bool
+wrongDirection(unsigned int   const row,
+               unsigned int   const col,
+               direction_type const dir,
+               bitmap_type    const bitmap,
+               bitmap_type    const marked) {
+
+    return (!is_valid_dir(row, col, dir, bitmap, marked)
+            || (!is_valid_dir(COMPUTE_DELTA(ROW, dir) + row,
+                              COMPUTE_DELTA(COL, dir) + col,
+                              dir, bitmap, marked)
+                && num_neighbors(row, col, bitmap) > 2)
+            || num_neighbors(row, col, bitmap) > 4
+            || num_neighbors(COMPUTE_DELTA(ROW, dir) + row,
+                             COMPUTE_DELTA(COL, dir) + col, bitmap) > 4
+            || (is_other_dir_marked(row, col, dir, marked)
+                && is_other_dir_marked(row + COMPUTE_DELTA(ROW, dir),
+                                       col + COMPUTE_DELTA(COL, dir),
+                                       dir, marked)));
+}
+
+
+
+pixel_outline_list_type
+find_centerline_pixels(bitmap_type         const bitmap,
+                       pixel               const bg_color, 
+                       at_progress_func          notify_progress,
+                       void *              const progress_data,
+                       at_testcancel_func        test_cancel,
+                       void *              const testcancel_data,
+                       at_exception_type * const exp) {
+
+  pixel_outline_list_type outline_list;
+  signed short row;
+  bitmap_type marked = new_bitmap(bitmap.width, bitmap.height);
+  unsigned int const max_progress = bitmap.height * bitmap.width;
+    
+  O_LIST_LENGTH(outline_list) = 0;
+  outline_list.data = NULL;
+
+  for (row = 0; row < bitmap.height; ++row) {
+      signed short col;
+      for (col = 0; col < bitmap.width; ) {
+          bool           const clockwise = false;
+
+          direction_type dir;
+          pixel_outline_type outline;
+
+          if (notify_progress)
+              notify_progress((float)(row * bitmap.width + col) /
+                              ((float) max_progress * (float)3.0),
+                              progress_data);
+
+		  if (PPM_EQUAL(getBitmapColor(bitmap, row, col), bg_color)) {
+	          ++col;
+			  continue;
+          }
+
+          dir = EAST;
+
+          if (wrongDirection(row, col, dir, bitmap, marked)) {
+              dir = SOUTHEAST;
+              if (wrongDirection(row, col, dir, bitmap, marked)) {
+                  dir = SOUTH;
+                  if (wrongDirection(row, col, dir, bitmap, marked)) {
+                      dir = SOUTHWEST;
+                      if (wrongDirection(row, col, dir, bitmap, marked)) {
+						  ++col;
+						  continue;
+                      }
+                  }
+              }
+          }
+
+          LOG2("#%u: (%sclockwise) ", O_LIST_LENGTH(outline_list),
+               clockwise ? "" : "counter");
+
+          outline = find_one_centerline(bitmap, dir, row, col, &marked);
+
+          /* If the outline is open (i.e., we didn't return to the
+             starting pixel), search from the starting pixel in the
+             opposite direction and concatenate the two outlines.
+          */
+
+          if (outline.open) {
+              pixel_outline_type partial_outline;
+              bool okay = false;
+
+              if (dir == EAST) {
+                  dir = SOUTH;
+                  okay = is_valid_dir(row, col, dir, bitmap, marked);
+                  if (!okay) {
+                      dir = SOUTHWEST;
+                      okay = is_valid_dir(row, col, dir, bitmap, marked);
+                      if (!okay) {
+                          dir = SOUTHEAST;
+                          okay = is_valid_dir(row, col, dir, bitmap, marked);
+                      }
+                  }
+              } else if (dir == SOUTHEAST) {
+                  dir = SOUTHWEST;
+                  okay = is_valid_dir(row, col, dir, bitmap, marked);
+                  if (!okay) {
+                      dir = EAST;
+                      okay=is_valid_dir(row, col, dir, bitmap, marked);
+                      if (!okay) {
+                          dir = SOUTH;
+                          okay = is_valid_dir(row, col, dir, bitmap, marked);
+                      }
+                  }
+              } else if (dir == SOUTH) {
+                  dir = EAST;
+                  okay = is_valid_dir(row, col, dir, bitmap, marked);
+                  if (!okay) {
+                      dir = SOUTHEAST;
+                      okay = is_valid_dir(row, col, dir, bitmap, marked);
+                      if (!okay) {
+                          dir = SOUTHWEST;
+                          okay = is_valid_dir(row, col, dir, bitmap, marked);
+                      }
+                  }
+              } else if (dir == SOUTHWEST) {
+                  dir = SOUTHEAST;
+                  okay = is_valid_dir(row, col, dir, bitmap, marked);
+                  if (!okay) {
+                      dir = EAST;
+                      okay = is_valid_dir(row, col, dir, bitmap, marked);
+                      if (!okay) {
+                          dir = SOUTH;
+                          okay = is_valid_dir(row, col, dir, bitmap, marked);
+                      }
+                  }
+              }
+              if (okay) {
+                  partial_outline =
+                      find_one_centerline(bitmap, dir, row, col, &marked);
+                  concat_pixel_outline(&outline, &partial_outline);
+                  if (partial_outline.data)
+                      free(partial_outline.data);
+              } else
+                  ++col;
+          }        
+            
+          /* Outside outlines will start at a top edge, and move
+             counterclockwise, and inside outlines will start at a
+             bottom edge, and move clockwise.  This happens because of
+             the order in which we look at the edges.
+          */
+          O_CLOCKWISE(outline) = clockwise;
+          if (O_LENGTH(outline) > 1)
+              append_pixel_outline(&outline_list, outline);
+          LOG1("(%s)", (outline.open ? " open" : " closed"));
+          LOG1(" [%u].\n", O_LENGTH(outline));
+          if (O_LENGTH(outline) == 1)
+              free_pixel_outline(&outline);
+        }
+  }
+  if (test_cancel && test_cancel(testcancel_data)) {
+      if (O_LIST_LENGTH (outline_list) != 0)
+          free_pixel_outline_list (&outline_list);
+  }
+  free_bitmap(&marked);
+  flush_log_output();
+  return outline_list;
+}
+
+
+
+/* Add an outline to an outline list. */
+
+static void
+append_pixel_outline (pixel_outline_list_type *outline_list,
+                      pixel_outline_type outline)
+{
+  O_LIST_LENGTH (*outline_list)++;
+  REALLOCARRAY_NOFAIL(outline_list->data, outline_list->length);
+  O_LIST_OUTLINE (*outline_list, O_LIST_LENGTH (*outline_list) - 1) = outline;
+}
+
+
+/* Free the list of outline lists. */
+
+void
+free_pixel_outline_list (pixel_outline_list_type *outline_list)
+{
+  unsigned this_outline;
+
+  for (this_outline = 0; this_outline < outline_list->length; this_outline++)
+    {
+      pixel_outline_type o = outline_list->data[this_outline];
+      free_pixel_outline (&o);
+    }
+  outline_list->length = 0;
+
+  if (outline_list->data != NULL)
+    {
+      free (outline_list->data);
+      outline_list->data = NULL;
+    }
+
+  flush_log_output ();
+}
+
+
+/* Return an empty list of pixels.  */
+
+
+static pixel_outline_type
+new_pixel_outline (void)
+{
+  pixel_outline_type pixel_outline;
+
+  O_LENGTH (pixel_outline) = 0;
+  pixel_outline.data = NULL;
+  pixel_outline.open = false;
+
+  return pixel_outline;
+}
+
+static void
+free_pixel_outline (pixel_outline_type * outline)
+{
+  if (outline->data)
+    {
+      free (outline->data) ;
+      outline->data   = NULL;
+      outline->length = 0;
+    }
+}
+
+/* Concatenate two pixel lists. The two lists are assumed to have the
+   same starting pixel and to proceed in opposite directions therefrom. */
+
+static void
+concat_pixel_outline(pixel_outline_type *o1, const pixel_outline_type *o2)
+{
+  int src, dst;
+  unsigned o1_length, o2_length;
+  if (!o1 || !o2 || O_LENGTH(*o2) <= 1) return;
+
+  o1_length = O_LENGTH(*o1);
+  o2_length = O_LENGTH(*o2);
+  O_LENGTH(*o1) += o2_length - 1;
+  /* Resize o1 to the sum of the lengths of o1 and o2 minus one (because
+     the two lists are assumed to share the same starting pixel). */
+  REALLOCARRAY_NOFAIL(o1->data, O_LENGTH(*o1));
+  /* Shift the contents of o1 to the end of the new array to make room
+     to prepend o2. */
+  for (src = o1_length - 1, dst = O_LENGTH(*o1) - 1; src >= 0; src--, dst--)
+    O_COORDINATE(*o1, dst) = O_COORDINATE(*o1, src);
+  /* Prepend the contents of o2 (in reverse order) to o1. */
+  for (src = o2_length - 1, dst = 0; src > 0; src--, dst++)
+    O_COORDINATE(*o1, dst) = O_COORDINATE(*o2, src);
+}
+
+
+/* If EDGE is not already marked, we mark it; otherwise, it's a fatal error.
+   The position ROW and COL should be inside the bitmap MARKED. EDGE can be
+   NO_EDGE. */
+
+static void
+mark_edge (edge_type edge, unsigned short row,
+           unsigned short col, bitmap_type *marked)
+{
+  *BITMAP_PIXEL (*marked, row, col) |= 1 << edge;
+}
+
+
+/* Mark the direction of the pixel ROW/COL in MARKED. */
+
+static void
+mark_dir(unsigned short row, unsigned short col, direction_type dir, bitmap_type *marked)
+{
+  *BITMAP_PIXEL(*marked, row, col) |= 1 << dir;
+}
+
+
+/* Test if the direction of pixel at ROW/COL in MARKED is marked. */
+
+static bool
+is_marked_dir(unsigned short row, unsigned short col, direction_type dir, bitmap_type marked)
+{
+  return (bool)((*BITMAP_PIXEL(marked, row, col) & 1 << dir) != 0);
+}
+
+
+static bool
+is_other_dir_marked(unsigned short row, unsigned short col, direction_type dir, bitmap_type marked)
+{
+  return (bool)((*BITMAP_PIXEL(marked, row, col) & (255 - (1 << dir) - (1 << ((dir + 4) % 8))) ) != 0);
+}
+
+
+/* Return the number of pixels adjacent to pixel ROW/COL that are black. */
+
+static unsigned
+num_neighbors(unsigned short row, unsigned short col, bitmap_type bitmap)
+{
+    unsigned dir, count = 0;
+    pixel color = getBitmapColor(bitmap, row, col);
+    for (dir = NORTH; dir <= NORTHEAST; dir++)
+    {
+	int delta_r = COMPUTE_DELTA(ROW, dir);
+	int delta_c = COMPUTE_DELTA(COL, dir);
+	unsigned int test_row = row + delta_r;
+	unsigned int test_col = col + delta_c;
+	if (BITMAP_VALID_PIXEL(bitmap, test_row, test_col)
+	    && PPM_EQUAL(getBitmapColor(bitmap, test_row, test_col), color))
+	    ++count;
+    }
+    return count;
+}
+
+
+/* Test if the edge EDGE at ROW/COL in MARKED is marked.  */
+
+static bool
+is_marked_edge (edge_type edge, unsigned short row, unsigned short col, bitmap_type marked)
+{
+  return
+    (bool)(edge == NO_EDGE ? false : (*BITMAP_PIXEL (marked, row, col) & (1 << edge)) != 0);
+}
+
+
+
+static void
+nextClockwisePointTop(bitmap_type         const bitmap,
+                      edge_type *         const edge,
+                      unsigned int *      const row,
+                      unsigned int *      const col,
+                      pixel               const color,
+                      bitmap_type         const marked,
+                      at_exception_type * const exp,
+                      pm_pixelcoord *     const posP) {
+
+    if ((*col >= 1 && !is_marked_edge(TOP, *row, *col-1, marked) &&
+             is_outline_edge(TOP, bitmap, *row, *col-1, color, exp))) {
+
+        /* WEST */
+
+        *edge = TOP;
+        --*col;
+        posP->col = *col;
+        posP->row = bitmap.height - *row;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+
+    if ((*col >= 1 && *row >= 1 &&
+         !is_marked_edge(RIGHT, *row-1, *col-1, marked) &&
+         is_outline_edge(RIGHT, bitmap, *row-1, *col-1,
+                         color, exp)) &&
+        !(is_marked_edge(LEFT, *row-1, *col, marked) &&
+          is_marked_edge(TOP, *row,*col-1, marked)) &&
+        !(is_marked_edge(BOTTOM, *row-1, *col, marked) &&
+          is_marked_edge(RIGHT, *row, *col-1, marked))) {
+
+        /* NORTHWEST */
+
+        *edge = RIGHT;
+        --*col;
+        --*row;
+        posP->col = *col + 1;
+        posP->row = bitmap.height - *row;
+        return;
+    } 
+
+    RETURN_IF_FATAL();
+
+    if ((!is_marked_edge(LEFT, *row, *col, marked)
+         && is_outline_edge(LEFT, bitmap, *row, *col, color, exp))) {
+
+        *edge = LEFT;
+        posP->col = *col;
+        posP->row = bitmap.height - *row - 1;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+
+    *edge = NO_EDGE;
+}
+
+
+
+static void
+nextClockwisePointRight(bitmap_type         const bitmap,
+                        edge_type *         const edge,
+                        unsigned int *      const row,
+                        unsigned int *      const col,
+                        pixel               const color,
+                        bitmap_type         const marked,
+                        at_exception_type * const exp,
+                        pm_pixelcoord *     const posP) {
+
+    if ((*row >= 1 &&
+         !is_marked_edge(RIGHT, *row-1, *col, marked) &&
+         is_outline_edge(RIGHT, bitmap, *row-1, *col, color, exp))) {
+
+         /* NORTH */
+        
+        *edge = RIGHT;
+        --*row;
+        posP->col = *col+1;
+        posP->row = bitmap.height - *row;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+    
+    if ((*col+1 < marked.width && *row >= 1 &&
+         !is_marked_edge(BOTTOM, *row-1, *col+1, marked) &&
+         is_outline_edge(BOTTOM, bitmap, *row-1, *col+1,
+                         color, exp)) &&
+        !(is_marked_edge(LEFT, *row, *col+1, marked) &&
+          is_marked_edge(BOTTOM, *row-1, *col, marked)) &&
+        !(is_marked_edge(TOP, *row, *col+1, marked) &&
+          is_marked_edge(RIGHT, *row-1, *col, marked))) {
+
+        /* NORTHEAST */
+        *edge = BOTTOM;
+        ++*col;
+        --*row;
+        posP->col = *col + 1;
+        posP->row = bitmap.height - *row - 1;
+        return;
+    } 
+
+    RETURN_IF_FATAL();
+
+    if ((!is_marked_edge(TOP, *row, *col, marked) &&
+         is_outline_edge(TOP, bitmap, *row, *col, color, exp))) {
+
+        *edge = TOP;
+        posP->col = *col;
+        posP->row = bitmap.height - *row;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+
+    *edge = NO_EDGE;
+}
+
+
+
+static void
+nextClockwisePointBottom(bitmap_type         const bitmap,
+                         edge_type *         const edge,
+                         unsigned int *      const row,
+                         unsigned int *      const col,
+                         pixel               const color,
+                         bitmap_type         const marked,
+                         at_exception_type * const exp,
+                         pm_pixelcoord *     const posP) {
+    
+    if ((*col+1 < marked.width &&
+         !is_marked_edge(BOTTOM, *row, *col+1, marked) &&
+         is_outline_edge(BOTTOM, bitmap, *row, *col+1, color, exp))) {
+
+        /* EAST */
+
+        *edge = BOTTOM;
+        ++*col;
+        posP->col = *col+1;
+        posP->row = bitmap.height - *row - 1;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+
+    if ((*col + 1 < marked.width && *row+1 < marked.height &&
+         !is_marked_edge(LEFT, *row+1,*col+1, marked) &&
+         is_outline_edge(LEFT, bitmap, *row+1, *col+1, color, exp)) &&
+        !(is_marked_edge(TOP, *row+1, *col, marked) &&
+          is_marked_edge(LEFT, *row, *col+1, marked)) &&
+        !(is_marked_edge(RIGHT, *row+1, *col, marked) &&
+          is_marked_edge(BOTTOM, *row, *col+1, marked))) {
+
+        /* SOUTHEAST */
+
+        *edge = LEFT;
+        ++*col;
+        ++*row;
+        posP->col = *col;
+        posP->row = bitmap.height - *row - 1;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+
+    if ((!is_marked_edge(RIGHT, *row, *col, marked) &&
+         is_outline_edge(RIGHT, bitmap, *row, *col, color, exp))) {
+
+        *edge = RIGHT;
+        posP->col = *col+1;
+        posP->row = bitmap.height - *row;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+
+    *edge = NO_EDGE;
+}
+
+
+
+static void
+nextClockwisePointLeft(bitmap_type         const bitmap,
+                       edge_type *         const edge,
+                       unsigned int *      const row,
+                       unsigned int *      const col,
+                       pixel               const color,
+                       bitmap_type         const marked,
+                       at_exception_type * const exp,
+                       pm_pixelcoord *     const posP) {
+
+    if ((*row+1 < marked.height &&
+         !is_marked_edge(LEFT, *row+1, *col, marked) &&
+         is_outline_edge(LEFT, bitmap, *row+1, *col, color, exp))) {
+
+        /* SOUTH */
+
+        *edge = LEFT;
+        ++*row;
+        posP->col = *col;
+        posP->row = bitmap.height - *row - 1;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+
+    if ((*col >= 1 && *row+1 < marked.height &&
+         !is_marked_edge(TOP, *row+1, *col-1, marked) &&
+         is_outline_edge(TOP, bitmap, *row+1, *col-1, color, exp)) &&
+        !(is_marked_edge(RIGHT, *row, *col-1, marked) &&
+          is_marked_edge(TOP, *row+1, *col, marked)) &&
+        !(is_marked_edge(BOTTOM, *row, *col-1, marked) &&
+          is_marked_edge(LEFT, *row+1, *col, marked))) {
+        
+        /* SOUTHWEST */
+        
+        *edge = TOP;
+        --*col;
+        ++*row;
+        posP->col = *col;
+        posP->row = bitmap.height - *row;
+        return;
+    } 
+
+    RETURN_IF_FATAL();
+
+    if ((!is_marked_edge(BOTTOM, *row, *col, marked) &&
+         is_outline_edge(BOTTOM, bitmap, *row, *col, color, exp))) {
+        *edge = BOTTOM;
+        posP->col = *col+1;
+        posP->row = bitmap.height - *row - 1;
+        return;
+    }
+    RETURN_IF_FATAL();
+
+    *edge = NO_EDGE;
+}
+
+
+
+static void
+nextClockwisePoint(bitmap_type         const bitmap,
+                   edge_type *         const edge,
+                   unsigned int *      const row,
+                   unsigned int *      const col,
+                   pixel               const color,
+                   bitmap_type         const marked,
+                   at_exception_type * const exp,
+                   pm_pixelcoord *     const posP) {
+    
+    switch (*edge) {
+    case TOP:
+        nextClockwisePointTop(  bitmap, edge, row, col, color,
+                                marked, exp, posP);
+        break;
+    case RIGHT: 
+        nextClockwisePointRight(bitmap, edge, row, col, color,
+                                marked, exp, posP);
+        break;
+    case BOTTOM: 
+        nextClockwisePointBottom(bitmap, edge, row, col, color,
+                                 marked, exp, posP);
+        break;
+    case LEFT: 
+        nextClockwisePointLeft(  bitmap, edge, row, col, color,
+                                 marked, exp, posP);
+        break;
+    case NO_EDGE:
+        break;
+    default:
+        *edge = NO_EDGE;
+        break;
+    }
+}
+
+
+
+static void
+nextCcwPointTop(bitmap_type         const bitmap,
+                edge_type *         const edge,
+                unsigned int *      const row,
+                unsigned int *      const col,
+                pixel               const color,
+                bitmap_type         const marked,
+                at_exception_type * const exp,
+                pm_pixelcoord *     const posP) {
+
+    if ((!is_marked_edge(LEFT, *row, *col, marked) &&
+         is_outline_edge(LEFT,bitmap,*row,*col, color, exp))) {
+
+        *edge = LEFT;
+        posP->col = *col;
+        posP->row = bitmap.height - *row - 1;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+
+    if ((*col >= 1 &&
+         !is_marked_edge(TOP, *row, *col-1, marked) &&
+         is_outline_edge(TOP, bitmap, *row, *col-1, color, exp))) {
+
+        /* WEST */
+
+        *edge = TOP;
+        --*col;
+        posP->col = *col;
+        posP->row = bitmap.height - *row;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+    
+    if ((*col >= 1 && *row >= 1 &&
+         !is_marked_edge(RIGHT, *row-1, *col-1, marked) &&
+         is_outline_edge(RIGHT, bitmap, *row-1, *col-1, color, exp))) {
+
+        /* NORTHWEST */
+
+        *edge = RIGHT;
+        --*col;
+        --*row;
+        posP->col = *col + 1;
+        posP->row = bitmap.height - *row;
+        return;
+    } 
+
+    RETURN_IF_FATAL();
+
+    *edge = NO_EDGE;
+}
+
+
+
+static void
+nextCcwPointRight(bitmap_type         const bitmap,
+                  edge_type *         const edge,
+                  unsigned int *      const row,
+                  unsigned int *      const col,
+                  pixel               const color,
+                  bitmap_type         const marked,
+                  at_exception_type * const exp,
+                  pm_pixelcoord *     const posP) {
+
+    if ((!is_marked_edge(TOP, *row, *col, marked) &&
+         is_outline_edge(TOP, bitmap, *row, *col, color, exp))) {
+
+        *edge = TOP;
+        posP->col = *col;
+        posP->row = bitmap.height - *row;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+
+    if ((*row >= 1 &&
+         !is_marked_edge(RIGHT, *row-1, *col, marked) &&
+         is_outline_edge(RIGHT, bitmap, *row-1, *col, color, exp))) {
+
+        /* NORTH */
+        
+        *edge = RIGHT;
+        --*row;
+        posP->col = *col + 1;
+        posP->row = bitmap.height - *row;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+
+    if ((*col + 1 < marked.width && *row >= 1 &&
+         !is_marked_edge(BOTTOM, *row-1, *col+1, marked) &&
+         is_outline_edge(BOTTOM, bitmap, *row-1, *col+1, color, exp))) {
+
+        /* NORTHEAST */
+
+        *edge = BOTTOM;
+        ++*col;
+        ++*row;
+        posP->col = *col + 1;
+        posP->row = bitmap.height - *row - 1;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+
+    *edge = NO_EDGE;
+}
+
+
+
+static void
+nextCcwPointBottom(bitmap_type         const bitmap,
+                   edge_type *         const edge,
+                   unsigned int *      const row,
+                   unsigned int *      const col,
+                   pixel               const color,
+                   bitmap_type         const marked,
+                   at_exception_type * const exp,
+                   pm_pixelcoord *     const posP) {
+
+    if ((!is_marked_edge(RIGHT, *row, *col, marked) &&
+         is_outline_edge(RIGHT, bitmap, *row, *col, color, exp))) {
+
+        *edge = RIGHT;
+        posP->col = *col + 1;
+        posP->row = bitmap.height - *row;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+
+    if ((*col + 1 < marked.width &&
+         !is_marked_edge(BOTTOM, *row, *col+1, marked) &&
+         is_outline_edge(BOTTOM, bitmap, *row, *col+1, color, exp))) {
+
+        /* EAST */
+
+        *edge = BOTTOM;
+        ++*col;
+        posP->col = *col + 1;
+        posP->row = bitmap.height - *row - 1;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+
+    if ((*col + 1 < marked.width && *row + 1 < marked.height &&
+         !is_marked_edge(LEFT, *row+1, *col+1, marked) &&
+         is_outline_edge(LEFT, bitmap, *row+1, *col+1, color, exp))) {
+
+        /* SOUTHEAST */
+
+        *edge = LEFT;
+        ++*col;
+        ++*row;
+        posP->col = *col;
+        posP->row = bitmap.height - *row - 1;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+    
+    *edge = NO_EDGE;
+}
+
+
+
+static void
+nextCcwPointLeft(bitmap_type         const bitmap,
+                 edge_type *         const edge,
+                 unsigned int *      const row,
+                 unsigned int *      const col,
+                 pixel               const color,
+                 bitmap_type         const marked,
+                 at_exception_type * const exp,
+                 pm_pixelcoord *     const posP) {
+
+
+    if ((!is_marked_edge(BOTTOM, *row, *col, marked) &&
+         is_outline_edge(BOTTOM, bitmap, *row, *col, color, exp))) {
+
+        *edge = BOTTOM;
+        posP->col = *col + 1;
+        posP->row = bitmap.height - *row - 1;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+
+    if ((*row + 1 < marked.height &&
+         !is_marked_edge(LEFT, *row+1, *col, marked) &&
+         is_outline_edge(LEFT, bitmap, *row+1, *col, color, exp))) {
+
+        /* SOUTH */
+
+        *edge = LEFT;
+        ++*row;
+        posP->col = *col;
+        posP->row = bitmap.height - *row - 1;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+    
+    if ((*col >= 1 && *row + 1 < marked.height &&
+         !is_marked_edge(TOP, *row+1, *col-1, marked) &&
+         is_outline_edge(TOP, bitmap, *row+1, *col-1, color, exp))) {
+
+        /* SOUTHWEST */
+
+        *edge = TOP;
+        --*col;
+        ++*row;
+        posP->col = *col;
+        posP->row = bitmap.height - *row;
+        return;
+    }
+
+    RETURN_IF_FATAL();
+
+    *edge = NO_EDGE;
+}
+
+static void
+nextCounterClockwisePoint(bitmap_type         const bitmap,
+                          edge_type *         const edge,
+                          unsigned int *      const row,
+                          unsigned int *      const col,
+                          pixel               const color,
+                          bitmap_type         const marked,
+                          at_exception_type * const exp,
+                          pm_pixelcoord *     const posP) {
+
+    switch (*edge) {
+    case TOP:
+        nextCcwPointTop(   bitmap, edge, row, col, color, marked, exp, posP);
+        break;
+    case RIGHT: 
+        nextCcwPointRight( bitmap, edge, row, col, color, marked, exp, posP);
+        break;
+    case BOTTOM: 
+        nextCcwPointBottom(bitmap, edge, row, col, color, marked, exp, posP);
+        break;
+    case LEFT: 
+        nextCcwPointLeft(  bitmap, edge, row, col, color, marked, exp, posP);
+        break;
+    case NO_EDGE:
+        break;
+    default: 
+        *edge = NO_EDGE;
+        break;
+    }
+}
+
+
+
+static void
+nextPoint(bitmap_type         const bitmap,
+          edge_type *         const edge,
+          unsigned int *      const row,
+          unsigned int *      const col,
+          pm_pixelcoord *     const nextPointP,
+          pixel               const color,
+          bool                const clockwise,
+          bitmap_type         const marked,
+          at_exception_type * const exp) {
+
+    if (!clockwise)
+        nextClockwisePoint(       bitmap, edge, row, col, color,
+                                  marked, exp, nextPointP);
+    else
+        nextCounterClockwisePoint(bitmap, edge, row, col, color,
+                                  marked, exp, nextPointP);
+}
+
+
+
+static pixel_outline_type
+find_one_outline(bitmap_type         const bitmap,
+                 edge_type           const originalEdge,
+                 unsigned int        const originalRow,
+                 unsigned int        const originalCol,
+                 bitmap_type *       const marked,
+                 bool                const clockwise,
+                 bool                const ignore,
+                 at_exception_type * const exp) {
+/*----------------------------------------------------------------------------
+  Calculate one single outline.  We pass the position of the
+  starting pixel and the starting edge.  We mark all edges we track along
+  and append the outline pixels to the coordinate list.
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+    unsigned int col;
+    edge_type    edge;
+
+    pixel_outline_type outline;
+    pm_pixelcoord pos;
+    
+    outline = new_pixel_outline();
+    outline.color = getBitmapColor(bitmap, originalRow, originalCol);
+
+    row  = originalRow;   /* initial value */
+    col  = originalCol;   /* initial value */
+    edge = originalEdge;  /* initial value */
+
+    /* Set initial position */
+    pos.col = col + ((edge == RIGHT) || (edge == BOTTOM) ? 1 : 0);
+    pos.row = bitmap.height - row - 1 + 
+        (edge == TOP || edge == RIGHT ? 1 : 0);
+
+    do {
+        /* Put this edge into the output list */
+        if (!ignore) {
+            LOG2(" (%d,%d)", pos.col, pos.row);
+            append_outline_pixel(&outline, pos);
+        }
+        
+        mark_edge(edge, row, col, marked);
+        nextPoint(bitmap, &edge, &row, &col, &pos, outline.color, clockwise,
+                  *marked, exp);
+            /* edge, row, and col are both input and output in the above */
+    } while (edge != NO_EDGE && !at_exception_got_fatal(exp));
+
+    if (ignore || at_exception_got_fatal(exp))
+        free_pixel_outline(&outline);
+
+    return outline;
+}
+
+
+
+pixel_outline_list_type
+find_outline_pixels(bitmap_type         const bitmap,
+                    bool                const bg_spec,
+                    pixel               const bg_color, 
+                    at_progress_func          notify_progress,
+                    void *              const progress_data,
+                    at_testcancel_func        test_cancel,
+                    void *              const testcancel_data,
+                    at_exception_type * const exp) {
+/*----------------------------------------------------------------------------
+   Return a list of outlines in the image whose raster is 'bitmap'.
+
+   The background color of the image is 'bg_color' if 'bg_spec' is true;
+   otherwise, there is no background color.
+-----------------------------------------------------------------------------*/
+    /* We go through a bitmap TOP to BOTTOM, LEFT to RIGHT, looking for
+       each pixel with an unmarked edge that we consider a starting point
+       of an outline.  When we find one, we trace the outline and add it
+       to the list, marking the edges in it as we go.
+    */
+    unsigned int const max_progress = bitmap.height * bitmap.width;
+    
+    pixel_outline_list_type outline_list;
+    unsigned int row;
+    bitmap_type marked;
+    
+    marked = new_bitmap (bitmap.width, bitmap.height);
+    
+    O_LIST_LENGTH(outline_list) = 0;
+    outline_list.data = NULL;
+    
+    for (row = 0; row < bitmap.height; ++row) {
+        unsigned int col;
+        for (col = 0; col < bitmap.width; ++col) {
+            pixel const color = getBitmapColor(bitmap, row, col);
+            bool const is_background =
+                bg_spec && PPM_EQUAL(color, bg_color);
+
+            if (notify_progress)
+                notify_progress((float)(row * bitmap.width + col) /
+                                ((float) max_progress * (float)3.0),
+                                progress_data);
+
+            /* A valid edge can be TOP for an outside outline.
+               Outside outlines are traced counterclockwise.
+            */
+
+            if (!is_background &&
+                is_unmarked_outline_edge(row, col, TOP,
+                                         bitmap, marked, color, exp)) {
+                pixel_outline_type outline;
+                
+                CHECK_FATAL();   /* FREE(DONE) outline_list */
+                
+                LOG1("#%u: (counterclockwise)", O_LIST_LENGTH(outline_list));
+                
+                outline = find_one_outline(bitmap, TOP, row, col, &marked,
+                                           false, false, exp);
+                CHECK_FATAL();    /* FREE(DONE) outline_list */
+                
+                O_CLOCKWISE(outline) = false;
+                append_pixel_outline(&outline_list, outline);
+                
+                LOG1(" [%u].\n", O_LENGTH (outline));
+            } else
+                CHECK_FATAL ();	/* FREE(DONE) outline_list */
+
+            /* A valid edge can be BOTTOM for an inside outline.
+               Inside outlines are traced clockwise.
+            */
+            if (row > 0) {
+                pixel const colorAbove = getBitmapColor(bitmap, row-1, col);
+                if (!(bg_spec && PPM_EQUAL(colorAbove, bg_color)) &&
+                    is_unmarked_outline_edge(row-1, col, BOTTOM,
+                                             bitmap, marked, colorAbove,exp)) {
+                    CHECK_FATAL(); /* FREE(DONE) outline_list */
+                    
+                    /* This lines are for debugging only:*/
+                    if (is_background) {
+                        pixel_outline_type outline;
+                    
+                        LOG1("#%u: (clockwise)", O_LIST_LENGTH(outline_list));
+                        
+                        outline = find_one_outline(bitmap, BOTTOM, row-1, col,
+                                                   &marked, true, false, exp);
+                        CHECK_FATAL(); /* FREE(DONE) outline_list */
+                        
+                        O_CLOCKWISE(outline) = true;
+                        append_pixel_outline(&outline_list, outline);
+                        
+                        LOG1(" [%u].\n", O_LENGTH(outline));
+                    } else {
+                        find_one_outline(bitmap, BOTTOM, row-1, col,
+                                         &marked, true, true, exp);
+                        CHECK_FATAL(); /* FREE(DONE) outline_list */
+                    }
+                } else
+                    CHECK_FATAL();	/* FREE(DONE) outline_list */
+            }
+            if (test_cancel && test_cancel(testcancel_data)) {
+                free_pixel_outline_list(&outline_list);
+                goto cleanup;
+            }
+        }
+    }
+ cleanup:
+    free_bitmap(&marked);
+    flush_log_output();
+    if (at_exception_got_fatal(exp))
+        free_pixel_outline_list(&outline_list);
+
+    return outline_list;
+}
+
diff --git a/converter/other/pamtosvg/pxl-outline.h b/converter/other/pamtosvg/pxl-outline.h
new file mode 100644
index 00000000..e37ccaf6
--- /dev/null
+++ b/converter/other/pamtosvg/pxl-outline.h
@@ -0,0 +1,79 @@
+/* pxl-outline.h: find a list of outlines which make up one character. */
+
+#ifndef PXL_OUTLINE_H
+#define PXL_OUTLINE_H
+
+#include "ppm.h"
+
+#include "autotrace.h"
+#include "exception.h"
+#include "bitmap.h"
+
+/* This is a list of contiguous points on the bitmap.  */
+typedef struct
+{
+  pm_pixelcoord * data;
+  unsigned length;
+  bool clockwise;
+  pixel color;
+  bool open;
+} pixel_outline_type;
+
+
+/* The Nth coordinate in the list.  */
+#define O_COORDINATE(p_o, n) ((p_o).data[n])
+
+
+/* The length of the list.  */
+#define O_LENGTH(p_o) ((p_o).length)
+
+/* Whether the outline moves clockwise or counterclockwise.  */
+#define O_CLOCKWISE(p_o) ((p_o).clockwise)
+
+/* Since a pixel outline is cyclic, the index of the next coordinate
+   after the last is the first, and the previous coordinate before the
+   first is the last.  */
+#define O_NEXT(p_o, n) (((n) + 1) % O_LENGTH (p_o))
+#define O_PREV(p_o, n) ((n) == 0				\
+                         ? O_LENGTH (p_o) - 1			\
+                         : (n) - 1)
+
+/* And the character turns into a list of such lists.  */
+typedef struct
+{
+  pixel_outline_type *data;
+  unsigned length;
+} pixel_outline_list_type;
+
+/* The Nth list in the list of lists.  */
+#define O_LIST_OUTLINE(p_o_l, n) ((p_o_l).data[n])
+
+/* The length of the list of lists.  */
+#define O_LIST_LENGTH(p_o_l) ((p_o_l).length)
+
+/* Find all pixels on the outline in the character C.  */
+pixel_outline_list_type
+find_outline_pixels (bitmap_type         const type,
+                     bool                const bg_spec,
+                     pixel               const bg_color, 
+                     at_progress_func          notify_progress,
+                     void *              const progress_data,
+                     at_testcancel_func        test_cancel,
+                     void *              const testcancel_data,
+                     at_exception_type * const exp);
+
+/* Find all pixels on the center line of the character C.  */
+pixel_outline_list_type
+find_centerline_pixels (bitmap_type         const type,
+                        pixel               const bg_color, 
+                        at_progress_func          notify_progress,
+                        void *              const progress_data,
+                        at_testcancel_func        test_cancel,
+                        void *              const testcancel_data,
+                        at_exception_type * const exp);
+
+/* Free the memory in the list.  */
+extern void
+free_pixel_outline_list (pixel_outline_list_type *);
+
+#endif /* not PXL_OUTLINE_H */
diff --git a/converter/other/pamtosvg/spline.c b/converter/other/pamtosvg/spline.c
new file mode 100644
index 00000000..5bdf0c0e
--- /dev/null
+++ b/converter/other/pamtosvg/spline.c
@@ -0,0 +1,193 @@
+/* spline.c: spline and spline list (represented as arrays) manipulation. */
+
+#include <assert.h>
+
+#include "mallocvar.h"
+
+#include "message.h"
+#include "point.h"
+#include "spline.h"
+#include "vector.h"
+
+/* Print a spline in human-readable form.  */
+
+void
+print_spline (FILE *f, spline_type s)
+{
+  assert(SPLINE_DEGREE (s) == LINEARTYPE || SPLINE_DEGREE (s) == CUBICTYPE);
+
+  if (SPLINE_DEGREE (s) == LINEARTYPE)
+    fprintf (f, "(%.3f,%.3f)--(%.3f,%.3f).\n",
+                START_POINT (s).x, START_POINT (s).y,
+                END_POINT (s).x, END_POINT (s).y);
+
+  else if (SPLINE_DEGREE (s) == CUBICTYPE)
+    fprintf (f, "(%.3f,%.3f)..ctrls(%.3f,%.3f)&(%.3f,%.3f)..(%.3f,%.3f).\n",
+                START_POINT (s).x, START_POINT (s).y,
+                CONTROL1 (s).x, CONTROL1 (s).y,
+                CONTROL2 (s).x, CONTROL2 (s).y,
+                END_POINT (s).x, END_POINT (s).y);
+}
+
+
+/* Evaluate the spline S at a given T value.  This is an implementation
+   of de Casteljau's algorithm.  See Schneider's thesis, p.37.
+   The variable names are taken from there.  */
+
+float_coord
+evaluate_spline (spline_type s, float t)
+{
+  spline_type V[4];    /* We need degree+1 splines, but assert degree <= 3.  */
+  signed i, j;
+  float one_minus_t = (float) 1.0 - t;
+  polynomial_degree degree = SPLINE_DEGREE (s);
+
+  for (i = 0; i <= degree; i++)
+    {
+      V[0].v[i].x = s.v[i].x;
+      V[0].v[i].y = s.v[i].y;
+      V[0].v[i].z = s.v[i].z;
+    }
+
+  for (j = 1; j <= degree; j++)
+    for (i = 0; i <= degree - j; i++)
+      {
+        float_coord t1 = Pmult_scalar (V[j - 1].v[i], one_minus_t);
+        float_coord t2 = Pmult_scalar (V[j - 1].v[i + 1], t);
+        float_coord temp = Padd (t1, t2);
+        V[j].v[i].x = temp.x;
+        V[j].v[i].y = temp.y;
+        V[j].v[i].z = temp.z;
+      }
+
+  return V[degree].v[0];
+}
+
+
+/* Return a new, empty, spline list.  */
+
+spline_list_type *
+new_spline_list (void)
+{
+  spline_list_type *answer;
+
+  MALLOCVAR(answer);
+  *answer = empty_spline_list();
+  return answer;
+}
+
+spline_list_type 
+empty_spline_list (void)
+{
+  spline_list_type answer;
+  SPLINE_LIST_DATA (answer) = NULL;
+  SPLINE_LIST_LENGTH (answer) = 0;
+  return answer;
+}
+
+/* Return a new spline list with SPLINE as the first element.  */
+
+spline_list_type *
+new_spline_list_with_spline (spline_type spline)
+{
+  spline_list_type *answer;
+
+  answer = new_spline_list();
+  MALLOCVAR(SPLINE_LIST_DATA(*answer));
+  SPLINE_LIST_ELT (*answer, 0) = spline;
+  SPLINE_LIST_LENGTH (*answer) = 1;
+
+  return answer;
+}
+
+
+/* Free the storage in a spline list.  We don't have to free the
+   elements, since they are arrays in automatic storage.  And we don't
+   want to free the list if it was empty.  */
+
+void
+free_spline_list (spline_list_type spline_list)
+{
+  if (SPLINE_LIST_DATA (spline_list) != NULL)
+    free (SPLINE_LIST_DATA (spline_list));
+}
+
+
+/* Append the spline S to the list SPLINE_LIST.  */
+
+void
+append_spline (spline_list_type *l, spline_type s)
+{
+  assert (l != NULL);
+
+  SPLINE_LIST_LENGTH (*l)++;
+  REALLOCARRAY(SPLINE_LIST_DATA(*l), SPLINE_LIST_LENGTH(*l));
+  LAST_SPLINE_LIST_ELT (*l) = s;
+}
+
+
+/* Tack the elements in the list S2 onto the end of S1.
+   S2 is not changed.  */
+
+void
+concat_spline_lists (spline_list_type *s1, spline_list_type s2)
+{
+  unsigned this_spline;
+  unsigned new_length;
+
+  assert (s1 != NULL);
+
+  new_length = SPLINE_LIST_LENGTH (*s1) + SPLINE_LIST_LENGTH (s2);
+
+  REALLOCARRAY_NOFAIL(SPLINE_LIST_DATA(*s1), new_length);
+
+  for (this_spline = 0; this_spline < SPLINE_LIST_LENGTH (s2); this_spline++)
+    SPLINE_LIST_ELT (*s1, SPLINE_LIST_LENGTH (*s1)++)
+      = SPLINE_LIST_ELT (s2, this_spline);
+}
+
+
+/* Return a new, empty, spline list array.  */
+
+spline_list_array_type
+new_spline_list_array (void)
+{
+  spline_list_array_type answer;
+
+  SPLINE_LIST_ARRAY_DATA (answer) = NULL;
+  SPLINE_LIST_ARRAY_LENGTH (answer) = 0;
+
+  return answer;
+}
+
+
+/* Free the storage in a spline list array.  We don't
+   want to free the list if it is empty.  */
+void
+free_spline_list_array (spline_list_array_type *spline_list_array)
+{
+  unsigned this_list;
+
+  for (this_list = 0;
+       this_list < SPLINE_LIST_ARRAY_LENGTH (*spline_list_array);
+       this_list++)
+    free_spline_list (SPLINE_LIST_ARRAY_ELT (*spline_list_array, this_list));
+
+  if (SPLINE_LIST_ARRAY_DATA (*spline_list_array) != NULL)
+    free (SPLINE_LIST_ARRAY_DATA (*spline_list_array));
+
+  flush_log_output ();
+}
+
+
+/* Append the spline S to the list SPLINE_LIST_ARRAY.  */
+
+void
+append_spline_list (spline_list_array_type *l, spline_list_type s)
+{
+  SPLINE_LIST_ARRAY_LENGTH (*l)++;
+  REALLOCARRAY_NOFAIL(SPLINE_LIST_ARRAY_DATA(*l),
+                      SPLINE_LIST_ARRAY_LENGTH(*l));
+  LAST_SPLINE_LIST_ARRAY_ELT (*l) = s;
+}
+
diff --git a/converter/other/pamtosvg/spline.h b/converter/other/pamtosvg/spline.h
new file mode 100644
index 00000000..05a56e23
--- /dev/null
+++ b/converter/other/pamtosvg/spline.h
@@ -0,0 +1,90 @@
+/* spline.h: manipulate the spline representation.  */
+
+#ifndef SPLINE_H
+#define SPLINE_H
+
+#include <stdio.h>
+
+#include "point.h"
+#include "autotrace.h"
+
+typedef at_polynomial_degree polynomial_degree;
+typedef at_spline_type spline_type;
+
+#define LINEARTYPE          AT_LINEARTYPE
+#define QUADRATICTYPE       AT_QUADRATICTYPE
+#define CUBICTYPE           AT_CUBICTYPE
+#define PARALLELELLIPSETYPE AT_PARALLELELLIPSETYPE
+#define ELLIPSETYPE         AT_ELLIPSETYPE
+#define CIRCLETYPE          AT_CIRCLETYPE
+
+#define START_POINT(spl)        ((spl).v[0])
+#define CONTROL1(spl)           ((spl).v[1])
+#define CONTROL2(spl)           ((spl).v[2])
+#define END_POINT(spl)          ((spl).v[3])
+#define SPLINE_DEGREE(spl)      ((spl).degree)
+#define SPLINE_LINEARITY(spl)   ((spl).linearity)
+
+#ifndef _IMPORTING
+/* Print a spline on the given file.  */
+extern void print_spline (FILE *, spline_type);
+
+/* Evaluate SPLINE at the given T value.  */
+extern float_coord evaluate_spline (spline_type spline, float t);
+#endif
+
+/* Each outline in a character is typically represented by many
+   splines.  So, here is a list structure for that:  */
+typedef at_spline_list_type spline_list_type;
+
+
+/* An empty list will have length zero (and null data).  */
+#define SPLINE_LIST_LENGTH(spll)  ((spll).length)
+
+/* The address of the beginning of the array of data.  */
+#define SPLINE_LIST_DATA(spll)    ((spll).data)
+
+/* The element with index 'index' in S_L.  */
+#define SPLINE_LIST_ELT(spll, index) ((spll).data[index])
+
+/* The last element in S_L.  */
+#define LAST_SPLINE_LIST_ELT(s_l) \
+  (SPLINE_LIST_DATA (s_l)[SPLINE_LIST_LENGTH (s_l) - 1])
+
+/* The previous and next elements to INDEX in S_L.  */
+#define NEXT_SPLINE_LIST_ELT(s_l, index)                \
+  SPLINE_LIST_ELT (s_l, ((index) + 1) % SPLINE_LIST_LENGTH (s_l))
+#define PREV_SPLINE_LIST_ELT(s_l, index)                \
+  SPLINE_LIST_ELT (s_l, index == 0                  \
+                        ? SPLINE_LIST_LENGTH (s_l) - 1          \
+                        : index - 1)
+
+#ifndef _IMPORTING
+/* Construct and destroy new `spline_list_type' objects.  */
+extern spline_list_type *new_spline_list (void); /* Allocate new memory */
+extern spline_list_type empty_spline_list (void); /* No allocation */
+extern spline_list_type *new_spline_list_with_spline (spline_type);
+extern void free_spline_list (spline_list_type);
+
+/* Append the spline S to the list S_LIST.  */
+extern void append_spline (spline_list_type *s_list, spline_type s);
+
+/* Append the elements in list S2 to S1, changing S1.  */
+extern void concat_spline_lists (spline_list_type *s1, spline_list_type s2);
+#endif
+
+typedef at_spline_list_array_type spline_list_array_type;
+
+/* Turns out we can use the same definitions for lists of lists as for
+   just lists.  But we define the usual names, just in case.  */
+#define SPLINE_LIST_ARRAY_LENGTH(spll) ((spll).length)
+#define SPLINE_LIST_ARRAY_DATA     SPLINE_LIST_DATA
+#define SPLINE_LIST_ARRAY_ELT(spll, index) ((spll).data[index])
+#define LAST_SPLINE_LIST_ARRAY_ELT LAST_SPLINE_LIST_ELT
+
+extern spline_list_array_type new_spline_list_array (void);
+extern void append_spline_list (spline_list_array_type *, spline_list_type);
+extern void free_spline_list_array (spline_list_array_type *);
+
+#endif /* not SPLINE_H */
+
diff --git a/converter/other/pamtosvg/testgrid.svg b/converter/other/pamtosvg/testgrid.svg
new file mode 100644
index 00000000..e82c5551
--- /dev/null
+++ b/converter/other/pamtosvg/testgrid.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" standalone="yes"?>
+<svg width="14" height="16">
+<path style="fill:#000000; stroke:none;" d="M0 0L0 16L14 16L14 1L13 0L0 0z"/>
+<path style="fill:#ffffff; stroke:none;" d="M1 0L2 1L1 0M3 0L4 1L3 0M5 0L6 1L5 0M7 0L8 1L7 0M9 0L10 1L9 0M11 0L12 1L11 0M13 0L14 1L13 0M1 2L2 3L1 2M3 2L4 3L3 2M5 2L6 3L5 2M7 2L8 3L7 2M9 2L10 3L9 2M11 2L12 3L11 2M13 2L14 3L13 2M1 4L2 5L1 4M3 4L4 5L3 4M5 4L6 5L5 4M7 4L8 5L7 4M9 4L10 5L9 4M11 4L12 5L11 4M13 4L14 5L13 4M1 6L2 7L1 6M3 6L4 7L3 6M5 6L6 7L5 6M7 6L8 7L7 6M9 6L10 7L9 6M11 6L12 7L11 6M13 6L14 7L13 6M1 8L2 9L1 8M3 8L4 9L3 8M5 8L6 9L5 8M7 8L8 9L7 8M9 8L10 9L9 8M11 8L12 9L11 8M13 8L14 9L13 8M1 10L2 11L1 10M3 10L4 11L3 10M5 10L6 11L5 10M7 10L8 11L7 10M9 10L10 11L9 10M11 10L12 11L11 10M13 10L14 11L13 10M1 12L2 13L1 12M3 12L4 13L3 12M5 12L6 13L5 12M7 12L8 13L7 12M9 12L10 13L9 12M11 12L12 13L11 12M13 12L14 13L13 12M1 14L2 15L1 14M3 14L4 15L3 14M5 14L6 15L5 14M7 14L8 15L7 14M9 14L10 15L9 14M11 14L12 15L11 14M13 14L14 15L13 14z"/>
+</svg>
diff --git a/converter/other/pamtosvg/testline.svg b/converter/other/pamtosvg/testline.svg
new file mode 100644
index 00000000..e704ce74
--- /dev/null
+++ b/converter/other/pamtosvg/testline.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" standalone="yes"?>
+<svg width="20" height="20">
+<path style="fill:#000000; stroke:none;" d="M0 0L0 20L20 20L20 0L0 0z"/>
+<path style="fill:#ffffff; stroke:none;" d="M5 2L15 18L16 18L5 2z"/>
+</svg>
diff --git a/converter/other/pamtosvg/thin-image.c b/converter/other/pamtosvg/thin-image.c
new file mode 100644
index 00000000..40ced794
--- /dev/null
+++ b/converter/other/pamtosvg/thin-image.c
@@ -0,0 +1,373 @@
+/* thin-image.c: thin binary image
+
+   Copyright (C) 2001, 2002 Martin Weber
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation; either version 2.1 of
+   the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+   USA. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "mallocvar.h"
+
+#include "thin-image.h"
+#include "logreport.h"
+#include "message.h"
+#include "bitmap.h"
+ 
+#define PIXEL_SET(p, new)  ((void)memcpy((p), (new), sizeof(Pixel)))
+#define PIXEL_EQUAL(p1, p2) \
+    ((p1)[0] == (p2)[0] && (p1)[1] == (p2)[1] && (p1)[2] == (p2)[2])
+
+ 
+typedef unsigned char Pixel[3];  /* RGB pixel data type */ 
+
+ 
+void thin3(bitmap_type *image, Pixel colour); 
+void thin1(bitmap_type *image, unsigned char colour); 
+ 
+ 
+/* -------------------------------- ThinImage - Thin binary image. --------------------------- * 
+ *                                                            
+ *    Description:                                                    
+ *        Thins the supplied binary image using Rosenfeld's parallel   
+ *        thinning algorithm.                                         
+ *                                                                     
+ *    On Entry:                                                        
+ *        image = Image to thin.                                       
+ *                                                                     
+ * -------------------------------------------------------------------------------------------- */ 
+ 
+ 
+/* Direction masks:                  */ 
+/*   N     S     W        E            */ 
+static        unsigned int     masks[]         = { 0200, 0002, 0040, 0010 }; 
+ 
+/*    True if pixel neighbor map indicates the pixel is 8-simple and  */ 
+/*    not an end point and thus can be deleted.  The neighborhood     */ 
+/*    map is defined as an integer of bits abcdefghi with a non-zero  */ 
+/*    bit representing a non-zero pixel.  The bit assignment for the  */ 
+/*    neighborhood is:                                                */ 
+/*                                                                    */ 
+/*                            a b c                                   */ 
+/*                            d e f                                   */ 
+/*                            g h i                                   */ 
+ 
+static        unsigned char   todelete[512] = { 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+              0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+              0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+              0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+              0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+              1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+              1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+              1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+              1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+              1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+              1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+              1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 
+              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+              1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 
+
+static pixel background;
+
+
+void
+thin_image(bitmap_type *image, bool bgSpec, pixel bg,
+           at_exception_type * exp)
+{ 
+    /* This is nasty as we need to call thin once for each  
+     * colour in the image the way I do this is to keep a second  
+     * copy of the bitmap and to use this to keep 
+     * track of which colours have not yet been processed, 
+     * trades time for pathological case memory.....*/ 
+    long m, n, num_pixels;
+    bitmap_type bm; 
+    unsigned int const spp = image->np;
+	unsigned int const width = image->width;
+	unsigned int const height = image->height;
+
+    if (bgSpec)
+        background = bg;
+    else 
+        PPM_ASSIGN(background, 255, 255, 255);
+
+    /* Clone the image */
+    bm.height = image->height;
+    bm.width = image->width;
+    bm.np = image->np;
+    MALLOCARRAY(bm.bitmap, height * width * spp); 
+    if (bm.bitmap == NULL)
+        pm_error("Unable to get memory for thin image bitmap clone");
+    memcpy(bm.bitmap, image->bitmap, height * width * spp); 
+
+    num_pixels = height * width;
+    switch (spp)
+    {
+	case 3:
+	{
+	    Pixel *ptr = (Pixel*)bm.bitmap;
+	    Pixel bg_color;
+	    bg_color[0] = PPM_GETR(background);
+	    bg_color[1] = PPM_GETG(background);
+	    bg_color[2] = PPM_GETB(background);
+
+	    for (n = num_pixels - 1; n >= 0L; --n)
+	    {
+		Pixel p;
+
+		PIXEL_SET(p, ptr[n]);
+		if (!PIXEL_EQUAL(p, bg_color))
+		{ 
+		    /* we have a new colour in the image */ 
+		    LOG3("Thinning colour (%x, %x, %x)\n", p[0], p[1], p[2]);
+		    for (m = n - 1; m >= 0L; --m)
+		    {
+			if (PIXEL_EQUAL(ptr[m], p))
+			    PIXEL_SET(ptr[m], bg_color);
+		    }
+		    thin3(image, p); 
+		} 
+	    } 
+	    break;
+	} 
+
+	case 1:
+	{
+	    unsigned char * const ptr = bm.bitmap;
+	    unsigned char bg_color;
+
+	    if (PPM_ISGRAY(background))
+            bg_color = PPM_GETR(background);
+	    else
+            bg_color = PPM_LUMIN(background);
+
+	    for (n = num_pixels - 1; n >= 0L; --n)
+	    {
+		unsigned char c = ptr[n];
+		if (c != bg_color)
+		{ 
+		    LOG1 ("Thinning colour %x\n", c);
+		    for (m = n - 1; m >= 0L; --m)
+			if (ptr[m] == c) ptr[m] = bg_color;
+		    thin1(image, c); 
+		} 
+	    } 
+	    break;
+	} 
+
+	default:
+	{
+	  LOG1 ("thin_image: %u-plane images are not supported", spp);
+	  at_exception_fatal(exp, "thin_image: wrong plane images are passed");
+	  goto cleanup;
+	}
+    }
+ cleanup:
+    free (bm.bitmap); 
+} 
+
+ 
+void thin3(bitmap_type *image, Pixel colour) 
+{ 
+      Pixel *ptr, *y_ptr, *y1_ptr;
+      Pixel bg_color;
+      unsigned int    xsize, ysize;   /* Image resolution             */ 
+      unsigned int    x, y;           /* Pixel location               */ 
+      unsigned int    i;              /* Pass index           */ 
+      unsigned int    pc      = 0;    /* Pass count           */ 
+      unsigned int    count   = 1;    /* Deleted pixel count          */ 
+      unsigned int    p, q;           /* Neighborhood maps of adjacent*/ 
+                                      /* cells                        */ 
+      unsigned char   *qb;            /* Neighborhood maps of previous*/ 
+                                      /* scanline                     */ 
+      unsigned int    m;              /* Deletion direction mask      */ 
+ 
+      bg_color[0] = PPM_GETR(background);
+      bg_color[1] = PPM_GETG(background);
+      bg_color[2] = PPM_GETB(background);
+
+      LOG (" Thinning image.....\n "); 
+      xsize = image->width;
+      ysize = image->height;
+      MALLOCARRAY_NOFAIL(qb, xsize); 
+      qb[xsize-1] = 0;                /* Used for lower-right pixel   */ 
+      ptr = (Pixel*)image->bitmap;
+ 
+      while ( count ) {               /* Scan image while deletions   */ 
+          pc++; 
+          count = 0; 
+ 
+          for ( i = 0 ; i < 4 ; i++ ) { 
+ 
+              m = masks[i]; 
+ 
+              /* Build initial previous scan buffer.                  */ 
+              p = PIXEL_EQUAL(ptr[0], colour); 
+              for ( x = 0 ; x < xsize-1 ; x++ ) 
+                  qb[x] = (unsigned char) (p = ((p<<1)&0006) | (unsigned int) PIXEL_EQUAL(ptr[x+1],
+				   colour)); 
+ 
+              /* Scan image for pixel deletion candidates.            */ 
+	      y_ptr = ptr; y1_ptr = ptr + xsize; 
+              for (y = 0; y < ysize - 1; y++, y_ptr += xsize, y1_ptr += xsize)
+	      { 
+                  q = qb[0]; 
+                  p = ((q<<2)&0330) | (unsigned int) PIXEL_EQUAL(y1_ptr[0], colour); 
+ 
+                  for ( x = 0 ; x < xsize-1 ; x++ ) { 
+                      q = qb[x]; 
+                      p = ((p<<1)&0666) | ((q<<3)&0110) | 
+			  (unsigned int) PIXEL_EQUAL(y1_ptr[x+1], colour);
+                      qb[x] = (unsigned char) p; 
+                      if ((i != 2 || x != 0) && ((p&m) == 0) && todelete[p] ) { 
+                          count++;  /* delete the pixel */ 
+			  PIXEL_SET(y_ptr[x], bg_color);
+                      } 
+                  } 
+ 
+                  /* Process right edge pixel.                        */ 
+                  p = (p<<1)&0666; 
+                  if  (i != 3 && (p&m) == 0 && todelete[p] ) { 
+                      count++; 
+		      PIXEL_SET(y_ptr[xsize-1], bg_color);
+                  } 
+              } 
+ 
+	      if (i != 1)
+	      {
+            /* Process bottom scan line.                            */ 
+            q = qb[0]; 
+            p = ((q<<2)&0330); 
+
+            y_ptr = ptr + xsize * (ysize - 1);
+            for ( x = 0 ; x < xsize ; x++ ) { 
+              q = qb[x]; 
+              p = ((p<<1)&0666) | ((q<<3)&0110); 
+              if ((i != 2 || x != 0) && (p&m) == 0 && todelete[p]) { 
+                count++; 
+                PIXEL_SET(y_ptr[x], bg_color);
+		      } 
+            } 
+           }
+          } 
+          LOG2 ("ThinImage: pass %d, %d pixels deleted\n", pc, count); 
+      } 
+      free (qb); 
+} 
+
+ 
+void thin1(bitmap_type *image, unsigned char colour) 
+{ 
+      unsigned char *ptr, *y_ptr, *y1_ptr;
+      unsigned char bg_color;
+      unsigned int    xsize, ysize;   /* Image resolution             */ 
+      unsigned int    x, y;           /* Pixel location               */ 
+      unsigned int    i;              /* Pass index           */ 
+      unsigned int    pc      = 0;    /* Pass count           */ 
+      unsigned int    count   = 1;    /* Deleted pixel count          */ 
+      unsigned int    p, q;           /* Neighborhood maps of adjacent*/ 
+                                      /* cells                        */ 
+      unsigned char   *qb;            /* Neighborhood maps of previous*/ 
+                                      /* scanline                     */ 
+      unsigned int    m;              /* Deletion direction mask      */ 
+
+      if (PPM_ISGRAY(background))
+          bg_color = PPM_GETR(background);
+      else
+          bg_color = PPM_LUMIN(background);
+
+      LOG (" Thinning image.....\n "); 
+      xsize = image->width;
+      ysize = image->height;
+      MALLOCARRAY_NOFAIL(qb, xsize); 
+      qb[xsize-1] = 0;                /* Used for lower-right pixel   */ 
+      ptr = image->bitmap;
+ 
+      while ( count ) {               /* Scan image while deletions   */ 
+          pc++; 
+          count = 0; 
+ 
+          for ( i = 0 ; i < 4 ; i++ ) { 
+ 
+              m = masks[i]; 
+ 
+              /* Build initial previous scan buffer.                  */ 
+              p = (ptr[0] == colour); 
+              for ( x = 0 ; x < xsize-1 ; x++ ) 
+                  qb[x] = (unsigned char) (p = ((p<<1)&0006) | (unsigned int)(ptr[x+1] == colour)); 
+ 
+              /* Scan image for pixel deletion candidates.            */ 
+	      y_ptr = ptr; y1_ptr = ptr + xsize; 
+              for (y = 0; y < ysize - 1; y++, y_ptr += xsize, y1_ptr += xsize)
+	      { 
+                  q = qb[0]; 
+                  p = ((q<<2)&0330) | (y1_ptr[0] == colour); 
+ 
+                  for ( x = 0 ; x < xsize-1 ; x++ ) { 
+                      q = qb[x]; 
+                      p = ((p<<1)&0666) | ((q<<3)&0110) | (unsigned int) (y1_ptr[x+1]==colour); 
+                      qb[x] = (unsigned char) p; 
+                      if  ( ((p&m) == 0) && todelete[p] ) { 
+                          count++; 
+			  y_ptr[x] = bg_color;  /* delete the pixel */ 
+                      } 
+                  } 
+ 
+                  /* Process right edge pixel.                        */ 
+                  p = (p<<1)&0666; 
+                  if  ( (p&m) == 0 && todelete[p] ) { 
+                      count++; 
+                      y_ptr[xsize-1] = bg_color;
+                  } 
+              } 
+ 
+              /* Process bottom scan line.                            */ 
+	      q = qb[0]; 
+	      p = ((q<<2)&0330); 
+ 
+	      y_ptr = ptr + xsize * (ysize - 1);
+              for ( x = 0 ; x < xsize ; x++ ) { 
+                  q = qb[x]; 
+                  p = ((p<<1)&0666) | ((q<<3)&0110); 
+                  if  ( (p&m) == 0 && todelete[p] ) { 
+                      count++; 
+                      y_ptr[x] = bg_color;
+                  } 
+              } 
+          } 
+          LOG2("thin1: pass %d, %d pixels deleted\n", pc, count); 
+      } 
+      free (qb); 
+} 
diff --git a/converter/other/pamtosvg/thin-image.h b/converter/other/pamtosvg/thin-image.h
new file mode 100644
index 00000000..4e0a77ee
--- /dev/null
+++ b/converter/other/pamtosvg/thin-image.h
@@ -0,0 +1,37 @@
+/* thin-image.h: thin binary image
+
+   Copyright (C) 2001, 2002 Martin Weber
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation; either version 2.1 of
+   the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+   USA. */
+
+#ifndef THIN_IMAGE_H
+#define THIN_IMAGE_H
+
+/*
+ * C code from the article
+ * "Efficient Binary Image Thinning using Neighborhood Maps"
+ * by Joseph M. Cychosz, 3ksnn64@ecn.purdue.edu
+ * in "Graphics Gems IV", Academic Press, 1994
+ */
+
+#include "bitmap.h"
+#include "exception.h"
+
+void
+thin_image(bitmap_type *image, bool bg_spec, pixel bg_color,
+           at_exception_type * exp);
+
+#endif /* not THIN_IMAGE_H */
diff --git a/converter/other/pamtosvg/vector.c b/converter/other/pamtosvg/vector.c
new file mode 100644
index 00000000..559163fc
--- /dev/null
+++ b/converter/other/pamtosvg/vector.c
@@ -0,0 +1,254 @@
+/* vector.c: vector/point operations. */
+
+#include <math.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+
+#include "pm_c_util.h"
+
+#include "vector.h"
+#include "message.h"
+#include "epsilon-equal.h"
+
+static float acos_d (float, at_exception_type * excep);
+
+
+/* Given the point COORD, return the corresponding vector.  */
+
+vector_type
+make_vector (const float_coord c)
+{
+  vector_type v;
+
+  v.dx = c.x;
+  v.dy = c.y;
+  v.dz = c.z;
+
+  return v;
+}
+
+
+/* And the converse: given a vector, return the corresponding point.  */
+
+float_coord
+vector_to_point (const vector_type v)
+{
+  float_coord coord;
+
+  coord.x = v.dx;
+  coord.y = v.dy;
+
+  return coord;
+}
+
+
+float
+magnitude (const vector_type v)
+{
+  return (float) sqrt (v.dx * v.dx + v.dy * v.dy + v.dz * v.dz);
+}
+
+
+vector_type
+normalize (const vector_type v)
+{
+  vector_type new_v;
+  float m = magnitude (v);
+
+  /* assert (m > 0.0); */
+
+  if (m > 0.0)
+  {
+    new_v.dx = v.dx / m;
+    new_v.dy = v.dy / m;
+    new_v.dz = v.dz / m;
+  }
+  else
+  {
+	new_v.dx = v.dx;
+    new_v.dy = v.dy;
+    new_v.dz = v.dz;
+  }
+
+  return new_v;
+}
+
+
+vector_type
+Vadd (const vector_type v1, const vector_type v2)
+{
+  vector_type new_v;
+
+  new_v.dx = v1.dx + v2.dx;
+  new_v.dy = v1.dy + v2.dy;
+  new_v.dz = v1.dz + v2.dz;
+
+  return new_v;
+}
+
+
+float
+Vdot (const vector_type v1, const vector_type v2)
+{
+  return v1.dx * v2.dx + v1.dy * v2.dy + v1.dz * v2.dz;
+}
+
+
+vector_type
+Vmult_scalar (const vector_type v, const float r)
+{
+  vector_type new_v;
+
+  new_v.dx = v.dx * r;
+  new_v.dy = v.dy * r;
+  new_v.dz = v.dz * r;
+
+  return new_v;
+}
+
+
+/* Given the IN_VECTOR and OUT_VECTOR, return the angle between them in
+   degrees, in the range zero to 180.  */
+
+float
+Vangle (const vector_type in_vector, 
+	const vector_type out_vector,
+	at_exception_type * exp)
+{
+  vector_type v1 = normalize (in_vector);
+  vector_type v2 = normalize (out_vector);
+
+  return acos_d (Vdot (v2, v1), exp);
+}
+
+
+float_coord
+Vadd_point (const float_coord c, const vector_type v)
+{
+  float_coord new_c;
+
+  new_c.x = c.x + v.dx;
+  new_c.y = c.y + v.dy;
+  new_c.z = c.z + v.dz;
+  return new_c;
+}
+
+
+float_coord
+Vsubtract_point (const float_coord c, const vector_type v)
+{
+  float_coord new_c;
+
+  new_c.x = c.x - v.dx;
+  new_c.y = c.y - v.dy;
+  new_c.z = c.z - v.dz;
+  return new_c;
+}
+
+
+pm_pixelcoord
+Vadd_int_point(pm_pixelcoord const c,
+               vector_type   const v) {
+
+    pm_pixelcoord a;
+
+    a.col = ROUND ((float) c.col + v.dx);
+    a.row = ROUND ((float) c.row + v.dy);
+    
+    return a;
+}
+
+
+vector_type
+Vabs (const vector_type v)
+{
+  vector_type new_v;
+
+  new_v.dx = (float) fabs (v.dx);
+  new_v.dy = (float) fabs (v.dy);
+  new_v.dz = (float) fabs (v.dz);
+  return new_v;
+}
+
+
+/* Operations on points.  */
+
+float_coord
+Padd (const float_coord coord1, const float_coord coord2)
+{
+  float_coord sum;
+
+  sum.x = coord1.x + coord2.x;
+  sum.y = coord1.y + coord2.y;
+  sum.z = coord1.z + coord2.z;
+
+  return sum;
+}
+
+
+float_coord
+Pmult_scalar (const float_coord coord, const float r)
+{
+  float_coord answer;
+
+  answer.x = coord.x * r;
+  answer.y = coord.y * r;
+  answer.z = coord.z * r;
+
+  return answer;
+}
+
+
+vector_type
+Psubtract (const float_coord c1, const float_coord c2)
+{
+  vector_type v;
+
+  v.dx = c1.x - c2.x;
+  v.dy = c1.y - c2.y;
+  v.dz = c1.z - c2.z;
+
+  return v;
+}
+
+
+
+/* Operations on integer points.  */
+
+vector_type
+IPsubtract(pm_pixelcoord const coord1,
+           pm_pixelcoord const coord2) {
+
+    vector_type v;
+
+    v.dx = (int) (coord1.col - coord2.col);
+    v.dy = (int) (coord1.row - coord2.row);
+    v.dz = 0.0;
+    
+    return v;
+}
+
+
+
+static float
+acos_d (float v, at_exception_type * excep)
+{
+  float a;
+
+  if (epsilon_equal (v, 1.0))
+    v = 1.0;
+  else if (epsilon_equal (v, -1.0))
+    v = -1.0;
+
+  errno = 0;
+  a = (float) acos (v);
+  if (errno == ERANGE || errno == EDOM)
+    {
+      at_exception_fatal(excep, strerror(errno));
+      return 0.0;
+    }
+  
+  
+  return a * (float) 180.0 / (float) M_PI;
+}
diff --git a/converter/other/pamtosvg/vector.h b/converter/other/pamtosvg/vector.h
new file mode 100644
index 00000000..74fb2fd2
--- /dev/null
+++ b/converter/other/pamtosvg/vector.h
@@ -0,0 +1,71 @@
+/* vector.h: operations on vectors and points. */
+
+#ifndef VECTOR_H
+#define VECTOR_H
+
+#include "point.h"
+#include "exception.h"
+
+/* Our vectors are represented as displacements along the x and y axes.  */
+
+typedef struct
+{
+  float dx, dy, dz;
+} vector_type;
+
+
+/* Consider a point as a vector from the origin.  */
+extern vector_type make_vector (const float_coord);
+
+/* And a vector as a point, i.e., a displacement from the origin.  */
+extern float_coord vector_to_point (const vector_type);
+
+
+/* Definitions for these common operations can be found in any decent
+   linear algebra book, and most calculus books.  */
+
+extern float magnitude (const vector_type);
+extern vector_type normalize (const vector_type);
+
+extern vector_type Vadd (const vector_type, const vector_type);
+extern float Vdot (const vector_type, const vector_type);
+extern vector_type Vmult_scalar (const vector_type, const float);
+extern float Vangle (const vector_type in, const vector_type out, at_exception_type * exp);
+
+/* These operations could have been named `P..._vector' just as well as
+   V..._point, so we may as well allow both names.  */
+#define Padd_vector Vadd_point
+extern float_coord Vadd_point
+  (const float_coord, const vector_type);
+
+#define Psubtract_vector Vsubtract_point
+extern float_coord Vsubtract_point
+  (const float_coord, const vector_type);
+
+/* This returns the rounded sum.  */
+#define IPadd_vector Vadd_int_point
+
+pm_pixelcoord
+Vadd_int_point(pm_pixelcoord const c,
+               vector_type   const v);
+
+/* Take the absolute value of both components.  */
+extern vector_type Vabs (const vector_type);
+
+/* Operations on points with real coordinates.  It is not orthogonal,
+   but more convenient, to have the subtraction operator return a
+   vector, and the addition operator return a point.  */
+extern vector_type Psubtract
+  (const float_coord, const float_coord);
+
+vector_type
+IPsubtract(pm_pixelcoord const coord1,
+           pm_pixelcoord const coord2);
+
+/* These are heavily used in spline fitting.  */
+extern float_coord Padd (const float_coord,
+                                  const float_coord);
+extern float_coord Pmult_scalar (const float_coord, const float);
+
+#endif
+
diff --git a/converter/other/pamtotga.c b/converter/other/pamtotga.c
new file mode 100644
index 00000000..1e0808ed
--- /dev/null
+++ b/converter/other/pamtotga.c
@@ -0,0 +1,572 @@
+/* pamtotga.c - read a portable pixmap and produce a TrueVision Targa file
+**
+** Copyright (C) 1989, 1991 by Mark Shand 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.
+*/
+
+#define _BSD_SOURCE  /* Make sure string.h contains strdup() */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include <string.h>
+
+#include "pam.h"
+#include "pammap.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "tga.h"
+
+/* Max number of colors allowed for colormapped output. */
+#define MAXCOLORS 256
+
+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 */
+    char *outName;
+    enum TGAbaseImageType imgType;
+    bool defaultFormat;
+    unsigned int norle;
+};
+
+
+
+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.
+-----------------------------------------------------------------------------*/
+    optStruct3 opt;  /* set by OPTENT3 */
+    optEntry *option_def = malloc(100*sizeof(optEntry));
+    unsigned int option_def_index;
+
+    unsigned int outNameSpec;
+    unsigned int cmap, mono, rgb;
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "name",       OPT_STRING, 
+            &cmdlineP->outName, &outNameSpec, 0);
+    OPTENT3(0,   "cmap",       OPT_FLAG, 
+            NULL, &cmap, 0);
+    OPTENT3(0,   "mono",       OPT_FLAG, 
+            NULL, &mono, 0);
+    OPTENT3(0,   "rgb",        OPT_FLAG, 
+            NULL, &rgb, 0);
+    OPTENT3(0,   "norle",      OPT_FLAG, 
+            NULL, &cmdlineP->norle, 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 (cmap + mono + rgb > 1)
+        pm_error("You may specify only one of -cmap, -mono, and -rgb.");
+
+    if (cmap + mono + rgb == 0)
+        cmdlineP->defaultFormat = TRUE;
+    else {
+        cmdlineP->defaultFormat = FALSE;
+    
+        if (cmap)
+            cmdlineP->imgType = TGA_MAP_TYPE;
+        else if (mono)
+            cmdlineP->imgType = TGA_MONO_TYPE;
+        else if (rgb)
+            cmdlineP->imgType = TGA_RGB_TYPE;
+    }
+
+    if (!outNameSpec)
+        cmdlineP->outName = NULL;
+    
+    if (argc-1 == 0) 
+        cmdlineP->inputFilespec = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
+    else
+        cmdlineP->inputFilespec = argv[1];
+
+}
+
+
+static void
+writeTgaHeader(struct ImageHeader const tgaHeader) {
+
+    unsigned char flags;
+
+    putchar(tgaHeader.IdLength);
+    putchar(tgaHeader.CoMapType);
+    putchar(tgaHeader.ImgType);
+    putchar(tgaHeader.Index_lo);
+    putchar(tgaHeader.Index_hi);
+    putchar(tgaHeader.Length_lo);
+    putchar(tgaHeader.Length_hi);
+    putchar(tgaHeader.CoSize);
+    putchar(tgaHeader.X_org_lo);
+    putchar(tgaHeader.X_org_hi);
+    putchar(tgaHeader.Y_org_lo);
+    putchar(tgaHeader.Y_org_hi);
+    putchar(tgaHeader.Width_lo);
+    putchar(tgaHeader.Width_hi);
+    putchar(tgaHeader.Height_lo);
+    putchar(tgaHeader.Height_hi);
+    putchar(tgaHeader.PixelSize);
+    flags = (tgaHeader.AttBits & 0xf) | 
+        ((tgaHeader.Rsrvd & 0x1) << 4) |
+        ((tgaHeader.OrgBit & 0x1) << 5) | 
+        ((tgaHeader.OrgBit & 0x3) << 6);
+    putchar(flags);
+
+    if (tgaHeader.IdLength > 0)
+        fwrite(tgaHeader.Id, 1, (int) tgaHeader.IdLength, stdout);
+}
+    
+
+
+static void
+putPixel(struct pam *          const pamP,
+         tuple                 const tuple, 
+         enum TGAbaseImageType const imgType, 
+         bool                  const withAlpha,
+         tuplehash             const cht) {
+/*----------------------------------------------------------------------------
+   Write a single pixel of the TGA raster to Standard Output.  The
+   pixel is to have color 'tuple'.  The raster has format 'imgType'.
+   The color palette from which the specified color is to be drawn, if
+   'imgType' indicates use of a color palette, is 'cht'.
+-----------------------------------------------------------------------------*/
+    if (imgType == TGA_MAP_TYPE) {
+        int retval;
+        int found;
+
+        pnm_lookuptuple(pamP, cht, tuple, &found, &retval);
+        if (!found)
+            pm_error("Internal error: color not found in map that was "
+                     "generated from all the colors in the image");
+        putchar(retval);
+    } else {
+        if (imgType == TGA_RGB_TYPE && pamP->depth < 3) {
+            /* Make RGB pixel out of a single input plane */
+            unsigned int plane;
+            
+            for (plane = 0; plane < 3; ++plane) 
+                putchar(pnm_scalesample(tuple[0], 
+                                        pamP->maxval, TGA_MAXVAL));
+        } else if (imgType == TGA_MONO_TYPE)
+            putchar(pnm_scalesample(tuple[0], 
+                                    pamP->maxval, TGA_MAXVAL));
+        else {
+            putchar(pnm_scalesample(tuple[PAM_BLU_PLANE], 
+                                    pamP->maxval, TGA_MAXVAL));
+            putchar(pnm_scalesample(tuple[PAM_GRN_PLANE], 
+                                    pamP->maxval, TGA_MAXVAL));
+            putchar(pnm_scalesample(tuple[PAM_RED_PLANE], 
+                                    pamP->maxval, TGA_MAXVAL));
+            if (withAlpha)
+                putchar(pnm_scalesample(tuple[PAM_TRN_PLANE], 
+                                        pamP->maxval, TGA_MAXVAL));
+        }
+    }
+}
+
+
+
+static void
+putMapEntry(struct pam * const pamP, 
+            tuple        const value, 
+            int          const size) {
+
+    if (size == 15 || size == 16) {
+        /* 5 bits each of red, green, and blue.  Watch for byte order */
+
+        tuple const tuple31 = pnm_allocpamtuple(pamP);
+
+        pnm_scaletuple(pamP, tuple31, value, 31);
+        {
+            int const mapentry = 
+                tuple31[PAM_BLU_PLANE] << 0 |
+                tuple31[PAM_GRN_PLANE] << 5 |
+                tuple31[PAM_RED_PLANE] << 10;
+            
+            putchar(mapentry % 256);
+            putchar(mapentry / 256);
+        }
+        pnm_freepamtuple(tuple31);
+    } else if (size == 8)
+        putchar(pnm_scalesample(value[0], 
+                                pamP->maxval, TGA_MAXVAL));
+    else {
+        /* Must be 24 or 32 */
+        putchar(pnm_scalesample(value[PAM_BLU_PLANE], 
+                                pamP->maxval, TGA_MAXVAL));
+        putchar(pnm_scalesample(value[PAM_GRN_PLANE], 
+                                pamP->maxval, TGA_MAXVAL));
+        putchar(pnm_scalesample(value[PAM_RED_PLANE], 
+                                    pamP->maxval, TGA_MAXVAL));
+        if (size == 32)
+            putchar(pnm_scalesample(value[PAM_TRN_PLANE], 
+                                    pamP->maxval, TGA_MAXVAL));
+    }
+}
+
+
+
+static void
+computeRunlengths(struct pam * const pamP, 
+                   tuple *      const tuplerow, 
+                   int *        const runlength) {
+
+    int col, start;
+
+    /* Initialize all run lengths to 0.  (This is just an error check.) */
+    for (col = 0; col < pamP->width; ++col)
+        runlength[col] = 0;
+    
+    /* Find runs of identical pixels. */
+    for ( col = 0; col < pamP->width; ) {
+        start = col;
+        do {
+            ++col;
+        } while ( col < pamP->width &&
+                  col - start < 128 &&
+                  pnm_tupleequal(pamP, tuplerow[col], tuplerow[start]));
+        runlength[start] = col - start;
+    }
+    
+    /* Now look for runs of length-1 runs, and turn them into negative runs. */
+    for (col = 0; col < pamP->width; ) {
+        if (runlength[col] == 1) {
+            start = col;
+            while (col < pamP->width &&
+                   col - start < 128 &&
+                   runlength[col] == 1 ) {
+                runlength[col] = 0;
+                ++col;
+            }
+            runlength[start] = - ( col - start );
+        } else
+            col += runlength[col];
+    }
+}
+
+
+
+static void
+computeOutName(struct cmdlineInfo const cmdline, 
+               const char ** const outNameP) {
+    
+    char * workarea;
+
+    if (cmdline.outName)
+        workarea = strdup(cmdline.outName);
+    else if (streq(cmdline.inputFilespec, "-"))
+        workarea = NULL;
+    else {
+        char * cp;
+        workarea = strdup(cmdline.inputFilespec);
+        cp = strchr(workarea, '.');
+        if (cp != NULL)
+        	*cp = '\0';	/* remove extension */
+    }
+    
+    if (workarea == NULL)
+        *outNameP = NULL;
+    else {
+        /* Truncate the name to fit TGA specs */
+        if (strlen(workarea) > IMAGEIDFIELDMAXSIZE)
+            workarea[IMAGEIDFIELDMAXSIZE] = '\0';
+        *outNameP = workarea;
+    }
+}
+
+
+
+static void
+validateTupleType(struct pam * const pamP) {
+
+    if (streq(pamP->tuple_type, "RGB_ALPHA")) {
+        if (pamP->depth < 4)
+            pm_error("Invalid depth for tuple type RGB_ALPHA.  "
+                     "Should have at least 4 planes, but has %d.", 
+                     pamP->depth);
+    } else if (streq(pamP->tuple_type, "RGB")) {
+        if (pamP->depth < 3)
+            pm_error("Invalid depth for tuple type RGB.  "
+                     "Should have at least 3 planes, but has %d.", 
+                     pamP->depth);
+    } else if (streq(pamP->tuple_type, "GRAYSCALE")) {
+    } else if (streq(pamP->tuple_type, "BLACKANDWHITE")) {
+    } else 
+        pm_error("Invalid type of input.  PAM tuple type is '%s'.  "
+                 "This programs understands only RGB_ALPHA, RGB, GRAYSCALE, "
+                 "and BLACKANDWHITE.", pamP->tuple_type);
+}
+
+
+
+static void
+computeImageType_cht(struct pam *            const pamP,
+                     struct cmdlineInfo      const cmdline, 
+                     tuple **                const tuples,
+                     enum TGAbaseImageType * const baseImgTypeP,
+                     bool *                  const withAlphaP,
+                     tupletable *            const chvP,
+                     tuplehash *             const chtP, 
+                     int *                   const ncolorsP) {
+
+    unsigned int ncolors;
+    enum TGAbaseImageType baseImgType;
+    bool withAlpha;
+
+    validateTupleType(pamP);
+
+    withAlpha = (streq(pamP->tuple_type, "RGB_ALPHA"));
+
+    if (cmdline.defaultFormat) {
+        /* default the image type */
+        if (withAlpha) {
+            baseImgType = TGA_RGB_TYPE;
+            *chvP = NULL;
+        } else if (pamP->depth > 1) {
+            pm_message("computing colormap...");
+            *chvP = 
+                pnm_computetuplefreqtable(pamP, tuples, MAXCOLORS, &ncolors);
+            if (*chvP == NULL) {
+                pm_message("Too many colors for colormapped TGA.  Doing RGB.");
+                baseImgType = TGA_RGB_TYPE;
+            } else 
+                baseImgType = TGA_MAP_TYPE;
+        } else {
+            baseImgType = TGA_MONO_TYPE;
+            *chvP = NULL;
+        }
+    } else {
+        baseImgType = cmdline.imgType;
+
+        if (baseImgType == TGA_MAP_TYPE) {
+            if (withAlpha)
+                pm_error("Can't do a colormap because image has transparency "
+                         "information");
+            pm_message("computing colormap...");
+            *chvP = 
+                pnm_computetuplefreqtable(pamP, tuples, MAXCOLORS, &ncolors);
+            if (*chvP == NULL) 
+                pm_error("Too many colors for colormapped TGA.  "
+                         "Use 'pnmquant %d' to reduce the number of colors.", 
+                         MAXCOLORS);
+        } else
+            *chvP = NULL;
+        if (baseImgType == TGA_MONO_TYPE && pamP->depth > 1)
+            pm_error("For Mono TGA output, input must be "
+                     "GRAYSCALE or BLACKANDWHITE PAM or PBM or PGM");
+    }
+    
+    if (baseImgType == TGA_MAP_TYPE) {
+        pm_message("%d colors found.", ncolors);
+        /* Make a hash table for fast color lookup. */
+        *chtP = pnm_computetupletablehash(pamP, *chvP, ncolors);
+    } else
+        *chtP = NULL;
+
+    *baseImgTypeP = baseImgType;
+    *withAlphaP   = withAlpha;
+    *ncolorsP     = ncolors;
+}
+
+
+
+static void
+computeTgaHeader(struct pam *          const pamP,
+                 enum TGAbaseImageType const baseImgType,
+                 bool                  const withAlpha,
+                 bool                  const rle, 
+                 int                   const ncolors,
+                 unsigned char         const orgBit,
+                 const char *          const id,
+                 struct ImageHeader *  const tgaHeaderP) {
+
+    if (rle) {
+        switch (baseImgType ) {
+        case TGA_MONO_TYPE: tgaHeaderP->ImgType = TGA_RLEMono;          break;
+        case TGA_MAP_TYPE:  tgaHeaderP->ImgType = TGA_RLEMap;           break;
+        case TGA_RGB_TYPE:  tgaHeaderP->ImgType = TGA_RLERGB;           break;
+        }
+    } else {
+        switch(baseImgType) {
+        case TGA_MONO_TYPE: tgaHeaderP->ImgType = TGA_Mono;          break;
+        case TGA_MAP_TYPE:  tgaHeaderP->ImgType = TGA_Map;           break;
+        case TGA_RGB_TYPE:  tgaHeaderP->ImgType = TGA_RGB;           break;
+        }
+    }
+    
+    if (id) {
+        tgaHeaderP->IdLength = strlen(id);
+        tgaHeaderP->Id = strdup(id);
+    } else
+        tgaHeaderP->IdLength = 0;
+    tgaHeaderP->Index_lo = 0;
+    tgaHeaderP->Index_hi = 0;
+    if (baseImgType == TGA_MAP_TYPE) {
+        tgaHeaderP->CoMapType = 1;
+        tgaHeaderP->Length_lo = ncolors % 256;
+        tgaHeaderP->Length_hi = ncolors / 256;
+        tgaHeaderP->CoSize = 8 * pamP->depth;
+    } else {
+        tgaHeaderP->CoMapType = 0;
+        tgaHeaderP->Length_lo = 0;
+        tgaHeaderP->Length_hi = 0;
+        tgaHeaderP->CoSize = 0;
+    }
+    switch (baseImgType) {
+    case TGA_MAP_TYPE:
+        tgaHeaderP->PixelSize = 8;
+        break;
+    case TGA_RGB_TYPE:
+        tgaHeaderP->PixelSize = 8 * MAX((withAlpha ? 4: 3), pamP->depth);
+        break;
+    case TGA_MONO_TYPE:
+        tgaHeaderP->PixelSize = 8;
+    }
+    tgaHeaderP->X_org_lo = tgaHeaderP->X_org_hi = 0;
+    tgaHeaderP->Y_org_lo = tgaHeaderP->Y_org_hi = 0;
+    tgaHeaderP->Width_lo = pamP->width % 256;
+    tgaHeaderP->Width_hi = pamP->width / 256;
+    tgaHeaderP->Height_lo = pamP->height % 256;
+    tgaHeaderP->Height_hi = pamP->height / 256;
+    tgaHeaderP->AttBits = 0;
+    tgaHeaderP->Rsrvd = 0;
+    tgaHeaderP->IntrLve = 0;
+    tgaHeaderP->OrgBit = orgBit;
+}
+
+
+
+static void
+releaseTgaHeader(struct ImageHeader const tgaHeader) {
+
+    strfree(tgaHeader.Id);
+}
+
+
+
+static void 
+writeTgaRaster(struct pam *          const pamP,
+               tuple **              const tuples, 
+               tuplehash             const cht,
+               enum TGAbaseImageType const imgType,
+               bool                  const withAlpha,
+               bool                  const rle,
+               unsigned char         const orgBit) {
+
+    int* runlength;  /* malloc'ed */
+    int row;
+
+    if (rle)
+        MALLOCARRAY(runlength, pamP->width);
+
+    for (row = 0; row < pamP->height; ++row) {
+        int realrow;
+        realrow = (orgBit != 0) ? row : pamP->height - row - 1;
+        if (rle) {
+            int col;
+            computeRunlengths(pamP, tuples[realrow], runlength);
+            for (col = 0; col < pamP->width; ) {
+                if (runlength[col] > 0) {
+                    putchar(0x80 + runlength[col] - 1);
+                    putPixel(pamP, tuples[realrow][col], imgType, withAlpha,
+                             cht);
+                    col += runlength[col];
+                } else if (runlength[col] < 0) {
+                    int i;
+                    putchar(-runlength[col] - 1);
+                    for (i = 0; i < -runlength[col]; ++i)
+                        putPixel(pamP, tuples[realrow][col+i], 
+                                 imgType, withAlpha, cht);
+                    col += -runlength[col];
+                } else
+                    pm_error("Internal error: zero run length");
+            }
+        } else {
+            int col;
+            for (col = 0; col < pamP->width; ++col)
+                putPixel(pamP, tuples[realrow][col], imgType, withAlpha, cht);
+        }
+    }
+    if (rle)
+        free(runlength);
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    tuple ** tuples;
+    struct pam pam;
+    int ncolors;
+    tupletable chv;
+    tuplehash cht;
+    struct ImageHeader tgaHeader;
+    enum TGAbaseImageType baseImgType;
+    bool withAlpha;
+    const char *outName;
+
+    pnm_init( &argc, argv );
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    computeOutName(cmdline, &outName);
+
+    tuples = pnm_readpam(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
+    pm_close(ifP);
+
+    computeImageType_cht(&pam, cmdline, tuples, 
+                         &baseImgType, &withAlpha, &chv, &cht, &ncolors);
+
+    /* Do the Targa header */
+    computeTgaHeader(&pam, baseImgType, withAlpha, !cmdline.norle,
+                     ncolors, 0, outName, &tgaHeader);
+    writeTgaHeader(tgaHeader);
+    
+    if (baseImgType == TGA_MAP_TYPE) {
+        /* Write out the Targa colormap. */
+        int i;
+        for (i = 0; i < ncolors; ++i)
+            putMapEntry(&pam, chv[i]->tuple, tgaHeader.CoSize);
+    }
+
+    writeTgaRaster(&pam, tuples, cht, baseImgType, withAlpha, 
+                   !cmdline.norle, 0);
+
+    if (cht)
+        pnm_destroytuplehash(cht);
+    if (chv)
+        pnm_freetupletable(&pam, chv);
+
+    releaseTgaHeader(tgaHeader);
+    strfree(outName);
+    pnm_freepamarray(tuples, &pam);
+
+    return 0;
+}
diff --git a/converter/other/pamtotiff.c b/converter/other/pamtotiff.c
new file mode 100644
index 00000000..4174a431
--- /dev/null
+++ b/converter/other/pamtotiff.c
@@ -0,0 +1,1110 @@
+/*
+** pnmtotiff.c - converts a PNM image to a TIFF (Tagged Image File) image
+**
+** Derived by Jef Poskanzer from ras2tif.c, which is:
+**
+** Copyright (c) 1990 by Sun Microsystems, Inc.
+**
+** Author: Patrick J. Naughton
+** naughton@wind.sun.com
+**
+** 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.
+*/
+
+#define _XOPEN_SOURCE    /* Make sure stdio.h contains fileno() */
+#define _BSD_SOURCE      /* Make sure string.h contains strcasecmp() */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#ifdef VMS
+#ifdef SYSV
+#undef SYSV
+#endif
+#include <tiffioP.h>
+#endif
+/* tiffio.h has a weird problem on AIX.  tiffio.h wrongly typedefs
+   "int32".  That's wrong because such a name is likely to be used in
+   other parts of the program that includes tiffio.h.  And in fact, on
+   AIX, <inttypes.h> defines it in some cases.  One case in which <inttypes.h>
+   does _not_ define int32 is where _XOPEN_SOURCE is declared.  So we always
+   do that when including tiffio.h.  06.01.05
+*/
+#include <tiffio.h>
+
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "pam.h"
+#include "pammap.h"
+#include "pm_tiff.h"
+
+#define MAXCOLORS 256
+
+/* 2001.09.03: Old tiff.h doesn't contain these values: */
+#ifndef COMPRESSION_ADOBE_DEFLATE
+#define COMPRESSION_ADOBE_DEFLATE 8
+#endif
+
+struct sizeset {
+    bool b1, b2, b4, b8;
+};
+
+
+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;  /* Filespecs of input files */
+    int compression;
+        /* COMPRESSION Tiff tag value, that corresponds to the compression
+           option the user specified, or -1 if he didn't specify any.
+           The -tag option doesn't count.
+        */
+    int fillorder;     /* FILLORDER Tiff tag value */
+    int g3options;     /* G3OPTIONS Tiff tag value or 0 for none */
+    int predictor;     /* PREDICTOR Tiff tag value or -1 for none */
+    int rowsperstrip;  /* -1 = unspecified */
+    unsigned int minisblack;   /* logical: User wants MINISBLACK photometric */
+    unsigned int miniswhite;   /* logical: User wants MINISWHITE photometric */
+    unsigned int truecolor;    /* logical: User wants truecolor, not cmap */
+    unsigned int color;        /* logical: assume not grayscale */
+    float xresolution; /* XRESOLUTION Tiff tag value or -1 for none */
+    float yresolution; /* YRESOLUTION Tiff tag value or -1 for none */
+    int resolutionUnit;  /* RESOLUTIONUNIT Tiff tag value */
+    struct sizeset indexsizeAllowed;
+    /* Which bit widths are allowable in a raster of palette indices */
+    unsigned int verbose;
+    unsigned int append;
+    float resolution;  /* X and Y resolution */
+    struct optNameValue * taglist;
+};
+
+
+
+static void
+validateTagList(struct optNameValue const taglist[]) {
+
+    unsigned int i;
+    for (i = 0; taglist[i].name; ++i) {
+        const char * const tagName = taglist[i].name;
+        const tagDefinition * tagDefP = tagDefFind(tagName);
+        
+        if (!tagDefP)
+            pm_error("Unknown tag name '%s'", tagName);
+        else {
+            /* We don't allow the user to set directly any tag that we
+               control explicitly.  These are normally tags that have to be
+               consistent with other stuff we put in the image.  But in some
+               cases, they're just tags we had special options for before
+               -tag existed.  The latter should probably be converted
+               eventually to prefer -tag.
+            */
+            switch (tagDefP->tagnum) {
+            case TIFFTAG_IMAGEWIDTH:
+            case TIFFTAG_IMAGELENGTH:
+            case TIFFTAG_BITSPERSAMPLE:
+            case TIFFTAG_COMPRESSION:
+            case TIFFTAG_GROUP3OPTIONS:
+            case TIFFTAG_PREDICTOR:
+            case TIFFTAG_PHOTOMETRIC:
+            case TIFFTAG_FILLORDER:
+            case TIFFTAG_SAMPLESPERPIXEL:
+            case TIFFTAG_ROWSPERSTRIP:
+            case TIFFTAG_PLANARCONFIG:
+            case TIFFTAG_COLORMAP:
+            case TIFFTAG_RESOLUTIONUNIT:
+            case TIFFTAG_XRESOLUTION:
+            case TIFFTAG_YRESOLUTION:
+                pm_error("You cannot specify a '%s' tag with -tag.  "
+                         "Pamtotiff controls that internally or via other "
+                         "options.", tagName);
+            }
+        }
+    }
+}
+
+
+
+static void
+parseCommandLine(int                        argc,
+                 char **              const 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;
+
+    optStruct3 opt;
+
+    unsigned int none, packbits, lzw, g3, g4, msb2lsb, lsb2msb, opt_2d, fill;
+    unsigned int flate, adobeflate;
+    char * indexbits;
+    char * resolutionUnit;
+
+    unsigned int predictorSpec, rowsperstripSpec, xresolutionSpec,
+        yresolutionSpec, indexbitsSpec, resolutionUnitSpec, tagSpec;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "verbose",      OPT_FLAG,   NULL, &cmdlineP->verbose,       0);
+    OPTENT3(0, "append",       OPT_FLAG,   NULL, &cmdlineP->append,        0);
+    OPTENT3(0, "none",         OPT_FLAG,   NULL, &none,                    0);
+    OPTENT3(0, "packbits",     OPT_FLAG,   NULL, &packbits,                0);
+    OPTENT3(0, "lzw",          OPT_FLAG,   NULL, &lzw,                     0);
+    OPTENT3(0, "g3",           OPT_FLAG,   NULL, &g3,                      0);
+    OPTENT3(0, "g4",           OPT_FLAG,   NULL, &g4,                      0);
+    OPTENT3(0, "flate",        OPT_FLAG,   NULL, &flate,                   0);
+    OPTENT3(0, "adobeflate",   OPT_FLAG,   NULL, &adobeflate,              0);
+    OPTENT3(0, "msb2lsb",      OPT_FLAG,   NULL, &msb2lsb,                 0);
+    OPTENT3(0, "lsb2msb",      OPT_FLAG,   NULL, &lsb2msb,                 0);
+    OPTENT3(0, "2d",           OPT_FLAG,   NULL, &opt_2d,                  0);
+    OPTENT3(0, "fill",         OPT_FLAG,   NULL, &fill,                    0);
+    OPTENT3(0, "minisblack",   OPT_FLAG,   NULL, &cmdlineP->minisblack,    0);
+    OPTENT3(0, "mb",           OPT_FLAG,   NULL, &cmdlineP->minisblack,    0);
+    OPTENT3(0, "miniswhite",   OPT_FLAG,   NULL, &cmdlineP->miniswhite,    0);
+    OPTENT3(0, "mw",           OPT_FLAG,   NULL, &cmdlineP->miniswhite,    0);
+    OPTENT3(0, "truecolor",    OPT_FLAG,   NULL, &cmdlineP->truecolor,     0);
+    OPTENT3(0, "color",        OPT_FLAG,   NULL, &cmdlineP->color,         0);
+    OPTENT3(0, "predictor",    OPT_UINT,   &cmdlineP->predictor,    
+            &predictorSpec,    0);
+    OPTENT3(0, "rowsperstrip", OPT_UINT,   &cmdlineP->rowsperstrip, 
+            &rowsperstripSpec, 0);
+    OPTENT3(0, "xresolution",  OPT_FLOAT,  &cmdlineP->xresolution,  
+            &xresolutionSpec,  0);
+    OPTENT3(0, "yresolution",  OPT_FLOAT,  &cmdlineP->yresolution,  
+            &yresolutionSpec,  0);
+    OPTENT3(0, "resolutionunit", OPT_STRING, &resolutionUnit,
+            &resolutionUnitSpec,    0);
+    OPTENT3(0, "indexbits",    OPT_STRING,   &indexbits, 
+            &indexbitsSpec,    0);
+    OPTENT3(0, "tag",          OPT_NAMELIST, &cmdlineP->taglist, &tagSpec, 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 (none + packbits + lzw + g3 + g4 + flate + adobeflate > 1)
+        pm_error("You specified more than one compression option.  "
+                 "Only one of -none, -packbits, -lze, -g3, and -g4 "
+                 "is allowed.");
+    
+    if (none)
+        cmdlineP->compression = COMPRESSION_NONE;
+    else if (packbits)
+        cmdlineP->compression = COMPRESSION_PACKBITS;
+    else if (lzw)
+        cmdlineP->compression = COMPRESSION_LZW;
+    else if (g3)
+        cmdlineP->compression = COMPRESSION_CCITTFAX3;
+    else if (g4)
+        cmdlineP->compression = COMPRESSION_CCITTFAX4;
+    else if (adobeflate)
+        cmdlineP->compression = COMPRESSION_ADOBE_DEFLATE;
+    else if (flate)
+        cmdlineP->compression = COMPRESSION_DEFLATE;
+    else
+        cmdlineP->compression = COMPRESSION_NONE;
+    
+    if (msb2lsb + lsb2msb > 1)
+        pm_error("You specified both -msb2lsb and -lsb2msb.  "
+                 "These are conflicting options.");
+
+    if (msb2lsb)
+        cmdlineP->fillorder = FILLORDER_MSB2LSB;
+    else if (lsb2msb)
+        cmdlineP->fillorder = FILLORDER_LSB2MSB;
+    else 
+        cmdlineP->fillorder = FILLORDER_MSB2LSB;
+    
+
+    if (cmdlineP->miniswhite && cmdlineP->minisblack)
+        pm_error("You cannot specify both -miniswhite and -minisblack");
+
+    cmdlineP->g3options = 0;  /* initial value */
+    if (opt_2d)
+        cmdlineP->g3options |= GROUP3OPT_2DENCODING;
+    if (fill)
+        cmdlineP->g3options |= GROUP3OPT_FILLBITS;
+
+    if (predictorSpec) {
+        if (cmdlineP->predictor != 1 && cmdlineP->predictor != 2)
+            pm_error("-predictor may be only 1 or 2.  You specified %d.", 
+                     cmdlineP->predictor);
+    } else
+        cmdlineP->predictor = -1;
+
+    if (rowsperstripSpec) {
+        if (cmdlineP->rowsperstrip < 1)
+            pm_error("-rowsperstrip must be positive.  You specified %d.",
+                     cmdlineP->rowsperstrip);
+    } else
+        cmdlineP->rowsperstrip = -1;
+
+    if (xresolutionSpec) {
+        if (cmdlineP->xresolution < 1)
+            pm_error("-xresolution must be positive.  You specified %f.",
+                     cmdlineP->xresolution);
+    } else
+        cmdlineP->xresolution = -1;
+
+    if (yresolutionSpec) {
+        if (cmdlineP->yresolution < 1)
+            pm_error("-yresolution must be positive.  You specified %f.",
+                     cmdlineP->yresolution);
+    } else
+        cmdlineP->yresolution = -1;
+
+    if (resolutionUnitSpec) {
+        if (streq(resolutionUnit, "inch"))
+            cmdlineP->resolutionUnit = RESUNIT_INCH;
+        else if (streq(resolutionUnit, "in"))
+            cmdlineP->resolutionUnit = RESUNIT_INCH;
+        else if (streq(resolutionUnit, "centimeter"))
+            cmdlineP->resolutionUnit = RESUNIT_CENTIMETER;
+        else if (streq(resolutionUnit, "cm"))
+            cmdlineP->resolutionUnit = RESUNIT_CENTIMETER;
+        else if (streq(resolutionUnit, "none"))
+            cmdlineP->resolutionUnit = RESUNIT_NONE;
+        else if (streq(resolutionUnit, "no"))
+            cmdlineP->resolutionUnit = RESUNIT_NONE;
+        else
+            pm_error("The only acceptable values for -resolutionunit are "
+                     "inch, centimeter, none, in, cm, and no.  "
+                     "You specified '%s'.", resolutionUnit);
+    } else
+        cmdlineP->resolutionUnit = RESUNIT_INCH;
+
+    if (indexbitsSpec) {
+        if (strstr(indexbits, "1"))
+            cmdlineP->indexsizeAllowed.b1 = TRUE;
+        else
+            cmdlineP->indexsizeAllowed.b1 = FALSE;
+        if (strstr(indexbits, "2"))
+            cmdlineP->indexsizeAllowed.b2 = TRUE;
+        else
+            cmdlineP->indexsizeAllowed.b2 = FALSE;
+        if (strstr(indexbits, "4"))
+            cmdlineP->indexsizeAllowed.b4 = TRUE;
+        else
+            cmdlineP->indexsizeAllowed.b4 = FALSE;
+        if (strstr(indexbits, "8"))
+            cmdlineP->indexsizeAllowed.b8 = TRUE;
+        else
+            cmdlineP->indexsizeAllowed.b8 = FALSE;
+    } else {
+        cmdlineP->indexsizeAllowed.b1 = FALSE;
+        cmdlineP->indexsizeAllowed.b2 = FALSE;
+        cmdlineP->indexsizeAllowed.b4 = FALSE;
+        cmdlineP->indexsizeAllowed.b8 = TRUE;
+    }
+
+    if (tagSpec)
+        validateTagList(cmdlineP->taglist);
+    else {
+        MALLOCARRAY_NOFAIL(cmdlineP->taglist, 1);
+        cmdlineP->taglist[0].name = NULL;
+        cmdlineP->taglist[0].value = NULL;
+    }
+
+    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 void
+putSample(sample           const s,
+          sample           const maxval,
+          sample           const tiff_maxval,
+          unsigned int     const bitspersample,
+          unsigned char ** const tPP) {
+
+    xelval s2;
+
+    s2 = s;
+    if (maxval != tiff_maxval)
+        s2 = s * tiff_maxval / maxval;
+    if (bitspersample > 8) {
+        *((unsigned short *)(*tPP)) = s;
+        (*tPP) += sizeof(short);
+    } else
+        *(*tPP)++ = s & 0xff;
+}
+
+
+
+
+/* Note: PUTSAMPLE doesn't work if bitspersample is 1-4. */
+
+#define PUTSAMPLE putSample(s, maxval, tiff_maxval, bitspersample, &tP);
+
+
+
+static void
+fillRowOfSubBytePixels(struct pam *    const pamP,
+                       const tuple *   const tuplerow,
+                       unsigned char * const buf,
+                       unsigned short  const tiff_maxval,
+                       unsigned short  const photometric,
+                       int             const fillorder,
+                       int             const bitspersample,
+                       tuplehash       const cht) {
+/*----------------------------------------------------------------------------
+  This subroutine deals with cases where multiple pixels are packed into
+  a single byte of the Tiff output image, i.e. bits per pixel < 8.
+-----------------------------------------------------------------------------*/
+    int col;
+    int bitshift;
+        /* The number of bits we have to shift a pixel value left to line
+           it up with where the current pixel goes in the current byte of
+           the output buffer.  
+        */
+    int const firstbitshift = 
+        (fillorder == FILLORDER_MSB2LSB) ? 8 - bitspersample : 0;
+        /* The value of 'bitshift' for the first pixel into a
+           byte of the output buffer.  (MSB2LSB is normal).
+        */
+    sample s;
+        /* The value of the current sample */
+    unsigned char* tP;
+        /* The address of the byte in the output buffer in which
+           the current pixel goes.
+        */
+    unsigned char byte;
+        /* The under-construction value of the byte pointed to by
+           tP, above.
+        */
+                
+    bitshift = firstbitshift;
+    byte = 0;
+    for (col = 0, tP = buf; col < pamP->width; ++col) {
+        if (cht == NULL) {
+            /* It's grayscale */
+            s = tuplerow[col][0];
+            if (pamP->maxval != tiff_maxval )
+                s = (long) s * tiff_maxval / pamP->maxval;
+ 
+            if (photometric == PHOTOMETRIC_MINISWHITE)
+                s = tiff_maxval - s;
+        } else {
+            /* It's a colormapped raster */
+            int si;
+            int found;
+            pnm_lookuptuple(pamP, cht, tuplerow[col], &found, &si);
+            if (!found)
+                pm_error("INTERNAL ERROR.  We made a color map, and a "
+                         "color map we need is not in it!.  "
+                         "col=%d  r=%lu g=%lu b=%lu",
+                         col, tuplerow[col][0], tuplerow[col][1],
+                         tuplerow[col][2]);
+            s = (unsigned char) si;
+        }
+        byte |= s << bitshift;
+        if (fillorder == FILLORDER_MSB2LSB)
+            bitshift -= bitspersample;  /* normal case */
+        else
+            bitshift += bitspersample;
+        if (bitshift < 0 || bitshift > 7) {
+            *tP++ = byte;
+            bitshift = firstbitshift;
+            byte = 0;
+        }
+    }
+    if (bitshift != firstbitshift)
+        *tP++ = byte;
+}
+
+
+
+static void
+fillRowOfWholeBytePixels(struct pam *    const pamP,
+                         tuple *         const tuplerow,
+                         unsigned char * const buf,
+                         unsigned short  const photometric,
+                         unsigned short  const tiffMaxval,
+                         unsigned int    const bitsPerSample) {
+
+    unsigned int col;
+    unsigned char * tP;
+    unsigned int planes;
+    
+    if (photometric == PHOTOMETRIC_RGB)
+        planes = pamP->depth;
+    else
+        /* Write only the first plane from the PAM, even if there
+           are more.
+        */
+        planes = 1;
+
+    for (col = 0, tP = buf; col < pamP->width; ++col) {
+        unsigned int plane;
+        for (plane = 0; plane < planes; ++plane) {
+            putSample(tuplerow[col][plane], pamP->maxval,
+                      tiffMaxval, bitsPerSample, &tP);
+            /* Advances tP */
+        }
+    }
+} 
+
+
+
+static void
+writeScanLines(struct pam *   const pamP,
+               TIFF *         const tif, 
+               tuplehash      const cht,
+               unsigned short const tiffMaxval,
+               unsigned short const bitspersample, 
+               unsigned short const photometric,
+               int            const bytesperrow, 
+               int            const fillorder) {
+/*----------------------------------------------------------------------------
+   Write out the raster for the input image described by 'pamP', whose
+   file is positioned to the raster of the image.
+-----------------------------------------------------------------------------*/
+    tuple * tuplerow;  /* malloc'ed */
+    unsigned char * buf; /* malloc'ed */
+    int row;
+
+    /* The man page for TIFFWriteScanLine doesn't tell the format of
+       it's 'buf' parameter, but here it is: Its format depends on the
+       bits per pixel of the TIFF image.  If it's 16, 'buf' is an
+       array of short (16 bit) integers, one per raster column.  If
+       it's 8, 'buf' is an array of characters (8 bit integers), one
+       per image column.  If it's less than 8, it's an array of characters,
+       each of which represents 1-8 raster columns, packed
+       into it in the order specified by the TIFF image's fill order,
+       with don't-care bits on the right such that each byte contains only
+       whole pixels.
+
+       In all cases, the array elements are in order left to right going
+       from low array indices to high array indices.
+    */
+    MALLOCARRAY(buf, bytesperrow);
+
+    if (buf == NULL)
+        pm_error("can't allocate memory for row buffer");
+
+    tuplerow = pnm_allocpamrow(pamP);
+    
+    for (row = 0; row < pamP->height; ++row) {
+        int col;
+
+        pnm_readpamrow(pamP, tuplerow);
+
+        if (cht == NULL) {
+            /* It's a direct, non-colormapped raster */
+
+            if (bitspersample == 8 || bitspersample == 16)
+                fillRowOfWholeBytePixels(pamP, tuplerow, buf, photometric,
+                                         tiffMaxval, bitspersample);
+            else
+                fillRowOfSubBytePixels(pamP, tuplerow, buf,
+                                       tiffMaxval, photometric,
+                                       fillorder, bitspersample, NULL);
+        } else {
+            /* It's a colormapped raster */
+            if (bitspersample == 8) {
+                for (col = 0; col < pamP->width; ++col) {
+                    int si;
+                    int found;
+                    
+                    pnm_lookuptuple(pamP, cht, tuplerow[col], &found, &si);
+                    
+                    if (!found)
+                        pm_error("INTERNAL ERROR.  We made a color map, and a "
+                                 "color map we need is not in it!  "
+                                 "col=%d  r=%lu g=%lu b=%lu",
+                                 col, tuplerow[col][0], tuplerow[col][1],
+                                 tuplerow[col][2]);
+                    buf[col] = (unsigned char) si;
+                }
+            } else {
+                fillRowOfSubBytePixels(pamP, tuplerow, buf, 0, 0, fillorder,
+                                       bitspersample, cht);
+            }
+        }
+        if (TIFFWriteScanline(tif, buf, row, 0) < 0)
+            pm_error("failed a scanline write on row %d", row);
+    }
+    pnm_freepamrow(tuplerow);
+    free(buf);
+}
+
+
+
+static void
+analyzeColorsInRgbInput(struct pam *        const pamP,
+                        struct cmdlineInfo  const cmdline,
+                        int                 const maxcolors, 
+                        tupletable *        const chvP, 
+                        unsigned int *      const colorsP, 
+                        bool *              const grayscaleP) {
+/*----------------------------------------------------------------------------
+   Same as analyzeColors(), except assuming input image has R/G/B tuples.
+-----------------------------------------------------------------------------*/
+    if (cmdline.color && cmdline.truecolor) {
+        *chvP = NULL;
+        *grayscaleP = FALSE;
+    } else {
+        tupletable chv;
+        bool grayscale;
+
+        pm_message("computing colormap...");
+        chv = pnm_computetuplefreqtable2(pamP, NULL, maxcolors,
+                                         pamP->maxval,
+                                         colorsP);
+        if (chv == NULL) {
+            grayscale = FALSE;
+        } else {
+            unsigned int i;
+            pm_message("%u color%s found", 
+                       *colorsP, *colorsP == 1 ? "" : "s");
+            grayscale = TRUE;  /* initial assumption */
+            for (i = 0; i < *colorsP && grayscale; ++i) {
+                if (!pnm_rgbtupleisgray(chv[i]->tuple))
+                    grayscale = FALSE;
+            }
+        }
+        *grayscaleP = grayscale;
+
+        if (grayscale || cmdline.truecolor) {
+            *chvP = NULL;
+            if (chv)
+                pnm_freetupletable(pamP, chv);
+        } else {
+            /* He wants a colormap.  But can we give it to him? */
+            if (chv)
+                *chvP = chv;
+            else {
+                pm_message("Too many colors - "
+                           "proceeding to write a 24-bit RGB file.");
+                pm_message("If you want an 8-bit palette file, "
+                           "try doing a 'pnmquant %d'.",
+                           maxcolors);
+                *chvP = NULL;
+            }
+        }
+    }
+}
+
+
+
+static void
+analyzeColors(struct pam *        const pamP,
+              struct cmdlineInfo  const cmdline,
+              int                 const maxcolors, 
+              tupletable *        const chvP, 
+              unsigned int *      const colorsP, 
+              bool *              const grayscaleP) {
+/*----------------------------------------------------------------------------
+   Analyze the colors in the input image described by 'pamP', whose file
+   is positioned to the raster. 
+
+   If the colors, combined with command line options 'cmdline', indicate
+   a colormapped TIFF should be generated, return as *chvP the address
+   of a color map (in newly malloc'ed space).  If a colormapped TIFF is
+   not indicated, return *chvP == NULL.
+
+   Return *grayscaleP == true iff the image should be stored as a grayscale
+   image (which means the image is monochromatic and the user doesn't
+   insist on color format).
+
+   Leave the file position undefined.
+-----------------------------------------------------------------------------*/
+    if (pamP->depth >= 3)
+        /* Assume it's a color-capable input image
+           (tuple type RGB or RGB_ALPHA)
+        */
+        analyzeColorsInRgbInput(pamP, cmdline, maxcolors,
+                                chvP, colorsP, grayscaleP);
+    else {
+        *chvP = NULL;
+        *grayscaleP = TRUE;
+    }
+}
+
+
+
+static void
+computeRasterParm(struct pam *     const pamP,
+                  tupletable       const chv, 
+                  int              const colors, 
+                  bool             const grayscale,
+                  int              const compression,
+                  bool             const minisblack,
+                  bool             const miniswhite,
+                  struct sizeset   const indexsizeAllowed,
+                  unsigned short * const samplesperpixelP,
+                  unsigned short * const bitspersampleP,
+                  unsigned short * const photometricP,
+                  int            * const bytesperrowP) {
+/*----------------------------------------------------------------------------
+   Compute the parameters of the raster portion of the TIFF image.
+
+   'minisblack' and 'miniswhite' mean the user requests the corresponding
+   photometric.  Both FALSE means user has no explicit requirement.
+-----------------------------------------------------------------------------*/
+    unsigned short defaultPhotometric;
+    /* The photometric we use if the user specified no preference */
+
+    /* We determine the bits per sample by the maxval, except that if
+       it would be more than 8, we just use 16.  I don't know if bits
+       per sample between 8 and 16 are legal, but they aren't very
+       nice in any case.  If users want them, we should provide an
+       option.  It is not clear why we don't use bits per pixel < 8
+       for RGB images.  Note that code to handle maxvals <= 255 was
+       written long before maxval > 255 was possible and there are
+       backward compatibility requirements.  
+    */
+
+    if (pamP->depth == 1 && pamP->maxval == 1) {
+        *samplesperpixelP = 1;
+        *bitspersampleP = 1;
+        if ((compression == COMPRESSION_CCITTFAX3) ||
+            (compression == COMPRESSION_CCITTFAX4))
+            defaultPhotometric = PHOTOMETRIC_MINISWHITE;
+        else
+            defaultPhotometric = PHOTOMETRIC_MINISBLACK;
+    } else {
+        if (chv) {
+            *samplesperpixelP = 1;  /* Pixel is just the one index value */
+            *bitspersampleP = 
+                colors <=   2 && indexsizeAllowed.b1 ? 1 :
+                colors <=   4 && indexsizeAllowed.b2 ? 2 :
+                colors <=  16 && indexsizeAllowed.b4 ? 4 :
+                colors <= 256 && indexsizeAllowed.b8 ? 8 :
+                0;
+            if (*bitspersampleP == 0)
+                pm_error("Your -indexbits option is insufficient for the "
+                         "%d colors in this image.", colors);
+
+            defaultPhotometric = PHOTOMETRIC_PALETTE;
+        } else {
+            if (grayscale) {
+                /* We'll write just the first plane regardless of how many are
+                   in the input PAM.
+                */
+                *samplesperpixelP = 1;
+                *bitspersampleP =
+                    pamP->maxval > 255 ? 16 : pm_maxvaltobits(pamP->maxval);
+                defaultPhotometric = PHOTOMETRIC_MINISBLACK;
+            } else {
+                *samplesperpixelP = pamP->depth;
+                *bitspersampleP = pamP->maxval > 255 ? 16 : 8;
+                defaultPhotometric = PHOTOMETRIC_RGB;
+            }
+        }
+    }
+
+    if (miniswhite)
+        *photometricP = PHOTOMETRIC_MINISWHITE;
+    else if (minisblack)
+        *photometricP = PHOTOMETRIC_MINISBLACK;
+    else
+        *photometricP = defaultPhotometric;
+
+    {
+        unsigned int const samplesPerRow = pamP->width * *samplesperpixelP;
+
+        if (*bitspersampleP < 8) {
+            int samplesperbyte;
+            samplesperbyte = 8 / *bitspersampleP;
+            *bytesperrowP =
+                (samplesPerRow + samplesperbyte-1) / samplesperbyte;
+        } else {
+            if (UINT_MAX / *bitspersampleP < samplesPerRow)
+                pm_error("Too many bytes per row to compute");
+            *bytesperrowP = (samplesPerRow * *bitspersampleP) / 8;
+        }
+    }
+}
+
+
+
+static void
+validateSeekableOutputFile(int          const ofd,
+                           const char * const outFileName) {
+/*----------------------------------------------------------------------------
+   Validate that the file attached to file descriptor 'ofd' is capable
+   of seeking.  If not, fail the program.
+
+   This is useful because the TIFF library requires seekable output and
+   fails with an unhelpful error message about a file I/O error if it is
+   not.  We, on the other hand, give a helpful error message.
+
+   We leave the file positioned to the beginning.
+-----------------------------------------------------------------------------*/
+    int rc;
+
+    /* We'd like to get the current position and leave the file positioned
+       where we found it.  But that entails the mess with some systems
+       having 32 bit file offsets and some having 64 bit file offsets.
+    */
+
+    /* Some files can seek ahead, but not back.  So we test for the
+       more difficult backward seek.
+    */
+    lseek(ofd, 1, SEEK_SET);
+    rc = lseek(ofd, 0, SEEK_SET);
+            
+    if (rc < 0)
+        pm_error("Output file (%s) is not seekable.  lseek() returned "
+                 "errno %d (%s).  "
+                 "The TIFF library can write only to "
+                 "a seekable file.", 
+                 outFileName, errno, strerror(errno));
+}
+
+
+
+static void
+createTiffGenerator(int          const ofd, 
+                    const char * const outFileName,
+                    bool         const append,
+                    TIFF **      const tifPP) {
+
+    const char * option;
+
+    /* Before 10.12 (November 2002), we set O_NONBLOCK here:
+
+       fcntl( 1, F_SETFL, O_NONBLOCK ) ; 
+   
+       I have no idea why.  The comment attached said, 
+
+         acooke dec99 - otherwise blocks on read inside 
+         next line (Linux i386) 
+    */
+
+    validateSeekableOutputFile(ofd, outFileName);
+
+    if (append)
+        option = "a";
+    else
+        option = "w";
+
+    *tifPP = TIFFFdOpen(ofd, outFileName, option);
+    if (*tifPP == NULL)
+        pm_error("error opening standard output as TIFF file.  "
+                 "TIFFFdOpen() failed.");
+}
+
+
+
+static void
+destroyTiffGenerator(TIFF * const tifP) {
+
+    TIFFFlushData(tifP);
+    TIFFClose(tifP);
+}
+
+
+
+static void
+createTiffColorMap(struct pam *       const pamP,
+                   unsigned int       const bitspersample,
+                   tupletable         const chv,
+                   unsigned int       const colors,
+                   unsigned short *** const tiffColorMapP) {
+
+    unsigned int const colorMapSize = 1 << bitspersample;
+    unsigned short ** tiffColorMap;
+    unsigned int plane;
+    unsigned int i;
+    
+    MALLOCARRAY_NOFAIL(tiffColorMap, pamP->depth);
+    for (plane = 0; plane < pamP->depth; ++plane)
+        MALLOCARRAY_NOFAIL(tiffColorMap[plane], colorMapSize);
+    
+    for (i = 0; i < colorMapSize; ++i) {
+        unsigned int plane;
+        for (plane = 0; plane < pamP->depth; ++plane) {
+            if (i < colors)
+                tiffColorMap[plane][i] =
+                    chv[i]->tuple[plane] * 65535L / pamP->maxval;
+            else
+                tiffColorMap[plane][i] = 0;
+        }
+    }
+    *tiffColorMapP = tiffColorMap;
+}
+        
+
+
+static void
+destroyTiffColorMap(struct pam *      const pamP,
+                    unsigned short ** const tiffColorMap) {
+
+    unsigned int plane;
+
+    for (plane = 0; plane < pamP->depth; ++plane)
+        free(tiffColorMap[plane]);
+
+    free(tiffColorMap);
+}
+
+
+
+static void
+setTagListFields(const struct optNameValue * const taglist,
+                 TIFF *                      const tifP) {
+
+    unsigned int i;
+
+    for (i = 0; taglist[i].name; ++i) {
+        const tagDefinition * const tagDefP = tagDefFind(taglist[i].name);
+        
+        if (tagDefP->put)
+            tagDefP->put(tifP, tagDefP->tagnum, taglist[i].value,
+                         tagDefP->choices);
+    }
+}
+
+
+
+static void
+setTiffFields(TIFF *              const tifP,
+              struct cmdlineInfo  const cmdline,
+              struct pam *        const pamP,
+              unsigned short      const bitspersample,
+              unsigned short      const photometric,
+              unsigned short      const samplesperpixel,
+              unsigned short **   const tiffColorMap,
+              const char *        const inputFileDescription,
+              struct optNameValue const taglist[]) {
+
+    /* TODO: It would be cleaner to set all tags in a tag list and
+       then call setTagListFields() on that list.  That way,
+       TIFFSetField() is in only one place and all tags get set the
+       same way and we don't have this icky setting of default and
+       then resetting with user choice (e.g. DOCUMENTNAME).
+    */
+
+    /* Set TIFF parameters. */
+    TIFFSetField(tifP, TIFFTAG_IMAGEWIDTH,    pamP->width);
+    TIFFSetField(tifP, TIFFTAG_IMAGELENGTH,   pamP->height);
+    TIFFSetField(tifP, TIFFTAG_BITSPERSAMPLE, bitspersample);
+    if (cmdline.compression != -1)
+        TIFFSetField(tifP, TIFFTAG_COMPRESSION, cmdline.compression);
+    if (cmdline.compression == COMPRESSION_CCITTFAX3 && cmdline.g3options != 0)
+        TIFFSetField( tifP, TIFFTAG_GROUP3OPTIONS, cmdline.g3options);
+    if (cmdline.compression == COMPRESSION_LZW && cmdline.predictor != -1)
+        TIFFSetField( tifP, TIFFTAG_PREDICTOR, cmdline.predictor);
+    TIFFSetField(tifP, TIFFTAG_PHOTOMETRIC, photometric);
+    if (cmdline.fillorder != -1)
+        TIFFSetField(tifP, TIFFTAG_FILLORDER, cmdline.fillorder);
+    TIFFSetField(tifP, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel);
+    if (cmdline.rowsperstrip != -1)
+        TIFFSetField(tifP, TIFFTAG_ROWSPERSTRIP, cmdline.rowsperstrip);
+    else
+        TIFFSetField(tifP, TIFFTAG_ROWSPERSTRIP,
+                     TIFFDefaultStripSize(tifP, 0));
+    if (cmdline.xresolution != -1 ||
+        cmdline.yresolution != -1 ||
+        cmdline.resolutionUnit != -1) {
+        TIFFSetField(tifP, TIFFTAG_RESOLUTIONUNIT,
+                     cmdline.resolutionUnit != -1 ?
+                     cmdline.resolutionUnit : RESUNIT_NONE);
+    }
+    if (cmdline.xresolution > 0)
+        TIFFSetField(tifP, TIFFTAG_XRESOLUTION, cmdline.xresolution);
+    if ( cmdline.yresolution > 0)
+        TIFFSetField(tifP, TIFFTAG_YRESOLUTION, cmdline.yresolution);
+    TIFFSetField(tifP, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+
+    if (tiffColorMap)
+        TIFFSetField(tifP, TIFFTAG_COLORMAP,
+                     tiffColorMap[PAM_RED_PLANE],
+                     tiffColorMap[PAM_GRN_PLANE],
+                     tiffColorMap[PAM_BLU_PLANE]);
+
+
+    TIFFSetField(tifP, TIFFTAG_DOCUMENTNAME,     inputFileDescription);
+    TIFFSetField(tifP, TIFFTAG_IMAGEDESCRIPTION, "converted PNM file");
+ 
+    /* Some of taglist[] overrides defaults we set above.  But taglist[]
+       is defined not to specify any tag types that are not purely user
+       choice.
+    */
+    setTagListFields(taglist, tifP);
+}
+
+
+
+static void
+convertImage(FILE *             const ifP,
+             TIFF *             const tifP,
+             const char *       const inputFileDescription,
+             struct cmdlineInfo const cmdline) {
+
+    tupletable chv;
+    tuplehash cht;
+    unsigned short ** tiffColorMap;  /* malloc'ed */
+    struct pam pam;
+    unsigned int colors;
+    bool grayscale;
+    unsigned short photometric;
+    unsigned short samplesperpixel;
+    unsigned short bitspersample;
+    unsigned short tiff_maxval;
+    /* This is the maxval of the samples in the tiff file.  It is 
+       determined solely by the bits per sample ('bitspersample').
+       */
+    int bytesperrow;
+    pm_filepos rasterPos;
+
+    pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
+
+    pm_tell2(ifP, &rasterPos, sizeof(rasterPos));
+
+    analyzeColors(&pam, cmdline, MAXCOLORS, &chv, &colors, &grayscale);
+
+    /* Go back to beginning of raster */
+    pm_seek2(ifP, &rasterPos, sizeof(rasterPos));  
+
+    /* Figure out TIFF parameters. */
+
+    computeRasterParm(&pam, chv, colors, grayscale, 
+                      cmdline.compression,
+                      cmdline.minisblack, cmdline.miniswhite,
+                      cmdline.indexsizeAllowed,
+                      &samplesperpixel, &bitspersample, &photometric,
+                      &bytesperrow);
+
+    tiff_maxval = pm_bitstomaxval(bitspersample);
+
+    if (!chv) {
+        cht = NULL;
+        tiffColorMap = NULL;
+    } else {
+        createTiffColorMap(&pam, bitspersample, chv, colors, &tiffColorMap);
+
+        /* Convert color vector to color hash table, for fast lookup. */
+        cht = pnm_computetupletablehash(&pam, chv, colors);
+        pnm_freetupletable(&pam, chv);
+    }
+
+    setTiffFields(tifP, cmdline, &pam, bitspersample, photometric,
+                  samplesperpixel, tiffColorMap, inputFileDescription,
+                  cmdline.taglist);
+
+    writeScanLines(&pam, tifP, cht,
+                   tiff_maxval, bitspersample, photometric, bytesperrow, 
+                   cmdline.fillorder);
+
+    if (tiffColorMap)
+        destroyTiffColorMap(&pam, tiffColorMap);
+}
+
+
+
+static void
+validateReadableStdout(void) {
+/*----------------------------------------------------------------------------
+  We validate that Standard Output is readable and fail the program if
+  it isn't.
+
+  This is useful because there are situations in which the TIFF library
+  must read the output file and if it can't, it fails with an unhelpful
+  error message about a file I/O error.  We, on the other hand, produce
+  a helpful error message.
+-----------------------------------------------------------------------------*/
+    int flags;
+
+    flags = fcntl(STDOUT_FILENO, F_GETFL);
+
+    if (flags < 0) {
+        /* We couldn't get the flags.  So just assume the file's OK */
+    } else {
+        if ((flags & O_RDONLY) || (flags & O_RDWR)) {
+            /* File is readable.  All is well. */
+        } else
+            pm_error("Standard Output is not opened for reading.  "
+                     "In order to create a multi-image TIFF stream, "
+                     "Standard Output must be both readable and writable.");
+    }
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+    struct cmdlineInfo cmdline;
+    const char * inputFileDescription;
+    FILE* ifP;
+    TIFF* tifP;
+    bool eof;
+    unsigned int imageSeq;
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+    
+    ifP = pm_openr_seekable(cmdline.input_filespec);
+
+    if (streq(cmdline.input_filespec, "-"))
+        inputFileDescription = "Standard Input";
+    else 
+        inputFileDescription = cmdline.input_filespec;
+
+    if (cmdline.append)
+        validateReadableStdout();
+
+    createTiffGenerator(STDOUT_FILENO, "Standard Output", cmdline.append,
+                        &tifP);
+
+    eof = FALSE;  /* initial assumption */
+    imageSeq = 0;
+
+    while (!eof) {
+        bool success;
+
+        if (cmdline.verbose)
+            pm_message("Converting Image %u", imageSeq);
+
+        pnm_nextimage(ifP, &eof);
+
+        if (!eof) {
+            if (imageSeq > 0)
+                validateReadableStdout();
+
+            convertImage(ifP, tifP, inputFileDescription, cmdline);
+            
+            success = TIFFWriteDirectory(tifP);
+            if (!success)
+                pm_error("Unable to write TIFF image %u to file.  "
+                         "tifWriteDirectory() failed.", imageSeq);
+            ++imageSeq;
+        }
+    }
+
+    destroyTiffGenerator(tifP);
+    pm_close(ifP);
+
+    return 0;
+}
diff --git a/converter/other/pamtouil.c b/converter/other/pamtouil.c
new file mode 100644
index 00000000..b9ddc749
--- /dev/null
+++ b/converter/other/pamtouil.c
@@ -0,0 +1,438 @@
+/* pamtouil.c - convert PBM, PGM, PPM, or PPM+alpha to Motif UIL icon file
+**
+** Bryan Henderson converted ppmtouil to pamtouil on 2002.05.04.
+**
+** Jef Poskanzer derived pamtouil from ppmtoxpm, which is
+** 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.
+*/
+
+#define _BSD_SOURCE  /* Make sure string.h contains strdup() */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+#include <ctype.h>
+#include <string.h>
+#include "pam.h"
+#include "pammap.h"
+#include "colorname.h"
+#include "shhopt.h"
+#include "nstring.h"
+
+/* Max number of colors allowed in ppm input. */
+#define MAXCOLORS 256
+
+/* Lower bound and upper bound of character-pixels printed in UIL output. */
+#define LOW_CHAR '`'
+#define HIGH_CHAR '~'
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *inputFilespec;  /* Filespecs of input files */
+    char *outname;         /* output filename, less "_icon" */
+    unsigned int verbose;
+};
+
+
+
+typedef struct {    /* character-pixel mapping */
+    const char* cixel;    /* character string printed for pixel */
+    const char* rgbname;  /* ascii rgb color, either mnemonic or #rgb value */
+    const char* uilname;  /* same, with spaces replaced by underbars */
+    bool        transparent;
+} cixel_map;
+
+
+
+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.  The outname array is in newly
+   malloc'ed storage.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def = malloc( 100*sizeof( optEntry ) );
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+    unsigned int outnameSpec;
+    const char *outnameOpt;
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "name",       OPT_STRING, &outnameOpt, 
+            &outnameSpec,       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 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->inputFilespec = "-";  /* stdin */
+    else if (argc-1 == 1)
+        cmdlineP->inputFilespec = argv[1];
+    else 
+        pm_error("Too many arguments (%d).  "
+                 "Only need one: the input filespec", argc-1);
+
+    if (outnameSpec) {
+        char * barPos;
+
+        cmdlineP->outname = strdup(outnameOpt);
+
+        /* Remove trailing "_icon" */
+        barPos = strrchr(cmdlineP->outname, '_');
+        if (STREQ(barPos, "_icon")) 
+            *barPos = '\0';
+    } else {
+        if (STREQ(cmdlineP->inputFilespec, "-"))
+            cmdlineP->outname = strdup("noname");
+        else {
+            char * dotPos;
+
+            cmdlineP->outname = strdup(cmdlineP->inputFilespec);
+
+            /* remove extension */
+            dotPos = strrchr(cmdlineP->outname, '.');
+            if (dotPos)
+                *dotPos = '\0';
+        }
+    }
+}
+
+
+
+
+static char*
+genNumstr(int  const number, 
+          int  const base, 
+          char const low_char, 
+          int  const digits ) {
+/*----------------------------------------------------------------------------
+  Generate a string 'digits' characters long in newly malloc'ed
+  storage that is the number 'number' displayed in base 'base'.  Fill it on
+  the left with zero digits and truncate on the left if it doesn't fit.
+
+  Use the characters 'low_char' to 'low_char' + 'base' to represent the
+  digits 0 to 'base'. 
+-----------------------------------------------------------------------------*/
+    char* str;
+    char* p;
+    int d;
+    int i;
+
+    /* Allocate memory for printed number.  Abort if error. */
+    str = (char*) malloc(digits + 1);
+    if (str == NULL)
+        pm_error("out of memory allocating number string");
+
+    /* Generate characters starting with least significant digit. */
+    i = number;
+    p = str + digits;
+    *p-- = '\0';    /* nul terminate string */
+    while (p >= str) {
+        d = i % base;
+        i /= base;
+        *p-- = low_char + d;
+    }
+    return str;
+}
+
+
+
+static const char * 
+uilName(const char * const rgbname, bool const transparent) {
+/*----------------------------------------------------------------------------
+   Return a string in newly malloc'ed storage which is an appropriate 
+   color name for the UIL palette.  It is the same as the rgb name,
+   except that blanks are replaced by underscores, and if 'transparent'
+   is true, it is "background color".  The latter is a strange name of
+   a color, but it works pretty much the same in the UIL colortable() value.
+-----------------------------------------------------------------------------*/
+    char * output;
+
+    if (transparent)
+        output = strdup("background color");
+    else {
+        int i;
+
+        output = malloc(strlen(rgbname) + 5 + 1);
+        if (output == NULL)
+            pm_error( "out of memory allocating color name" );
+        
+        for (i = 0; i < strlen(rgbname); ++i) {
+            if (rgbname[i] == ' ')
+                output[i] = '_';
+            else
+                output[i] = rgbname[i];
+        }
+        output[strlen(rgbname)] = '\0';
+    }
+
+    return output;
+}
+
+
+
+static void
+genCmap(struct pam *   const pamP,
+        tupletable     const chv, 
+        unsigned int   const ncolors, 
+        cixel_map            cmap[MAXCOLORS], 
+        unsigned int * const charsppP,
+        bool           const verbose) {
+
+    unsigned int const base = (int) HIGH_CHAR - (int) LOW_CHAR + 1;
+    char* colorname;
+    unsigned int colorIndex;
+    {
+        /* Figure out how many characters per pixel we'll be using.
+        ** Don't want to be forced to link with libm.a, so using a
+        ** division loop rather than a log function.  
+        */
+        unsigned int i;
+        for (*charsppP = 0, i = ncolors; i > 0; ++(*charsppP))
+            i /= base;
+    }
+
+    /* Generate the character-pixel string and the rgb name for each colormap
+    ** entry.
+    */
+    for (colorIndex = 0; colorIndex < ncolors; ++colorIndex) {
+        bool nameAlreadyInCmap;
+        unsigned int indexOfName;
+        bool transparent;
+        int j;
+        
+        if (pamP->depth-1 < PAM_TRN_PLANE)
+            transparent = FALSE;
+        else 
+            transparent = 
+                chv[colorIndex]->tuple[PAM_TRN_PLANE] < pamP->maxval/2;
+
+        /* Generate color name string. */
+        colorname = pam_colorname(pamP, chv[colorIndex]->tuple, 
+                                  PAM_COLORNAME_ENGLISH);
+
+        /* We may have already assigned a character code to this color
+           name/transparency because the same color name can apply to
+           two different colors because we said we wanted the closest
+           matching color that has an English name, and we recognize
+           only one transparent color.  If that's the case, we just
+           make a cross-reference.  
+        */
+        nameAlreadyInCmap = FALSE;   /* initial assumption */
+        for (j = 0; j < colorIndex; ++j) {
+            if (cmap[j].rgbname != NULL && 
+                STREQ(colorname, cmap[j].rgbname) &&
+                cmap[j].transparent == transparent) {
+                nameAlreadyInCmap = TRUE;
+                indexOfName = j;
+            }
+        }
+        if (nameAlreadyInCmap) {
+            /* Make the entry a cross-reference to the earlier entry */
+            cmap[colorIndex].uilname = NULL;
+            cmap[colorIndex].rgbname = NULL;
+            cmap[colorIndex].cixel = cmap[indexOfName].cixel;
+        } else {
+            cmap[colorIndex].uilname = uilName(colorname, transparent);
+            cmap[colorIndex].rgbname = strdup(colorname);
+            if (cmap[colorIndex].rgbname == NULL)
+                pm_error( "out of memory allocating color name" );
+            
+            cmap[colorIndex].transparent = transparent;
+            
+            /* Generate color value characters. */
+            cmap[colorIndex].cixel = 
+                genNumstr(colorIndex, base, LOW_CHAR, *charsppP);
+            if (verbose)
+                pm_message("Adding color '%s' %s = '%s' to UIL colormap",
+                           cmap[colorIndex].rgbname, 
+                           cmap[colorIndex].transparent ? "TRANS" : "OPAQUE",
+                           cmap[colorIndex].cixel);
+        }
+    }
+}
+
+
+
+static void
+writeUilHeader(const char * const outname) {
+    /* Write out the UIL header. */
+    printf( "module %s\n", outname );
+    printf( "version = 'V1.0'\n" );
+    printf( "names = case_sensitive\n" );
+    printf( "include file 'XmAppl.uil';\n" );
+}
+
+
+
+static void
+writeColormap(const char * const outname, 
+              cixel_map          cmap[MAXCOLORS], 
+              unsigned int const ncolors) {
+
+    {
+        int i;
+
+        /* Write out the colors. */
+        printf("\n");
+        printf("value\n");
+        for (i = 0; i < ncolors; ++i)
+            if (cmap[i].uilname != NULL && !cmap[i].transparent) 
+                printf("    %s : color( '%s' );\n", 
+                       cmap[i].uilname, cmap[i].rgbname );
+    }
+    {
+        /* Write out the ascii colormap. */
+
+        int i;
+        bool printedOne;
+
+        printf("\n");
+        printf("value\n");
+        printf("  %s_rgb : color_table (\n", outname);
+        printedOne = FALSE; 
+        for (i = 0; i < ncolors; ++i)
+            if (cmap[i].uilname != NULL) {
+                if (printedOne)
+                    printf(",\n");
+                printf("    %s = '%s'", cmap[i].uilname, cmap[i].cixel);
+                printedOne = TRUE;
+            }     
+        printf("\n");
+        printf("    );\n");
+    }
+}
+
+
+
+static void
+writeRaster(struct pam *  const pamP, 
+            tuple **      const tuples,
+            const char *  const outname,
+            cixel_map           cmap[MAXCOLORS], 
+            unsigned int  const ncolors,
+            tuplehash     const cht, 
+            unsigned int  const charspp) {
+    
+    int row;
+    /* Write out the ascii character-pixel image. */
+
+    printf("\n");
+    printf("%s_icon : exported icon( color_table = %s_rgb,\n",
+           outname, outname);
+    for (row = 0; row < pamP->height; ++row) {
+        int col;
+
+        printf("    '");
+        for (col = 0; col < pamP->width; ++col) {
+            int colorIndex;
+            int found;
+            if ((col * charspp) % 70 == 0 && col > 0)
+                printf( "\\\n" );       /* line continuation */
+            pnm_lookuptuple(pamP, cht, tuples[row][col], &found, &colorIndex);
+            if (!found)
+                pm_error("INTERNAL ERROR: color not found in colormap");
+            printf("%s", cmap[colorIndex].cixel);
+        }
+        if (row != pamP->height - 1)
+            printf("',\n");
+        else
+            printf("'\n"); 
+    }
+    printf(");\n");
+    printf("\n");
+}
+
+
+static void
+freeString(const char * const s) {
+/*----------------------------------------------------------------------------
+   This is just free(), but with type checking for const char *.
+-----------------------------------------------------------------------------*/
+    free((void *)s);
+}
+
+
+
+static void
+freeCmap(cixel_map cmap[], unsigned int const ncolors) {
+
+    int i;
+
+    for (i = 0; i < ncolors; ++i) {
+        cixel_map const cmapEntry = cmap[i];
+        if (cmapEntry.uilname)
+            freeString(cmapEntry.uilname);
+        if (cmapEntry.rgbname)
+            freeString(cmapEntry.uilname);
+    }
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    struct pam pam;   /* Input PAM image */
+    FILE* ifP;
+    tuple** tuples;
+    unsigned int ncolors;
+    tuplehash cht;
+    tupletable chv;
+    cixel_map cmap[MAXCOLORS];
+    unsigned int charspp;
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+    tuples = pnm_readpam(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
+    pm_close(ifP);
+
+    pm_message("computing colormap...");
+
+    chv = pnm_computetuplefreqtable(&pam, tuples, MAXCOLORS, &ncolors);
+    if (chv == NULL)
+        pm_error("too many colors - try doing a 'pnmquant %u'", MAXCOLORS);
+    if (cmdline.verbose)
+        pm_message("%u colors found", ncolors);
+
+    /* Make a hash table for fast color lookup. */
+    cht = pnm_computetupletablehash(&pam, chv, ncolors);
+
+    /* Now generate the character-pixel colormap table. */
+    pm_message("looking up color names, assigning character codes...");
+    genCmap(&pam, chv, ncolors, cmap, &charspp, cmdline.verbose);
+
+    pm_message("generating UIL...");
+    writeUilHeader(cmdline.outname);
+
+    writeColormap(cmdline.outname, cmap, ncolors);
+
+    writeRaster(&pam, tuples, cmdline.outname, cmap, ncolors, cht, charspp);
+
+    printf("end module;\n");
+
+    free(cmdline.outname);
+    freeCmap(cmap, ncolors);
+
+    return 0;
+}
diff --git a/converter/other/pamtoxvmini.c b/converter/other/pamtoxvmini.c
new file mode 100644
index 00000000..dc56e912
--- /dev/null
+++ b/converter/other/pamtoxvmini.c
@@ -0,0 +1,250 @@
+/*=============================================================================
+                                    pamtoxvmini
+===============================================================================
+   Convert Netpbm image to XV mini thumbnail.
+
+   Written by Bryan Henderson in April 2006 and contributed to the public
+   domain.
+=============================================================================*/
+
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "nstring.h"
+#include "pam.h"
+#include "pammap.h"
+
+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
+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
+writeXvHeader(FILE *       const ofP,
+              unsigned int const cols,
+              unsigned int const rows,
+              unsigned int const maxval) {
+           
+    fprintf(ofP, "P7 332\n");
+
+    fprintf(ofP, "# Created by Pamtoxvmini\n");
+    fprintf(ofP, "#END_OF_COMMENTS\n");
+
+    fprintf(ofP, "%u %u %u\n", cols, rows, maxval);
+}
+
+
+
+static void
+findClosestColor(struct pam *      const pamP,
+                 tuple             const tuple, 
+                 const xvPalette * const xvPaletteP,
+                 unsigned int *    const paletteIndexP) {
+/*----------------------------------------------------------------------------
+   Find the color in the palette *xvPaletteP that is closest to the color
+   'tuple' and return its index in the palette.
+-----------------------------------------------------------------------------*/
+    unsigned int paletteIndex;
+    unsigned int bestPaletteIndex;
+    unsigned int bestDistanceSoFar;
+
+    /* An entry condition is that the tuple have the same form as the
+       colors in the XV palette:
+    */
+    assert(pamP->depth >= 3);
+    assert(pamP->maxval = 255);
+
+    bestPaletteIndex = 0;
+    bestDistanceSoFar = UINT_MAX;
+
+    for (paletteIndex = 0; paletteIndex < 256; ++paletteIndex) {
+        unsigned int const tupleRed = tuple[PAM_RED_PLANE];
+        unsigned int const tupleGrn = tuple[PAM_GRN_PLANE];
+        unsigned int const tupleBlu = tuple[PAM_BLU_PLANE];
+        
+        unsigned int const paletteRed = xvPaletteP->red[paletteIndex];
+        unsigned int const paletteGrn = xvPaletteP->grn[paletteIndex];
+        unsigned int const paletteBlu = xvPaletteP->blu[paletteIndex];
+
+        unsigned int const distance = 
+            SQR((int)tupleRed - (int)paletteRed) +
+            SQR((int)tupleGrn - (int)paletteGrn) +
+            SQR((int)tupleBlu - (int)paletteBlu);
+
+        if (distance < bestDistanceSoFar) {
+            bestDistanceSoFar = distance;
+            bestPaletteIndex = paletteIndex;
+        }
+    }
+    *paletteIndexP = bestPaletteIndex;
+}
+
+
+
+static void
+getPaletteIndexThroughCache(struct pam *      const pamP,
+                            tuple             const tuple,
+                            const xvPalette * const xvPaletteP,
+                            tuplehash         const paletteHash,
+                            unsigned int *    const paletteIndexP) {
+/*----------------------------------------------------------------------------
+   Return as *paletteIndexP the index into the palette *xvPaletteP of
+   the color that most closely resembles the color 'tuple'.
+
+   Use the hash table *paletteIndexP as a cache to speed up the search.
+   If the tuple-index association is in *paletteIndexP, use it.  If not,
+   find it the hard way and add it to *palettedIndexP for the next guy.
+-----------------------------------------------------------------------------*/
+    bool found;
+    int paletteIndex;
+
+    pnm_lookuptuple(pamP, paletteHash, tuple, &found, &paletteIndex);
+    if (found)
+        *paletteIndexP = paletteIndex;
+    else {
+        bool fits;
+        findClosestColor(pamP, tuple, xvPaletteP, paletteIndexP);
+        
+        pnm_addtotuplehash(pamP, paletteHash, tuple, *paletteIndexP, &fits);
+        
+        if (!fits)
+            pm_error("Can't get memory for palette hash.");
+    }
+}
+
+    
+
+static void
+writeXvRaster(struct pam * const pamP,
+              xvPalette *  const xvPaletteP,
+              FILE *       const ofP) {
+/*----------------------------------------------------------------------------
+   Write out the XV image, from the Netpbm input file ifP, which is
+   positioned to the raster.
+
+   The XV raster contains palette indices into the palette *xvPaletteP.
+
+   If there is any color in the image which is not in the palette, we
+   fail the program.  We really should use the closest color in the palette
+   instead.
+-----------------------------------------------------------------------------*/
+    tuplehash paletteHash;
+    tuple * tuplerow;
+    unsigned int row;
+    unsigned char * xvrow;
+
+    paletteHash = pnm_createtuplehash();
+
+    tuplerow = pnm_allocpamrow(pamP);
+    xvrow = (unsigned char*)pm_allocrow(pamP->width, 1);
+
+    for (row = 0; row < pamP->height; ++row) {
+        unsigned int col;
+
+        pnm_readpamrow(pamP, tuplerow);
+        pnm_scaletuplerow(pamP, tuplerow, tuplerow, 255);
+        pnm_makerowrgb(pamP, tuplerow);
+
+        for (col = 0; col < pamP->width; ++col) {
+            unsigned int paletteIndex;
+
+            getPaletteIndexThroughCache(pamP, tuplerow[col], xvPaletteP,
+                                        paletteHash, &paletteIndex);
+
+            assert(paletteIndex < 256);
+
+            xvrow[col] = paletteIndex;
+        }
+        fwrite(xvrow, 1, pamP->width, ofP);
+    }
+
+    pm_freerow((char*)xvrow);
+    pnm_freepamrow(pamP);
+
+    pnm_destroytuplehash(paletteHash);
+}
+
+
+
+int 
+main(int    argc,
+     char * argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    struct pam pam;
+    xvPalette xvPalette;
+ 
+    ppm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    makeXvPalette(&xvPalette);
+
+    pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(allocation_depth));
+
+    pnm_setminallocationdepth(&pam, 3);
+
+    writeXvHeader(stdout, pam.width, pam.height, pam.maxval);
+    
+    writeXvRaster(&pam, &xvPalette, stdout);
+
+    pm_close(ifP);
+
+    return 0;
+}
diff --git a/converter/other/pbmtopgm.c b/converter/other/pbmtopgm.c
new file mode 100644
index 00000000..69b20fb2
--- /dev/null
+++ b/converter/other/pbmtopgm.c
@@ -0,0 +1,80 @@
+/* pbmtopgm.c - convert PBM to PGM by totalling pixels over sample area
+ * AJCD 12/12/90
+ */
+
+#include <stdio.h>
+
+#include "pm_c_util.h"
+#include "pgm.h"
+
+int
+main(int argc, char *argv[]) {
+
+    gray *outrow, maxval;
+    int right, left, down, up;
+    bit **inbits;
+    int rows, cols;
+    FILE *ifd;
+    int row;
+    int width, height;
+    const char * const usage = "<w> <h> [pbmfile]";
+   
+
+    pgm_init( &argc, argv );
+
+    if (argc > 4 || argc < 3)
+        pm_usage(usage);
+
+    width = atoi(argv[1]);
+    height = atoi(argv[2]);
+    if (width < 1 || height < 1)
+        pm_error("width and height must be > 0");
+    left=width/2; right=width-left;
+    up=width/2; down=height-up;
+
+    if (argc == 4)
+        ifd = pm_openr(argv[3]);
+    else
+        ifd = stdin ;
+
+    inbits = pbm_readpbm(ifd, &cols, &rows) ;
+    
+    if (width > cols)
+        pm_error("You specified a sample width (%u columns) which is greater "
+                 "than the image width (%u columns)", height, rows);
+    if (height > rows)
+        pm_error("You specified a sample height (%u rows) which is greater "
+                 "than the image height (%u rows)", height, rows);
+
+    outrow = pgm_allocrow(cols) ;
+    maxval = MIN(PGM_OVERALLMAXVAL, width*height);
+    pgm_writepgminit(stdout, cols, rows, maxval, 0) ;
+
+    for (row = 0; row < rows; row++) {
+        int const t = (row > up) ? (row-up) : 0;
+        int const b = (row+down < rows) ? (row+down) : rows;
+        int const onv = height - (t-row+up) - (row+down-b);
+        unsigned int col;
+        for (col = 0; col < cols; col++) {
+            int const l = (col > left) ? (col-left) : 0;
+            int const r = (col+right < cols) ? (col+right) : cols;
+            int const onh = width - (l-col+left) - (col+right-r);
+            int value;
+            int x;
+
+            value = 0;  /* initial value */
+
+            for (x = l; x < r; ++x) {
+                int y;
+                for (y = t; y < b; ++y)
+                    if (inbits[y][x] == PBM_WHITE) 
+                        ++value;
+            }
+            outrow[col] = maxval*value/(onh*onv);
+        }
+        pgm_writepgmrow(stdout, outrow, cols, maxval, 0) ;
+    }
+    pm_close(ifd);
+
+    return 0;
+}
diff --git a/converter/other/pclxl.h b/converter/other/pclxl.h
new file mode 100644
index 00000000..b1d1c043
--- /dev/null
+++ b/converter/other/pclxl.h
@@ -0,0 +1,390 @@
+#ifndef PCLXL_H_INCLUDED
+#define PCLXL_H_INCLUDED
+
+/* This file defines elements of Hewlett Packard's PCL-XL printer
+   control language (fka PCL-6)
+*/
+
+enum ArcDirection {
+    eClockWise = 0,
+    eCounterClockWise = 1
+};
+
+enum CharSubModeArray {
+    eNoSubstitution = 0,
+    eVerticalSubstitution = 1
+};
+
+enum ClipRegion {
+    eInterior = 0,
+    eExterior = 1
+};
+
+enum ColorDepth {
+    e1Bit = 0,
+    e4Bit = 1,
+    e8Bit = 2
+};
+
+enum ColrMapping {
+    eDirectPixel = 0,
+    eIndexedPixel = 1
+};
+
+enum Colorspace {
+    eGray = 1,
+    eRGB = 2
+};
+
+enum Compression {
+    eNoCompression = 0,
+    eRLECompression = 1,
+    eJPEGCompression = 2
+};
+
+enum DataOrg {
+    eBinaryHighByteFirst = 0,
+    eBinaryLowByteFirst = 1
+};
+
+enum DataSource {
+    eDefault = 0
+};
+
+enum DataType {
+    eUByte = 0,
+    eSByte = 1,
+    eUint16 = 2,
+    eSint16 = 3
+};
+
+enum DitherMatrix {  
+    eDeviceBest = 0
+}; 
+
+
+enum DuplexPageMode {
+    eDuplexHorizontalBinding = 0,
+    eDuplexVerticalBinding = 1
+};
+
+enum DuplexPageSide {
+    eFrontMediaSide = 0,
+    eBackMediaSide = 1
+};
+
+enum ErrorReport {
+    eNoReporting = 0,
+    eBackChannel = 1,
+    eErrorPage = 2,
+    eBackChAndErrPage = 3,
+    eNWBackChannel = 4 ,
+    eNWErrorPage = 5,
+    eNWBackChAndErrPage = 6
+};
+
+enum FillMode {
+    eNonZeroWinding = 0,
+    eEvenOdd = 1
+};
+
+enum LineCap {
+    eButtCap = 0,
+    eRoundCap = 1,
+    eSquareCap = 2,
+    eTriangleCap = 3
+};
+
+enum LineJoin {
+    eMiterJoin = 0,
+    eRoundJoin = 1,
+    eBevelJoin = 2,
+    eNoJoin = 3
+};
+
+enum Measure {
+    eInch = 0,
+    eMillimeter = 1,
+    eTenthsOfAMillimeter = 2
+};
+
+enum MediaSource {
+    eDefaultSource = 0,
+    eAutoSelect = 1,
+    eManualFeed = 2,
+    eMultiPurposeTray = 3,
+    eUpperCassette = 4,
+    eLowerCassette = 5,
+    eEnvelopeTray = 6,
+    eThirdCassette = 7
+};
+
+enum MediaDestination {
+    eDefaultDestination = 0,
+    eFaceDownBin = 1,
+    eFaceUpBin = 2,
+    eJobOffsetBin = 3
+};
+
+enum Orientation {
+    ePortraitOrientation = 0,
+    eLandscapeOrientation = 1,
+    eReversePortrait = 2,
+    eReverseLandscape = 3
+};
+
+enum PatternPersistence {
+    eTempPattern = 0,
+    ePagePattern = 1,
+    eSessionPattern = 2
+};
+
+enum SimplexPageMode {
+    eSimplexFrontSide = 0
+};
+
+enum TxMode {
+    eOpaque = 0,
+    eTransparent = 1
+};
+
+enum WritingMode{
+    eHorizontal = 0,
+    eVertical = 1
+};
+
+enum Attribute {
+    aPaletteDepth        =   2,
+    aColorSpace          =   3,
+    aNullBrush           =   4,
+    aNullPen             =   5,
+    aPaletteData         =   6,
+    aPatternSelectID     =   8,
+    aGrayLevel           =   9,
+    aRGBColor            =  11,
+    aPatternOrigin       =  12,
+    aNewDestinationSize  =  13,
+    aPrimaryArray        =  14,
+    aPrimaryDepth        =  15,
+    aDeviceMatrix        =  33,
+    aDitherMatrixDataType=  34,
+    aDitherOrigin        =  35,
+    aMediaDestination    =  36,
+    aMediaSize           =  37,
+    aMediaSource         =  38,
+    aMediaType           =  39,
+    aOrientation         =  40,
+    aPageAngle           =  41,
+    aPageOrigin          =  42,
+    aPageScale           =  43,
+    aROP3                =  44,
+    aTxMode              =  45,
+    aCustomMediaSize     =  47,
+    aCustomMediaUnits    =  48,
+    aPageCopies          =  49,
+    aDitherMatrixSize    =  50,
+    aDitherMatrixDepth   =  51,
+    aSimplexPageMode     =  52,
+    aDuplexPageMode      =  53,
+    aDuplexPageSide      =  54,
+    aArcDirection        =  65,
+    aBoundingBox         =  66,
+    aDashOffset          =  67,
+    aEllipseDimension    =  68,
+    aEndPoint            =  69,
+    aFillMode            =  70,
+    aLineCapStyle        =  71,
+    aLineJoinStyle       =  72,
+    aMiterLength         =  73,
+    aLineDashStyle       =  74,
+    aPenWidth            =  75,
+    aPoint               =  76,
+    aNumberOfPoints      =  77,
+    aSolidLine           =  78,
+    aStartPoint          =  79,
+    aPointType           =  80,
+    aControlPoint1       =  81,
+    aControlPoint2       =  82,
+    aClipRegion          =  83,
+    aClipMode            =  84,
+    aColorDepth          =  98,
+    aBlockHeight         =  99,
+    aColorMapping        = 100,
+    aCompressMode        = 101,
+    aDestinationBox      = 102,
+    aDestinationSize     = 103,
+    aPatternPersistence  = 104,
+    aPatternDefineID     = 105,
+    aSourceHeight        = 107,
+    aSourceWidth         = 108,
+    aStartLine           = 109,
+    aPadBytesMultiple    = 110,
+    aBlockByteLength     = 111,
+    aNumberOfScanLines   = 115,
+    aColorTreatment      = 120,
+    aCommentData         = 129,
+    aDataOrg             = 130,
+    aMeasure             = 134,
+    aSourceType          = 136,
+    aUnitsPerMeasure     = 137,
+    aStreamName          = 139,
+    aStreamDataLength    = 140,
+    aErrorReport         = 143,
+    aCharAngle           = 161,
+    aCharCode            = 162,
+    aCharDataSize        = 163,
+    aCharScale           = 164,
+    aCharShear           = 165,
+    aCharSize            = 166,
+    aFontHeaderLength    = 167,
+    aFontName            = 168,
+    aFontFormat          = 169,
+    aSymbolSet           = 170,
+    aTextData            = 171,
+    aCharSubModeArray    = 172,
+    aXSpacingData        = 175,
+    aYSpacingData        = 176,
+    aCharBoldValue       = 177
+};
+                                          
+enum Operator {
+    oBeginSession        = 0x41,
+    oEndSession          = 0x42,
+    oBeginPage           = 0x43,
+    oEndPage             = 0x44,
+    oComment             = 0x47,
+    oOpenDataSource      = 0x48,
+    oCloseDataSource     = 0x49,
+    oBeginFontHeader     = 0x4f,
+    oReadFontHeader      = 0x50,
+    oEndFontHeader       = 0x51,
+    oBeginChar           = 0x52,
+    oReadChar            = 0x53,
+    oEndChar             = 0x54,
+    oRemoveFont          = 0x55,
+    oSetCharAttributes   = 0x56,
+    oBeginStream         = 0x5b,
+    oReadStream          = 0x5c,
+    oEndStream           = 0x5d,
+    oExecStream          = 0x5e,
+    oRemoveStream        = 0x5f,
+    oPopGS               = 0x60,
+    oPushGS              = 0x61,
+    oSetClipReplace      = 0x62,
+    oSetBrushSource      = 0x63,
+    oSetCharAngle        = 0x64,
+    oSetCharScale        = 0x65,
+    oSetCharShear        = 0x66,
+    oSetClipIntersect    = 0x67,
+    oSetClipRectangle    = 0x68,
+    oSetClipToPage       = 0x69,
+    oSetColorSpace       = 0x6a,
+    oSetCursor           = 0x6b,
+    oSetCursorRel        = 0x6c,
+    oSetHalftoneMethod   = 0x6d,
+    oSetFillMode         = 0x6e,
+    oSetFont             = 0x6f,
+    oSetLineDash         = 0x70,
+    oSetLineCap          = 0x71,
+    oSetLineJoin         = 0x72,
+    oSetMiterLimit       = 0x73,
+    oSetPageDefaultCTM   = 0x74,
+    oSetPageOrigin       = 0x75,
+    oSetPageRotation     = 0x76,
+    oSetPageScale        = 0x77,
+    oSetPatternTxMode    = 0x78,
+    oSetPenSource        = 0x79,
+    oSetPenWidth         = 0x7a,
+    oSetROP              = 0x7b,
+    oSetSourceTxMode     = 0x7c,
+    oSetCharBoldValue    = 0x7d,
+    oSetClipMode         = 0x7f,
+    oSetPathToClip       = 0x80,
+    oSetCharSubMode      = 0x81,
+    oCloseSubPath        = 0x84,
+    oNewPath             = 0x85,
+    oPaintPath           = 0x86,
+    oArcPath             = 0x91,
+    oBezierPath          = 0x93,
+    oBezierRelPath       = 0x95,
+    oChord               = 0x96,
+    oChordPath           = 0x97,
+    oEllipse             = 0x98,
+    oEllipsePath         = 0x99,
+    oLinePath            = 0x9b,
+    oLineRelPath         = 0x9d,
+    oPie                 = 0x9e,
+    oPiePath             = 0x9f,
+    oRectangle           = 0xa0,
+    oRectanglePath       = 0xa1,
+    oRoundRectangle      = 0xa2,  
+    oRoundRectanglePath  = 0xa3,
+    oText                = 0xa8,
+    oTextPath            = 0xa9,
+    oBeginImage          = 0xb0,
+    oReadImage           = 0xb1,
+    oEndImage            = 0xb2,
+    oBeginRastPattern    = 0xb3,
+    oReadRastPattern     = 0xb4,
+    oEndRastPattern      = 0xb5,
+    oBeginScan           = 0xb6,
+    oEndScan             = 0xb8,
+    oScanLineRel         = 0xb9
+};
+
+enum MediaSize {
+    eLetterPaper,
+    eLegalPaper,
+    eA4Paper,
+    eExecPaper,
+    eLedgerPaper,
+    eA3Paper,
+    eCOM10Envelope,
+    eMonarchEnvelope,
+    eC5Envelope,
+    eDLEnvelope,
+    eJB4Paper,
+    eJB5Paper,
+    etralala,
+    eB5Envelope,
+    eJPostcard,
+    eJDoublePostcard,
+    eA5Paper,
+    eA6Paper,
+    eJB6Paper
+};
+
+struct sPaperFormat {
+    const char *name;
+    int xl_nr;
+    float width,height; /* inch */
+} xlPaperFormats[] = {
+    {"letter",eLetterPaper,8.5,11.0},
+    {"legal",eLegalPaper,8.5,14},
+    {"a4",eA4Paper,8.26389,11.6944},
+    {"exec",eExecPaper,190/2.54,254/2.54},
+    {"ledger",eLedgerPaper,279/2.54,432/2.54},
+    {"a3",eA3Paper,11.6944,16.5278},
+    {"com10envelope",eCOM10Envelope,105/2.54,241/2.54},
+    {"monarchenvelope",eMonarchEnvelope,98/2.54,191/2.54},
+    {"c5envelope",eC5Envelope,162/2.54,229/2.54},
+    {"dlenvelope",eDLEnvelope,110/2.54,220/2.54},
+    {"jb4",eJB4Paper,257/2.54,364/2.54},
+    {"jb5",eJB5Paper,182/2.54,257/2.54},
+    {"b5envelope",eB5Envelope,176/2.54,250/2.54},
+    {"",-1,0,0},
+    {"jpostcard",eJPostcard,148/2.54,200/2.54},
+    {"jdoublepostcard",eJDoublePostcard,100/2.54,148/2.54},
+    {"a5",eA5Paper, 5.84722,8.26389},
+    {"a6",eA6Paper,4.125,5.84722},
+    {"jb6",eJB6Paper,0,0},
+    {NULL,0,0,0}
+};
+
+enum {
+    eSTART,
+    eRLE,
+    eLIT 
+} RLEstates;
+
+#endif
diff --git a/converter/other/pfmtopam.c b/converter/other/pfmtopam.c
new file mode 100644
index 00000000..9430f5fa
--- /dev/null
+++ b/converter/other/pfmtopam.c
@@ -0,0 +1,395 @@
+/*****************************************************************************
+                                  pfmtopam
+******************************************************************************
+  This program converts a PFM (Portable Float Map) image to PAM.
+  
+  By Bryan Henderson, San Jose, CA April 2004.
+
+  Contributed to the public domain by its author.
+
+*****************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <assert.h>
+
+#include "pam.h"
+#include "pm_gamma.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+
+
+struct cmdlineInfo {
+    const char * inputFilespec;
+    unsigned int verbose;
+    sample maxval;
+};
+
+
+
+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 = malloc( 100*sizeof( optEntry ) );
+    /* Instructions to optParseOptions3 on how to parse our options. */
+    optStruct3 opt;
+  
+    unsigned int option_def_index;
+    unsigned int maxvalSpec;
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "maxval",   OPT_UINT, &cmdlineP->maxval, &maxvalSpec,        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 (!maxvalSpec)
+        cmdlineP->maxval = PNM_MAXMAXVAL;
+
+    if (cmdlineP->maxval > PNM_OVERALLMAXVAL)
+        pm_error("Maximum allowed -maxval is %u.  You specified %u",
+                 PNM_OVERALLMAXVAL, (unsigned)cmdlineP->maxval);
+    else if (cmdlineP->maxval == 0)
+        pm_error("-maxval cannot be 0");
+
+    /* Get the program parameters */
+
+    if (argc-1 >= 1)
+        cmdlineP->inputFilespec = argv[1];
+    else
+        cmdlineP->inputFilespec = "-";
+    
+    if (argc-1 > 1)
+        pm_error("Program takes at most one argument:  the file name.  "
+                 "You specified %d", argc-1);
+}
+
+
+enum endian {ENDIAN_BIG, ENDIAN_LITTLE};
+
+
+static enum endian machineEndianness;
+
+
+
+static enum endian
+thisMachineEndianness(void) {
+/*----------------------------------------------------------------------------
+   Endianness is a component of the format in which a machine represents
+   a number in memory or a register.  It is the only component of the format
+   that varies among typical machines.
+
+   Big endianness is the natural format.  In this format, if an integer is
+   4 bytes, to be stored at memory address 100-103, the most significant 
+   byte goes at 100, the next most significant at 101, and the least
+   significant byte at 103.  This is natural because it matches the way
+   humans read and write numbers.  I.e. 258 is stored as 0x00000102.
+
+   Little endian is extremely common because it is used by IA32.  In the
+   example above, the least significant byte goes first, so 258 would be
+   stored as 0x02010000.
+
+   You can extend this concept to floating point numbers, even though the
+   bytes of a floating point number differ by more than significance.
+-----------------------------------------------------------------------------*/
+    short const testNumber = 0x0001;
+
+    unsigned char * const storedNumber = (unsigned char *)&testNumber;
+    enum endian endianness;
+    
+    if (storedNumber[0] == 0x01)
+        endianness = ENDIAN_LITTLE;
+    else
+        endianness = ENDIAN_BIG;
+
+    return endianness;
+}
+
+
+
+typedef union {
+    unsigned char bytes[4];      /* as read from the file */
+    float value;
+        /* This is valid only if the pfmSample has the same endianness
+           as the machine we're running on.
+        */
+} pfmSample;
+
+
+
+static float
+floatFromPfmSample(pfmSample   const sample, 
+                   enum endian const pfmEndianness) {
+/*----------------------------------------------------------------------------
+   Type converter
+-----------------------------------------------------------------------------*/
+    if (machineEndianness == pfmEndianness) {
+        return sample.value;
+    } else {
+        pfmSample rightEndianSample;
+        unsigned int i, j;
+        
+        for (i = 0, j = sizeof(sample.bytes)-1; 
+             i < sizeof(sample.bytes); 
+             ++i, --j)
+            
+            rightEndianSample.bytes[i] = sample.bytes[j];
+
+        return rightEndianSample.value;
+    }
+}
+
+
+
+struct pfmHeader {
+    unsigned int width;
+    unsigned int height;
+    bool color;
+    float scaleFactor;
+    enum endian endian;
+};
+
+
+static void
+readPfmHeader(FILE *             const ifP,
+              struct pfmHeader * const pfmHeaderP) {
+
+    int firstChar;
+    int secondChar;
+    float scaleFactorEndian;
+
+    firstChar = fgetc(ifP);
+    if (firstChar == EOF)
+        pm_error("Error reading first character of PFM file");
+    secondChar = fgetc(ifP);
+    if (secondChar == EOF)
+        pm_error("Error reading second character of PFM file");
+
+    if (firstChar != 'P' || (secondChar != 'F' && secondChar != 'f'))
+        pm_error("First two characters of input file are '%c%c', but "
+                 "for a valid PFM file, they must be 'PF' or 'Pf'.",
+                 firstChar, secondChar);
+
+    {
+        int whitespace;
+
+        whitespace = fgetc(ifP);
+        if (whitespace == EOF)
+            pm_error("Error reading third character of PFM file");
+
+        if (!isspace(whitespace))
+            pm_error("The 3rd character of the input file is not whitespace.");
+    }
+    {
+        int rc;
+        char whitespace;
+
+        rc = fscanf(ifP, "%u %u%c", 
+                    &pfmHeaderP->width, &pfmHeaderP->height, &whitespace);
+
+        if (rc == EOF)
+            pm_error("Error reading the width and height from input file.");
+        else if (rc != 3)
+            pm_error("Invalid input file format where width and height "
+                     "are supposed to be (should be two positive decimal "
+                     "integers separated by a space and followed by "
+                     "white space)");
+        
+        if (!isspace(whitespace))
+            pm_error("Invalid input file format -- '%c' instead of "
+                     "white space after height", whitespace);
+
+        if (pfmHeaderP->width == 0)
+            pm_error("Invalid input file: image width is zero");
+        if (pfmHeaderP->height == 0)
+            pm_error("Invalid input file: image height is zero");
+    }
+    {
+        int rc;
+        char whitespace;
+
+        rc = fscanf(ifP, "%f%c", &scaleFactorEndian, &whitespace);
+
+        if (rc == EOF)
+            pm_error("Error reading the scale factor from input file.");
+        else if (rc != 2)
+            pm_error("Invalid input file format where scale factor "
+                     "is supposed to be (should be a floating point decimal "
+                     "number followed by white space");
+        
+        if (!isspace(whitespace))
+            pm_error("Invalid input file format -- '%c' instead of "
+                     "white space after scale factor", whitespace);
+    }
+
+    pfmHeaderP->color = (secondChar == 'F');  
+        /* 'PF' = RGB, 'Pf' = monochrome */
+
+    if (scaleFactorEndian > 0.0) {
+        pfmHeaderP->endian = ENDIAN_BIG;
+        pfmHeaderP->scaleFactor = scaleFactorEndian;
+    } else if (scaleFactorEndian < 0.0) {
+        pfmHeaderP->endian = ENDIAN_LITTLE;
+        pfmHeaderP->scaleFactor = - scaleFactorEndian;
+    } else
+        pm_error("Scale factor/endianness in PFM header is 0");
+}
+
+
+static void
+dumpPfmHeader(struct pfmHeader const pfmHeader) {
+
+    pm_message("width: %u, height: %u", pfmHeader.width, pfmHeader.height);
+    pm_message("color: %s", pfmHeader.color ? "YES" : "NO");
+    pm_message("endian: %s", 
+               pfmHeader.endian == ENDIAN_BIG ? "BIG" : "LITTLE");
+    pm_message("scale factor: %f", pfmHeader.scaleFactor);
+}
+
+
+
+static void
+initPam(struct pam * const pamP, 
+        int          const width, 
+        int          const height, 
+        bool         const color,
+        sample       const maxval) {
+
+    pamP->size        = sizeof(*pamP);
+    pamP->len         = PAM_STRUCT_SIZE(tuple_type);
+    pamP->file        = stdout;
+    pamP->format      = PAM_FORMAT;
+    pamP->plainformat = FALSE;
+    pamP->width       = width;
+    pamP->height      = height;
+    pamP->maxval      = maxval;
+    if (color) {
+        pamP->depth = 3;
+        strcpy(pamP->tuple_type, "RGB");
+    } else {
+        pamP->depth = 1;
+        strcpy(pamP->tuple_type, "GRAYSCALE");
+    }
+}
+
+
+
+static void
+makePamRow(struct pam * const pamP,
+           FILE *       const ifP,
+           unsigned int const pfmRow,
+           unsigned int const pfmSamplesPerRow,
+           tuplen **    const tuplenArray,
+           enum endian  const endian,
+           float        const scaleFactor,
+           pfmSample *  const pfmRowBuffer) {
+/*----------------------------------------------------------------------------
+   Make a PAM (tuple) row of the form described by *pamP, from the next
+   row in the PFM file identified by 'ifP'.  Place it in the proper location
+   in the tuple array 'tuplenArray'.
+  
+   'endian' is the endianness of the samples in the PFM file.
+
+   'pfmRowNum' is the sequence number (starting at 0, which is the
+   bottommost row of the image) of the row we are converting in the
+   PFM file.
+
+   Use 'pfmRowBuffer' as a work space; Caller must ensure this array
+   has at least 'pfmSamplesPerRow' elements of space in it.
+-----------------------------------------------------------------------------*/
+    int      const row       = pamP->height - pfmRow - 1;
+    tuplen * const tuplenRow = tuplenArray[row];
+
+    int col;
+    int pfmCursor;
+    int rc;
+
+    rc = fread(pfmRowBuffer, sizeof(pfmSample), pfmSamplesPerRow, ifP);
+    if (rc != pfmSamplesPerRow)
+        pm_error("End of file in the middle of row %d", pfmRow);
+
+    pfmCursor = 0;
+
+    for (col = 0; col < pamP->width; ++col) {
+        /* The order of planes (R, G, B) is the same in PFM as in PAM. */
+        unsigned int plane;
+        for (plane = 0; plane < pamP->depth; ++plane) {
+            float const val = 
+                floatFromPfmSample(pfmRowBuffer[pfmCursor++], endian);
+            tuplenRow[col][plane] = val / scaleFactor;
+        }
+    }
+    assert(pfmCursor == pfmSamplesPerRow);
+}
+
+
+
+int
+main(int argc, char **argv ) {
+
+    struct cmdlineInfo cmdline;
+    FILE* ifP;
+    struct pam pam;
+    struct pfmHeader pfmHeader;
+    pfmSample * pfmRowBuffer;
+    unsigned int pfmSamplesPerRow;
+    unsigned pfmRow;
+    tuplen ** tuplenArray;
+
+    machineEndianness = thisMachineEndianness();
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    readPfmHeader(ifP, &pfmHeader);
+
+    if (cmdline.verbose)
+        dumpPfmHeader(pfmHeader);
+
+    initPam(&pam, 
+            pfmHeader.width, pfmHeader.height, pfmHeader.color,
+            cmdline.maxval);
+
+    tuplenArray = pnm_allocpamarrayn(&pam);
+
+    pfmSamplesPerRow = pam.width * pam.depth;
+    
+    MALLOCARRAY_NOFAIL(pfmRowBuffer, pfmSamplesPerRow);
+
+    /* PFMs are upside down like BMPs */
+    for (pfmRow = 0; pfmRow < pam.height; ++pfmRow)
+        makePamRow(&pam, ifP, pfmRow, pfmSamplesPerRow,
+                   tuplenArray, pfmHeader.endian, pfmHeader.scaleFactor,
+                   pfmRowBuffer);
+
+    pnm_writepamn(&pam, tuplenArray);
+
+    pnm_freepamarrayn(tuplenArray, &pam);
+    free(pfmRowBuffer);
+    
+    pm_close(ifP);
+    pm_close(pam.file);
+
+    return 0;
+}
diff --git a/converter/other/pgmtopbm.c b/converter/other/pgmtopbm.c
new file mode 100644
index 00000000..98bdc332
--- /dev/null
+++ b/converter/other/pgmtopbm.c
@@ -0,0 +1,723 @@
+/* pgmtopbm.c - read a portable graymap and write a portable bitmap
+**
+** 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 <assert.h>
+#include "pgm.h"
+#include "dithers.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+
+enum halftone {QT_FS, QT_THRESH, QT_DITHER8, QT_CLUSTER, QT_HILBERT};
+
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *  inputFilespec;
+    enum halftone halftone;
+    unsigned int  clumpSize;
+    unsigned int  clusterRadius;  
+        /* Defined only for halftone == QT_CLUSTER */
+    float         threshval;
+};
+
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo *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;
+    unsigned int floydOpt, hilbertOpt, thresholdOpt, dither8Opt,
+        cluster3Opt, cluster4Opt, cluster8Opt;
+    unsigned int valueSpec, clumpSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "floyd",     OPT_FLAG,  NULL, &floydOpt,     0);
+    OPTENT3(0, "fs",        OPT_FLAG,  NULL, &floydOpt,     0);
+    OPTENT3(0, "threshold", OPT_FLAG,  NULL, &thresholdOpt, 0);
+    OPTENT3(0, "hilbert",   OPT_FLAG,  NULL, &hilbertOpt,   0);
+    OPTENT3(0, "dither8",   OPT_FLAG,  NULL, &dither8Opt,   0);
+    OPTENT3(0, "d8",        OPT_FLAG,  NULL, &dither8Opt,   0);
+    OPTENT3(0, "cluster3",  OPT_FLAG,  NULL, &cluster3Opt,  0);
+    OPTENT3(0, "c3",        OPT_FLAG,  NULL, &cluster3Opt,  0);
+    OPTENT3(0, "cluster4",  OPT_FLAG,  NULL, &cluster4Opt,  0);
+    OPTENT3(0, "c4",        OPT_FLAG,  NULL, &cluster4Opt,  0);
+    OPTENT3(0, "cluster8",  OPT_FLAG,  NULL, &cluster8Opt,  0);
+    OPTENT3(0, "c8",        OPT_FLAG,  NULL, &cluster8Opt,  0);
+    OPTENT3(0, "value",     OPT_FLOAT, &cmdlineP->threshval, 
+            &valueSpec, 0);
+    OPTENT3(0, "clump",     OPT_UINT,  &cmdlineP->clumpSize, 
+            &clumpSpec, 0);
+
+    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 (floydOpt + thresholdOpt + hilbertOpt + dither8Opt + 
+        cluster3Opt + cluster4Opt + cluster8Opt == 0)
+        cmdlineP->halftone = QT_FS;
+    else if (floydOpt + thresholdOpt + dither8Opt + 
+        cluster3Opt + cluster4Opt + cluster8Opt > 1)
+        pm_error("No cannot specify more than one halftoning type");
+    else {
+        if (floydOpt)
+            cmdlineP->halftone = QT_FS;
+        else if (thresholdOpt)
+            cmdlineP->halftone = QT_THRESH;
+        else if (hilbertOpt)
+            cmdlineP->halftone = QT_HILBERT;
+        else if (dither8Opt)
+            cmdlineP->halftone = QT_DITHER8;
+        else if (cluster3Opt) {
+            cmdlineP->halftone = QT_CLUSTER;
+            cmdlineP->clusterRadius = 3;
+        } else if (cluster4Opt) {
+            cmdlineP->halftone = QT_CLUSTER;
+            cmdlineP->clusterRadius = 4;
+        } else if (cluster8Opt) {
+            cmdlineP->halftone = QT_CLUSTER;
+            cmdlineP->clusterRadius = 8;
+        } else 
+            pm_error("INTERNAL ERROR.  No halftone option");
+    }
+
+    if (!valueSpec)
+        cmdlineP->threshval = 0.5;
+    else {
+        if (cmdlineP->threshval < 0.0)
+            pm_error("-value cannot be negative.  You specified %f",
+                     cmdlineP->threshval);
+        if (cmdlineP->threshval > 1.0)
+            pm_error("-value cannot be greater than one.  You specified %f",
+                     cmdlineP->threshval);
+    }
+            
+    if (!clumpSpec)
+        cmdlineP->clumpSize = 5;
+    else {
+        if (cmdlineP->clumpSize < 2)
+            pm_error("-clump must be at least 2.  You specified %u",
+                     cmdlineP->clumpSize);
+    }
+
+    if (argc-1 > 1)
+        pm_error("Too many arguments (%d).  There is at most one "
+                 "non-option argument:  the file name",
+                 argc-1);
+    else if (argc-1 == 1)
+        cmdlineP->inputFilespec = argv[1];
+    else
+        cmdlineP->inputFilespec = "-";
+}
+
+
+
+/* Hilbert curve tracer */
+
+#define MAXORD 18
+
+static int hil_order,hil_ord;
+static int hil_turn;
+static int hil_dx,hil_dy;
+static int hil_x,hil_y;
+static int hil_stage[MAXORD];
+static int hil_width,hil_height;
+
+static void 
+init_hilbert(int const w, 
+             int const h) {
+/*----------------------------------------------------------------------------
+  Initialize the Hilbert curve tracer 
+-----------------------------------------------------------------------------*/
+    int big,ber;
+    hil_width = w;
+    hil_height = h;
+    big = w > h ? w : h;
+    for (ber = 2, hil_order = 1; ber < big; ber <<= 1, hil_order++);
+    if (hil_order > MAXORD)
+        pm_error("Sorry, hilbert order is too large");
+    hil_ord = hil_order;
+    hil_order--;
+}
+
+
+
+static int 
+hilbert(int * const px, int * const py) {
+/*----------------------------------------------------------------------------
+  Return non-zero if got another point
+-----------------------------------------------------------------------------*/
+    int temp;
+    if (hil_ord > hil_order) {
+        /* have to do first point */
+
+        hil_ord--;
+        hil_stage[hil_ord] = 0;
+        hil_turn = -1;
+        hil_dy = 1;
+        hil_dx = hil_x = hil_y = 0;
+        *px = *py = 0;
+        return 1;
+    }
+
+    /* Operate the state machine */
+    for(;;)  {
+        switch (hil_stage[hil_ord]) {
+        case 0:
+            hil_turn = -hil_turn;
+            temp = hil_dy;
+            hil_dy = -hil_turn * hil_dx;
+            hil_dx = hil_turn * temp;
+            if (hil_ord > 0) {
+                hil_stage[hil_ord] = 1;
+                hil_ord--;
+                hil_stage[hil_ord]=0;
+                continue;
+            }
+        case 1:
+            hil_x += hil_dx;
+            hil_y += hil_dy;
+            if (hil_x < hil_width && hil_y < hil_height) {
+                hil_stage[hil_ord] = 2;
+                *px = hil_x;
+                *py = hil_y;
+                return 1;
+            }
+        case 2:
+            hil_turn = -hil_turn;
+            temp = hil_dy;
+            hil_dy = -hil_turn * hil_dx;
+            hil_dx = hil_turn * temp;
+            if (hil_ord > 0) { 
+                /* recurse */
+
+                hil_stage[hil_ord] = 3;
+                hil_ord--;
+                hil_stage[hil_ord]=0;
+                continue;
+            }
+        case 3:
+            hil_x += hil_dx;
+            hil_y += hil_dy;
+            if (hil_x < hil_width && hil_y < hil_height) {
+                hil_stage[hil_ord] = 4;
+                *px = hil_x;
+                *py = hil_y;
+                return 1;
+            }
+        case 4:
+            if (hil_ord > 0) {
+                /* recurse */
+                hil_stage[hil_ord] = 5;
+                hil_ord--;
+                hil_stage[hil_ord]=0;
+                continue;
+            }
+        case 5:
+            temp = hil_dy;
+            hil_dy = -hil_turn * hil_dx;
+            hil_dx = hil_turn * temp;
+            hil_turn = -hil_turn;
+            hil_x += hil_dx;
+            hil_y += hil_dy;
+            if (hil_x < hil_width && hil_y < hil_height) {
+                hil_stage[hil_ord] = 6;
+                *px = hil_x;
+                *py = hil_y;
+                return 1;
+            }
+        case 6:
+            if (hil_ord > 0) {
+                /* recurse */
+                hil_stage[hil_ord] = 7;
+                hil_ord--;
+                hil_stage[hil_ord]=0;
+                continue;
+            }
+        case 7:
+            temp = hil_dy;
+            hil_dy = -hil_turn * hil_dx;
+            hil_dx = hil_turn * temp;
+            hil_turn = -hil_turn;
+            /* Return from a recursion */
+            if (hil_ord < hil_order)
+                hil_ord++;
+            else
+                return 0;
+        }
+    }
+}
+
+
+
+static void doHilbert(FILE *       const ifP,
+                      unsigned int const clump_size) {
+/*----------------------------------------------------------------------------
+  Use hilbert space filling curve dithering
+-----------------------------------------------------------------------------*/
+    /*
+     * This is taken from the article "Digital Halftoning with
+     * Space Filling Curves" by Luiz Velho, proceedings of
+     * SIGRAPH '91, page 81.
+     *
+     * This is not a terribly efficient or quick version of
+     * this algorithm, but it seems to work. - Graeme Gill.
+     * graeme@labtam.labtam.OZ.AU
+     *
+     */
+
+    int cols, rows;
+    gray maxval;
+    gray **grays;
+    bit **bits;
+    int end;
+    int *x,*y;
+    int sum = 0;
+
+    grays = pgm_readpgm(ifP, &cols,&rows, &maxval);
+    bits = pbm_allocarray(cols,rows);
+
+    MALLOCARRAY(x, clump_size);
+    MALLOCARRAY(y, clump_size);
+    if (x == NULL  || y == NULL)
+        pm_error("out of memory");
+    init_hilbert(cols,rows);
+
+    end = clump_size;
+    while (end == clump_size) {
+        int i;
+        /* compute the next clust co-ordinates along hilbert path */
+        for (i = 0; i < end; i++) {
+            if (hilbert(&x[i],&y[i])==0)
+                end = i;    /* we reached the end */
+        }
+        /* sum levels */
+        for (i = 0; i < end; i++)
+            sum += grays[y[i]][x[i]];
+        /* dither half and half along path */
+        for (i = 0; i < end; i++) {
+            if (sum >= maxval) {
+                bits[y[i]][x[i]] = PBM_WHITE;
+                sum -= maxval;
+            } else
+                bits[y[i]][x[i]] = PBM_BLACK;
+        }
+    }
+    pbm_writepbm(stdout, bits, cols, rows, 0);
+}
+
+
+
+struct converter {
+    void (*convertRow)(struct converter * const converterP,
+                       unsigned int       const row,
+                       gray                     grayrow[], 
+                       bit                      bitrow[]);
+    void (*destroy)(struct converter * const converterP);
+    unsigned int cols;
+    gray maxval;
+    void * stateP;
+};
+
+
+
+unsigned int const fs_scale      = 1024;
+unsigned int const half_fs_scale = 512;
+
+struct fsState {
+    long* thiserr;
+    long* nexterr;
+    bool fs_forward;
+    long threshval;  /* Threshold gray value, scaled by FS_SCALE */
+};
+
+
+static void
+fsConvertRow(struct converter * const converterP,
+             unsigned int       const row,
+             gray                     grayrow[],
+             bit                      bitrow[]) {
+
+    struct fsState * const stateP = converterP->stateP;
+
+    long * const thiserr = stateP->thiserr;
+    long * const nexterr = stateP->nexterr;
+
+    bit* bP;
+    gray* gP;
+
+    unsigned int limitcol;
+    unsigned int col;
+    
+    for (col = 0; col < converterP->cols + 2; ++col)
+        nexterr[col] = 0;
+    if (stateP->fs_forward) {
+        col = 0;
+        limitcol = converterP->cols;
+        gP = grayrow;
+        bP = bitrow;
+    } else {
+        col = converterP->cols - 1;
+        limitcol = -1;
+        gP = &(grayrow[col]);
+        bP = &(bitrow[col]);
+    }
+    do {
+        long sum;
+        sum = ((long) *gP * fs_scale) / converterP->maxval + 
+            thiserr[col + 1];
+        if (sum >= stateP->threshval) {
+            *bP = PBM_WHITE;
+            sum = sum - stateP->threshval - half_fs_scale;
+        } else
+            *bP = PBM_BLACK;
+        
+        if (stateP->fs_forward) {
+            thiserr[col + 2] += (sum * 7) / 16;
+            nexterr[col    ] += (sum * 3) / 16;
+            nexterr[col + 1] += (sum * 5) / 16;
+            nexterr[col + 2] += (sum    ) / 16;
+            
+            ++col;
+            ++gP;
+            ++bP;
+        } else {
+            thiserr[col    ] += (sum * 7) / 16;
+            nexterr[col + 2] += (sum * 3) / 16;
+            nexterr[col + 1] += (sum * 5) / 16;
+            nexterr[col    ] += (sum    ) / 16;
+            
+            --col;
+            --gP;
+            --bP;
+        }
+    } while (col != limitcol);
+    
+    stateP->thiserr = nexterr;
+    stateP->nexterr = thiserr;
+    stateP->fs_forward = ! stateP->fs_forward;
+}
+
+
+
+static void
+fsDestroy(struct converter * const converterP) {
+    free(converterP->stateP);
+}
+
+
+
+static struct converter
+createFsConverter(unsigned int const cols, 
+                  gray         const maxval,
+                  float        const threshFraction) {
+
+    struct fsState * stateP;
+    struct converter converter;
+
+    MALLOCVAR_NOFAIL(stateP);
+
+    /* Initialize Floyd-Steinberg error vectors. */
+    MALLOCARRAY_NOFAIL(stateP->thiserr, cols + 2);
+    MALLOCARRAY_NOFAIL(stateP->nexterr, cols + 2);
+    srand((int)(time(NULL) ^ getpid()));
+
+    {
+        /* (random errors in [-fs_scale/8 .. fs_scale/8]) */
+        unsigned int col;
+        for (col = 0; col < cols + 2; ++col)
+            stateP->thiserr[col] = 
+                (long)(rand() % fs_scale - half_fs_scale) / 4;
+    }
+
+    stateP->fs_forward = TRUE;
+    stateP->threshval = threshFraction * fs_scale;
+    converter.stateP = stateP;
+    converter.cols = cols;
+    converter.maxval = maxval;
+    converter.convertRow = &fsConvertRow;
+    converter.destroy = &fsDestroy;
+
+    return converter;
+}
+
+
+
+struct threshState {
+    gray threshval;
+};
+
+
+static void
+threshConvertRow(struct converter * const converterP,
+                 unsigned int       const row,
+                 gray                     grayrow[],
+                 bit                      bitrow[]) {
+    
+    struct threshState * const stateP = converterP->stateP;
+
+    unsigned int col;
+    for (col = 0; col < converterP->cols; ++col)
+        if (grayrow[col] >= stateP->threshval)
+            bitrow[col] = PBM_WHITE;
+        else
+            bitrow[col] = PBM_BLACK;
+}
+
+
+
+static void
+threshDestroy(struct converter * const converterP) {
+    free(converterP->stateP);
+}
+
+
+
+static struct converter
+createThreshConverter(unsigned int const cols, 
+                      gray         const maxval,
+                      float        const threshFraction) {
+
+    struct threshState * stateP;
+    struct converter converter;
+
+    MALLOCVAR_NOFAIL(stateP);
+
+    converter.cols       = cols;
+    converter.maxval     = maxval;
+    converter.convertRow = &threshConvertRow;
+    converter.destroy    = &threshDestroy;
+
+    stateP->threshval    = ROUNDU(maxval * threshFraction);
+    converter.stateP     = stateP;
+
+    return converter;
+}
+
+
+
+static void
+dither8ConvertRow(struct converter * const converterP,
+                  unsigned int       const row,
+                  gray                     grayrow[],
+                  bit                      bitrow[]) {
+
+    unsigned int col;
+
+    for (col = 0; col < converterP->cols; ++col)
+        if (grayrow[col] > dither8[row % 16][col % 16])
+            bitrow[col] = PBM_WHITE;
+        else
+            bitrow[col] = PBM_BLACK;
+}
+
+
+
+static struct converter
+createDither8Converter(unsigned int const cols, 
+                       gray         const maxval) {
+
+    struct converter converter;
+
+    unsigned int row;
+
+    converter.cols = cols;
+    converter.convertRow = &dither8ConvertRow;
+    converter.destroy = NULL;
+
+    /* Scale dither matrix. */
+    for (row = 0; row < 16; ++row) {
+        unsigned int col;
+        for (col = 0; col < 16; ++col)
+            dither8[row][col] = dither8[row][col] * maxval / 256;
+    }
+    return converter;
+}
+
+
+
+struct clusterState {
+    unsigned int radius;
+    int ** clusterMatrix;
+};
+
+
+
+static void
+clusterConvertRow(struct converter * const converterP,
+                  unsigned int       const row,
+                  gray                     grayrow[],
+                  bit                      bitrow[]) {
+
+    struct clusterState * const stateP = converterP->stateP;
+    unsigned int const diameter = 2 * stateP->radius;
+
+    unsigned int col;
+
+    for (col = 0; col < converterP->cols; ++col)
+        if (grayrow[col] >
+            stateP->clusterMatrix[row % diameter][col % diameter])
+            bitrow[col] = PBM_WHITE;
+        else
+            bitrow[col] = PBM_BLACK;
+}
+
+
+
+static void
+clusterDestroy(struct converter * const converterP) {
+
+    struct clusterState * const stateP = converterP->stateP;
+    unsigned int const diameter = 2 * stateP->radius;
+
+    unsigned int row;
+
+    for (row = 0; row < diameter; ++row)
+        free(stateP->clusterMatrix[row]);
+
+    free(stateP->clusterMatrix);
+    
+    free(stateP);
+}
+
+
+
+static struct converter
+createClusterConverter(unsigned int const radius,
+                       unsigned int const cols, 
+                       gray         const maxval) {
+    
+    int const clusterNormalizer = radius * radius * 2;
+    unsigned int const diameter = 2 * radius;
+
+    struct converter converter;
+    struct clusterState * stateP;
+    unsigned int row;
+
+    converter.cols = cols;
+    converter.convertRow = &clusterConvertRow;
+    converter.destroy = &clusterDestroy;
+
+    MALLOCVAR_NOFAIL(stateP);
+
+    stateP->radius = radius;
+
+    MALLOCARRAY_NOFAIL(stateP->clusterMatrix, diameter);
+    for (row = 0; row < diameter; ++row) {
+        unsigned int col;
+
+        MALLOCARRAY_NOFAIL(stateP->clusterMatrix[row], diameter);
+        
+        for (col = 0; col < diameter; ++col) {
+            int val;
+            switch (radius) {
+            case 3: val = cluster3[row][col]; break;
+            case 4: val = cluster4[row][col]; break;
+            case 8: val = cluster8[row][col]; break;
+            default:
+                pm_error("INTERNAL ERROR: invalid radius");
+            }
+            stateP->clusterMatrix[row][col] = val * maxval / clusterNormalizer;
+        }
+    }            
+
+    converter.stateP = stateP;
+
+    return converter;
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE* ifP;
+    gray* grayrow;
+    bit* bitrow;
+
+    pgm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    if (cmdline.halftone == QT_HILBERT)
+        doHilbert(ifP, cmdline.clumpSize);
+    else {
+        struct converter converter;
+        int cols, rows;
+        gray maxval;
+        int format;
+        int row;
+
+        pgm_readpgminit(ifP, &cols, &rows, &maxval, &format);
+        
+        pbm_writepbminit(stdout, cols, rows, 0);
+
+        switch (cmdline.halftone) {
+        case QT_FS:
+            converter = createFsConverter(cols, maxval, cmdline.threshval);
+            break;
+        case QT_THRESH:
+            converter = createThreshConverter(cols, maxval, cmdline.threshval);
+            break;
+        case QT_DITHER8: 
+            converter = createDither8Converter(cols, maxval); 
+            break;
+        case QT_CLUSTER: 
+            converter = 
+                createClusterConverter(cmdline.clusterRadius, cols, maxval);
+            break;
+        case QT_HILBERT: 
+                pm_error("INTERNAL ERROR: halftone is QT_HILBERT where it "
+                         "shouldn't be.");
+                break;
+        }
+
+        grayrow = pgm_allocrow(cols);
+        bitrow  = pbm_allocrow(cols);
+
+        for (row = 0; row < rows; ++row) {
+            pgm_readpgmrow(ifP, grayrow, cols, maxval, format);
+
+            converter.convertRow(&converter, row, grayrow, bitrow);
+            
+            pbm_writepbmrow(stdout, bitrow, cols, 0);
+        }
+        pbm_freerow(bitrow);
+        pgm_freerow(grayrow);
+
+        if (converter.destroy)
+            converter.destroy(&converter);
+    }
+
+    pm_close(ifP);
+
+    return 0;
+}
diff --git a/converter/other/pgmtoppm.c b/converter/other/pgmtoppm.c
new file mode 100644
index 00000000..c695ddd5
--- /dev/null
+++ b/converter/other/pgmtoppm.c
@@ -0,0 +1,153 @@
+/* pgmtoppm.c - colorize a portable graymap 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 <string.h>
+#include "ppm.h"
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    gray* grayrow;
+    register gray* gP;
+    pixel p;
+    pixel* pixelrow;
+    register pixel* pP;
+    pixel** mappixels;
+    int argn, rows, cols, format, maprows, mapcols, mapmaxcolor, row;
+    register int col;
+    gray maxval;
+    pixval mapmaxval;
+    char* color0;
+    char* color1;
+    pixval red0, grn0, blu0, red1, grn1, blu1;
+    const char* const usage = "<colorspec> [pgmfile]\n                 <colorspec1>,<colorspec2> [pgmfile]\n                 -map mapfile [pgmfile]";
+
+
+    ppm_init( &argc, argv );
+
+    argn = 1;
+    mappixels = (pixel**) 0;
+
+    if ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
+	{
+	if ( pm_keymatch( argv[argn], "-map", 2 ) )
+	    {
+	    ++argn;
+	    if ( argn == argc )
+		pm_usage( usage );
+	    ifp = pm_openr( argv[argn] );
+	    mappixels = ppm_readppm( ifp, &mapcols, &maprows, &mapmaxval );
+	    pm_close( ifp );
+	    mapmaxcolor = maprows * mapcols - 1;
+	    }
+	else
+	    pm_usage( usage );
+	++argn;
+	}
+
+    if ( mappixels == (pixel**) 0 )
+	{
+	if ( argn == argc )
+	    pm_usage( usage );
+	color0 = argv[argn];
+	++argn;
+	}
+
+    if ( argn != argc )
+	{
+	ifp = pm_openr( argv[argn] );
+	++argn;
+	}
+    else
+	ifp = stdin;
+
+    if ( argn != argc )
+	pm_usage( usage );
+
+    pgm_readpgminit( ifp, &cols, &rows, &maxval, &format );
+    grayrow = pgm_allocrow( cols );
+    if ( mappixels == (pixel**) 0 )
+	ppm_writeppminit( stdout, cols, rows, (pixval) maxval, 0 );
+    else
+	ppm_writeppminit( stdout, cols, rows, mapmaxval, 0 );
+    pixelrow = ppm_allocrow( cols );
+
+    if ( mappixels == (pixel**) 0 )
+	{
+	color1 = strchr( color0, '-' );
+	if ( color1 == 0 )
+	    {
+	    color1 = color0;
+	    red0 = 0;
+	    grn0 = 0;
+	    blu0 = 0;
+	    }
+	else
+	    {
+	    *color1 = '\0';
+	    ++color1;
+	    p = ppm_parsecolor( color0, (pixval) maxval );
+	    red0 = PPM_GETR( p );
+	    grn0 = PPM_GETG( p );
+	    blu0 = PPM_GETB( p );
+	    }
+	p = ppm_parsecolor( color1, (pixval) maxval );
+	red1 = PPM_GETR( p );
+	grn1 = PPM_GETG( p );
+	blu1 = PPM_GETB( p );
+	}
+
+    for ( row = 0; row < rows; ++row )
+	{
+	pgm_readpgmrow( ifp, grayrow, cols, maxval, format );
+
+	if ( mappixels == (pixel**) 0 )
+	    {
+	    for ( col = 0, gP = grayrow, pP = pixelrow;
+		  col < cols;
+		  ++col, ++gP, ++pP )
+		PPM_ASSIGN(
+		    *pP,
+		    ( red0 * ( maxval - *gP ) + red1 * *gP ) / maxval,
+		    ( grn0 * ( maxval - *gP ) + grn1 * *gP ) / maxval,
+		    ( blu0 * ( maxval - *gP ) + blu1 * *gP ) / maxval );
+
+	    }
+	else
+	    {
+	    register int c;
+
+	    for ( col = 0, gP = grayrow, pP = pixelrow;
+		  col < cols;
+		  ++col, ++gP, ++pP )
+		{
+		if ( maxval == mapmaxcolor )
+		    c = *gP;
+		else
+		    c = *gP * mapmaxcolor / maxval;
+		*pP = mappixels[c / mapcols][c % mapcols];
+		}
+	    }
+
+	ppm_writeppmrow( stdout, pixelrow, cols, (pixval) maxval, 0 );
+	}
+
+    pm_close( ifp );
+
+    /* If the program failed, it previously aborted with nonzero completion
+       code, via various function calls.
+    */
+    return 0;
+    }
diff --git a/converter/other/pm_tiff.h b/converter/other/pm_tiff.h
new file mode 100644
index 00000000..f98110e3
--- /dev/null
+++ b/converter/other/pm_tiff.h
@@ -0,0 +1,41 @@
+#ifndef PM_TIFF_H_INCLUDED
+#define PM_TIFF_H_INCLUDED
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   This is an association between a tag value name and the integer that
+   represents the tag value in the TIFF.
+
+   E.g. for an ORIENTATION tag, the value named "TOPLEFT" is represented
+   by the integer 1.
+-----------------------------------------------------------------------------*/
+    const char *  name;
+    unsigned long value;
+} tagvalmap;
+
+typedef struct tagDefinition {
+/*----------------------------------------------------------------------------
+   This is the definition of a type of tag, e.g. ORIENTATION.
+-----------------------------------------------------------------------------*/
+    const char * name;
+        /* The name by which our user knows the tag type, e.g. 
+           "ORIENTATION"
+        */
+    unsigned int tagnum;
+        /* The integer by which libtiff knows the tag type, e.g.
+           TIFFTAG_ORIENTATION
+        */
+    void (*      put)(TIFF *, unsigned int, const char *, const tagvalmap *);
+    const tagvalmap * choices;
+        /* List of the possible values for the tag, if it is one with
+           enumerated values.  e.g. for ORIENTATION, it's TOPLEFT,
+           TOPRIGHT, etc.
+        */
+} tagDefinition;
+
+
+
+const tagDefinition *
+tagDefFind(const char * const name);
+
+#endif
diff --git a/converter/other/pngtopnm.c b/converter/other/pngtopnm.c
new file mode 100644
index 00000000..bb8afb8d
--- /dev/null
+++ b/converter/other/pngtopnm.c
@@ -0,0 +1,1096 @@
+/*
+** pngtopnm.c -
+** read a Portable Network Graphics file and produce a portable anymap
+**
+** Copyright (C) 1995,1998 by Alexander Lehmann <alex@hal.rhein-main.de>
+**                        and Willem van Schaik <willem@schaik.com>
+**
+** 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.
+**
+** modeled after giftopnm by David Koblas and
+** with lots of bits pasted from libpng.txt by Guy Eric Schalnat
+*/
+
+/* 
+   BJH 20000408:  rename PPM_MAXMAXVAL to PPM_OVERALLMAXVAL
+   BJH 20000303:  fix include statement so dependencies work out right.
+*/
+/* GRR 19991203:  moved VERSION to new version.h header file */
+
+/* GRR 19990713:  fixed redundant freeing of png_ptr and info_ptr in setjmp()
+ *  blocks and added "pm_close(ifp)" in each.  */
+
+/* GRR 19990317:  declared "clobberable" automatic variables in convertpng()
+ *  static to fix Solaris/gcc stack-corruption bug.  Also installed custom
+ *  error-handler to avoid jmp_buf size-related problems (i.e., jmp_buf
+ *  compiled with one size in libpng and another size here).  */
+
+#ifndef PNMTOPNG_WARNING_LEVEL
+#  define PNMTOPNG_WARNING_LEVEL 0   /* use 0 for backward compatibility, */
+#endif                               /*  2 for warnings (1 == error) */
+
+#include <math.h>
+#include <png.h>    /* includes zlib.h and setjmp.h */
+#define VERSION "2.37.4 (5 December 1999) +netpbm"
+
+#include "pnm.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+
+typedef struct _jmpbuf_wrapper {
+  jmp_buf jmpbuf;
+} jmpbuf_wrapper;
+
+/* GRR 19991205:  this is used as a test for pre-1999 versions of netpbm and
+ *   pbmplus vs. 1999 or later (in which pm_close was split into two)
+ */
+#ifdef PBMPLUS_RAWBITS
+#  define pm_closer pm_close
+#  define pm_closew pm_close
+#endif
+
+#ifndef TRUE
+#  define TRUE 1
+#endif
+#ifndef FALSE
+#  define FALSE 0
+#endif
+#ifndef NONE
+#  define NONE 0
+#endif
+
+enum alpha_handling {ALPHA_NONE, ALPHA_ONLY, ALPHA_MIX};
+
+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;
+    enum alpha_handling alpha;
+    const char * background;
+    float gamma;  /* -1.0 means unspecified */
+    const char * text;
+    unsigned int time;
+};
+
+
+typedef struct pngcolor {
+/*----------------------------------------------------------------------------
+   A color in a format compatible with the PNG library.
+
+   Note that the PNG library declares types png_color and png_color_16
+   which are similar.
+-----------------------------------------------------------------------------*/
+    png_uint_16 r;
+    png_uint_16 g;
+    png_uint_16 b;
+} pngcolor;
+
+
+static png_uint_16 maxval;
+static int verbose = FALSE;
+static int mtime;
+static jmpbuf_wrapper pngtopnm_jmpbuf_struct;
+
+
+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;
+
+    unsigned int alphaSpec, mixSpec, backgroundSpec, gammaSpec, textSpec;
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "verbose",     OPT_FLAG,   NULL,                  
+            &cmdlineP->verbose,       0);
+    OPTENT3(0, "alpha",       OPT_FLAG,   NULL,                  
+            &alphaSpec,               0);
+    OPTENT3(0, "mix",         OPT_FLAG,   NULL,                  
+            &mixSpec,                 0);
+    OPTENT3(0, "background",  OPT_STRING, &cmdlineP->background,
+            &backgroundSpec,          0);
+    OPTENT3(0, "gamma",       OPT_FLOAT,  &cmdlineP->gamma,
+            &gammaSpec,               0);
+    OPTENT3(0, "text",        OPT_STRING, &cmdlineP->text,
+            &textSpec,                0);
+    OPTENT3(0, "time",        OPT_FLAG,   NULL,                  
+            &cmdlineP->time,          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 (alphaSpec && mixSpec)
+        pm_error("You cannot specify both -alpha and -mix");
+    else if (alphaSpec)
+        cmdlineP->alpha = ALPHA_ONLY;
+    else if (mixSpec)
+        cmdlineP->alpha = ALPHA_MIX;
+    else
+        cmdlineP->alpha = ALPHA_NONE;
+
+    if (backgroundSpec && !mixSpec)
+        pm_error("-background is useless without -mix");
+
+    if (!backgroundSpec)
+        cmdlineP->background = NULL;
+
+    if (!gammaSpec)
+        cmdlineP->gamma = -1.0;
+
+    if (!textSpec)
+        cmdlineP->text = NULL;
+
+    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 name.  "
+            "you specified %d", argc-1);
+}
+
+
+
+
+#define get_png_val(p) _get_png_val (&(p), info_ptr->bit_depth)
+
+static png_uint_16
+_get_png_val (png_byte ** const pp,
+              int         const bit_depth) {
+
+    png_uint_16 c;
+    
+    if (bit_depth == 16)
+        c = (*((*pp)++)) << 8;
+    else
+        c = 0;
+
+    c |= (*((*pp)++));
+    
+    return c;
+}
+
+
+
+static void 
+setXel(xel *               const xelP, 
+       pngcolor            const foreground,
+       pngcolor            const background,
+       enum alpha_handling const alpha_handling,
+       png_uint_16         const alpha) {
+
+    if (alpha_handling == ALPHA_ONLY) {
+        PNM_ASSIGN1(*xelP, alpha);
+    } else {
+        if ((alpha_handling == ALPHA_MIX) && (alpha != maxval)) {
+            double const opacity      = (double)alpha / maxval;
+            double const transparency = 1.0 - opacity;
+
+            pngcolor mix;
+
+            mix.r = foreground.r * opacity + background.r * transparency + 0.5;
+            mix.g = foreground.g * opacity + background.g * transparency + 0.5;
+            mix.b = foreground.b * opacity + background.b * transparency + 0.5;
+            PPM_ASSIGN(*xelP, mix.r, mix.g, mix.b);
+        } else
+            PPM_ASSIGN(*xelP, foreground.r, foreground.g, foreground.b);
+    }
+}
+
+
+
+static png_uint_16
+gamma_correct(png_uint_16 const v,
+              float       const g) {
+
+    if (g != -1.0)
+        return (png_uint_16) (pow ((double) v / maxval, 
+                                   (1.0 / g)) * maxval + 0.5);
+    else
+        return v;
+}
+
+
+
+#ifdef __STDC__
+static int iscolor (png_color c)
+#else
+static int iscolor (c)
+png_color c;
+#endif
+{
+  return c.red != c.green || c.green != c.blue;
+}
+
+#ifdef __STDC__
+static void save_text (png_info *info_ptr, FILE *tfp)
+#else
+static void save_text (info_ptr, tfp)
+png_info *info_ptr;
+FILE *tfp;
+#endif
+{
+  int i, j, k;
+
+  for (i = 0 ; i < info_ptr->num_text ; i++) {
+    j = 0;
+    while (info_ptr->text[i].key[j] != '\0' && info_ptr->text[i].key[j] != ' ')
+      j++;    
+    if (info_ptr->text[i].key[j] != ' ') {
+      fprintf (tfp, "%s", info_ptr->text[i].key);
+      for (j = strlen (info_ptr->text[i].key) ; j < 15 ; j++)
+        putc (' ', tfp);
+    } else {
+      fprintf (tfp, "\"%s\"", info_ptr->text[i].key);
+      for (j = strlen (info_ptr->text[i].key) ; j < 13 ; j++)
+        putc (' ', tfp);
+    }
+    putc (' ', tfp); /* at least one space between key and text */
+    
+    for (j = 0 ; j < info_ptr->text[i].text_length ; j++) {
+      putc (info_ptr->text[i].text[j], tfp);
+      if (info_ptr->text[i].text[j] == '\n')
+        for (k = 0 ; k < 16 ; k++)
+          putc ((int)' ', tfp);
+    }
+    putc ((int)'\n', tfp);
+  }
+}
+
+#ifdef __STDC__
+static void show_time (png_info *info_ptr)
+#else
+static void show_time (info_ptr)
+png_info *info_ptr;
+#endif
+{
+    static const char * const month[] = {
+        "", "January", "February", "March", "April", "May", "June",
+        "July", "August", "September", "October", "November", "December"
+    };
+
+  if (info_ptr->valid & PNG_INFO_tIME) {
+    pm_message ("modification time: %02d %s %d %02d:%02d:%02d",
+                info_ptr->mod_time.day, month[info_ptr->mod_time.month],
+                info_ptr->mod_time.year, info_ptr->mod_time.hour,
+                info_ptr->mod_time.minute, info_ptr->mod_time.second);
+  }
+}
+
+#ifdef __STDC__
+static void pngtopnm_error_handler (png_structp png_ptr, png_const_charp msg)
+#else
+static void pngtopnm_error_handler (png_ptr, msg)
+png_structp png_ptr;
+png_const_charp msg;
+#endif
+{
+  jmpbuf_wrapper  *jmpbuf_ptr;
+
+  /* this function, aside from the extra step of retrieving the "error
+   * pointer" (below) and the fact that it exists within the application
+   * rather than within libpng, is essentially identical to libpng's
+   * default error handler.  The second point is critical:  since both
+   * setjmp() and longjmp() are called from the same code, they are
+   * guaranteed to have compatible notions of how big a jmp_buf is,
+   * regardless of whether _BSD_SOURCE or anything else has (or has not)
+   * been defined. */
+
+  pm_message("fatal libpng error: %s", msg);
+
+  jmpbuf_ptr = png_get_error_ptr(png_ptr);
+  if (jmpbuf_ptr == NULL) {
+      /* we are completely hosed now */
+      pm_error("EXTREMELY fatal error: jmpbuf unrecoverable; terminating.");
+  }
+
+  longjmp(jmpbuf_ptr->jmpbuf, 1);
+}
+
+
+
+static void
+dump_png_info(png_info *info_ptr) {
+
+    const char *type_string;
+    const char *filter_string;
+
+    switch (info_ptr->color_type) {
+      case PNG_COLOR_TYPE_GRAY:
+        type_string = "gray";
+        break;
+
+      case PNG_COLOR_TYPE_GRAY_ALPHA:
+        type_string = "gray+alpha";
+        break;
+
+      case PNG_COLOR_TYPE_PALETTE:
+        type_string = "palette";
+        break;
+
+      case PNG_COLOR_TYPE_RGB:
+        type_string = "truecolor";
+        break;
+
+      case PNG_COLOR_TYPE_RGB_ALPHA:
+        type_string = "truecolor+alpha";
+        break;
+    }
+
+    switch (info_ptr->filter_type) {
+    case PNG_FILTER_TYPE_BASE:
+        asprintfN(&filter_string, "base filter");
+        break;
+    default:
+        asprintfN(&filter_string, "unknown filter type %d", 
+                  info_ptr->filter_type);
+    }
+
+    pm_message("reading a %ldw x %ldh image, %d bit%s",
+               info_ptr->width, info_ptr->height,
+               info_ptr->bit_depth, info_ptr->bit_depth > 1 ? "s" : "");
+    pm_message("%s, %s, %s",
+               type_string,
+               info_ptr->interlace_type ? 
+               "Adam7 interlaced" : "not interlaced",
+               filter_string);
+    pm_message("background {index, gray, red, green, blue} = "
+               "{%d, %d, %d, %d, %d}",
+               info_ptr->background.index,
+               info_ptr->background.gray,
+               info_ptr->background.red,
+               info_ptr->background.green,
+               info_ptr->background.blue);
+
+    strfree(filter_string);
+
+    if (info_ptr->valid & PNG_INFO_tRNS)
+        pm_message("tRNS chunk (transparency): %u entries",
+                   info_ptr->num_trans);
+    else
+        pm_message("tRNS chunk (transparency): not present");
+
+    if (info_ptr->valid & PNG_INFO_gAMA)
+        pm_message("gAMA chunk (image gamma): gamma = %4.2f", info_ptr->gamma);
+    else
+        pm_message("gAMA chunk (image gamma): not present");
+
+    if (info_ptr->valid & PNG_INFO_sBIT)
+        pm_message("sBIT chunk: present");
+    else
+        pm_message("sBIT chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_cHRM)
+        pm_message("cHRM chunk: present");
+    else
+        pm_message("cHRM chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_PLTE)
+        pm_message("PLTE chunk: %d entries", info_ptr->num_palette);
+    else
+        pm_message("PLTE chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_bKGD)
+        pm_message("bKGD chunk: present");
+    else
+        pm_message("bKGD chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_PLTE)
+        pm_message("hIST chunk: present");
+    else
+        pm_message("hIST chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_pHYs)
+        pm_message("pHYs chunk: present");
+    else
+        pm_message("pHYs chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_oFFs)
+        pm_message("oFFs chunk: present");
+    else
+        pm_message("oFFs chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_tIME)
+        pm_message("tIME chunk: present");
+    else
+        pm_message("tIME chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_pCAL)
+        pm_message("pCAL chunk: present");
+    else
+        pm_message("pCAL chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_sRGB)
+        pm_message("sRGB chunk: present");
+    else
+        pm_message("sRGB chunk: not present");
+}
+
+
+
+static bool
+isTransparentColor(pngcolor   const color,
+                   png_info * const info_ptr,
+                   double     const totalgamma) {
+/*----------------------------------------------------------------------------
+   Return TRUE iff pixels of color 'color' are supposed to be transparent
+   everywhere they occur.  Assume it's an RGB image.
+-----------------------------------------------------------------------------*/
+    bool retval;
+
+    if (info_ptr->valid & PNG_INFO_tRNS) {
+        const png_color_16 * const transColorP = &info_ptr->trans_values;
+    
+
+        /* There seems to be a problem here: you can't compare real
+           numbers for equality.  Also, I'm not sure the gamma
+           corrected/uncorrected color spaces are right here.  
+        */
+
+        retval = 
+            color.r == gamma_correct(transColorP->red,   totalgamma) &&
+            color.g == gamma_correct(transColorP->green, totalgamma) &&
+            color.b == gamma_correct(transColorP->blue,  totalgamma);
+    } else 
+        retval = FALSE;
+
+    return retval;
+}
+
+
+
+#define SIG_CHECK_SIZE 4
+
+static void
+read_sig_buf(FILE * const ifP) {
+
+    unsigned char sig_buf[SIG_CHECK_SIZE];
+    size_t bytesRead;
+
+    bytesRead = fread(sig_buf, 1, SIG_CHECK_SIZE, ifP);
+    if (bytesRead != SIG_CHECK_SIZE)
+        pm_error ("input file is empty or too short");
+
+    if (png_sig_cmp(sig_buf, (png_size_t) 0, (png_size_t) SIG_CHECK_SIZE)
+        != 0)
+        pm_error ("input file is not a PNG file");
+}
+
+
+
+static void
+setupGammaCorrection(png_struct * const png_ptr,
+                     png_info *   const info_ptr,
+                     float        const displaygamma,
+                     float *      const totalgammaP) {
+
+    if (displaygamma == -1.0)
+        *totalgammaP = -1.0;
+    else {
+        if (info_ptr->valid & PNG_INFO_gAMA) {
+            if (displaygamma != info_ptr->gamma) {
+                png_set_gamma(png_ptr, displaygamma, info_ptr->gamma);
+                *totalgammaP =
+                    (double) info_ptr->gamma * (double) displaygamma;
+                /* in case of gamma-corrections, sBIT's as in the
+                   PNG-file are not valid anymore 
+                */
+                info_ptr->valid &= ~PNG_INFO_sBIT;
+                if (verbose)
+                    pm_message("image gamma is %4.2f, "
+                               "converted for display gamma of %4.2f",
+                               info_ptr->gamma, displaygamma);
+            }
+        } else {
+            if (displaygamma != info_ptr->gamma) {
+                png_set_gamma (png_ptr, displaygamma, 1.0);
+                *totalgammaP = (double) displaygamma;
+                info_ptr->valid &= ~PNG_INFO_sBIT;
+                if (verbose)
+                    pm_message("image gamma assumed 1.0, "
+                               "converted for display gamma of %4.2f",
+                               displaygamma);
+            }
+        }
+    }
+}
+
+
+
+static bool
+paletteHasPartialTransparency(png_info * const info_ptr) {
+
+    bool retval;
+
+    if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
+        if (info_ptr->valid & PNG_INFO_tRNS) {
+            bool foundGray;
+            unsigned int i;
+            
+            for (i = 0, foundGray = FALSE;
+                 i < info_ptr->num_trans && !foundGray;
+                 ++i) {
+                if (info_ptr->trans[i] != 0 &&
+                    info_ptr->trans[i] != maxval) {
+                    foundGray = TRUE;
+                }
+            }
+            retval = foundGray;
+        } else
+            retval = FALSE;
+    } else
+        retval = FALSE;
+
+    return retval;
+}
+
+
+
+static void
+setupSignificantBits(png_struct *        const png_ptr,
+                     png_info *          const info_ptr,
+                     enum alpha_handling const alpha,
+                     png_uint_16 *       const maxvalP,
+                     int *               const errorlevelP) {
+/*----------------------------------------------------------------------------
+  Figure out what maxval would best express the information in the PNG
+  described by 'png_ptr' and 'info_ptr', with 'alpha' telling which
+  information in the PNG we care about (image or alpha mask).
+
+  Return the result as *maxvalP.
+-----------------------------------------------------------------------------*/
+    /* Initial assumption of maxval */
+    if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
+        if (alpha == ALPHA_ONLY) {
+            if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
+                info_ptr->color_type == PNG_COLOR_TYPE_RGB)
+                /* The alpha mask will be all opaque, so maxval 1 is plenty */
+                *maxvalP = 1;
+            else if (paletteHasPartialTransparency(info_ptr))
+                /* Use same maxval as PNG transparency palette for simplicity*/
+                *maxvalP = 255;
+            else
+                /* A common case, so we conserve bits */
+                *maxvalP = 1;
+        } else
+            /* Use same maxval as PNG palette for simplicity */
+            *maxvalP = 255;
+    } else {
+        *maxvalP = (1l << info_ptr->bit_depth) - 1;
+    }
+
+    /* sBIT handling is very tricky. If we are extracting only the
+       image, we can use the sBIT info for grayscale and color images,
+       if the three values agree. If we extract the transparency/alpha
+       mask, sBIT is irrelevant for trans and valid for alpha. If we
+       mix both, the multiplication may result in values that require
+       the normal bit depth, so we will use the sBIT info only for
+       transparency, if we know that only solid and fully transparent
+       is used 
+    */
+    
+    if (info_ptr->valid & PNG_INFO_sBIT) {
+        switch (alpha) {
+        case ALPHA_MIX:
+            if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
+                info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+                break;
+            if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+                (info_ptr->valid & PNG_INFO_tRNS)) {
+
+                bool trans_mix;
+                unsigned int i;
+                trans_mix = TRUE;
+                for (i = 0; i < info_ptr->num_trans; ++i)
+                    if (info_ptr->trans[i] != 0 && info_ptr->trans[i] != 255) {
+                        trans_mix = FALSE;
+                        break;
+                    }
+                if (!trans_mix)
+                    break;
+            }
+
+            /* else fall though to normal case */
+
+        case ALPHA_NONE:
+            if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||
+                 info_ptr->color_type == PNG_COLOR_TYPE_RGB ||
+                 info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&
+                (info_ptr->sig_bit.red != info_ptr->sig_bit.green ||
+                 info_ptr->sig_bit.red != info_ptr->sig_bit.blue) &&
+                alpha == ALPHA_NONE) {
+                pm_message("This program cannot handle "
+                           "different bit depths for color channels");
+                pm_message("writing file with %d bit resolution",
+                           info_ptr->bit_depth);
+                *errorlevelP = PNMTOPNG_WARNING_LEVEL;
+            } else {
+                if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) &&
+                    (info_ptr->sig_bit.red < 255)) {
+                    unsigned int i;
+                    for (i = 0; i < info_ptr->num_palette; ++i) {
+                        info_ptr->palette[i].red   >>=
+                            (8 - info_ptr->sig_bit.red);
+                        info_ptr->palette[i].green >>=
+                            (8 - info_ptr->sig_bit.green);
+                        info_ptr->palette[i].blue  >>=
+                            (8 - info_ptr->sig_bit.blue);
+                    }
+                    *maxvalP = (1l << info_ptr->sig_bit.red) - 1;
+                    if (verbose)
+                        pm_message ("image has fewer significant bits, "
+                                    "writing file with %d bits per channel", 
+                                    info_ptr->sig_bit.red);
+                } else
+                    if ((info_ptr->color_type == PNG_COLOR_TYPE_RGB ||
+                         info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&
+                        (info_ptr->sig_bit.red < info_ptr->bit_depth)) {
+                        png_set_shift (png_ptr, &(info_ptr->sig_bit));
+                        *maxvalP = (1l << info_ptr->sig_bit.red) - 1;
+                        if (verbose)
+                            pm_message("image has fewer significant bits, "
+                                       "writing file with %d "
+                                       "bits per channel", 
+                                       info_ptr->sig_bit.red);
+                    } else 
+                        if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
+                             info_ptr->color_type ==
+                                 PNG_COLOR_TYPE_GRAY_ALPHA) &&
+                            (info_ptr->sig_bit.gray < info_ptr->bit_depth)) {
+                            png_set_shift (png_ptr, &(info_ptr->sig_bit));
+                            *maxvalP = (1l << info_ptr->sig_bit.gray) - 1;
+                            if (verbose)
+                                pm_message("image has fewer significant bits, "
+                                           "writing file with %d bits",
+                                           info_ptr->sig_bit.gray);
+                        }
+            }
+            break;
+
+        case ALPHA_ONLY:
+            if ((info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
+                 info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) && 
+                (info_ptr->sig_bit.gray < info_ptr->bit_depth)) {
+                png_set_shift (png_ptr, &(info_ptr->sig_bit));
+                if (verbose)
+                    pm_message ("image has fewer significant bits, "
+                                "writing file with %d bits", 
+                                info_ptr->sig_bit.alpha);
+                *maxvalP = (1l << info_ptr->sig_bit.alpha) - 1;
+            }
+            break;
+
+        }
+    }
+}
+
+
+
+static bool
+imageHasColor(png_info * const info_ptr) {
+
+    bool retval;
+
+    if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
+        info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+
+        retval = FALSE;
+    else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
+        bool foundColor;
+        unsigned int i;
+            
+        for (i = 0, foundColor = FALSE;
+             i < info_ptr->num_palette && !foundColor;
+             ++i) {
+            if (iscolor(info_ptr->palette[i]))
+                foundColor = TRUE;
+        }
+        retval = foundColor;
+    } else
+        retval = TRUE;
+
+    return retval;
+}
+
+
+
+static void
+determineOutputType(png_info *          const info_ptr,
+                    enum alpha_handling const alphaHandling,
+                    xelval              const maxval,
+                    int *               const pnmTypeP) {
+
+    if (alphaHandling != ALPHA_ONLY && imageHasColor(info_ptr))
+        *pnmTypeP = PPM_TYPE;
+    else {
+        if (maxval > 1)
+            *pnmTypeP = PGM_TYPE;
+        else
+            *pnmTypeP = PBM_TYPE;
+    }
+}
+
+
+
+static void
+getBackgroundColor(png_info *        const info_ptr,
+                   const char *      const requestedColor,
+                   float             const totalgamma,
+                   xelval            const maxval,
+                   struct pngcolor * const bgColorP) {
+/*----------------------------------------------------------------------------
+   Figure out what the background color should be.  If the user requested
+   a particular color ('requestedColor' not null), that's the one.
+   Otherwise, if the PNG specifies a background color, that's the one.
+   And otherwise, it's white.
+-----------------------------------------------------------------------------*/
+    if (requestedColor) {
+        /* Background was specified from the command-line; we always
+           use that.  I chose to do no gamma-correction in this case;
+           which is a bit arbitrary.  
+        */
+        pixel const backcolor = ppm_parsecolor(requestedColor, maxval);
+        switch (info_ptr->color_type) {
+        case PNG_COLOR_TYPE_GRAY:
+        case PNG_COLOR_TYPE_GRAY_ALPHA:
+            bgColorP->r = bgColorP->g = bgColorP->b = PNM_GET1(backcolor);
+            break;
+        case PNG_COLOR_TYPE_PALETTE:
+        case PNG_COLOR_TYPE_RGB:
+        case PNG_COLOR_TYPE_RGB_ALPHA:
+            bgColorP->r = PPM_GETR(backcolor);
+            bgColorP->g = PPM_GETG(backcolor);
+            bgColorP->b = PPM_GETB(backcolor);
+            break;
+        }
+    } else if (info_ptr->valid & PNG_INFO_bKGD) {
+        /* didn't manage to get libpng to work (bugs?) concerning background
+           processing, therefore we do our own.
+        */
+        switch (info_ptr->color_type) {
+        case PNG_COLOR_TYPE_GRAY:
+        case PNG_COLOR_TYPE_GRAY_ALPHA:
+            bgColorP->r = bgColorP->g = bgColorP->b = 
+                gamma_correct(info_ptr->background.gray, totalgamma);
+            break;
+        case PNG_COLOR_TYPE_PALETTE: {
+            png_color const rawBgcolor = 
+                info_ptr->palette[info_ptr->background.index];
+            bgColorP->r = gamma_correct(rawBgcolor.red, totalgamma);
+            bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma);
+            bgColorP->b = gamma_correct(rawBgcolor.blue, totalgamma);
+        }
+        break;
+        case PNG_COLOR_TYPE_RGB:
+        case PNG_COLOR_TYPE_RGB_ALPHA: {
+            png_color_16 const rawBgcolor = info_ptr->background;
+            
+            bgColorP->r = gamma_correct(rawBgcolor.red,   totalgamma);
+            bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma);
+            bgColorP->b = gamma_correct(rawBgcolor.blue,  totalgamma);
+        }
+        break;
+        }
+    } else 
+        /* when no background given, we use white [from version 2.37] */
+        bgColorP->r = bgColorP->g = bgColorP->b = maxval;
+}
+
+
+
+static void
+writePnm(FILE *              const ofP,
+         xelval              const maxval,
+         int                 const pnm_type,
+         png_info *          const info_ptr,
+         png_byte **         const png_image,
+         pngcolor            const bgColor,
+         enum alpha_handling const alpha_handling,
+         double              const totalgamma) {
+/*----------------------------------------------------------------------------
+   Write a PNM of either the image or the alpha mask, according to
+   'alpha_handling' that is in the PNG image described by 'info_ptr' and
+   png_image.
+
+   'pnm_type' and 'maxval' are of the output image.
+
+   Use background color 'bgColor' in the output if the PNG is such that a
+   background color is needed.
+-----------------------------------------------------------------------------*/
+    xel * xelrow;
+    unsigned int row;
+
+    if (verbose)
+        pm_message ("writing a %s file (maxval=%u)",
+                    pnm_type == PBM_TYPE ? "PBM" :
+                    pnm_type == PGM_TYPE ? "PGM" :
+                    pnm_type == PPM_TYPE ? "PPM" :
+                    "UNKNOWN!", 
+                    maxval);
+    
+    xelrow = pnm_allocrow(info_ptr->width);
+
+    pnm_writepnminit(stdout, info_ptr->width, info_ptr->height, maxval,
+                     pnm_type, FALSE);
+
+    for (row = 0; row < info_ptr->height; ++row) {
+        png_byte * png_pixelP;
+        int col;
+
+        png_pixelP = &png_image[row][0];  /* initial value */
+        for (col = 0; col < info_ptr->width; ++col) {
+            switch (info_ptr->color_type) {
+            case PNG_COLOR_TYPE_GRAY: {
+                pngcolor fgColor;
+                fgColor.r = fgColor.g = fgColor.b = get_png_val(png_pixelP);
+                setXel(&xelrow[col], fgColor, bgColor, alpha_handling,
+                       ((info_ptr->valid & PNG_INFO_tRNS) &&
+                        (fgColor.r == 
+                         gamma_correct(info_ptr->trans_values.gray,
+                                       totalgamma))) ?
+                       0 : maxval);
+            }
+            break;
+
+            case PNG_COLOR_TYPE_GRAY_ALPHA: {
+                pngcolor fgColor;
+                png_uint_16 alpha;
+
+                fgColor.r = fgColor.g = fgColor.b = get_png_val(png_pixelP);
+                alpha = get_png_val(png_pixelP);
+                setXel(&xelrow[col], fgColor, bgColor, alpha_handling, alpha);
+            }
+            break;
+
+            case PNG_COLOR_TYPE_PALETTE: {
+                png_uint_16 const index        = get_png_val(png_pixelP);
+                png_color   const paletteColor = info_ptr->palette[index];
+
+                pngcolor fgColor;
+
+                fgColor.r = paletteColor.red;
+                fgColor.g = paletteColor.green;
+                fgColor.b = paletteColor.blue;
+
+                setXel(&xelrow[col], fgColor, bgColor, alpha_handling,
+                       (info_ptr->valid & PNG_INFO_tRNS) &&
+                       index < info_ptr->num_trans ?
+                       info_ptr->trans[index] : maxval);
+            }
+            break;
+                
+            case PNG_COLOR_TYPE_RGB: {
+                pngcolor fgColor;
+
+                fgColor.r = get_png_val(png_pixelP);
+                fgColor.g = get_png_val(png_pixelP);
+                fgColor.b = get_png_val(png_pixelP);
+                setXel(&xelrow[col], fgColor, bgColor, alpha_handling,
+                       isTransparentColor(fgColor, info_ptr, totalgamma) ?
+                       0 : maxval);
+            }
+            break;
+
+            case PNG_COLOR_TYPE_RGB_ALPHA: {
+                pngcolor fgColor;
+                png_uint_16 alpha;
+
+                fgColor.r = get_png_val(png_pixelP);
+                fgColor.g = get_png_val(png_pixelP);
+                fgColor.b = get_png_val(png_pixelP);
+                alpha     = get_png_val(png_pixelP);
+                setXel(&xelrow[col], fgColor, bgColor, alpha_handling, alpha);
+            }
+            break;
+
+            default:
+                pm_error ("unknown PNG color type: %d", info_ptr->color_type);
+            }
+        }
+        pnm_writepnmrow(ofP, xelrow, info_ptr->width, maxval, pnm_type, FALSE);
+    }
+    pnm_freerow (xelrow);
+}
+
+
+
+static void 
+convertpng(FILE *             const ifp, 
+           FILE *             const tfp, 
+           struct cmdlineInfo const cmdline,
+           int *              const errorlevelP) {
+
+    png_struct *png_ptr;
+    png_info *info_ptr;
+    png_byte **png_image;
+    int x, y;
+    int linesize;
+    int pnm_type;
+    pngcolor bgColor;
+    float totalgamma;
+
+    *errorlevelP = 0;
+
+    read_sig_buf(ifp);
+
+    png_ptr = png_create_read_struct(
+        PNG_LIBPNG_VER_STRING,
+        &pngtopnm_jmpbuf_struct, pngtopnm_error_handler, NULL);
+    if (png_ptr == NULL)
+        pm_error("cannot allocate main libpng structure (png_ptr)");
+
+    info_ptr = png_create_info_struct (png_ptr);
+    if (info_ptr == NULL)
+        pm_error("cannot allocate LIBPNG structures");
+
+    if (setjmp(pngtopnm_jmpbuf_struct.jmpbuf))
+        pm_error ("setjmp returns error condition");
+
+    png_init_io (png_ptr, ifp);
+    png_set_sig_bytes (png_ptr, SIG_CHECK_SIZE);
+    png_read_info (png_ptr, info_ptr);
+
+    MALLOCARRAY(png_image, info_ptr->height);
+    if (png_image == NULL) {
+        png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
+        pm_closer (ifp);
+        pm_error ("couldn't allocate space for image");
+    }
+
+    if (info_ptr->bit_depth == 16)
+        linesize = 2 * info_ptr->width;
+    else
+        linesize = info_ptr->width;
+
+    if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+        linesize *= 2;
+    else
+        if (info_ptr->color_type == PNG_COLOR_TYPE_RGB)
+            linesize *= 3;
+        else
+            if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+                linesize *= 4;
+
+    for (y = 0 ; y < info_ptr->height ; y++) {
+        png_image[y] = malloc (linesize);
+        if (png_image[y] == NULL) {
+            for (x = 0 ; x < y ; x++)
+                free (png_image[x]);
+            free (png_image);
+            png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
+            pm_closer (ifp);
+            pm_error ("couldn't allocate space for image");
+        }
+    }
+
+    if (info_ptr->bit_depth < 8)
+        png_set_packing (png_ptr);
+
+    setupGammaCorrection(png_ptr, info_ptr, cmdline.gamma, &totalgamma);
+
+    setupSignificantBits(png_ptr, info_ptr, cmdline.alpha,
+                         &maxval, errorlevelP);
+
+    getBackgroundColor(info_ptr, cmdline.background, totalgamma, maxval,
+                       &bgColor);
+
+    png_read_image (png_ptr, png_image);
+    png_read_end (png_ptr, info_ptr);
+
+    if (verbose)
+        /* Note that some of info_ptr is not defined until png_read_end() 
+       completes.  That's because it comes from chunks that are at the
+       end of the stream.
+    */
+        dump_png_info(info_ptr);
+
+    if (mtime)
+        show_time (info_ptr);
+    if (tfp)
+        save_text (info_ptr, tfp);
+
+    if (info_ptr->valid & PNG_INFO_pHYs) {
+        float r;
+        r = (float)info_ptr->x_pixels_per_unit / info_ptr->y_pixels_per_unit;
+        if (r != 1.0) {
+            pm_message ("warning - non-square pixels; "
+                        "to fix do a 'pamscale -%cscale %g'",
+                        r < 1.0 ? 'x' : 'y',
+                        r < 1.0 ? 1.0 / r : r );
+            *errorlevelP = PNMTOPNG_WARNING_LEVEL;
+        }
+    }
+
+    determineOutputType(info_ptr, cmdline.alpha, maxval, &pnm_type);
+
+    writePnm(stdout, maxval, pnm_type, info_ptr, png_image, bgColor, 
+             cmdline.alpha, totalgamma);
+
+    fflush(stdout);
+    for (y = 0 ; y < info_ptr->height ; y++)
+        free (png_image[y]);
+    free (png_image);
+    png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
+}
+
+
+
+int 
+main(int argc, char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE *ifp, *tfp;
+    int errorlevel;
+
+    pnm_init (&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    verbose = cmdline.verbose;
+    mtime = cmdline.time;
+
+    ifp = pm_openr(cmdline.inputFilespec);
+
+    if (cmdline.text)
+        tfp = pm_openw(cmdline.text);
+    else
+        tfp = NULL;
+
+    convertpng (ifp, tfp, cmdline, &errorlevel);
+
+    if (tfp)
+        pm_close(tfp);
+
+    pm_close(ifp);
+    pm_close(stdout);
+
+    return errorlevel;
+}
diff --git a/converter/other/pngtxt.c b/converter/other/pngtxt.c
new file mode 100644
index 00000000..bbbec099
--- /dev/null
+++ b/converter/other/pngtxt.c
@@ -0,0 +1,315 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <png.h>
+
+#include "nstring.h"
+#include "pngtxt.h"
+#include "pm.h"
+#include "mallocvar.h"
+
+#define MAXCOMMENTS 256
+
+
+
+static void
+readOffKey(char           const textline[],
+           unsigned int   const lineLength,
+           unsigned int * const cursorP,
+           char **        const keyP) {
+
+    /* Get the comment key */
+    char * cp;
+    unsigned int cursor;
+
+    cursor = *cursorP;
+    
+    MALLOCARRAY(cp, lineLength + 1);  /* leave room for terminating NUL */
+    if (cp == NULL) 
+        pm_error("Unable to allocate memory for text chunks");
+    
+    *keyP = cp;
+    
+    if (textline[0] == '"') {
+        ++cursor;  /* skip past opening " */
+        while (textline[cursor] != '"') {
+            if (textline[cursor] == '\0') {
+                *cp = '\0';
+                pm_error("Invalid comment file format:  keyword contains "
+                         "a NUL character.  Text leading up to the NUL "
+                         "character is '%s'", *keyP);
+            }
+            *(cp++) = textline[cursor++];
+        }
+        ++cursor;  /* skip past closing " */
+    } else {
+        while (cursor < lineLength && 
+               textline[cursor] != ' '  && textline[cursor] != '\t' &&
+               textline[cursor] != '\0')
+            *(cp++) = textline[cursor++];
+    }
+    *cp++ = '\0';
+
+    *cursorP = cursor;
+}
+
+
+
+
+static void
+startComment(struct png_text_struct * const commentP, 
+             char                     const textline[],
+             unsigned int             const lineLength,
+             bool                     const compressed) {
+/*----------------------------------------------------------------------------
+   Assuming 'textline' is the first line of a comment in a comment file,
+   put the information from it in the comment record *commentP.
+   Use the text on this line as the comment text, even though the true
+   comment text may include text from subsequent continuation lines as
+   well.
+
+   'textline' is not NUL-terminated.  Its length is 'lineLength', and
+   it is at least one character long.  'textline' does not contain a
+   newline character.
+
+   'compressed' means the comment text is compressed.
+-----------------------------------------------------------------------------*/
+    unsigned int cursor;
+
+    /* the following is a not that accurate check on Author or Title */
+    if ((!compressed) || (textline[0] == 'A') || (textline[0] == 'T'))
+        commentP->compression = -1;
+    else
+        commentP->compression = 0;
+
+    cursor = 0;
+
+    readOffKey(textline, lineLength, &cursor, &commentP->key);
+
+    /* skip over delimiters between key and comment text */
+    while (cursor < lineLength && 
+           (textline[cursor] == ' ' || textline[cursor] == '\t' ||
+           textline[cursor] == '\0'))
+        ++cursor;
+    
+    {
+        /* Get the first line of the comment text */
+        unsigned int const startPos = cursor;
+        char *cp;
+
+        MALLOCARRAY(cp, lineLength+1);  /* leave room for safety NUL */
+        if (!cp) 
+            pm_error("Unable to allocate memory for text chunks");
+
+        memcpy(cp, textline + startPos, lineLength - startPos);
+        cp[lineLength - startPos] = '\0';  /* for safety - not part of text */
+        commentP->text = cp;
+        commentP->text_length = lineLength - startPos;
+    }
+}
+
+
+
+static void
+continueComment(struct png_text_struct * const commentP, 
+                char                     const textline[],
+                unsigned int             const lineLength) {
+/*----------------------------------------------------------------------------
+   Update the comment record *commentP by adding to it the text
+   from textline[], which is a continuation line from a comment file.
+
+   'textline' is not NUL-terminated.  Its length is 'lineLength', and
+   it is at least one character long.  'textline' does not contain a
+   newline character.
+-----------------------------------------------------------------------------*/
+    unsigned int cursor;  /* cursor into textline[] */
+
+    unsigned int const newTextLength =
+        commentP->text_length + lineLength + 1 + 1;
+
+    REALLOCARRAY(commentP->text, newTextLength);
+
+    if (commentP->text == NULL)
+        pm_error("Unable to allocate %u bytes of memory for comment chunk",
+                 newTextLength);
+
+    commentP->text[commentP->text_length++] = '\n';
+
+    /* Skip past leading delimiter characters in file line */
+    cursor = 0;
+    while (textline[cursor] == ' ' || textline[cursor] == '\t' ||
+           textline[cursor] == '\0')
+        ++cursor;
+
+    memcpy(commentP->text + commentP->text_length,
+           textline + cursor,
+           lineLength - cursor);
+
+    commentP->text_length += lineLength - cursor;
+
+    commentP->text[commentP->text_length] = '\0';  /* for safety */
+}
+
+
+
+static void
+getFileLine(FILE *         const fileP, 
+            const char **  const textP, 
+            unsigned int * const lengthP) {
+/*----------------------------------------------------------------------------
+   Read the next line (characters from current position through the first
+   newline character) and return it.  Put the text in newly malloc'ed 
+   storage.
+
+   Do not include the newline.
+
+   Add a terminating NUL for safety, but note that you can't rely on this
+   as the end of line marker because the line may contain a NUL.  *lengthP
+   does not include the NUL that we add.
+
+   If there are no more characters in the file, return NULL.
+-----------------------------------------------------------------------------*/
+    char * textline;  /* malloc'ed */
+    unsigned int cursor;  /* cursor into textline[] */
+    unsigned int allocated;
+        /* The number of characters of space that are allocated for
+           'textline' 
+        */
+    bool eol;
+    bool gotSomething;
+
+    allocated = 128;  /* initial value */
+
+    MALLOCARRAY(textline, allocated);
+    if (textline == NULL)
+        pm_error("Unable to allocate buffer to read a line of a file.");
+    
+    cursor = 0;
+    eol = FALSE;
+    gotSomething = FALSE;
+
+    while (!eol) {
+        int const c = getc(fileP);
+        
+        if (c != EOF)
+            gotSomething = TRUE;
+
+        if (c == '\n' || c == EOF)
+            eol = TRUE;
+        else {
+            if (cursor > allocated - 1 - 1) { /* leave space for safety NUL */
+                allocated *= 2;
+                REALLOCARRAY(textline, allocated);
+                if (textline == NULL)
+                    pm_error("Unable to allocate buffer to read a line of "
+                             "a file.");
+            }
+            textline[cursor++] = c;
+        }
+    }
+    textline[cursor] = '\0';  /* For safety; not part of line */
+
+    if (gotSomething) {
+        *textP = textline;
+        *lengthP = cursor;
+    } else {
+        free(textline);
+        *textP = NULL;
+    }
+}
+
+
+
+static void
+handleArrayAllocation(png_text **    const arrayP,
+                      unsigned int * const allocatedCommentsP,
+                      unsigned int   const commentIdx) {
+
+    if (commentIdx >= *allocatedCommentsP) {
+        *allocatedCommentsP *= 2;
+        REALLOCARRAY(*arrayP, *allocatedCommentsP);
+        if (*arrayP == NULL) 
+            pm_error("unable to allocate memory for comment array");
+    }
+}
+
+
+/******************************************************************************
+                            EXTERNAL SUBROUTINES
+******************************************************************************/
+
+
+void 
+pnmpng_read_text (png_info * const info_ptr, 
+                  FILE *     const tfp, 
+                  bool       const ztxt,
+                  bool       const verbose) {
+
+    const char * textline;
+    unsigned int lineLength;
+    unsigned int commentIdx;
+    bool noCommentsYet;
+    bool eof;
+    unsigned int allocatedComments;
+        /* Number of entries currently allocated for the info_ptr->text
+           array 
+        */
+
+    allocatedComments = 256;  /* initial value */
+
+    MALLOCARRAY(info_ptr->text, allocatedComments);
+    if (info_ptr->text == NULL) 
+        pm_error("unable to allocate memory for comment array");
+
+    commentIdx = 0;
+    noCommentsYet = TRUE;
+   
+    eof = FALSE;
+    while (!eof) {
+        getFileLine(tfp, &textline, &lineLength);
+        if (textline == NULL)
+            eof = TRUE;
+        else {
+            if (lineLength == 0) {
+                /* skip this empty line */
+            } else {
+                handleArrayAllocation(&info_ptr->text, &allocatedComments,
+                                      commentIdx);
+                if ((textline[0] != ' ') && (textline[0] != '\t')) {
+                    /* Line doesn't start with white space, which
+                       means it starts a new comment.  
+                    */
+                    if (noCommentsYet) {
+                        /* No previous comment to move past */
+                    } else
+                        ++commentIdx;
+                    noCommentsYet = FALSE;
+
+                    startComment(&info_ptr->text[commentIdx], 
+                                 textline, lineLength, ztxt);
+                } else {
+                    /* Line starts with whitespace, which means it is
+                       a continuation of the current comment.
+                    */
+                    if (noCommentsYet)
+                        pm_error("Invalid comment file format: "
+                                 "first line is a continuation line! "
+                                 "(It starts with whitespace)");
+                    continueComment(&info_ptr->text[commentIdx], 
+                                    textline, lineLength);
+                }
+            }
+            strfree(textline);
+        }
+    } 
+    if (noCommentsYet)
+        info_ptr->num_text = 0;
+    else
+        info_ptr->num_text = commentIdx + 1;
+
+    if (verbose)
+        pm_message("%d comments placed in text chunk", info_ptr->num_text);
+}
+
+
+
diff --git a/converter/other/pngtxt.h b/converter/other/pngtxt.h
new file mode 100644
index 00000000..ae7fe2c4
--- /dev/null
+++ b/converter/other/pngtxt.h
@@ -0,0 +1,13 @@
+#ifndef PNGTXT_H_INCLUDED
+#define PNGTXT_H_INCLUDED
+
+#include "pm_c_util.h"
+#include <png.h>
+
+void 
+pnmpng_read_text (png_info * const info_ptr, 
+                  FILE *     const tfp, 
+                  bool const ztxt,
+                  bool const verbose);
+
+#endif
diff --git a/converter/other/pnmtoddif.c b/converter/other/pnmtoddif.c
new file mode 100644
index 00000000..65152865
--- /dev/null
+++ b/converter/other/pnmtoddif.c
@@ -0,0 +1,611 @@
+/*
+ * $Log: pnmtoddif.c,v $
+ * Revision 1.6  1993/01/25  08:14:06  neideck
+ * Placed into public use form.
+ *
+ * Revision 1.5  1992/12/02  08:15:18  neideck
+ *  Added RCS id.
+ *
+ */
+
+/*
+ * Author:      Burkhard Neidecker-Lutz
+ *              Digital CEC Karlsruhe
+ *      neideck@nestvx.enet.dec.com 
+
+ Copyright (c) Digital Equipment Corporation, 1992
+
+ 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, and that the name of Digital Equipment
+ Corporation not be used in advertising or publicity pertaining to
+ distribution of the software without specific, written prior
+ permission. Digital Equipment Corporation makes no representations
+ about the suitability of this software for any purpose. It is provided
+ "as is" without express or implied warranty.
+
+ Version: 1.0           30.07.92
+
+*/
+
+#include <string.h>
+#include "pnm.h"
+
+/* The structure we use to convey all sorts of "magic" data to the DDIF */
+/* header write procedure.                      */
+
+typedef struct {
+    int width;
+    int height;
+    int h_res;            /* Resolutions in dpi for bounding box */
+    int v_res;
+    int bits_per_pixel;
+    int bytes_per_line;
+    int spectral;         /* 2 == monochrome, 5 == rgb        */
+    int components;
+    int bits_per_component;
+    int polarity;         /* zeromin == 2 , zeromax == 1      */
+} imageparams;
+
+
+
+/* ASN.1 basic encoding rules magic number */
+#define UNIVERSAL 0
+#define APPLICATION 1
+#define CONTEXT 2
+#define PRIVATE 3
+
+#define PRIM 0
+#define CONS 1
+
+/* "tag": Emit an ASN tag of the specified class and tag number.    */
+/* This is used in conjuntion with the                  */
+/* wr_xxx routines that follow to construct the various ASN.1 entities. */
+/* Writing each entity is a two-step process, where first the tag is    */
+/* written and then the length and value.               */
+/* All of these routines take a pointer to a pointer into an output */
+/* buffer in the first argument and update it accordingly.      */
+
+static void tag(unsigned char ** buffer, int cl, int constructed,
+                unsigned int t)
+{
+    int tag_first;
+    unsigned int stack[10];
+    int sp;
+    unsigned char *p = *buffer;
+
+    tag_first = (cl << 6) | constructed << 5;
+    if (t < 31) {         /* Short tag form   */
+        *p++ = tag_first | t;
+    } else {          /* Long tag form */
+        *p++ = tag_first | 31;
+        sp = 0;
+        while (t > 0) {
+            stack[sp++] = t & 0x7f; 
+            t >>= 7;
+        }
+        while (--sp > 0) {  /* Tag values with continuation bits */
+            *p++ = stack[sp] | 0x80;
+        }
+        *p++ = stack[0];    /* Last tag portion without continuation bit */
+    }
+    *buffer = p;      /* Update buffer pointer */
+}
+
+
+
+/* Emit indefinite length encoding */
+static void 
+ind(unsigned char **buffer)
+{
+    unsigned char *p = *buffer;
+
+    *p++ = 0x80;
+    *buffer = p;
+}
+
+
+
+/* Emit ASN.1 NULL */
+static void 
+wr_null(unsigned char **buffer)
+{
+    unsigned char *p = *buffer;
+
+    *p++ = 0;
+    *buffer = p;
+}
+
+
+
+/* Emit ASN.1 length only into buffer, no data */
+static void 
+wr_length(unsigned char ** buffer, int amount)
+{
+    int length;
+    unsigned int mask;
+    unsigned char *p = *buffer;
+
+    length = 4;
+    mask = 0xff000000;
+
+    if (amount < 128) {
+        *p++ = amount;
+    } else {          /* > 127 */
+        while (!(amount & mask)) {  /* Search for first non-zero byte */
+            mask >>= 8;
+            --length;
+        }
+
+        *p++ = length | 0x80;       /* Number of length bytes */
+
+        while (--length >= 0) {     /* Put length bytes   */
+            *p++ =(amount >> (8*length)) & 0xff;
+        }
+
+    }
+    *buffer = p;
+}
+
+
+
+/* BER encode an integer and write it's length and value */
+static void 
+wr_int(unsigned char ** buffer, int val)
+{
+    int length;
+    int sign;
+    unsigned int mask;
+    unsigned char *p = *buffer;
+
+    if (val == 0) {
+        *p++ = 1;               /* length */
+        *p++ = 0;               /* value  */
+    } else {
+        sign = val < 0 ? 0xff : 0x00;   /* Sign bits */
+        length = 4;
+#ifdef __STDC__
+        mask  = 0xffu << 24;
+#else
+        mask  = 0xff << 24;
+#endif
+        while ((val & mask) == sign) {  /* Find the smallest representation */
+            length--;
+            mask >>= 8;
+        }
+        sign = (0x80 << ((length-1)*8)) & val; /* New sign bit */
+        if (((val < 0) && !sign) || ((val > 0) && sign)) { /* Sign error */
+            length++;
+        }
+        *p++ = length;          /* length */
+        while (--length >= 0) {
+            *p++ = (val >> (8*length)) & 0xff;
+        }
+    }
+    *buffer = p;
+}
+
+
+
+/* Emit and End Of Coding sequence  */
+static void 
+eoc(unsigned char ** buffer)
+{
+    unsigned char *p = *buffer;
+
+    *p++ = 0;
+    *p++ = 0;
+    *buffer = p;
+}
+
+
+
+/* Emit a simple string */
+static 
+void wr_string(unsigned char ** const buffer, const char * const val)
+{
+    int length;
+    unsigned char *p = *buffer;
+
+    length  = strlen(val);        
+    if (length > 127) {
+        fprintf(stderr,"Can't encode length > 127 yet (%d)\n",length);
+        exit(1);
+    }
+    *p++ = length;
+    {
+        const char * valCursor;
+        for (valCursor = val; *valCursor; ++valCursor)
+            *p++ = *valCursor;
+    }
+    *buffer = p;
+}
+
+
+
+/* Emit a ISOLATIN-1 string */
+static void 
+emit_isolatin1(unsigned char ** const buffer, const char * const val)
+{
+    int length;
+    unsigned char *p = *buffer;
+
+    length  = strlen(val) + 1;        /* One NULL byte and charset leader */
+    if (length > 127) {
+        fprintf(stderr,"Can't encode length > 127 yet (%d)\n",length);
+        exit(1);
+    }
+    *p++ = length;
+    *p++ = 1;             /* ISO LATIN-1 */
+    {
+        const char * valCursor;
+        for (valCursor = val; *valCursor; ++valCursor)
+            *p++ = *valCursor;
+    }
+    *buffer = p;
+}
+
+
+
+/* Write the DDIF grammar onto "file" up to the actual starting location */
+/* of the image data. The "ip" structure needs to be set to the right    */
+/* values. A lot of the values here are hardcoded to be just right for   */
+/* the bit grammars that the PBMPLUS formats want.           */
+
+static int 
+write_header(FILE *file, imageparams *ip)
+{
+    unsigned char buffer[300];            /* Be careful with the size ! */
+    unsigned char *p = buffer;
+    int headersize;
+    int bounding_x;
+    int bounding_y;
+    int i;
+
+    /* Calculate the bounding box from the resolutions    */
+    bounding_x = ((int) (1200 * ((double) (ip->width) / ip->h_res)));
+    bounding_y = ((int) (1200 * ((double) (ip->height) / ip->v_res)));
+
+    /* This is gross. The entire DDIF grammar is constructed by   */
+    /* hand. The indentation is meant to indicate DDIF document structure */
+
+    tag(&p,PRIVATE,CONS,16383); ind(&p);      /* DDIF Document */
+    tag(&p,CONTEXT,CONS, 0); ind(&p);        /* Document Descriptor */
+    tag(&p,CONTEXT,PRIM, 0); wr_int(&p,1);  /* Major Version */
+    tag(&p,CONTEXT,PRIM, 1); wr_int(&p,3);  /* Minor Version */
+    tag(&p,CONTEXT,PRIM, 2); wr_string(&p,"PBM+"); /* Product Indentifier */
+    tag(&p,CONTEXT,CONS, 3); ind(&p);       /* Product Name */
+    tag(&p,PRIVATE,PRIM, 9); emit_isolatin1(&p,"PBMPLUS Writer V1.0");
+    eoc(&p);
+    eoc(&p);                 /* Document Descriptor */
+    tag(&p,CONTEXT,CONS, 1); ind(&p);        /* Document Header     */
+    tag(&p,CONTEXT,CONS, 3); ind(&p);       /* Version */
+    tag(&p,PRIVATE,PRIM, 9); emit_isolatin1(&p,"1.0");
+    eoc(&p);
+    eoc(&p);                 /* Document Header */
+    tag(&p,CONTEXT,CONS, 2); ind(&p);        /* Document Content    */
+    tag(&p,APPLICATION,CONS,2); ind(&p);    /* Segment Primitive    */
+    eoc(&p);
+    tag(&p,APPLICATION,CONS,2); ind(&p);    /* Segment  */
+    tag(&p,CONTEXT,CONS, 3); ind(&p);      /* Segment Specific Attributes */
+    tag(&p,CONTEXT,PRIM, 2); wr_string(&p,"$I");  /* Category */
+    tag(&p,CONTEXT,CONS,22); ind(&p);     /* Image Attributes */
+    tag(&p,CONTEXT,CONS, 0); ind(&p);    /* Image Presentation Attributes */
+    tag(&p,CONTEXT,PRIM, 1); wr_int(&p,0);  /* Pixel Path */
+    tag(&p,CONTEXT,PRIM, 2); wr_int(&p,270); /* Line Progression */
+    tag(&p,CONTEXT,CONS, 3); ind(&p);   /* Pixel Aspect Ratio */
+    tag(&p,CONTEXT,PRIM, 0); wr_int(&p,1); /* PP Pixel Dist */
+    tag(&p,CONTEXT,PRIM, 1); wr_int(&p,1); /* LP Pixel Dist */
+    eoc(&p);                /* Pixel Aspect Ratio */
+    tag(&p,CONTEXT,PRIM, 4); wr_int(&p,ip->polarity);  
+        /* Brightness Polarity */
+    tag(&p,CONTEXT,PRIM, 5); wr_int(&p,1);  /* Grid Type    */
+    tag(&p,CONTEXT,PRIM, 7); wr_int(&p,ip->spectral);  /* Spectral Mapping */
+    tag(&p,CONTEXT,CONS,10); ind(&p);   /* Pixel Group Info */
+    tag(&p,CONTEXT,PRIM, 0); wr_int(&p,1); /* Pixel Group Size */
+    tag(&p,CONTEXT,PRIM, 1); wr_int(&p,1); /* Pixel Group Order */
+    eoc(&p);                /* Pixel Group Info */
+    eoc(&p);                     /* Image Presentation Attributes */
+    tag(&p,CONTEXT,CONS, 1); ind(&p);    /* Component Space Attributes */
+    tag(&p,CONTEXT,PRIM, 0); wr_int(&p,1);  /* Component Space Organization */
+    tag(&p,CONTEXT,PRIM, 1); wr_int(&p,1);  /* Planes per Pixel */
+    tag(&p,CONTEXT,PRIM, 2); wr_int(&p,1);  /* Plane Significance   */
+    tag(&p,CONTEXT,PRIM, 3); wr_int(&p,ip->components);  
+        /* Number of Components    */
+    tag(&p,CONTEXT,CONS, 4); ind(&p);   /* Bits per Component   */
+    for (i = 0; i < ip->components; i++) {
+        tag(&p,UNIVERSAL,PRIM,2); wr_int(&p,ip->bits_per_component);
+    }
+    eoc(&p);                /* Bits per Component   */
+    tag(&p,CONTEXT,CONS, 5); ind(&p);   /* Component Quantization Levels */
+    for (i = 0; i < ip->components; i++) {
+        tag(&p,UNIVERSAL,PRIM,2); wr_int(&p,1 << ip->bits_per_component);
+    }
+    eoc(&p);                /* Component Quantization Levels */
+    eoc(&p);                 /* Component Space Attributes */
+    eoc(&p);                  /* Image Attributes */
+    tag(&p,CONTEXT,CONS,23); ind(&p);     /* Frame Parameters */
+    tag(&p,CONTEXT,CONS, 1); ind(&p);    /* Bounding Box */
+    tag(&p,CONTEXT,CONS, 0); ind(&p);   /* lower-left   */
+    tag(&p,CONTEXT,CONS, 0); ind(&p);  /* XCoordinate  */
+    tag(&p,CONTEXT,PRIM, 0); wr_int(&p,0);
+    eoc(&p);                           /* XCoordinate  */
+    tag(&p,CONTEXT,CONS, 1); ind(&p);  /* YCoordinate  */
+    tag(&p,CONTEXT,PRIM, 0); wr_int(&p,0);
+    eoc(&p);                               /* YCoordinate  */
+    eoc(&p);                /* lower left */
+    tag(&p,CONTEXT,CONS, 1); ind(&p);       /* upper right */
+    tag(&p,CONTEXT,CONS, 0); ind(&p);      /* XCoordinate  */
+    tag(&p,CONTEXT,PRIM, 0); wr_int(&p,bounding_x);
+    eoc(&p);               /* XCoordinate  */
+    tag(&p,CONTEXT,CONS, 1); ind(&p);      /* YCoordinate  */
+    tag(&p,CONTEXT,PRIM, 0); wr_int(&p,bounding_y);
+    eoc(&p);                   /* YCoordinate  */
+    eoc(&p);                            /* upper right */
+    eoc(&p);                 /* Bounding Box */
+    tag(&p,CONTEXT,CONS, 4); ind(&p);    /* Frame Position */
+    tag(&p,CONTEXT,CONS, 0); ind(&p);   /* XCoordinate  */
+    tag(&p,CONTEXT,PRIM, 0); wr_int(&p,0);
+    eoc(&p);                /* XCoordinate  */
+    tag(&p,CONTEXT,CONS, 1); ind(&p);   /* YCoordinate  */
+    tag(&p,CONTEXT,PRIM, 0); wr_int(&p,0);
+    eoc(&p);                    /* YCoordinate  */
+    eoc(&p);                 /* Frame Position */
+    eoc(&p);                  /* Frame Parameters */
+    eoc(&p);                        /* Segment Specific Attributes */
+    eoc(&p);                    /* Segment */
+    tag(&p,APPLICATION,CONS,17); ind(&p);   /* Image Data Descriptor */
+    tag(&p,UNIVERSAL,CONS,16); ind(&p);    /* Sequence */
+    tag(&p,CONTEXT,CONS, 0); ind(&p);     /* Image Coding Attributes */
+    tag(&p,CONTEXT,PRIM, 1); wr_int(&p,ip->width); /* Pixels per Line    */
+    tag(&p,CONTEXT,PRIM, 2); wr_int(&p,ip->height);  /* Number of Lines  */
+    tag(&p,CONTEXT,PRIM, 3); wr_int(&p,2);   /* Compression Type */
+    tag(&p,CONTEXT,PRIM, 5); wr_int(&p,0);   /* Data Offset  */
+    tag(&p,CONTEXT,PRIM, 6); wr_int(&p,ip->bits_per_pixel);  /* Pixel Stride */
+    tag(&p,CONTEXT,PRIM, 7); wr_int(&p,ip->bytes_per_line * 8);
+        /* Scanline Stride    */
+    tag(&p,CONTEXT,PRIM, 8); wr_int(&p,1);   /* Bit Order        */
+    tag(&p,CONTEXT,PRIM, 9); wr_int(&p,ip->bits_per_pixel);  
+        /* Planebits per Pixel */
+    tag(&p,CONTEXT,CONS,10); ind(&p);    /* Byteorder Info   */
+    tag(&p,CONTEXT,PRIM, 0); wr_int(&p,1);  /* Byte Unit        */
+    tag(&p,CONTEXT,PRIM, 1); wr_int(&p,1);  /* Byte Order   */
+    eoc(&p);                 /* Byteorder Info   */
+    tag(&p,CONTEXT,PRIM,11); wr_int(&p,3);   /* Data Type        */
+    eoc(&p);                              /* Image Coding Attributes */
+    tag(&p,CONTEXT,PRIM, 1); wr_length(&p,ip->bytes_per_line*ip->height);  
+        /* Component Plane Data */
+    /* End of DDIF document Indentation */
+    headersize = p - buffer;
+    if (headersize >= 300)  {
+        fprintf(stderr,"Overran buffer area %d >= 300\n",headersize);
+        exit(1);
+    }
+
+    return (fwrite(buffer, 1, headersize, file) == headersize);
+}
+
+
+
+/* Write all the closing brackets of the DDIF grammar that are missing */
+/* The strange indentation reflects exactly the same indentation that  */
+/* we left off in the write_header procedure.                  */
+static int 
+write_trailer(FILE * file)
+{
+    unsigned char buffer[30];
+    unsigned char *p = buffer;
+    int trailersize;
+
+    /* Indentation below gives DDIF document structure */
+    eoc(&p);                        /* Sequence */
+    eoc(&p);                     /* Image Data Descriptor */
+    tag(&p,APPLICATION,PRIM,1); wr_null(&p);     /* End Segment */
+    tag(&p,APPLICATION,PRIM,1); wr_null(&p);     /* End Segment */
+    eoc(&p);                  /* Document Content */
+    eoc(&p);                   /* DDIF Document */
+    /* End of DDIF document Indentation */
+    trailersize = p - buffer;
+    if (trailersize >= 30)  {
+        fprintf(stderr,"Overran buffer area %d >= 30\n",trailersize);
+        exit(1);
+    }
+
+    return(fwrite(buffer, 1, trailersize, file) == trailersize);
+}
+
+
+
+int main(int argc, char *argv[])
+{
+    FILE           *ifd;
+    FILE       *ofd;
+    int             rows, cols;
+    xelval          maxval;
+    int             format;
+    const char     * const usage = "[-resolution x y] [pnmfile [ddiffile]]";
+    int             i, j;
+    char           *outfile;
+    int       argn;
+    int hor_resolution = 75;
+    int ver_resolution = 75;
+    imageparams ip;
+    unsigned char  *data, *p;
+
+    pnm_init(&argc, argv);
+
+    for (argn = 1;argn < argc && argv[argn][0] == '-';argn++) {
+        int arglen = strlen(argv[argn]);
+
+        if (!strncmp (argv[argn],"-resolution", arglen)) {
+            if (argn + 2 < argc) {
+                hor_resolution = atoi(argv[argn+1]);
+                ver_resolution = atoi(argv[argn+2]);
+                argn += 2;
+                continue;
+            } else {
+                pm_usage(usage);
+            }
+        } else {
+            pm_usage(usage);
+        }
+    }
+
+    if (hor_resolution <= 0 || ver_resolution <= 0) {
+        fprintf(stderr,"Unreasonable resolution values: %d x %d\n",
+                hor_resolution,ver_resolution);
+        exit(1);
+    }
+
+    if (argn == argc - 2) {
+        ifd = pm_openr(argv[argn]);
+        outfile = argv[argn+1];
+        if (!(ofd = fopen(outfile,"wb"))) {
+            perror(outfile);
+            exit(1);
+        }
+    } else if (argn == argc - 1) {
+        ifd = pm_openr(argv[argn]);
+        ofd = stdout;
+    } else {
+        ifd = stdin;
+        ofd = stdout;
+    }
+
+    pnm_readpnminit(ifd, &cols, &rows, &maxval, &format);
+
+    ip.width = cols;
+    ip.height = rows;
+    ip.h_res = hor_resolution;
+    ip.v_res = ver_resolution;
+
+    switch (PNM_FORMAT_TYPE(format)) {
+    case PBM_TYPE:
+        ip.bits_per_pixel = 1;
+        ip.bytes_per_line = (cols + 7) / 8;
+        ip.spectral = 2;
+        ip.components = 1;
+        ip.bits_per_component = 1;
+        ip.polarity = 1;
+        break;
+    case PGM_TYPE:
+        ip.bytes_per_line = cols;
+        ip.bits_per_pixel = 8;
+        ip.spectral = 2;
+        ip.components = 1;
+        ip.bits_per_component = 8;
+        ip.polarity = 2;
+        break;
+    case PPM_TYPE:
+        ip.bytes_per_line = 3 * cols;
+        ip.bits_per_pixel = 24;
+        ip.spectral = 5;
+        ip.components = 3;
+        ip.bits_per_component = 8;
+        ip.polarity = 2;
+        break;
+    default:
+        fprintf(stderr, "Unrecognized PBMPLUS format %d\n", format);
+        exit(1);
+    }
+
+    if (!write_header(ofd,&ip)) {
+        perror("Writing header");
+        exit(1);
+    }
+
+    if (!(p = data = (unsigned char*)  malloc(ip.bytes_per_line))) {
+        perror("allocating line buffer");
+        exit(1);
+    }
+
+    switch (PNM_FORMAT_TYPE(format)) {
+    case PBM_TYPE:
+    {
+        bit            *pixels;
+        int             mask;
+        int             k;
+
+        pixels = pbm_allocrow(cols);
+
+        for (i = 0; i < rows; i++) {
+            pbm_readpbmrow(ifd, pixels, cols, format);
+            mask = 0;
+            p = data;
+            for (j = 0, k = 0; j < cols; j++) {
+                if (pixels[j] == PBM_BLACK) {
+                    mask |= 1 << k;
+                }
+                if (k == 7) {
+                    *p++ = mask;
+                    mask = 0;
+                    k = 0;
+                } else {
+                    k++;
+                }
+            }
+            if (k != 7) {       /* Flush the rest of the column */
+                *p = mask;
+            }
+            if (fwrite(data,1,ip.bytes_per_line,ofd) != ip.bytes_per_line) {
+                perror("Writing image data\n");
+                exit(1);
+            }
+        }
+    }
+    break;
+    case PGM_TYPE:
+    {
+        gray           *pixels;
+
+        pixels = (gray *) data;
+
+        for (i = 0; i < rows; i++) {
+            pgm_readpgmrow(ifd, pixels, cols, maxval, format);
+            if (fwrite(data,1,ip.bytes_per_line,ofd) != ip.bytes_per_line) {
+                perror("Writing image data\n");
+                exit(1);
+            }
+        }
+    }
+    break;
+    case PPM_TYPE:
+    {
+        pixel          *pixels = ppm_allocrow(cols);
+
+        for (i = 0; i < rows; i++) {
+            p = data;
+            ppm_readppmrow(ifd, pixels, cols, maxval, format);
+            for (j = 0; j < cols; j++) {
+                *p++ = PPM_GETR(pixels[j]);
+                *p++ = PPM_GETG(pixels[j]);
+                *p++ = PPM_GETB(pixels[j]);
+            }
+            if (fwrite(data,1,ip.bytes_per_line,ofd) != ip.bytes_per_line) {
+                perror("Writing image data\n");
+                exit(1);
+            }
+        }
+        ppm_freerow(pixels);
+    }
+    break;
+    }
+
+    pm_close(ifd);
+
+    free(data);
+
+    if (!write_trailer(ofd)) {
+        perror("Writing trailer");
+        exit(1);
+    }
+
+    if (fclose(ofd) == EOF) {
+        perror("Closing output file");
+        exit(1);
+    };
+
+    return(0);
+}
diff --git a/converter/other/pnmtojpeg.c b/converter/other/pnmtojpeg.c
new file mode 100644
index 00000000..a0262331
--- /dev/null
+++ b/converter/other/pnmtojpeg.c
@@ -0,0 +1,1098 @@
+/*****************************************************************************
+                                pnmtojpeg
+******************************************************************************
+  This program is part of the Netpbm package.
+
+  This program converts from the PNM formats to the JFIF format
+  which is based on JPEG.
+
+  This program is by Bryan Henderson on 2000.03.06, but is derived
+  with permission from the program cjpeg, which is in the Independent
+  Jpeg Group's JPEG library package.  Under the terms of that permission,
+  redistribution of this software is restricted as described in the 
+  file README.JPEG.
+
+  Copyright (C) 1991-1998, Thomas G. Lane.
+
+*****************************************************************************/
+
+#define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include <ctype.h>		/* to declare isdigit(), etc. */
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <assert.h>
+/* Note: jpeglib.h prerequires stdlib.h and ctype.h.  It should include them
+   itself, but doesn't.
+*/
+#include <jpeglib.h>
+#include "pnm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+
+#define EXIT_WARNING 2   /* Goes with EXIT_SUCCESS, EXIT_FAILURE in stdlib.h */
+
+enum restart_unit {RESTART_MCU, RESTART_ROW, RESTART_NONE};
+enum density_unit {DEN_UNSPECIFIED, DEN_DOTS_PER_INCH, DEN_DOTS_PER_CM};
+
+struct density {
+    enum density_unit unit;
+        /* The units of density for 'horiz', 'vert' */
+    unsigned short horiz;  /* Not 0 */
+        /* Horizontal density, in units specified by 'unit' */
+    unsigned short vert;   /* Not 0 */
+        /* Same as 'horiz', but vertical */
+};
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+     */
+    char *input_filespec;
+    unsigned int verbose;
+    unsigned int quality;
+    unsigned int force_baseline;
+    unsigned int progressive;
+    unsigned int arith_code;
+    J_DCT_METHOD dct_method;
+    unsigned int grayscale;
+    unsigned int rgb;
+    long int max_memory_to_use;
+    unsigned int trace_level;
+    char *qslots;
+    char *qtablefile;
+    char *sample;
+    char *scans;
+    int smoothing_factor;
+    unsigned int optimize;
+    unsigned int restart_value;
+    enum restart_unit restart_unit;
+    char *restart;
+    char *comment;              /* NULL if none */
+    const char *exif_filespec;  /* NULL if none */
+    unsigned int density_spec;
+        /* boolean: JFIF should specify a density.  If false, 'density'
+           is undefined.
+        */
+    struct density density;
+};
+
+static void 
+interpret_maxmemory (const char * const maxmemory, 
+                     long int * const max_memory_to_use_p) { 
+    long int lval;
+    char ch;
+    
+    if (maxmemory == NULL) {
+        *max_memory_to_use_p = -1;  /* unspecified */
+    } else if (sscanf(maxmemory, "%ld%c", &lval, &ch) < 1) {
+        pm_error("Invalid value for --maxmemory option: '%s'.", maxmemory);
+        exit(EXIT_FAILURE);
+    } else {
+        if (ch == 'm' || ch == 'M') lval *= 1000L;
+        *max_memory_to_use_p = lval * 1000L;
+    }
+}
+
+
+
+static void
+interpret_restart(const char * const restart,
+                  unsigned int * const restart_value_p,
+                  enum restart_unit * const restart_unit_p) {
+/*----------------------------------------------------------------------------
+   Interpret the restart command line option.  Return values suitable
+   for plugging into a jpeg_compress_struct to control compression.
+-----------------------------------------------------------------------------*/
+    if (restart == NULL) {
+        /* No --restart option.  Set default */
+        *restart_unit_p = RESTART_NONE;
+    } else {
+        /* Restart interval in MCU rows (or in MCUs with 'b'). */
+        long lval;
+        char ch;
+        unsigned int matches;
+        
+        matches= sscanf(restart, "%ld%c", &lval, &ch);
+        if (matches == 0) 
+            pm_error("Invalid value for the --restart option : '%s'.",
+                     restart);
+        else {
+            if (lval < 0 || lval > 65535L) {
+                pm_error("--restart value %ld is out of range.", lval);
+                exit(EXIT_FAILURE);
+            } else {
+                if (matches == 1) {
+                    *restart_value_p = lval;
+                    *restart_unit_p = RESTART_ROW;
+                } else {
+                    if (ch == 'b' || ch == 'B') {
+                        *restart_value_p = lval;
+                        *restart_unit_p = RESTART_MCU;
+                    } else pm_error("Invalid --restart value '%s'.", restart);
+                }
+            }
+        }
+    }
+}
+
+
+
+
+static void
+interpret_density(const char *        const densityString,
+                  struct density *    const densityP) {
+/*----------------------------------------------------------------------------
+   Interpret the value of the "-density" option.
+-----------------------------------------------------------------------------*/
+    if (strlen(densityString) < 1)
+        pm_error("-density value cannot be null.");
+    else {
+        char * unitName;  /* malloc'ed */
+        int matched;
+        int horiz, vert;
+
+        unitName = malloc(strlen(densityString)+1);
+    
+        matched = sscanf(densityString, "%dx%d%s", &horiz, &vert, unitName);
+
+        if (matched < 2)
+            pm_error("Invalid format for density option value '%s'.  It "
+                     "should follow the example '3x2' or '3x2dpi' or "
+                     "'3x2dpcm'.", densityString);
+        else {
+            if (horiz <= 0 || horiz >= 1<<16)
+                pm_error("Horizontal density %d is outside the range 1-65535",
+                         horiz);
+            else if (vert <= 0 || vert >= 1<<16)
+                pm_error("Vertical density %d is outside the range 1-65535",
+                         vert);
+            else {
+                densityP->horiz = horiz;
+                densityP->vert  = vert;
+
+                if (matched < 3) 
+                    densityP->unit = DEN_UNSPECIFIED;
+                else {
+                    if (streq(unitName, "dpi") || streq(unitName, "DPI"))
+                        densityP->unit = DEN_DOTS_PER_INCH;
+                    else if (streq(unitName, "dpcm") ||
+                             streq(unitName, "DPCM"))
+                        densityP->unit = DEN_DOTS_PER_CM;
+                    else
+                        pm_error("Unrecognized unit '%s' in the density value "
+                                 "'%s'.  I recognize only 'dpi' and 'dpcm'",
+                                 unitName, densityString);
+                }
+            }
+        }
+        free(unitName);
+    }
+}
+
+
+
+static void
+parseCommandLine(const 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!
+
+   On the other hand, unlike other option processing functions, we do
+   not change argv at all.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def = malloc(100*sizeof(optEntry));
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    int i;  /* local loop variable */
+
+    const char *dctval;
+    const char *maxmemory;
+    const char *restart;
+    const char *density;
+
+    unsigned int qualitySpec, smoothSpec;
+
+    unsigned int option_def_index;
+
+    int argc_parse;       /* argc, except we modify it as we parse */
+    char ** argv_parse;
+        /* argv, except we modify it as we parse */
+
+    MALLOCARRAY(argv_parse, argc);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "verbose",     OPT_FLAG,   NULL, &cmdlineP->verbose,        0);
+    OPTENT3(0, "quality",     OPT_UINT,   &cmdlineP->quality, 
+            &qualitySpec,        0);
+    OPTENT3(0, "baseline",    OPT_FLAG,   NULL, &cmdlineP->force_baseline, 0);
+    OPTENT3(0, "progressive", OPT_FLAG,   NULL, &cmdlineP->progressive,    0);
+    OPTENT3(0, "arithmetic",  OPT_FLAG,   NULL, &cmdlineP->arith_code,     0);
+    OPTENT3(0, "dct",         OPT_STRING, &dctval, NULL,                    0);
+    OPTENT3(0, "grayscale",   OPT_FLAG,   NULL, &cmdlineP->grayscale,      0);
+    OPTENT3(0, "greyscale",   OPT_FLAG,   NULL, &cmdlineP->grayscale,      0);
+    OPTENT3(0, "rgb",         OPT_FLAG,   NULL, &cmdlineP->rgb,            0);
+    OPTENT3(0, "maxmemory",   OPT_STRING, &maxmemory, NULL,                 0);
+    OPTENT3(0, "tracelevel",  OPT_UINT,   &cmdlineP->trace_level, NULL,    0);
+    OPTENT3(0, "qslots",      OPT_STRING, &cmdlineP->qslots,      NULL,    0);
+    OPTENT3(0, "qtables",     OPT_STRING, &cmdlineP->qtablefile,  NULL,    0);
+    OPTENT3(0, "sample",      OPT_STRING, &cmdlineP->sample,      NULL,    0);
+    OPTENT3(0, "scans",       OPT_STRING, &cmdlineP->scans,       NULL,    0);
+    OPTENT3(0, "smooth",      OPT_UINT,   &cmdlineP->smoothing_factor, 
+            &smoothSpec,  0);
+    OPTENT3(0, "optimize",    OPT_FLAG,   NULL, &cmdlineP->optimize,       0);
+    OPTENT3(0, "optimise",    OPT_FLAG,   NULL, &cmdlineP->optimize,       0);
+    OPTENT3(0, "restart",     OPT_STRING, &restart, NULL,                   0);
+    OPTENT3(0, "comment",     OPT_STRING, &cmdlineP->comment, NULL,        0);
+    OPTENT3(0, "exif",        OPT_STRING, &cmdlineP->exif_filespec, NULL,  0);
+    OPTENT3(0, "density",     OPT_STRING, &density, 
+            &cmdlineP->density_spec, 0);
+
+    /* Set the defaults */
+    dctval = NULL;
+    maxmemory = NULL;
+    cmdlineP->trace_level = 0;
+    cmdlineP->qslots = NULL;
+    cmdlineP->qtablefile = NULL;
+    cmdlineP->sample = NULL;
+    cmdlineP->scans = NULL;
+    restart = NULL;
+    cmdlineP->comment = NULL;
+    cmdlineP->exif_filespec = NULL;
+
+    /* Make private copy of arguments for optParseOptions to corrupt */
+    argc_parse = argc;
+    for (i=0; i < argc; i++) argv_parse[i] = argv[i];
+
+    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_parse, argv_parse, opt, sizeof(opt), 0);
+
+    if (!qualitySpec)
+        cmdlineP->quality = -1;  /* unspecified */
+
+    if (!smoothSpec)
+        cmdlineP->smoothing_factor = -1;
+
+    if (cmdlineP->rgb && cmdlineP->grayscale)
+        pm_error("You can't specify both -rgb and -grayscale");
+
+    if (argc_parse - 1 == 0)
+        cmdlineP->input_filespec = strdup("-");  /* he wants stdin */
+    else if (argc_parse - 1 == 1)
+        cmdlineP->input_filespec = strdup(argv_parse[1]);
+    else 
+        pm_error("Too many arguments.  The only argument accepted "
+                 "is the input file specification.");
+    if (dctval == NULL)
+        cmdlineP->dct_method = JDCT_DEFAULT;
+    else {
+        if (streq(dctval, "int"))
+            cmdlineP->dct_method = JDCT_ISLOW;
+        else if (streq(dctval, "fast"))
+            cmdlineP->dct_method = JDCT_IFAST;
+        else if (streq(dctval, "float"))
+            cmdlineP->dct_method = JDCT_FLOAT;
+        else
+            pm_error("Invalid value for the --dct option: '%s'.", dctval);
+    }
+
+    interpret_maxmemory(maxmemory, &cmdlineP->max_memory_to_use);
+    interpret_restart(restart, &cmdlineP->restart_value,
+                      &cmdlineP->restart_unit);
+    if (cmdlineP->density_spec) 
+        interpret_density(density, &cmdlineP->density);
+    
+    if (cmdlineP->smoothing_factor > 100)
+        pm_error("Smoothing factor %d is greater than 100 (%%).",
+                 cmdlineP->smoothing_factor);
+
+    if (streq(cmdlineP->input_filespec, "=") &&
+        cmdlineP->exif_filespec && 
+        streq(cmdlineP->exif_filespec, "-"))
+
+        pm_error("Cannot have both input image and exif header be from "
+                 "Standard Input.");
+
+
+    free(argv_parse);
+}
+
+
+static void
+compute_rescaling_array(JSAMPLE ** const rescale_p, const pixval maxval,
+                        const struct jpeg_compress_struct cinfo);
+static void
+convert_scanlines(struct jpeg_compress_struct *cinfo_p, FILE *input_file,
+                  const pixval maxval, const int input_fmt,
+                  JSAMPLE xlate_table[]);
+
+static boolean read_quant_tables (j_compress_ptr cinfo, char * filename,
+                                  int scale_factor, boolean force_baseline);
+
+static boolean read_scan_script (j_compress_ptr cinfo, char * filename);
+
+static boolean set_quant_slots (j_compress_ptr cinfo, char *arg);
+
+static boolean set_sample_factors (j_compress_ptr cinfo, char *arg);
+
+
+static void
+report_compressor(const struct jpeg_compress_struct cinfo) {
+    
+    if (cinfo.scan_info == NULL)
+        pm_message("No scan script is being used");
+    else {
+        int i;
+        pm_message("A scan script with %d entries is being used:",
+                   cinfo.num_scans);
+        for (i = 0; i < cinfo.num_scans; i++) {
+            int j;
+            pm_message("    Scan %2d: Ss=%2d Se=%2d Ah=%2d Al=%2d  "
+                       "%d components", 
+                       i,
+                       cinfo.scan_info[i].Ss,
+                       cinfo.scan_info[i].Se,
+                       cinfo.scan_info[i].Ah,
+                       cinfo.scan_info[i].Al,
+                       cinfo.scan_info[i].comps_in_scan
+                       );
+            for (j = 0; j < cinfo.scan_info[i].comps_in_scan; j++)
+                pm_message("        Color component %d index: %d", j,
+                           cinfo.scan_info[i].component_index[j]);
+        }
+    }
+}
+
+
+
+static void
+setup_jpeg_source_parameters(struct jpeg_compress_struct * const cinfoP, 
+                             int const width, int const height, 
+                             int const format) {
+/*----------------------------------------------------------------------------
+   Set up in the compressor descriptor *cinfoP the description of the 
+   source image as required by the compressor.
+-----------------------------------------------------------------------------*/
+
+    switch PNM_FORMAT_TYPE(format) {
+    case PBM_TYPE:
+    case PGM_TYPE:
+        cinfoP->in_color_space = JCS_GRAYSCALE;
+        cinfoP->input_components = 1;
+        break;
+    case PPM_TYPE:
+        cinfoP->in_color_space = JCS_RGB; 
+        cinfoP->input_components = 3;
+        break;
+    default:
+        pm_error("INTERNAL ERROR; invalid format in "
+                 "setup_jpeg_source_parameters()");
+    }
+}
+
+
+
+static void
+setup_jpeg_density(struct jpeg_compress_struct * const cinfoP, 
+                   struct density                const density) {
+/*----------------------------------------------------------------------------
+   Set up in the compressor descriptor *cinfoP the density information
+   'density'.
+-----------------------------------------------------------------------------*/
+    switch(density.unit) {
+    case DEN_UNSPECIFIED:   cinfoP->density_unit = 0; break;
+    case DEN_DOTS_PER_INCH: cinfoP->density_unit = 1; break;
+    case DEN_DOTS_PER_CM:   cinfoP->density_unit = 2; break;
+    }
+    
+    cinfoP->X_density = density.horiz;
+    cinfoP->Y_density = density.vert;
+}
+
+
+
+static void
+setup_jpeg(struct jpeg_compress_struct * const cinfoP,
+           struct jpeg_error_mgr       * const jerrP,
+           struct cmdlineInfo            const cmdline, 
+           int                           const width,
+           int                           const height,
+           pixval                        const maxval,
+           int                           const input_fmt,
+           FILE *                        const output_file) {
+  
+    int quality;
+    int q_scale_factor;
+
+    /* Initialize the JPEG compression object with default error handling. */
+    cinfoP->err = jpeg_std_error(jerrP);
+    jpeg_create_compress(cinfoP);
+
+    setup_jpeg_source_parameters(cinfoP, width, height, input_fmt);
+
+    jpeg_set_defaults(cinfoP);
+
+    cinfoP->data_precision = BITS_IN_JSAMPLE; 
+        /* we always rescale data to this */
+    cinfoP->image_width = (unsigned int) width;
+    cinfoP->image_height = (unsigned int) height;
+
+    cinfoP->arith_code = cmdline.arith_code;
+    cinfoP->dct_method = cmdline.dct_method;
+    if (cmdline.trace_level == 0 && cmdline.verbose) 
+        cinfoP->err->trace_level = 1;
+    else cinfoP->err->trace_level = cmdline.trace_level;
+    if (cmdline.grayscale)
+        jpeg_set_colorspace(cinfoP, JCS_GRAYSCALE);
+    else if (cmdline.rgb)
+        /* This is not legal if the input is not JCS_RGB too, i.e. it's PPM */
+        jpeg_set_colorspace(cinfoP, JCS_RGB);
+    else
+        /* This default will be based on the in_color_space set above */
+        jpeg_default_colorspace(cinfoP);
+    if (cmdline.max_memory_to_use != -1)
+        cinfoP->mem->max_memory_to_use = cmdline.max_memory_to_use;
+    cinfoP->optimize_coding = cmdline.optimize;
+    if (cmdline.quality == -1) {
+        quality = 75;
+        q_scale_factor = 100;
+    } else {
+        quality = cmdline.quality;
+        q_scale_factor = jpeg_quality_scaling(cmdline.quality);
+    }
+    if (cmdline.smoothing_factor != -1) 
+        cinfoP->smoothing_factor = cmdline.smoothing_factor;
+
+    /* Set quantization tables for selected quality. */
+    /* Some or all may be overridden if user specified --qtables. */
+    jpeg_set_quality(cinfoP, quality, cmdline.force_baseline);
+                   
+    if (cmdline.qtablefile != NULL) {
+        if (! read_quant_tables(cinfoP, cmdline.qtablefile,
+                                q_scale_factor, cmdline.force_baseline)) 
+            pm_error("Can't use quantization table file '%s'.",
+                     cmdline.qtablefile);
+    }
+   
+    if (cmdline.qslots != NULL) {
+        if (! set_quant_slots(cinfoP, cmdline.qslots))
+            pm_error("Bad quantization-table-selectors parameter string '%s'.", 
+                     cmdline.qslots);
+    }
+          
+    if (cmdline.sample != NULL) {
+        if (! set_sample_factors(cinfoP, cmdline.sample))
+            pm_error("Bad sample-factors parameters string '%s'.",
+                     cmdline.sample);
+    }
+
+    if (cmdline.progressive)
+        jpeg_simple_progression(cinfoP);
+
+    if (cmdline.density_spec)
+        setup_jpeg_density(cinfoP, cmdline.density);
+
+    if (cmdline.scans != NULL) {
+        if (! read_scan_script(cinfoP, cmdline.scans)) {
+            pm_message("Error in scan script '%s'.", cmdline.scans);
+        }
+    }
+
+    /* Specify data destination for compression */
+    jpeg_stdio_dest(cinfoP, output_file);
+
+    if (cmdline.verbose) report_compressor(*cinfoP);
+
+    /* Start compressor */
+    jpeg_start_compress(cinfoP, TRUE);
+
+}
+
+
+
+static void
+write_exif_header(struct jpeg_compress_struct * const cinfoP,
+                  const char * const exif_filespec) {
+/*----------------------------------------------------------------------------
+   Generate an APP1 marker in the JFIF output that is an Exif header.
+
+   The contents of the Exif header are in the file with filespec 
+   'exif_filespec' (file spec and contents are not validated).
+
+   exif_filespec = "-" means Standard Input.
+
+   If the file contains just two bytes of zero, don't write any marker
+   but don't recognize any error either.
+-----------------------------------------------------------------------------*/
+    FILE * exif_file;
+    unsigned short length;
+    
+    exif_file = pm_openr(exif_filespec);
+
+    pm_readbigshort(exif_file, (short*)&length);
+
+    if (length == 0) {
+        /* Special value meaning "no header" */
+    } else if (length < 3)
+        pm_error("Invalid length %u at start of exif file", length);
+    else {
+        unsigned char * exif_data;
+        int rc;
+        size_t const data_length = length - 2;  
+            /* Subtract 2 byte length field*/
+
+        assert(data_length > 0);
+
+        exif_data = malloc(data_length);
+        if (exif_data == NULL)
+            pm_error("Unable to allocate %d bytes for exif header buffer",
+                     data_length);
+
+        rc = fread(exif_data, 1, data_length, exif_file);
+
+        if (rc != data_length)
+            pm_error("Premature end of file on exif header file.  Should be "
+                     "%d bytes of data, read only %d", data_length, rc);
+
+        jpeg_write_marker(cinfoP, JPEG_APP0+1, 
+                          (const JOCTET *) exif_data, data_length);
+
+        free(exif_data);
+    }
+    
+    pm_close(exif_file);
+}
+
+                  
+
+static void
+compute_rescaling_array(JSAMPLE ** const rescale_p, const pixval maxval,
+                        const struct jpeg_compress_struct cinfo) {
+/*----------------------------------------------------------------------------
+   Compute the rescaling array for a maximum pixval of 'maxval'.
+   Allocate the memory for it too.
+-----------------------------------------------------------------------------*/
+  const long half_maxval = maxval / 2;
+  long val;
+
+  *rescale_p = (JSAMPLE *)
+    (cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_IMAGE,
+                              (size_t) (((long) maxval + 1L) * 
+                                        sizeof(JSAMPLE)));
+  for (val = 0; val <= maxval; val++) {
+    /* The multiplication here must be done in 32 bits to avoid overflow */
+    (*rescale_p)[val] = (JSAMPLE) ((val*MAXJSAMPLE + half_maxval)/maxval);
+  }
+}
+
+
+
+static void
+translate_row(const pixel pnm_buffer[], 
+              JSAMPLE jpeg_buffer[], 
+              int const width, 
+              int const input_components,
+              const JSAMPLE translate[]) {
+/*----------------------------------------------------------------------------
+   Convert the input row, in pnm format, to an output row in JPEG compressor
+   input format.
+
+   This is a byte for byte copy, translated through the array 'translate'.
+-----------------------------------------------------------------------------*/
+  unsigned int column;
+  /* I'm not sure why the JPEG library data structures don't have some kind
+     of pixel data structure (such that a row buffer is an array of pixels,
+     rather than an array of samples).  But because of this, we have to
+     index jpeg_buffer the old fashioned way.
+     */
+
+  switch (input_components) {
+  case 1:
+      for (column = 0; column < width; column++) 
+          jpeg_buffer[column] = translate[(int)PNM_GET1(pnm_buffer[column])];
+      break;
+  case 3:
+      for (column = 0; column < width; column++) {
+          jpeg_buffer[column*3+0] = 
+              translate[(int)PPM_GETR(pnm_buffer[column])];
+          jpeg_buffer[column*3+1] = 
+              translate[(int)PPM_GETG(pnm_buffer[column])];
+          jpeg_buffer[column*3+2] = 
+              translate[(int)PPM_GETB(pnm_buffer[column])];
+      }
+      break;
+  default:
+      pm_error("INTERNAL ERROR: invalid number of input components in "
+               "translate_row()");
+  }
+
+}
+
+
+
+static void
+convert_scanlines(struct jpeg_compress_struct * const cinfo_p,
+                  FILE *                        const input_file,
+                  pixval                        const maxval,
+                  int                           const input_fmt,
+                  JSAMPLE                             xlate_table[]){
+/*----------------------------------------------------------------------------
+   Read scan lines from the input file, which is already opened in the 
+   netpbm library sense and ready for reading, and write them to the 
+   output JPEG object.  Translate the pnm sample values to JPEG sample
+   values through the thable xlate_table[].
+-----------------------------------------------------------------------------*/
+  xel * pnm_buffer;  
+    /* contains the row of the input image currently being processed,
+       in pnm_readpnmrow format
+    */
+  JSAMPARRAY buffer;
+    /* Row 0 of this array contains the row of the output image currently 
+       being processed, in JPEG compressor input format.  The array only
+       has that one row.
+    */
+
+  /* Allocate the libpnm output and compressor input buffers */
+  buffer = (*cinfo_p->mem->alloc_sarray)
+    ((j_common_ptr) cinfo_p, JPOOL_IMAGE,
+     (unsigned int) cinfo_p->image_width * cinfo_p->input_components, 
+     (unsigned int) 1);
+  
+  pnm_buffer = pnm_allocrow(cinfo_p->image_width);
+
+  while (cinfo_p->next_scanline < cinfo_p->image_height) {
+    if (cinfo_p->err->trace_level > 1) 
+        pm_message("Converting Row %d...", cinfo_p->next_scanline);
+    pnm_readpnmrow(input_file, pnm_buffer, cinfo_p->image_width, 
+                   maxval, input_fmt);
+    translate_row(pnm_buffer, buffer[0], 
+                  cinfo_p->image_width, cinfo_p->input_components,
+                  xlate_table);
+    jpeg_write_scanlines(cinfo_p, buffer, 1);
+    if (cinfo_p->err->trace_level > 1) 
+        pm_message("Done.");
+  }
+
+  pnm_freerow(pnm_buffer);
+  /* Dont' worry about the compressor input buffer; it gets freed 
+     automatically
+  */
+
+}
+
+/*----------------------------------------------------------------------------
+   The functions below here are essentially the file rdswitch.c from
+   the JPEG library.  They perform the functions specifed by the following
+   pnmtojpeg options:
+
+   -qtables file          Read quantization tables from text file
+   -scans file            Read scan script from text file
+   -qslots N[,N,...]      Set component quantization table selectors
+   -sample HxV[,HxV,...]  Set component sampling factors
+-----------------------------------------------------------------------------*/
+
+static int
+text_getc (FILE * file)
+/* Read next char, skipping over any comments (# to end of line) */
+/* A comment/newline sequence is returned as a newline */
+{
+    register int ch;
+  
+    ch = getc(file);
+    if (ch == '#') {
+        do {
+            ch = getc(file);
+        } while (ch != '\n' && ch != EOF);
+    }
+    return ch;
+}
+
+
+static boolean
+read_text_integer (FILE * file, long * result, int * termchar)
+/* Read an unsigned decimal integer from a file, store it in result */
+/* Reads one trailing character after the integer; returns it in termchar */
+{
+    register int ch;
+    register long val;
+  
+    /* Skip any leading whitespace, detect EOF */
+    do {
+        ch = text_getc(file);
+        if (ch == EOF) {
+            *termchar = ch;
+            return FALSE;
+        }
+    } while (isspace(ch));
+  
+    if (! isdigit(ch)) {
+        *termchar = ch;
+        return FALSE;
+    }
+
+    val = ch - '0';
+    while ((ch = text_getc(file)) != EOF) {
+        if (! isdigit(ch))
+            break;
+        val *= 10;
+        val += ch - '0';
+    }
+    *result = val;
+    *termchar = ch;
+    return TRUE;
+}
+
+
+static boolean
+read_quant_tables (j_compress_ptr cinfo, char * filename,
+                   int scale_factor, boolean force_baseline)
+/* Read a set of quantization tables from the specified file.
+ * The file is plain ASCII text: decimal numbers with whitespace between.
+ * Comments preceded by '#' may be included in the file.
+ * There may be one to NUM_QUANT_TBLS tables in the file, each of 64 values.
+ * The tables are implicitly numbered 0,1,etc.
+ * NOTE: does not affect the qslots mapping, which will default to selecting
+ * table 0 for luminance (or primary) components, 1 for chrominance components.
+ * You must use -qslots if you want a different component->table mapping.
+ */
+{
+    FILE * fp;
+    int tblno, i, termchar;
+    long val;
+    unsigned int table[DCTSIZE2];
+
+    if ((fp = fopen(filename, "rb")) == NULL) {
+        pm_message("Can't open table file %s", filename);
+        return FALSE;
+    }
+    tblno = 0;
+
+    while (read_text_integer(fp, &val, &termchar)) { /* read 1st element of table */
+        if (tblno >= NUM_QUANT_TBLS) {
+            pm_message("Too many tables in file %s", filename);
+            fclose(fp);
+            return FALSE;
+        }
+        table[0] = (unsigned int) val;
+        for (i = 1; i < DCTSIZE2; i++) {
+            if (! read_text_integer(fp, &val, &termchar)) {
+                pm_message("Invalid table data in file %s", filename);
+                fclose(fp);
+                return FALSE;
+            }
+            table[i] = (unsigned int) val;
+        }
+        jpeg_add_quant_table(cinfo, tblno, table, scale_factor, 
+                             force_baseline);
+        tblno++;
+    }
+
+    if (termchar != EOF) {
+        pm_message("Non-numeric data in file %s", filename);
+        fclose(fp);
+        return FALSE;
+    }
+
+    fclose(fp);
+    return TRUE;
+}
+
+
+static boolean
+read_scan_integer (FILE * file, long * result, int * termchar)
+/* Variant of read_text_integer that always looks for a non-space termchar;
+ * this simplifies parsing of punctuation in scan scripts.
+ */
+{
+    register int ch;
+
+    if (! read_text_integer(file, result, termchar))
+        return FALSE;
+    ch = *termchar;
+    while (ch != EOF && isspace(ch))
+        ch = text_getc(file);
+    if (isdigit(ch)) {		/* oops, put it back */
+        if (ungetc(ch, file) == EOF)
+            return FALSE;
+        ch = ' ';
+    } else {
+        /* Any separators other than ';' and ':' are ignored;
+         * this allows user to insert commas, etc, if desired.
+         */
+        if (ch != EOF && ch != ';' && ch != ':')
+            ch = ' ';
+    }
+    *termchar = ch;
+    return TRUE;
+}
+
+
+boolean
+read_scan_script (j_compress_ptr cinfo, char * filename)
+/* Read a scan script from the specified text file.
+ * Each entry in the file defines one scan to be emitted.
+ * Entries are separated by semicolons ';'.
+ * An entry contains one to four component indexes,
+ * optionally followed by a colon ':' and four progressive-JPEG parameters.
+ * The component indexes denote which component(s) are to be transmitted
+ * in the current scan.  The first component has index 0.
+ * Sequential JPEG is used if the progressive-JPEG parameters are omitted.
+ * The file is free format text: any whitespace may appear between numbers
+ * and the ':' and ';' punctuation marks.  Also, other punctuation (such
+ * as commas or dashes) can be placed between numbers if desired.
+ * Comments preceded by '#' may be included in the file.
+ * Note: we do very little validity checking here;
+ * jcmaster.c will validate the script parameters.
+ */
+{
+    FILE * fp;
+    int nscans, ncomps, termchar;
+    long val;
+#define MAX_SCANS  100      /* quite arbitrary limit */
+    jpeg_scan_info scans[MAX_SCANS];
+
+    if ((fp = fopen(filename, "r")) == NULL) {
+        pm_message("Can't open scan definition file %s", filename);
+        return FALSE;
+    }
+    nscans = 0;
+
+    while (read_scan_integer(fp, &val, &termchar)) {
+        nscans++;  /* We got another scan */
+        if (nscans > MAX_SCANS) {
+            pm_message("Too many scans defined in file %s", filename);
+            fclose(fp);
+            return FALSE;
+        }
+        scans[nscans-1].component_index[0] = (int) val;
+        ncomps = 1;
+        while (termchar == ' ') {
+            if (ncomps >= MAX_COMPS_IN_SCAN) {
+                pm_message("Too many components in one scan in file %s", 
+                           filename);
+                fclose(fp);
+                return FALSE;
+            }
+            if (! read_scan_integer(fp, &val, &termchar))
+                goto bogus;
+            scans[nscans-1].component_index[ncomps] = (int) val;
+            ncomps++;
+        }
+        scans[nscans-1].comps_in_scan = ncomps;
+        if (termchar == ':') {
+            if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ')
+                goto bogus;
+            scans[nscans-1].Ss = (int) val;
+            if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ')
+                goto bogus;
+            scans[nscans-1].Se = (int) val;
+            if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ')
+                goto bogus;
+            scans[nscans-1].Ah = (int) val;
+            if (! read_scan_integer(fp, &val, &termchar))
+                goto bogus;
+            scans[nscans-1].Al = (int) val;
+        } else {
+            /* set non-progressive parameters */
+            scans[nscans-1].Ss = 0;
+            scans[nscans-1].Se = DCTSIZE2-1;
+            scans[nscans-1].Ah = 0;
+            scans[nscans-1].Al = 0;
+        }
+        if (termchar != ';' && termchar != EOF) {
+        bogus:
+            pm_message("Invalid scan entry format in file %s", filename);
+            fclose(fp);
+            return FALSE;
+        }
+    }
+
+    if (termchar != EOF) {
+        pm_message("Non-numeric data in file %s", filename);
+        fclose(fp);
+        return FALSE;
+    }
+
+    if (nscans > 0) {
+        /* Stash completed scan list in cinfo structure.  NOTE: in
+         * this program, JPOOL_IMAGE is the right lifetime for this
+         * data, but if you want to compress multiple images you'd
+         * want JPOOL_PERMANENT.  
+         */
+        const unsigned int scan_info_size = nscans * sizeof(jpeg_scan_info);
+        jpeg_scan_info * const scan_info = 
+            (jpeg_scan_info *)
+            (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+                                        scan_info_size);
+        memcpy(scan_info, scans, scan_info_size);
+        cinfo->scan_info = scan_info;
+        cinfo->num_scans = nscans;
+    }
+
+    fclose(fp);
+    return TRUE;
+}
+
+
+static boolean
+set_quant_slots (j_compress_ptr cinfo, char *arg)
+/* Process a quantization-table-selectors parameter string, of the form
+ *     N[,N,...]
+ * If there are more components than parameters, the last value is replicated.
+ */
+{
+    int val = 0;			/* default table # */
+    int ci;
+    char ch;
+
+    for (ci = 0; ci < MAX_COMPONENTS; ci++) {
+        if (*arg) {
+            ch = ',';			/* if not set by sscanf, will be ',' */
+            if (sscanf(arg, "%d%c", &val, &ch) < 1)
+                return FALSE;
+            if (ch != ',')		/* syntax check */
+                return FALSE;
+            if (val < 0 || val >= NUM_QUANT_TBLS) {
+                pm_message("Invalid quantization table number: %d.  "
+                           "JPEG quantization tables are numbered 0..%d",
+                           val, NUM_QUANT_TBLS - 1);
+                return FALSE;
+            }
+            cinfo->comp_info[ci].quant_tbl_no = val;
+            while (*arg && *arg++ != ',') 
+                /* advance to next segment of arg string */
+                ;
+        } else {
+            /* reached end of parameter, set remaining components to last tbl*/
+            cinfo->comp_info[ci].quant_tbl_no = val;
+        }
+    }
+    return TRUE;
+}
+
+
+static boolean
+set_sample_factors (j_compress_ptr cinfo, char *arg)
+/* Process a sample-factors parameter string, of the form
+ *     HxV[,HxV,...]
+ * If there are more components than parameters, "1x1" is assumed for the rest.
+ */
+{
+    int ci, val1, val2;
+    char ch1, ch2;
+
+    for (ci = 0; ci < MAX_COMPONENTS; ci++) {
+        if (*arg) {
+            ch2 = ',';		/* if not set by sscanf, will be ',' */
+            if (sscanf(arg, "%d%c%d%c", &val1, &ch1, &val2, &ch2) < 3)
+                return FALSE;
+            if ((ch1 != 'x' && ch1 != 'X') || ch2 != ',') /* syntax check */
+                return FALSE;
+            if (val1 <= 0 || val1 > 4) {
+                pm_message("Invalid sampling factor: %d.  " 
+                           "JPEG sampling factors must be 1..4", val1);
+                return FALSE;
+            }
+            if (val2 <= 0 || val2 > 4) {
+                pm_message("Invalid sampling factor: %d.  "
+                           "JPEG sampling factors must be 1..4", val2);
+                return FALSE;
+            }
+            cinfo->comp_info[ci].h_samp_factor = val1;
+            cinfo->comp_info[ci].v_samp_factor = val2;
+            while (*arg && *arg++ != ',') 
+                /* advance to next segment of arg string */
+                ;
+        } else {
+            /* reached end of parameter, set remaining components 
+               to 1x1 sampling */
+            cinfo->comp_info[ci].h_samp_factor = 1;
+            cinfo->comp_info[ci].v_samp_factor = 1;
+        }
+    }
+    return TRUE;
+}
+
+
+
+int
+main(int argc, char ** argv) {
+
+    struct cmdlineInfo cmdline;
+    struct jpeg_compress_struct cinfo;
+    struct jpeg_error_mgr jerr;
+    FILE *input_file;
+    FILE * output_file;
+    int height;  
+        /* height of the input image in rows, as specified by its header */
+    int width;   
+        /* width of the input image in columns, as specified by its header */
+    pixval maxval;  
+        /* maximum value of an input pixel component, as specified by header */
+    int input_fmt;
+        /* The input format, as determined by its header.  */
+    JSAMPLE *rescale;         /* => maxval-remapping array, or NULL */
+        /* This is an array that maps each possible pixval in the input to
+           a new value such that while the range of the input values is
+           0 .. maxval, the range of the output values is 0 .. MAXJSAMPLE.
+        */
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    input_file = pm_openr(cmdline.input_filespec);
+    free(cmdline.input_filespec);
+
+    output_file = stdout;
+
+    /* Open the pnm input */
+    pnm_readpnminit(input_file, &width, &height, &maxval, &input_fmt);
+    if (cmdline.verbose) {
+        pm_message("Input file has format %c%c.\n"
+                   "It has %d rows of %d columns of pixels "
+                   "with max sample value of %d.", 
+                   (char) (input_fmt/256), (char) (input_fmt % 256),
+                   height, width, maxval);
+    }
+
+    setup_jpeg(&cinfo, &jerr, cmdline, width, height, maxval, input_fmt,
+               output_file);
+
+    compute_rescaling_array(&rescale, maxval, cinfo);
+
+    if (cmdline.comment) 
+        jpeg_write_marker(&cinfo, JPEG_COM, (const JOCTET *) cmdline.comment, 
+                          strlen(cmdline.comment));
+
+    if (cmdline.exif_filespec)
+        write_exif_header(&cinfo, cmdline.exif_filespec);
+    
+    /* Translate and copy over the actual scanlines */
+    convert_scanlines(&cinfo, input_file, maxval, input_fmt, rescale);
+
+    /* Finish compression and release memory */
+    jpeg_finish_compress(&cinfo);
+    jpeg_destroy_compress(&cinfo);
+
+    /* Close files, if we opened them */
+    if (input_file != stdin)
+        fclose(input_file);
+
+    /* Program may have exited with non-zero completion code via
+       various function calls above. 
+    */
+    return jerr.num_warnings > 0 ? EXIT_WARNING : EXIT_SUCCESS;
+}
diff --git a/converter/other/pnmtopalm/LICENSE b/converter/other/pnmtopalm/LICENSE
new file mode 100644
index 00000000..740b6080
--- /dev/null
+++ b/converter/other/pnmtopalm/LICENSE
@@ -0,0 +1,16 @@
+LICENSE FOR PNMTOPALM SUBPACKAGE OF NETPBM.  THIS LICENSE APPLIES TO ALL
+THE MATERIALS IN THE PNMTOPALM DIRECTORY OF THE NETPBM SOURCE PACKAGE.
+
+Copyright 1995-2001 by Ian Goldberg, George Caswell, and Bill Janssen.
+
+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.
+
+
+(Bryan Henderson extracted this license memorandum from the comments in the
+original man page nroff source supplied to him with the rest of the Pnmtopalm
+package in January 2001 by Bill Janssen).
diff --git a/converter/other/pnmtopalm/Makefile b/converter/other/pnmtopalm/Makefile
new file mode 100644
index 00000000..2a76297e
--- /dev/null
+++ b/converter/other/pnmtopalm/Makefile
@@ -0,0 +1,35 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/other/pnmtopalm
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+BINARIES = palmtopnm pnmtopalm
+SCRIPTS =
+OBJECTS = $(BINARIES:%=%.o) palmcolormap.o
+MERGE_OBJECTS = $(BINARIES:%=%.o2) palmcolormap.o
+MERGEBINARIES = $(BINARIES)
+DATAFILES = palmcolor8.map palmgray1.map palmgray2.map palmgray4.map
+
+all: $(BINARIES)
+
+include $(SRCDIR)/Makefile.common
+
+LIBOPTS = $(shell $(LIBOPT) $(NETPBMLIB))
+
+$(BINARIES): %: %.o palmcolormap.o $(NETPBMLIB) $(LIBOPT)
+	$(LD) $(LDFLAGS) -o $@ $< palmcolormap.o $(LIBOPTS) \
+	  $(MATHLIB) $(LDLIBS) $(RPATH) $(LADD)
+
+gen_palm_colormap : $(SUBDIR)/gen_palm_colormap.c palmcolormap.o
+	$(CC) $(INCLUDES) $(CFLAGS) $(LDFLAGS) -o $@ $< palmcolormap.o \
+	  $(LIBOPTS) $(MATHLIB) $(LDLIBS) $(LADD)
+
+
+clean: cleanspecial
+.PHONY: cleanspecial
+cleanspecial:
+	rm -f gen_palm_colormap
diff --git a/converter/other/pnmtopalm/README b/converter/other/pnmtopalm/README
new file mode 100644
index 00000000..ebae9492
--- /dev/null
+++ b/converter/other/pnmtopalm/README
@@ -0,0 +1,57 @@
+This is version 1.0 of pnmpalm, netpbm converters for Palm pixmaps.
+
+It's derived from version 1.0 of ppmtoTbmp.  The original 'ppmtoTbmp'
+program has been renamed 'pnmtopalm', and has been rewritten to handle
+the various bitmaps that exist as of PalmOS 3.5, and to handle
+colormaps, a transparent color, and the various compression formats
+recognized by Palm OS.  The 'Tbmptopnm' program is now 'palmtopnm',
+and has been updated to handle the various newer bitmap formats as
+well.  Man pages for 'pnmtopalm' and 'palmtopnm' have been added.  See
+those man pages for more information.  Note that command-line switches
+have changed.
+
+As with Tbmptoppm, ppm images must be quantified properly before
+being passed to pnmtopalm.  Several colormap files are provided to
+use for this:
+ - palmgray1.map -- 1-bit grayscale (i.e. monochrome)
+ - palmgray2.map -- 2-bit grayscale
+ - palmgray4.map -- 4-bit grayscale
+ - palmcolor8.map -- 8-bit color, using the default color palette
+
+-- Bill Janssen  <bill@janssen.org>
+
+
+One source of information on Palm image formats is
+http://www.kawt.de/doc/palmimages.html.
+
+
+
+The original ppmtoTbmp README read as follows:
+
+This is version 1.0 of ppmtoTbmp, a ppm to Pilot bitmap converter.
+
+To compile it, you'll need the netpbm package.  Last I checked, it was
+available at "ftp://ftp.x.org/contrib/utilities/netpbm-1mar1994.p1.tar.gz".
+Change the Makefile to point to the directory that contains the netpbm
+header files, and make.
+
+Usage: ppmtoTbmp [-2bit] [file.ppm]
+
+If the ppm file is not specified, one is read from stdin.  The -2bit option
+produces a 2-bit bitmap instead of the normal 1-bit bitmap.  The ppm must
+have at most 4 colors in it (for 2-bit) or at most 2 colors (for 1-bit).
+Common invokations might be:
+
+xbmtopbm icon.xbm | ppmquant -fs -map q2.map | ppmtoTbmp > tAIB03e8.bin
+giftopnm image.gif | ppmquant -fs -map q4.map | ppmtoTbmp -2bit > Tbmp0bb8.bin
+
+(q2.map and q4.map are trivial 2 and 4 color maps, respectively, and are
+included in this distribution.)
+
+To include the resulting bitmap in a .prc file, just name the output
+tAIB03e8.bin (for an application icon) or Tbmpxxxx.bin (for a form bitmap,
+where xxxx is replaced by four hex digits giving the bitmap ID).
+
+I'll probably make Tbmptopnm later, and put it in the 1.1 release.
+
+   - Ian Goldberg <iang@cs.berkeley.edu>
diff --git a/converter/other/pnmtopalm/gen_palm_colormap.c b/converter/other/pnmtopalm/gen_palm_colormap.c
new file mode 100644
index 00000000..4b65e631
--- /dev/null
+++ b/converter/other/pnmtopalm/gen_palm_colormap.c
@@ -0,0 +1,24 @@
+/* gen_palm_colormap.c - generate a ppm file containing the default Palm colormap
+ *
+ * Bill Janssen  <bill@janssen.org>
+ */
+
+#include "pnm.h"
+
+#include "palm.h"
+
+int main( int argc, char **argv ) {
+
+  int i;
+  Color_s current;
+  Colormap default_map = palmcolor_build_default_8bit_colormap ();
+
+  printf("P3\n%d 1\n255\n", default_map->ncolors);
+  for (i = 0;  i < default_map->ncolors;  i++) {
+    current = default_map->color_entries[i];
+    printf ("%d %d %d\n", (current & 0xFF0000) >> 16, (current & 0xFF00) >> 8, (current & 0xFF));
+    /* printf ("%x:  %d %d %d\n", (current & 0xFF000000) >> 24, (current & 0xFF0000) >> 16, (current & 0xFF00) >> 8, (current & 0xFF)); */
+  };
+  return 0;
+}
+
diff --git a/converter/other/pnmtopalm/palm.h b/converter/other/pnmtopalm/palm.h
new file mode 100644
index 00000000..170c8cec
--- /dev/null
+++ b/converter/other/pnmtopalm/palm.h
@@ -0,0 +1,66 @@
+#ifndef PALM_H_INCLUDED
+#define PALM_H_INCLUDED
+
+#define PALM_IS_COMPRESSED_FLAG     0x8000
+#define PALM_HAS_COLORMAP_FLAG      0x4000
+#define PALM_HAS_TRANSPARENCY_FLAG  0x2000
+#define PALM_INDIRECT_BITMAP        0x1000  /* Palm says internal use only */
+#define PALM_FOR_SCREEN             0x0800  /* Palm says internal use only */
+#define PALM_DIRECT_COLOR_FLAG      0x0400
+#define PALM_INDIRECT_COLORMAP      0x0200  /* Palm says internal use only */ 
+#define PALM_NO_DITHER_FLAG         0x0100  /* rather mysterious */
+
+#define PALM_COMPRESSION_SCANLINE   0x00
+#define PALM_COMPRESSION_RLE        0x01
+#define PALM_COMPRESSION_PACKBITS   0x02
+#define PALM_COMPRESSION_END        0x03    /* Palm says internal use only */
+#define PALM_COMPRESSION_BEST       0x64    /* Palm says internal use only */
+#define PALM_COMPRESSION_NONE       0xFF    /* Palm says internal use only */
+
+#define PALM_DENSITY_LOW             72
+#define PALM_DENSITY_ONEANDAHALF    108
+#define PALM_DENSITY_DOUBLE         144
+#define PALM_DENSITY_TRIPLE         216
+#define PALM_DENSITY_QUADRUPLE      288
+
+#define PALM_FORMAT_INDEXED     0x00
+#define PALM_FORMAT_565         0x01
+#define PALM_FORMAT_565LE       0x02    /* Palm says internal use only */
+#define PALM_FORMAT_INDEXEDLE   0x03    /* Palm says internal use only */
+
+typedef unsigned long Color_s;
+
+typedef Color_s * Color;
+
+typedef struct {
+    unsigned int nentries;
+        /* number of allocated entries in 'color_entries' */
+    unsigned int ncolors;
+        /* number of colors actually in 'color_entries' -- entries are
+           filled from 0 consecutively, one color per entry.
+        */
+    Color_s * color_entries;  /* Array of colors */
+} Colormap_s;
+
+typedef Colormap_s * Colormap;
+
+int
+palmcolor_compare_indices(const void * const p1,
+                          const void * const p2);
+
+int
+palmcolor_compare_colors(const void * const p1,
+                         const void * const p2);
+
+Colormap
+palmcolor_build_custom_8bit_colormap(unsigned int const rows,
+                                     unsigned int const cols,
+                                     pixel **     const pixels);
+
+Colormap
+palmcolor_build_default_8bit_colormap(void);
+
+Colormap
+palmcolor_read_colormap (FILE * const ifP);
+
+#endif
diff --git a/converter/other/pnmtopalm/palmcolor8.map b/converter/other/pnmtopalm/palmcolor8.map
new file mode 100644
index 00000000..2e054616
--- /dev/null
+++ b/converter/other/pnmtopalm/palmcolor8.map
@@ -0,0 +1,235 @@
+P3
+232 1
+255
+0 0 0
+0 0 0
+0 0 51
+0 0 102
+0 0 153
+0 0 204
+0 0 255
+0 51 0
+0 51 51
+0 51 102
+0 51 153
+0 51 204
+0 51 255
+0 102 0
+0 102 51
+0 102 102
+0 102 153
+0 102 204
+0 102 255
+0 128 0
+0 128 128
+0 153 0
+0 153 51
+0 153 102
+0 153 153
+0 153 204
+0 153 255
+0 204 0
+0 204 51
+0 204 102
+0 204 153
+0 204 204
+0 204 255
+0 255 0
+0 255 51
+0 255 102
+0 255 153
+0 255 204
+0 255 255
+17 17 17
+34 34 34
+51 0 0
+51 0 51
+51 0 102
+51 0 153
+51 0 204
+51 0 255
+51 51 0
+51 51 51
+51 51 102
+51 51 153
+51 51 204
+51 51 255
+51 102 0
+51 102 51
+51 102 102
+51 102 153
+51 102 204
+51 102 255
+51 153 0
+51 153 51
+51 153 102
+51 153 153
+51 153 204
+51 153 255
+51 204 0
+51 204 51
+51 204 102
+51 204 153
+51 204 204
+51 204 255
+51 255 0
+51 255 51
+51 255 102
+51 255 153
+51 255 204
+51 255 255
+68 68 68
+85 85 85
+102 0 0
+102 0 51
+102 0 102
+102 0 153
+102 0 204
+102 0 255
+102 51 0
+102 51 51
+102 51 102
+102 51 153
+102 51 204
+102 51 255
+102 102 0
+102 102 51
+102 102 102
+102 102 153
+102 102 204
+102 102 255
+102 153 0
+102 153 51
+102 153 102
+102 153 153
+102 153 204
+102 153 255
+102 204 0
+102 204 51
+102 204 102
+102 204 153
+102 204 204
+102 204 255
+102 255 0
+102 255 51
+102 255 102
+102 255 153
+102 255 204
+102 255 255
+119 119 119
+128 0 0
+128 0 128
+136 136 136
+153 0 0
+153 0 51
+153 0 102
+153 0 153
+153 0 204
+153 0 255
+153 51 0
+153 51 51
+153 51 102
+153 51 153
+153 51 204
+153 51 255
+153 102 0
+153 102 51
+153 102 102
+153 102 153
+153 102 204
+153 102 255
+153 153 0
+153 153 51
+153 153 102
+153 153 153
+153 153 204
+153 153 255
+153 204 0
+153 204 51
+153 204 102
+153 204 153
+153 204 204
+153 204 255
+153 255 0
+153 255 51
+153 255 102
+153 255 153
+153 255 204
+153 255 255
+170 170 170
+187 187 187
+192 192 192
+204 0 0
+204 0 51
+204 0 102
+204 0 153
+204 0 204
+204 0 255
+204 51 0
+204 51 51
+204 51 102
+204 51 153
+204 51 204
+204 51 255
+204 102 0
+204 102 51
+204 102 102
+204 102 153
+204 102 204
+204 102 255
+204 153 0
+204 153 51
+204 153 102
+204 153 153
+204 153 204
+204 153 255
+204 204 0
+204 204 51
+204 204 102
+204 204 153
+204 204 204
+204 204 255
+204 255 0
+204 255 51
+204 255 102
+204 255 153
+204 255 204
+204 255 255
+221 221 221
+238 238 238
+255 0 0
+255 0 51
+255 0 102
+255 0 153
+255 0 204
+255 0 255
+255 51 0
+255 51 51
+255 51 102
+255 51 153
+255 51 204
+255 51 255
+255 102 0
+255 102 51
+255 102 102
+255 102 153
+255 102 204
+255 102 255
+255 153 0
+255 153 51
+255 153 102
+255 153 153
+255 153 204
+255 153 255
+255 204 0
+255 204 51
+255 204 102
+255 204 153
+255 204 204
+255 204 255
+255 255 0
+255 255 51
+255 255 102
+255 255 153
+255 255 204
+255 255 255
diff --git a/converter/other/pnmtopalm/palmcolormap.c b/converter/other/pnmtopalm/palmcolormap.c
new file mode 100644
index 00000000..a1a1cec1
--- /dev/null
+++ b/converter/other/pnmtopalm/palmcolormap.c
@@ -0,0 +1,277 @@
+/* See LICENSE file for licensing information.
+*/
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "pnm.h"
+
+#include "palm.h"
+
+int
+palmcolor_compare_indices(const void * const p1,
+                          const void * const p2) {
+
+    if ((*((Color) p1) & 0xFF000000) < (*((Color) p2) & 0xFF000000))
+        return -1;
+    else if ((*((Color) p1) & 0xFF000000) > (*((Color) p2) & 0xFF000000))
+        return 1;
+    else
+        return 0;
+}
+
+
+
+int
+palmcolor_compare_colors(const void * const p1,
+                         const void * const p2) {
+
+    unsigned long const val1 = *((const unsigned long *) p1) & 0xFFFFFF;
+    unsigned long const val2 = *((const unsigned long *) p2) & 0xFFFFFF;
+
+    if (val1 < val2)
+        return -1;
+    else if (val1 > val2)
+        return 1;
+    else
+        return 0;
+}
+
+/***********************************************************************
+ ***********************************************************************
+ ***********************************************************************
+ ******* colortables from pilrc-2.6/bitmap.c ***************************
+ ***********************************************************************
+ ***********************************************************************
+ ***********************************************************************/
+
+#if 0
+
+/*
+ * The 1bit-2 color system palette for Palm Computing Devices.
+ */
+static int PalmPalette1bpp[2][3] = 
+{
+  { 255, 255, 255}, {   0,   0,   0 }
+};
+
+/*
+ * The 2bit-4 color system palette for Palm Computing Devices.
+ */
+static int PalmPalette2bpp[4][3] = 
+{
+  { 255, 255, 255}, { 192, 192, 192}, { 128, 128, 128 }, {   0,   0,   0 }
+};
+
+/*
+ * The 4bit-16 color system palette for Palm Computing Devices.
+ */
+static int PalmPalette4bpp[16][3] = 
+{
+  { 255, 255, 255}, { 238, 238, 238 }, { 221, 221, 221 }, { 204, 204, 204 },
+  { 187, 187, 187}, { 170, 170, 170 }, { 153, 153, 153 }, { 136, 136, 136 },
+  { 119, 119, 119}, { 102, 102, 102 }, {  85,  85,  85 }, {  68,  68,  68 },
+  {  51,  51,  51}, {  34,  34,  34 }, {  17,  17,  17 }, {   0,   0,   0 }
+};
+
+/*
+ * The 4bit-16 color system palette for Palm Computing Devices.
+ */
+static int PalmPalette4bppColor[16][3] = 
+{
+  { 255, 255, 255}, { 128, 128, 128 }, { 128,   0,   0 }, { 128, 128,   0 },
+  {   0, 128,   0}, {   0, 128, 128 }, {   0,   0, 128 }, { 128,   0, 128 },
+  { 255,   0, 255}, { 192, 192, 192 }, { 255,   0,   0 }, { 255, 255,   0 },
+  {   0, 255,   0}, {   0, 255, 255 }, {   0,   0, 255 }, {   0,   0,   0 }
+};
+
+#endif  /* 0 */
+
+/*
+ * The 8bit-256 color system palette for Palm Computing Devices.
+ *
+ * NOTE:  only the first 231, plus the last one, are valid.
+ */
+static int PalmPalette8bpp[256][3] = 
+{
+  { 255, 255, 255 }, { 255, 204, 255 }, { 255, 153, 255 }, { 255, 102, 255 }, 
+  { 255,  51, 255 }, { 255,   0, 255 }, { 255, 255, 204 }, { 255, 204, 204 }, 
+  { 255, 153, 204 }, { 255, 102, 204 }, { 255,  51, 204 }, { 255,   0, 204 }, 
+  { 255, 255, 153 }, { 255, 204, 153 }, { 255, 153, 153 }, { 255, 102, 153 }, 
+  { 255,  51, 153 }, { 255,   0, 153 }, { 204, 255, 255 }, { 204, 204, 255 },
+  { 204, 153, 255 }, { 204, 102, 255 }, { 204,  51, 255 }, { 204,   0, 255 },
+  { 204, 255, 204 }, { 204, 204, 204 }, { 204, 153, 204 }, { 204, 102, 204 },
+  { 204,  51, 204 }, { 204,   0, 204 }, { 204, 255, 153 }, { 204, 204, 153 },
+  { 204, 153, 153 }, { 204, 102, 153 }, { 204,  51, 153 }, { 204,   0, 153 },
+  { 153, 255, 255 }, { 153, 204, 255 }, { 153, 153, 255 }, { 153, 102, 255 },
+  { 153,  51, 255 }, { 153,   0, 255 }, { 153, 255, 204 }, { 153, 204, 204 },
+  { 153, 153, 204 }, { 153, 102, 204 }, { 153,  51, 204 }, { 153,   0, 204 },
+  { 153, 255, 153 }, { 153, 204, 153 }, { 153, 153, 153 }, { 153, 102, 153 },
+  { 153,  51, 153 }, { 153,   0, 153 }, { 102, 255, 255 }, { 102, 204, 255 },
+  { 102, 153, 255 }, { 102, 102, 255 }, { 102,  51, 255 }, { 102,   0, 255 },
+  { 102, 255, 204 }, { 102, 204, 204 }, { 102, 153, 204 }, { 102, 102, 204 },
+  { 102,  51, 204 }, { 102,   0, 204 }, { 102, 255, 153 }, { 102, 204, 153 },
+  { 102, 153, 153 }, { 102, 102, 153 }, { 102,  51, 153 }, { 102,   0, 153 },
+  {  51, 255, 255 }, {  51, 204, 255 }, {  51, 153, 255 }, {  51, 102, 255 },
+  {  51,  51, 255 }, {  51,   0, 255 }, {  51, 255, 204 }, {  51, 204, 204 },
+  {  51, 153, 204 }, {  51, 102, 204 }, {  51,  51, 204 }, {  51,   0, 204 },
+  {  51, 255, 153 }, {  51, 204, 153 }, {  51, 153, 153 }, {  51, 102, 153 },
+  {  51,  51, 153 }, {  51,   0, 153 }, {   0, 255, 255 }, {   0, 204, 255 },
+  {   0, 153, 255 }, {   0, 102, 255 }, {   0,  51, 255 }, {   0,   0, 255 },
+  {   0, 255, 204 }, {   0, 204, 204 }, {   0, 153, 204 }, {   0, 102, 204 },
+  {   0,  51, 204 }, {   0,   0, 204 }, {   0, 255, 153 }, {   0, 204, 153 },
+  {   0, 153, 153 }, {   0, 102, 153 }, {   0,  51, 153 }, {   0,   0, 153 },
+  { 255, 255, 102 }, { 255, 204, 102 }, { 255, 153, 102 }, { 255, 102, 102 },
+  { 255,  51, 102 }, { 255,   0, 102 }, { 255, 255,  51 }, { 255, 204,  51 },
+  { 255, 153,  51 }, { 255, 102,  51 }, { 255,  51,  51 }, { 255,   0,  51 },
+  { 255, 255,   0 }, { 255, 204,   0 }, { 255, 153,   0 }, { 255, 102,   0 },
+  { 255,  51,   0 }, { 255,   0,   0 }, { 204, 255, 102 }, { 204, 204, 102 },
+  { 204, 153, 102 }, { 204, 102, 102 }, { 204,  51, 102 }, { 204,   0, 102 },
+  { 204, 255,  51 }, { 204, 204,  51 }, { 204, 153,  51 }, { 204, 102,  51 },
+  { 204,  51,  51 }, { 204,   0,  51 }, { 204, 255,   0 }, { 204, 204,   0 },
+  { 204, 153,   0 }, { 204, 102,   0 }, { 204,  51,   0 }, { 204,   0,   0 },
+  { 153, 255, 102 }, { 153, 204, 102 }, { 153, 153, 102 }, { 153, 102, 102 },
+  { 153,  51, 102 }, { 153,   0, 102 }, { 153, 255,  51 }, { 153, 204,  51 },
+  { 153, 153,  51 }, { 153, 102,  51 }, { 153,  51,  51 }, { 153,   0,  51 },
+  { 153, 255,   0 }, { 153, 204,   0 }, { 153, 153,   0 }, { 153, 102,   0 },
+  { 153,  51,   0 }, { 153,   0,   0 }, { 102, 255, 102 }, { 102, 204, 102 },
+  { 102, 153, 102 }, { 102, 102, 102 }, { 102,  51, 102 }, { 102,   0, 102 },
+  { 102, 255,  51 }, { 102, 204,  51 }, { 102, 153,  51 }, { 102, 102,  51 },
+  { 102,  51,  51 }, { 102,   0,  51 }, { 102, 255,   0 }, { 102, 204,   0 },
+  { 102, 153,   0 }, { 102, 102,   0 }, { 102,  51,   0 }, { 102,   0,   0 },
+  {  51, 255, 102 }, {  51, 204, 102 }, {  51, 153, 102 }, {  51, 102, 102 },
+  {  51,  51, 102 }, {  51,   0, 102 }, {  51, 255,  51 }, {  51, 204,  51 },
+  {  51, 153,  51 }, {  51, 102,  51 }, {  51,  51,  51 }, {  51,   0,  51 },
+  {  51, 255,   0 }, {  51, 204,   0 }, {  51, 153,   0 }, {  51, 102,   0 },
+  {  51,  51,   0 }, {  51,   0,   0 }, {   0, 255, 102 }, {   0, 204, 102 },
+  {   0, 153, 102 }, {   0, 102, 102 }, {   0,  51, 102 }, {   0,   0, 102 },
+  {   0, 255,  51 }, {   0, 204,  51 }, {   0, 153,  51 }, {   0, 102,  51 },
+  {   0,  51,  51 }, {   0,   0,  51 }, {   0, 255,   0 }, {   0, 204,   0 },
+  {   0, 153,   0 }, {   0, 102,   0 }, {   0,  51,   0 }, {  17,  17,  17 },
+  {  34,  34,  34 }, {  68,  68,  68 }, {  85,  85,  85 }, { 119, 119, 119 },
+  { 136, 136, 136 }, { 170, 170, 170 }, { 187, 187, 187 }, { 221, 221, 221 },
+  { 238, 238, 238 }, { 192, 192, 192 }, { 128,   0,   0 }, { 128,   0, 128 },
+  {   0, 128,   0 }, {   0, 128, 128 }, {   0,   0,   0 }, {   0,   0,   0 },
+  {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 },
+  {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 },
+  {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 },
+  {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 },
+  {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 },
+  {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 }
+};
+
+
+
+Colormap
+palmcolor_build_default_8bit_colormap(void) {
+
+    unsigned int i;
+
+    Colormap cm;
+
+    MALLOCVAR_NOFAIL(cm);
+    cm->nentries = 232;
+    MALLOCARRAY_NOFAIL(cm->color_entries, cm->nentries);
+
+    /* Fill in the colors */
+    for (i = 0; i < 231; ++i) {
+        cm->color_entries[i] = ((i << 24) |
+                                (PalmPalette8bpp[i][0] << 16) |
+                                (PalmPalette8bpp[i][1] << 8) |
+                                (PalmPalette8bpp[i][2]));
+    }
+    cm->color_entries[231] = 0xFF000000;
+    cm->ncolors = 232;
+
+    /* now sort the table */
+    qsort (cm->color_entries, cm->ncolors, sizeof(Color_s), 
+           palmcolor_compare_colors);
+    return cm;
+}
+
+
+
+Colormap
+palmcolor_build_custom_8bit_colormap(unsigned int const rows,
+                                     unsigned int const cols,
+                                     pixel **     const pixels) {
+    unsigned int row;
+    Colormap colormap;
+
+    MALLOCVAR_NOFAIL(colormap);
+    colormap->nentries = 256;
+    MALLOCARRAY_NOFAIL(colormap->color_entries, colormap->nentries);
+    colormap->ncolors = 0;  /* initial value */
+    
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        for (col = 0; col < cols; ++col) {
+            Color found;
+            Color_s temp;
+
+            temp = ((PPM_GETR(pixels[row][col]) << 16) |
+                    (PPM_GETG(pixels[row][col]) << 8) |
+                    PPM_GETB(pixels[row][col]));
+            found = (bsearch (&temp,
+                              colormap->color_entries, colormap->ncolors,
+                              sizeof(Color_s), palmcolor_compare_colors));
+            if (!found) {
+                if (colormap->ncolors >= colormap->nentries)
+                    pm_error("Too many colors for custom colormap "
+                             "(max 256).  "
+                             "Try using pnmquant to reduce the number "
+                             "of colors.");
+                else {
+                    /* add the new color, and re-sort */
+                    temp |= ((colormap->ncolors) << 24);
+                    colormap->color_entries[colormap->ncolors] = temp;
+                    colormap->ncolors += 1;
+                    qsort(colormap->color_entries, colormap->ncolors, 
+                          sizeof(Color_s), palmcolor_compare_colors);
+                }
+            }
+        }
+    }
+    return colormap;
+}
+
+
+
+Colormap
+palmcolor_read_colormap (FILE * const ifP) {
+
+    unsigned short ncolors;
+    Colormap retval;
+    int rc;
+    
+    rc = pm_readbigshort(ifP, (short *) &ncolors);
+    if (rc != 0)
+        retval = NULL;
+    else {
+        long colorentry;
+        Colormap colormap;
+        unsigned int i;
+        bool error;
+
+        MALLOCVAR_NOFAIL(colormap);
+        colormap->nentries = ncolors;
+        MALLOCARRAY_NOFAIL(colormap->color_entries, colormap->nentries);
+        
+        for (i = 0, error = FALSE;  i < ncolors && !error;  ++i) {
+            int rc;
+            rc = pm_readbiglong(ifP, &colorentry);
+            if (rc != 0)
+                error = TRUE;
+            else
+                colormap->color_entries[i] = (colorentry & 0xFFFFFFFF);
+        }
+        if (error) {
+            free (colormap->color_entries);
+            free (colormap);
+            retval = NULL;
+        } else {
+            colormap->ncolors = ncolors;
+            retval = colormap;
+        }
+    }
+    return retval;
+}
diff --git a/converter/other/pnmtopalm/palmgray1.map b/converter/other/pnmtopalm/palmgray1.map
new file mode 100644
index 00000000..96e20400
--- /dev/null
+++ b/converter/other/pnmtopalm/palmgray1.map
@@ -0,0 +1,4 @@
+P1
+# Black and white palette
+1 2
+0 1
diff --git a/converter/other/pnmtopalm/palmgray2.map b/converter/other/pnmtopalm/palmgray2.map
new file mode 100644
index 00000000..e8c95b01
--- /dev/null
+++ b/converter/other/pnmtopalm/palmgray2.map
@@ -0,0 +1,6 @@
+P2
+# 4 level grayscale palette
+2 2
+3
+0 1
+2 3
diff --git a/converter/other/pnmtopalm/palmgray4.map b/converter/other/pnmtopalm/palmgray4.map
new file mode 100644
index 00000000..bba2e6b8
--- /dev/null
+++ b/converter/other/pnmtopalm/palmgray4.map
@@ -0,0 +1,8 @@
+P2
+# 16 level grayscale palette
+4 4
+15
+ 0  1  2  3
+ 4  5  6  7
+ 8  9 10 11
+12 13 14 15
diff --git a/converter/other/pnmtopalm/palmtopnm.c b/converter/other/pnmtopalm/palmtopnm.c
new file mode 100644
index 00000000..9cd695e3
--- /dev/null
+++ b/converter/other/pnmtopalm/palmtopnm.c
@@ -0,0 +1,1131 @@
+/******************************************************************************
+                             palmtopnm
+*******************************************************************************
+  By Bryan Henderson, San Jose, California, June 2004.
+
+  Inspired by and using methods from Tbmptopnm by Ian Goldberg
+  <iang@cs.berkeley.edu>, and Bill Janssen <bill@janssen.org>.
+
+  Major fixes and new capability added by Paul Bolle <pebolle@tiscali.nl>
+  in late 2004 / early 2005.
+
+  Bryan's work is contributed to the public domain by its author.
+******************************************************************************/
+
+#include <string.h>
+#include <assert.h>
+
+#include "pnm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+
+#include "palm.h"
+
+
+
+enum palmCompressionType {
+    COMPRESSION_NONE, 
+    COMPRESSION_RLE, 
+    COMPRESSION_SCANLINE,
+    COMPRESSION_PACKBITS
+};
+
+struct palmHeader {
+    unsigned short cols;
+    unsigned short rows;
+    unsigned short bytesPerRow;
+    unsigned short flags;
+    bool           directColor;
+        /* The header indicates a direct color raster, either by flag
+           (the old way) or by pixel format (the new way)
+        */
+    bool           hasColormap;
+    bool           hasTransparency;
+    unsigned char  pixelSizeCode;
+    unsigned int   pixelSize;
+    unsigned char  version;
+    unsigned int   transparentIndex;
+    enum palmCompressionType compressionType;
+    /* version 3 encoding specific */
+    unsigned char  size;
+    unsigned char  pixelFormat;
+    unsigned short density;
+    unsigned long  transparentValue;
+};
+
+
+
+struct directPixelFormat {
+    unsigned int redbits;
+    unsigned int greenbits;
+    unsigned int bluebits;
+};
+
+
+
+struct directColorInfo {
+    struct directPixelFormat pixelFormat;
+    Color_s                  transparentColor;
+};
+
+
+
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFilespec;
+    unsigned int verbose;
+    unsigned int rendition;
+    unsigned int showhist;
+    unsigned int transparent;
+};
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo *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 = malloc( 100*sizeof( optEntry ) );
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int renditionSpec;
+
+    unsigned int option_def_index;
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "verbose",     OPT_FLAG, NULL,
+            &cmdlineP->verbose,  0);
+    OPTENT3(0, "showhist",    OPT_FLAG, NULL,
+            &cmdlineP->showhist, 0);
+    OPTENT3(0, "transparent",    OPT_FLAG, NULL,
+            &cmdlineP->transparent, 0);
+    OPTENT3(0, "rendition",  OPT_UINT, &cmdlineP->rendition, 
+            &renditionSpec, 0);
+
+    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 (renditionSpec) {
+        if (cmdlineP->rendition < 1)
+            pm_error("The -rendition value must be at least 1");
+    } else 
+        cmdlineP->rendition = 1;
+    
+    if (cmdlineP->transparent && cmdlineP->showhist)
+        pm_error("You can't specify -showhist with -transparent");
+
+    if (argc-1 < 1)
+        cmdlineP->inputFilespec = "-";
+    else {
+        cmdlineP->inputFilespec = argv[1];
+        if (argc-1 > 1)
+            pm_error("Too many arguments (%d).  The only non-option "
+                     "argument is the file name", argc-1);
+    }
+}
+
+
+
+static xelval *
+createGraymap(unsigned int const ncolors, 
+              xelval       const maxval) {
+    int i;
+    xelval *map;
+
+    MALLOCARRAY_NOFAIL(map, ncolors);
+    for (i = 0; i < ncolors; ++i) {
+        map[i] = maxval - (i * maxval) / (ncolors - 1);
+    }
+    return map;
+}
+
+
+
+static void 
+skipbytes(FILE *       const ifP, 
+          unsigned int const nbytes) {
+
+    unsigned char buf[256];
+    unsigned int n;
+    size_t bytesRead;
+
+    n = nbytes;  /* initial value */
+
+    while (n > 0) {
+        if (n > sizeof(buf)) {
+            bytesRead = fread(buf, sizeof(char), sizeof(buf), ifP);
+            if (bytesRead != sizeof(buf))
+               pm_error("Error reading Palm file.  Short read.");
+            n -= sizeof(buf);
+        } else {
+            bytesRead = fread(buf, sizeof(char), n, ifP);
+            if (bytesRead != n)
+               pm_error("Error reading Palm file.  Short read.");
+            n = 0;
+        }
+    }    
+}
+
+
+
+static void
+interpretCompression(unsigned char              const compressionValue,
+                     enum palmCompressionType * const compressionTypeP) {
+    
+    switch (compressionValue) {
+    case PALM_COMPRESSION_RLE:
+        *compressionTypeP = COMPRESSION_RLE;
+        break;
+    case PALM_COMPRESSION_SCANLINE:
+        *compressionTypeP = COMPRESSION_SCANLINE;
+        break;
+    case PALM_COMPRESSION_PACKBITS:
+        *compressionTypeP = COMPRESSION_PACKBITS;
+        break;
+    case PALM_COMPRESSION_NONE:
+        /* according to the spec this is not possible */
+        *compressionTypeP = COMPRESSION_NONE;
+        break;
+    default:
+        pm_error("The Palm image header has an unrecognized value for "
+                 "compression type: 0x%02x", (unsigned)compressionValue);
+    }
+}
+
+
+
+static void
+readRestOfHeaderVersion3(FILE *           const ifP,
+                         unsigned int     const pixelSize,
+                         unsigned char *  const sizeP,
+                         unsigned char *  const pixelFormatP,
+                         unsigned char *  const compressionTypeP,
+                         short *          const densityP,
+                         unsigned int *   const transparentIndexP,
+                         long *           const transparentValueP,
+                         long *           const nextBitmapOffsetP,
+                         short *          const nextDepthOffsetP) {
+
+    unsigned char unused;
+    
+    pm_readcharu(ifP, sizeP);
+    /* should be 0x18, but I can't see why we should really care */
+    if (*sizeP != 0x18)
+        pm_message("Strange value for Palm bitmap header size: %hu", *sizeP);
+
+    pm_readcharu(ifP, pixelFormatP);
+    if (*pixelFormatP != PALM_FORMAT_INDEXED &&
+        *pixelFormatP != PALM_FORMAT_565)
+        pm_error("Unrecognized pixelformat type: %u", *pixelFormatP);
+    
+    pm_readcharu(ifP, &unused);
+
+
+    pm_readcharu(ifP, compressionTypeP);
+
+    pm_readbigshort(ifP, densityP);
+    /* the specs imply that 0x00 is not valid */
+    if (*densityP != PALM_DENSITY_LOW &&
+        *densityP != PALM_DENSITY_ONEANDAHALF &&
+        *densityP != PALM_DENSITY_DOUBLE &&
+        *densityP != PALM_DENSITY_TRIPLE &&
+        *densityP != PALM_DENSITY_QUADRUPLE)
+        pm_error("Invalid value for -density: %d.", *densityP);
+ 
+    pm_readbiglong(ifP, transparentValueP);
+    if (pixelSize < 16)
+        *transparentIndexP = *transparentValueP;
+    else
+        *transparentIndexP = 0;
+ 
+    pm_readbiglong(ifP, nextBitmapOffsetP);
+    
+    /* version < 3 specific */
+    *nextDepthOffsetP = 0;
+}
+
+
+
+static void
+readRestOfHeaderOld(FILE *           const ifP,
+                    unsigned char *  const sizeP,
+                    unsigned char *  const pixelFormatP,
+                    unsigned char *  const compressionTypeP,
+                    short *          const densityP,
+                    unsigned int *   const transparentIndexP,
+                    long *           const transparentValueP,
+                    long *           const nextBitmapOffsetP,
+                    short *          const nextDepthOffsetP) {
+    
+    short pad;
+    unsigned char transparentIndex;
+    
+    pm_readbigshort(ifP, nextDepthOffsetP);
+    pm_readcharu(ifP, &transparentIndex);
+    *transparentIndexP = transparentIndex;
+ 
+    pm_readcharu(ifP,compressionTypeP);
+    
+    pm_readbigshort(ifP, &pad); /* reserved by Palm as of 8/9/00 */
+    
+    /* version 3 specific */
+    *sizeP = 0;
+    *pixelFormatP = 0;
+    *densityP = 0;
+    *transparentValueP = 0;
+    *nextBitmapOffsetP = 0;
+}
+
+
+
+static void
+interpretHeader(struct palmHeader * const palmHeaderP,
+                short               const cols,
+                short               const rows,
+                short               const bytesPerRow,
+                short               const flags,
+                unsigned char       const pixelSizeCode,
+                unsigned int        const pixelSize,
+                unsigned char       const version,
+                unsigned char       const size,
+                unsigned char       const pixelFormat,
+                short               const density,
+                long                const transparentValue,
+                unsigned int        const transparentIndex,
+                unsigned char       const compressionType) {
+    
+    palmHeaderP->cols = cols;
+    palmHeaderP->rows = rows;
+    palmHeaderP->bytesPerRow = bytesPerRow;
+    palmHeaderP->flags = flags;  /* Just for diagnostics */
+    palmHeaderP->hasColormap = !!(flags & PALM_HAS_COLORMAP_FLAG);
+    palmHeaderP->hasTransparency = !!(flags & PALM_HAS_TRANSPARENCY_FLAG);
+    palmHeaderP->pixelSizeCode = pixelSizeCode;
+    palmHeaderP->pixelSize = pixelSize;
+    palmHeaderP->version = version;
+    palmHeaderP->size = size;
+    palmHeaderP->pixelFormat = pixelFormat;
+    palmHeaderP->density = density;
+    palmHeaderP->transparentValue = transparentValue;
+    palmHeaderP->transparentIndex = transparentIndex;
+
+    if (palmHeaderP->version == 3 && (flags & PALM_DIRECT_COLOR_FLAG))
+        /* There's no directColorInfoType section in a version 3 Palm Bitmap */
+        pm_error("PALM_DIRECT_COLOR_FLAG is not valid for version 3 "
+                 "encoding type.");
+        
+    palmHeaderP->directColor = ((flags & PALM_DIRECT_COLOR_FLAG) || 
+                                palmHeaderP->pixelFormat == PALM_FORMAT_565);
+    
+    if (flags & PALM_IS_COMPRESSED_FLAG) 
+        interpretCompression(compressionType,
+                             &palmHeaderP->compressionType);
+    else
+        palmHeaderP->compressionType = COMPRESSION_NONE;
+}
+
+
+
+static void
+readHeader(FILE *              const ifP,
+           unsigned int        const requestedRendition,
+           struct palmHeader * const palmHeaderP) {
+/*----------------------------------------------------------------------------
+   Read the Palm Bitmap header from the file 'ifP'.  Read past all
+   renditions up to 'requestedRendition' and read the header of that
+   rendition.  Return the information contained in the header as *palmHeaderP.
+-----------------------------------------------------------------------------*/
+    bool gotHeader;
+    unsigned int currentRendition;
+
+    gotHeader = FALSE;
+    currentRendition = 1;
+    while (!gotHeader) {
+        short cols, rows, bytesPerRow, flags, nextDepthOffset, density;
+        unsigned char pixelSizeCode, version, compressionType, 
+            size, pixelFormat;
+        long transparentValue, nextBitmapOffset;
+        unsigned int pixelSize, transparentIndex;
+
+        pm_readbigshort(ifP, &cols);
+        pm_readbigshort(ifP, &rows);
+        pm_readbigshort(ifP, &bytesPerRow);
+        pm_readbigshort(ifP, &flags);
+ 
+        pm_readcharu(ifP, &pixelSizeCode);
+        pixelSize = pixelSizeCode == 0 ? 1 : pixelSizeCode;
+        if (pixelSizeCode != 0x00 &&
+            pixelSizeCode != 0x01 &&
+            pixelSizeCode != 0x02 &&
+            pixelSizeCode != 0x04 &&
+            pixelSizeCode != 0x08 &&
+            pixelSizeCode != 0x10 &&
+            pixelSizeCode != 0xFF)
+            pm_error("Invalid value for bits per pixel: %u.", pixelSizeCode);
+
+        if ((bytesPerRow * 8) < (cols * pixelSize))
+            pm_error("%u bytes per row is not valid with %u columns and %u "
+                     "bits per pixel.", bytesPerRow, cols, pixelSize);
+
+        pm_readcharu(ifP, &version);
+        if (version > 3) 
+            pm_error("Unknown encoding version type: %d", version);
+        else if (version == 3)
+            readRestOfHeaderVersion3(ifP, pixelSize,
+                                     &size, &pixelFormat, &compressionType,
+                                     &density, &transparentIndex, 
+                                     &transparentValue, &nextBitmapOffset,
+                                     &nextDepthOffset);
+        else
+            readRestOfHeaderOld(ifP,
+                                &size, &pixelFormat, &compressionType,
+                                &density, &transparentIndex,
+                                &transparentValue, &nextBitmapOffset,
+                                &nextDepthOffset);
+
+        if (currentRendition < requestedRendition) {
+             if (version < 3 && nextDepthOffset == 0 && pixelSizeCode != 0xFF) 
+                 pm_error("Not enough renditions in the input Palm Bitmap "
+                          "to extract the %dth", requestedRendition);
+             if (version == 3 && nextBitmapOffset == 0) 
+                 pm_error("Not enough renditions in the input Palm Bitmap "
+                          "to extract the %dth", requestedRendition);
+             /* nextDepthOffset is calculated in 4 byte words
+                from the beginning of this bitmap (so it equals its size) 
+             */
+             if (version < 3 && pixelSizeCode != 0xFF )
+                 skipbytes(ifP, (nextDepthOffset*4)-16);
+             else if (version == 3)
+                 /* FIXME rewrite skipbytes to accept longs? */
+                 skipbytes(ifP, (short) nextBitmapOffset-24); 
+             if (pixelSizeCode != 0xFF)
+                 ++currentRendition;
+        } else if (pixelSizeCode != 0xFF) {
+            gotHeader = TRUE;
+            
+            interpretHeader(palmHeaderP,
+                            cols, rows, bytesPerRow, flags, pixelSizeCode,
+                            pixelSize, version, size, pixelFormat, density,
+                            transparentValue, transparentIndex, 
+                            compressionType);
+        }
+    }
+}
+
+
+
+static const char *
+yesno(bool const arg) {
+
+    if (arg)
+        return "YES";
+    else
+        return "NO";
+}
+
+
+static void
+reportPalmHeader(struct palmHeader      const palmHeader,
+                 struct directColorInfo const directColorInfo) {
+
+    const char *ctype;
+
+    switch (palmHeader.compressionType) {
+    case COMPRESSION_RLE:
+        ctype = "rle (Palm OS 3.5)";
+        break;
+    case COMPRESSION_SCANLINE:
+        ctype = "scanline (Palm OS 2.0)";
+        break;
+    case COMPRESSION_PACKBITS:
+        ctype = "packbits (Palm OS 4.0)";
+        break;
+    case COMPRESSION_NONE:
+        ctype = "none";
+        break;
+    }
+    pm_message("Dimensions: %hu columns x %hu rows",
+               palmHeader.cols, palmHeader.rows);
+    pm_message("Row layout: %hu bytes per row, %hu bits per pixel",
+               palmHeader.bytesPerRow, palmHeader.pixelSize);
+    pm_message("Pixel Size code: %hu", palmHeader.pixelSizeCode);
+    pm_message("Flags: 0x%04hx", palmHeader.flags);
+    pm_message("  Direct Color: %s", yesno(palmHeader.directColor));
+    pm_message("  Colormap:     %s", yesno(palmHeader.hasColormap));
+    pm_message("  Transparency: %s", yesno(palmHeader.hasTransparency));
+    pm_message("Version %d", palmHeader.version);
+    if (palmHeader.hasTransparency) {
+        if (palmHeader.directColor) {
+            /* Copied from doTransparent(...) */
+            Color_s const color = directColorInfo.transparentColor;
+            pm_message("Transparent value: #%02x%02x%02x", 
+                       (unsigned int)((color >> 16) & 0xFF), 
+                       (unsigned int)((color >>  8) & 0xFF), 
+                       (unsigned int)((color >>  0) & 0xFF));
+        } else
+            pm_message("Transparent index: %u", palmHeader.transparentIndex);
+    }
+    pm_message("Compression type: %s", ctype);
+    if (palmHeader.version == 3)
+        pm_message("Density: %d", palmHeader.density);
+}
+
+
+
+static void
+determineOutputFormat(struct palmHeader const palmHeader,
+                      int *             const formatP,
+                      xelval *          const maxvalP) {
+
+    if (palmHeader.directColor) {
+        *formatP = PPM_TYPE;
+        *maxvalP = 255;
+    } else if (palmHeader.hasColormap) {
+        *formatP = PPM_TYPE;
+        *maxvalP = 255;
+    } else if (palmHeader.pixelSize == 1) {
+        *formatP = PBM_TYPE;
+        *maxvalP = 1;
+    } else if (palmHeader.pixelSize >= 8) {
+        *formatP = PPM_TYPE;
+        *maxvalP = pm_bitstomaxval(palmHeader.pixelSize);
+    } else {
+        *formatP = PGM_TYPE;
+        *maxvalP = pm_bitstomaxval(palmHeader.pixelSize);
+    }
+}
+
+
+
+static void
+readRgbFormat(FILE *                     const ifP,
+              struct directPixelFormat * const pixelFormatP) {
+
+    unsigned char r, g, b;
+
+    pm_readcharu(ifP, &r);
+    pm_readcharu(ifP, &g);
+    pm_readcharu(ifP, &b);
+    
+    if (r != 5 || g != 6 || b != 5)
+        pm_error("This image has a direct color pixel format of "
+                 "%u red, %u green, %u blue bits.  This program "
+                 "can handle only 5, 6, 5.", r, g, b);
+    else {
+        pixelFormatP->redbits   = r;
+        pixelFormatP->greenbits = g;
+        pixelFormatP->bluebits  = b;
+    }
+}
+
+
+
+static void
+readDirectTransparentColor(FILE *    const ifP,
+                           Color_s * const colorP) {
+
+    unsigned char r, g, b;
+
+    pm_readcharu(ifP, &r);
+    pm_readcharu(ifP, &g);
+    pm_readcharu(ifP, &b);
+
+    *colorP = (r << 16) | (g << 8) | (b << 0);
+}
+
+
+
+static void
+readDirectInfoType(FILE *                   const ifP,
+                   struct palmHeader        const palmHeader,
+                   struct directColorInfo * const directInfoTypeP) {
+/*----------------------------------------------------------------------------
+   Read the Palm Bitmap Direct Info Type section, if any.
+
+   The Direct Info Type section is a section of a pre-Version 3 direct
+   color Palm Bitmap that tells how to interpret the direct color
+   raster.
+
+   Return an undefined value as *directInfoTypeP if there is no such
+   section in this Palm Bitmap.
+-----------------------------------------------------------------------------*/
+    if ((palmHeader.directColor) && palmHeader.pixelSize != 16)
+        pm_error("The image is of the direct color type, but has %u "
+                 "bits per pixel.  The only kind of direct color images "
+                 "this program understands are 16 bit ones.", 
+                 palmHeader.pixelSize);
+
+    if (palmHeader.version == 3) {
+        /* All direct color info is in the header, because it'sversion
+           3 encoding.  No Direct Info Type section.  
+        */
+    } else {
+        if (palmHeader.directColor) {
+            unsigned char padding;
+
+            readRgbFormat(ifP, &directInfoTypeP->pixelFormat);
+
+            pm_readcharu(ifP, &padding);
+            pm_readcharu(ifP, &padding);
+
+            readDirectTransparentColor(ifP, 
+                                       &directInfoTypeP->transparentColor);
+        } else {
+            /* Not a direct color image; no Direct Info Type section. */
+        }
+    }
+}
+
+
+
+static void
+readColormap(FILE *            const ifP,
+             struct palmHeader const palmHeader,
+             Colormap *        const colormapP) {
+/*----------------------------------------------------------------------------
+   Read the colormap, if any from the Palm Bitmap.
+
+   If the image described by 'palmHeader' doesn't have a colormap,
+   return an undefined value as *colormapP.
+-----------------------------------------------------------------------------*/
+    if (palmHeader.hasColormap)
+        *colormapP = palmcolor_read_colormap(ifP);
+}
+
+
+
+static void
+getColorInfo(struct palmHeader        const palmHeader,
+             struct directColorInfo   const directInfoType,
+             Colormap                 const colormapFromImage,
+             Colormap *               const colormapP,
+             unsigned int *           const ncolorsP,
+             struct directColorInfo * const directColorInfoP) {
+/*----------------------------------------------------------------------------
+   Gather color encoding information from the various sources.
+
+   Note that 'directInfoType' and 'colormapFromImage' are meaningful only
+   with certain values of 'palmHeader'.
+
+   If it's a version 3 direct color, the pixel format must be "565".
+-----------------------------------------------------------------------------*/
+    if (palmHeader.version == 3 && palmHeader.directColor) {
+        *colormapP = NULL;
+
+        assert(palmHeader.pixelFormat == PALM_FORMAT_565);
+
+        directColorInfoP->pixelFormat.redbits   = 5;
+        directColorInfoP->pixelFormat.greenbits = 6;
+        directColorInfoP->pixelFormat.bluebits  = 5;
+        /* FIXME Just guessing here ... */
+        directColorInfoP->transparentColor = 
+            (((palmHeader.transparentValue >> 11) & 0x1F) << 16) |
+            (((palmHeader.transparentValue >>  5) & 0x3F) <<  8) |
+            (((palmHeader.transparentValue >>  0) & 0x1F) <<  0);
+    } else if (palmHeader.directColor) {
+        *colormapP = NULL;
+        *directColorInfoP = directInfoType;
+    } else if (palmHeader.hasColormap)
+        *colormapP = colormapFromImage;
+    else if (palmHeader.pixelSize >= 8) {
+        Colormap colormap;
+        colormap = palmcolor_build_default_8bit_colormap();
+        qsort(colormap->color_entries, colormap->ncolors, 
+              sizeof(Color_s), palmcolor_compare_indices);
+        *colormapP = colormap;
+    } else
+        *colormapP = NULL;
+
+    *ncolorsP = 1 << palmHeader.pixelSize;
+}
+
+
+
+static void
+doTransparent(FILE *                 const ofP,
+              bool                   const hasTransparency,
+              bool                   const directColor,
+              unsigned char          const transparentIndex,
+              unsigned char          const pixelSize,
+              Colormap               const colormap,
+              struct directColorInfo const directColorInfo) {
+/*----------------------------------------------------------------------------
+   Generate a PNM comment on *ofP telling what color in the raster is
+   supposed to be transparent.
+
+   Note that PNM itself doesn't have any way to represent transparency.
+   (But this program could be converted to a PAM program and use the
+   RGB_ALPHA and GRAYSCALE_ALPHA tuple types).
+-----------------------------------------------------------------------------*/
+    if (hasTransparency) {
+        if (colormap) {
+            Color_s const color = transparentIndex << 24;
+            Color const actualColor = (bsearch(&color,
+                                               colormap->color_entries, 
+                                               colormap->ncolors,
+                                               sizeof(color), 
+                                               palmcolor_compare_indices));
+            fprintf(ofP, "#%02x%02x%02x\n", 
+                   (unsigned int) ((*actualColor >> 16) & 0xFF),
+                   (unsigned int) ((*actualColor >>  8) & 0xFF), 
+                   (unsigned int) ((*actualColor >>  0) & 0xFF));
+        } else if (directColor) {
+            Color_s const color = directColorInfo.transparentColor;
+            fprintf(ofP, "#%02x%02x%02x\n", 
+                   (unsigned int)((color >> 16) & 0xFF), 
+                   (unsigned int)((color >>  8) & 0xFF), 
+                   (unsigned int)((color >>  0) & 0xFF));
+        } else {
+            unsigned int const maxval = pm_bitstomaxval(pixelSize);
+            unsigned int const grayval = 
+                ((maxval - transparentIndex) * 256) / maxval;
+            fprintf(ofP, "#%02x%02x%02x\n", grayval, grayval, grayval);
+        }
+    }
+}
+
+
+
+static void
+createHistogram(unsigned int    const ncolors,
+                unsigned int ** const seenP) {
+
+    unsigned int * seen;
+
+    MALLOCARRAY(seen, ncolors);
+    if (!seen)
+        pm_error("Can't allocate array for keeping track of "
+                 "how many pixels of each of %u colors are in the image.", 
+                 ncolors);
+
+    {    
+        /* Initialize the counter for each color to zero */
+        unsigned int i;
+        for (i = 0; i < ncolors; ++i)
+            seen[i] = 0;
+    }
+    *seenP = seen;
+}
+
+
+
+static void
+readScanlineRow(FILE *          const ifP,
+                unsigned char * const palmrow,
+                unsigned char * const lastrow,
+                unsigned int    const bytesPerRow,
+                bool            const firstRow) {
+
+    unsigned int j;
+
+    for (j = 0; j < bytesPerRow; j += 8) {
+        unsigned char diffmask;
+            /* A mask telling whether each of the 8 raster bytes indexed
+               j through j+7 is the same as in the previous row ('lastrow')
+               or is to be read from the file.  Bit 0 of the mask refers
+               to byte j, Bit 1 to byte j + 1, etc.
+            */
+        unsigned int byteCount;
+            /* How many bytes are covered by 'diffmask'.  Normally 8, but
+               at the end of the row, could be less.
+            */
+        unsigned int k;
+
+        pm_readcharu(ifP, &diffmask);
+        byteCount = MIN(bytesPerRow - j, 8);
+        
+        for (k = 0; k < byteCount; ++k) {
+            /* the first row cannot be compressed */
+            if (firstRow || ((diffmask & (1 << (7 - k))) != 0)) {
+                unsigned char inval;
+                pm_readcharu(ifP, &inval);
+                palmrow[j + k] = inval;
+            } else
+                palmrow[j + k] = lastrow[j + k];
+        }
+    }
+    memcpy(lastrow, palmrow, bytesPerRow);
+}
+
+
+
+static void
+readRleRow(FILE *          const ifP,
+           unsigned char * const palmrow,
+           unsigned int    const bytesPerRow) {
+
+    unsigned int j;
+
+    for (j = 0;  j < bytesPerRow; ) {
+        unsigned char incount;
+        unsigned char inval;
+
+        pm_readcharu(ifP, &incount);
+        if (incount == 0)
+            pm_error("Invalid (zero) count in RLE compression.");
+        if (j + incount > bytesPerRow)
+            pm_error("Bytes in RLE compressed row exceed bytes per row.  "
+                     "Bytes per row is %u.  A run length of %u bytes "
+                     "pushes the bytes in this row up to %u bytes (and then"
+                     "we gave up.", bytesPerRow, incount, j + incount);
+        pm_readcharu(ifP, &inval);
+        memset(palmrow + j, inval, incount);
+        j += incount;
+    }
+} 
+
+
+
+static void
+readPackBitsRow(FILE *          const ifP,
+                unsigned char * const palmrow,
+                unsigned int    const bytesPerRow) {
+
+    unsigned int j;
+
+    for (j = 0;  j < bytesPerRow; ) {
+        char incount;
+        pm_readchar(ifP, &incount);
+        if (incount < 0) { 
+            /* How do we handle incount == -128 ? */
+            unsigned int const runlength = -incount + 1;
+            unsigned char inval;
+            pm_readcharu(ifP, &inval);
+            memset(palmrow + j, inval, runlength);
+            j += runlength;
+        } else {
+            unsigned int const nonrunlength = incount + 1;
+            unsigned int k;
+            for (k = 0; k < nonrunlength; ++k) {
+                unsigned char inval;
+                pm_readcharu(ifP, &inval);
+                palmrow[j + k] = inval;
+            }
+            j += nonrunlength;
+        }
+        if (j > bytesPerRow) 
+            pm_error("Bytes in PackBits compressed row exceed bytes per row.");
+    }
+} 
+
+
+
+static void
+readUncompressedRow(FILE *          const ifP,
+                    unsigned char * const palmrow,
+                    unsigned int    const bytesPerRow) {
+
+    int bytesRead;
+    
+    bytesRead = fread(palmrow, 1, bytesPerRow, ifP);
+    if (bytesRead != bytesPerRow)
+        pm_error("Error reading Palm file.  Short read.");
+}
+
+
+
+static void
+readDecompressedRow(FILE *                   const ifP,
+                    unsigned char *          const palmrow,
+                    unsigned char *          const lastrow,
+                    enum palmCompressionType const compressionType,
+                    unsigned int             const bytesPerRow,
+                    bool                     const firstRow) {
+/*----------------------------------------------------------------------------
+   Read a row from Palm file 'ifP', in uncompressed form (i.e. decompress if
+   necessary).  Assume the row contains 'bytesPerRow' uncompressed bytes,
+   compressed according to 'compressionType'.  Return the data at 'palmrow'.
+
+   'firstRow' means decompress it as if it is the first row of the image
+   (some compression schemes transform the first row differently from the
+   rest, because each row depends on the row before it).
+
+   If 'compressionType' is COMPRESSION_SCANLINE, (which means
+   transformation of a row depends on the contents of the row before
+   it), then 'lastRow' is as input the uncompressed contents of the
+   previous row (undefined if 'firstRow' is true).  In that case, we
+   modify 'lastrow' to contain a copy of 'palmrow' (so Caller can
+   conveniently use it to read the next row).
+
+   If 'compressionType' is not COMPRESSION_SCANLINE, 'lastrow' is
+   undefined both as input and output.
+-----------------------------------------------------------------------------*/
+    switch (compressionType) {
+    case COMPRESSION_RLE:
+        readRleRow(ifP, palmrow, bytesPerRow);
+        break;
+    case COMPRESSION_SCANLINE:
+        readScanlineRow(ifP, palmrow, lastrow, bytesPerRow, firstRow);
+        break;
+    case COMPRESSION_PACKBITS:
+        readPackBitsRow(ifP, palmrow, bytesPerRow);
+        break;
+    case COMPRESSION_NONE:
+        readUncompressedRow(ifP, palmrow, bytesPerRow);
+        break;
+    }
+}
+
+
+
+static void
+convertRowToPnmDirect(const unsigned char * const palmrow,
+                      xel *                 const xelrow,
+                      unsigned int          const cols,
+                      xelval                const maxval,
+                      unsigned int *        const seen) {
+
+    /* There's a problem with this.  Take the Palm 16-bit
+       direct color.  That's 5 bits for the red, 6 for the
+       green, and 5 for the blue.  So what should the MAXVAL
+       be?  I decided to use 255 (8 bits) for everything,
+       since that's the theoretical max of the number of bits
+       in any one color, according to Palm.  So the Palm color
+       0xFFFF (white) would be red=0x1F, green=0x3F, and
+       blue=0x1F.  How do we promote those colors?  Simple
+       shift would give us R=248,G=252,B=248; which is
+       slightly green.  Hardly seems right.
+       
+       So I've perverted the math a bit.  Each color value is
+       multiplied by 255, then divided by either 31 (red or
+       blue) or 63 (green).  That's the right way to do it
+       anyway.  
+    */
+
+    const unsigned char *inbyte;
+    unsigned int j;
+    
+    for (inbyte = palmrow, j = 0;  j < cols;  ++j) {
+        unsigned int inval;
+        inval = *inbyte++ << 8;
+        inval |= *inbyte++;
+        
+        if (seen)
+            ++seen[inval];
+        
+        PPM_ASSIGN(xelrow[j], 
+                   (((inval >> 11) & 0x1F) * maxval) / 0x1F, 
+                   (((inval >>  5) & 0x3F) * maxval) / 0x3F, 
+                   (((inval >>  0) & 0x1F) * maxval) / 0x1F
+            );
+    }
+}
+
+
+
+static void
+convertRowToPnmNotDirect(const unsigned char * const palmrow,
+                         xel *                 const xelrow,
+                         unsigned int          const cols,
+                         Colormap              const colormap,
+                         xelval *              const graymap,
+                         unsigned int *        const seen,
+                         unsigned int          const pixelSize) {
+
+    unsigned int const mask = (1 << pixelSize) - 1;
+
+    const unsigned char *inbyte;
+    unsigned int inbit;
+    unsigned int j;
+    
+    inbit = 8 - pixelSize;
+    inbyte = palmrow;
+    for (j = 0; j < cols; ++j) {
+        short const color = ((*inbyte) & (mask << inbit)) >> inbit;
+        if (seen)
+            ++seen[color];
+        
+        if (colormap) {
+            Color_s const color2      = color << 24;
+            Color   const actualColor = (bsearch (&color2,
+                                                  colormap->color_entries, 
+                                                  colormap->ncolors,
+                                                  sizeof(color2), 
+                                                  palmcolor_compare_indices));
+            PPM_ASSIGN(xelrow[j], 
+                       (*actualColor >> 16) & 0xFF, 
+                       (*actualColor >>  8) & 0xFF, 
+                       (*actualColor >>  0) & 0xFF);
+        } else
+            PNM_ASSIGN1(xelrow[j], graymap[color]);
+        
+        if (!inbit) {
+            ++inbyte;
+            inbit = 8 - pixelSize;
+        } else
+            inbit -= pixelSize;
+    }
+}
+
+
+
+static void
+writePnm(FILE *            const ofP,
+         struct palmHeader const palmHeader,
+         FILE *            const ifP,
+         Colormap          const colormap,
+         xelval *          const graymap,
+         unsigned int      const nColors,
+         int               const format,
+         xelval            const maxval,
+         unsigned int **   const seenP) {
+
+    int const cols = palmHeader.cols;
+    int const rows = palmHeader.rows;
+
+    unsigned char * palmrow;
+    unsigned char * lastrow;
+    xel *           xelrow;
+    unsigned int *  seen;
+    unsigned int    row;
+    
+    pnm_writepnminit(ofP, cols, rows, maxval, format, 0);
+    xelrow = pnm_allocrow(cols);
+
+    /* Read the picture data, one row at a time */
+    MALLOCARRAY_NOFAIL(palmrow, palmHeader.bytesPerRow);
+    MALLOCARRAY_NOFAIL(lastrow, palmHeader.bytesPerRow); 
+    
+    if (seenP) {
+        createHistogram(nColors, &seen);
+        *seenP = seen;
+    } else
+        seen = NULL;
+
+    /* We should actually use compressedDataSizeNN for checking the sanity 
+       of the data we're reading ... 
+    */
+    if (palmHeader.compressionType != COMPRESSION_NONE) {
+        if (palmHeader.version < 3) {
+            short compressedDataSize16;
+            pm_readbigshort(ifP, &compressedDataSize16);
+        } else {
+            long compressedDataSize32;
+            pm_readbiglong(ifP, &compressedDataSize32);
+        }
+    }
+
+    for (row = 0; row < rows; ++row) {
+        readDecompressedRow(ifP, palmrow, lastrow, 
+                            palmHeader.compressionType, 
+                            palmHeader.bytesPerRow,
+                            row == 0);
+
+        if (palmHeader.directColor) {
+            assert(palmHeader.pixelSize == 16);
+            convertRowToPnmDirect(palmrow, xelrow, cols, maxval, seen);
+        } else
+            convertRowToPnmNotDirect(palmrow, xelrow, cols, colormap, graymap,
+                                     seen, palmHeader.pixelSize);
+
+        pnm_writepnmrow(ofP, xelrow, cols, maxval, format, 0);
+    }
+    free(lastrow);
+    free(palmrow);
+    pnm_freerow(xelrow);
+}
+
+
+
+static void
+showHistogram(unsigned int * const seen,
+              Colormap       const colormap,
+              const xelval * const graymap,
+              unsigned int   const ncolors) {
+
+    unsigned int colorIndex;
+    
+    for (colorIndex = 0;  colorIndex < ncolors; ++colorIndex) {
+        if (!colormap)
+            pm_message("%.3d -> %.3d:  %d", 
+                       colorIndex, graymap[colorIndex], seen[colorIndex]);
+        else {
+            Color_s const color = colorIndex << 24;
+            Color const actualColor = (bsearch(&color,
+                                               colormap->color_entries, 
+                                               colormap->ncolors,
+                                               sizeof(color), 
+                                               palmcolor_compare_indices));
+            if (actualColor)
+                pm_message("%.3d -> %ld,%ld,%ld:  %d", colorIndex,
+                           (*actualColor >> 16) & 0xFF,
+                           (*actualColor >> 8) & 0xFF,
+                           (*actualColor & 0xFF), seen[colorIndex]);
+        }
+    }
+}
+
+
+
+int
+main(int argc, char **argv) {
+
+    struct cmdlineInfo cmdline;
+
+    FILE* ifP;
+    struct palmHeader palmHeader;
+    struct directColorInfo directInfoType;
+    Colormap colormapFromImage;
+    Colormap colormap;
+    struct directColorInfo directColorInfo;
+    int format;
+    xelval maxval;
+    unsigned int nColors;
+
+    /* Parse default params */
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    readHeader(ifP, cmdline.rendition, &palmHeader);
+
+    readDirectInfoType(ifP, palmHeader, &directInfoType);
+   
+    readColormap(ifP, palmHeader, &colormapFromImage);
+    
+    determineOutputFormat(palmHeader, &format, &maxval);
+    
+    getColorInfo(palmHeader, directInfoType, colormapFromImage,
+                 &colormap, &nColors, &directColorInfo);
+
+    if (cmdline.verbose)
+        reportPalmHeader(palmHeader, directColorInfo);
+
+    if (cmdline.transparent)
+        doTransparent(stdout, 
+                      palmHeader.hasTransparency, palmHeader.directColor,
+                      palmHeader.transparentIndex, 
+                      palmHeader.pixelSize, colormap, directColorInfo);
+    else {
+        unsigned int * seen;
+        xelval * graymap;
+
+        graymap = createGraymap(nColors, maxval);
+
+        writePnm(stdout,
+                 palmHeader, ifP, colormap, graymap, nColors, format, maxval, 
+                 cmdline.showhist ? &seen : NULL);
+        
+        if (cmdline.showhist)
+            showHistogram(seen, colormap, graymap, nColors);
+        
+        free(graymap);
+    }
+    pm_close(ifP);
+
+    return 0;
+}
diff --git a/converter/other/pnmtopalm/pnmtopalm.c b/converter/other/pnmtopalm/pnmtopalm.c
new file mode 100644
index 00000000..3b9eec8f
--- /dev/null
+++ b/converter/other/pnmtopalm/pnmtopalm.c
@@ -0,0 +1,1235 @@
+/* pnmtopalm.c - read a PNM image and write a Palm Bitmap file
+ *
+ * Inspired by and using methods from ppmtoTbmp.c by Ian Goldberg
+ * <iang@cs.berkeley.edu>, which was based on ppmtopuzz.c by Jef
+ * Poskanzer, from the netpbm-1mar1994 package.
+ *
+ * Mods for multiple bits per pixel were added to ppmtoTbmp.c by
+ * George Caswell <tetsujin@sourceforge.net> and Bill Janssen
+ * <bill@janssen.org>.
+ *
+ * Major fixes and new capability added by Paul Bolle <pebolle@tiscali.nl>
+ * in late 2004 / early 2005.
+ *
+ * See LICENSE file for licensing information.
+ *
+ *  
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+
+#include "pnm.h"
+#include "palm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+
+enum compressionType {COMP_NONE, COMP_SCANLINE, COMP_RLE, COMP_PACKBITS};
+
+struct cmdline_info {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *inputFilespec;  /* Filespecs of input files */
+    char *transparent;          /* -transparent value.  Null if unspec */
+    unsigned int depth;         /* -depth value.  0 if unspec */
+    unsigned int maxdepth;      /* -maxdepth value.  0 if unspec */
+    enum compressionType compression;
+    unsigned int verbose;
+    unsigned int colormap;
+    unsigned int offset;        /* -offset specified */
+    unsigned int density;       /* screen density */
+    unsigned int withdummy;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optStruct3 opt;  /* set by OPTENT3 */
+    optEntry *option_def;
+    unsigned int option_def_index;
+
+    unsigned int transSpec, depthSpec, maxdepthSpec, densitySpec;
+    unsigned int scanline_compression, rle_compression, packbits_compression;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "transparent",      OPT_STRING, 
+            &cmdlineP->transparent, &transSpec, 0);
+    OPTENT3(0, "depth",            OPT_UINT, 
+            &cmdlineP->depth,       &depthSpec, 0);
+    OPTENT3(0, "maxdepth",         OPT_UINT, 
+            &cmdlineP->maxdepth,    &maxdepthSpec, 0);
+    OPTENT3(0, "scanline_compression", OPT_FLAG, 
+            NULL,                   &scanline_compression, 0);
+    OPTENT3(0, "rle_compression",  OPT_FLAG, 
+            NULL,                   &rle_compression, 0);
+    OPTENT3(0, "packbits_compression", OPT_FLAG, 
+            NULL,                   &packbits_compression, 0);
+    OPTENT3(0, "verbose",          OPT_FLAG, 
+            NULL,                   &cmdlineP->verbose, 0);
+    OPTENT3(0, "colormap",         OPT_FLAG, 
+            NULL,                   &cmdlineP->colormap, 0);
+    OPTENT3(0, "offset",           OPT_FLAG, 
+            NULL,                   &cmdlineP->offset, 0);
+    OPTENT3(0, "density",          OPT_UINT, 
+            &cmdlineP->density,     &densitySpec, 0);
+    OPTENT3(0, "withdummy",        OPT_FLAG, 
+            NULL,                   &cmdlineP->withdummy, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE; /* We have some 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 (depthSpec) {
+        if (cmdlineP->depth != 1 && cmdlineP->depth != 2 
+            && cmdlineP->depth != 4 && cmdlineP->depth != 8
+            && cmdlineP->depth != 16)
+            pm_error("invalid value for -depth: %u.  Valid values are "
+                     "1, 2, 4, 8, and 16", cmdlineP->depth);
+    } else
+        cmdlineP->depth = 0;
+
+    if (maxdepthSpec) {
+        if (cmdlineP->maxdepth != 1 && cmdlineP->maxdepth != 2 
+            && cmdlineP->maxdepth != 4 && cmdlineP->maxdepth != 8
+            && cmdlineP->maxdepth != 16)
+            pm_error("invalid value for -maxdepth: %u.  Valid values are "
+                     "1, 2, 4, 8, and 16", cmdlineP->maxdepth);
+    } else
+        cmdlineP->maxdepth = 0;
+
+    if (depthSpec && maxdepthSpec && 
+        cmdlineP->depth > cmdlineP->maxdepth)
+        pm_error("-depth value (%u) is greater than -maxdepth (%u) value.",
+                 cmdlineP->depth, cmdlineP->maxdepth);
+
+    if (!transSpec)
+        cmdlineP->transparent = NULL;
+
+    if (densitySpec) {
+        if (cmdlineP->density != PALM_DENSITY_LOW &&
+            cmdlineP->density != PALM_DENSITY_ONEANDAHALF &&
+            cmdlineP->density != PALM_DENSITY_DOUBLE &&
+            cmdlineP->density != PALM_DENSITY_TRIPLE &&
+            cmdlineP->density != PALM_DENSITY_QUADRUPLE)
+            pm_error("Invalid value for -density: %d.  Valid values are "
+                     "%d, %d, %d, %d and %d.", cmdlineP->density, 
+                     PALM_DENSITY_LOW, PALM_DENSITY_ONEANDAHALF,
+                     PALM_DENSITY_DOUBLE, PALM_DENSITY_TRIPLE,
+                     PALM_DENSITY_QUADRUPLE);
+    } else
+        cmdlineP->density = PALM_DENSITY_LOW;
+
+    if (cmdlineP->density != PALM_DENSITY_LOW && cmdlineP->withdummy)
+            pm_error("You can't specify -withdummy with -density value %u.  "
+                     "It is valid only with low density (%u)",
+                     cmdlineP->density, PALM_DENSITY_LOW);
+
+    if (cmdlineP->withdummy && !cmdlineP->offset)
+        pm_error("-withdummy does not make sense without -offset");
+
+    if (scanline_compression + rle_compression + packbits_compression > 1)
+        pm_error("You may specify only one of -scanline_compression, "
+                 "-rle_compression, and -packbits_compression");
+    else {
+        if (scanline_compression)
+            cmdlineP->compression = COMP_SCANLINE;
+        else if (rle_compression)
+            cmdlineP->compression = COMP_RLE;
+        else if (packbits_compression)
+            cmdlineP->compression = COMP_PACKBITS;
+        else
+            cmdlineP->compression = COMP_NONE;
+    }
+        
+    if (argc-1 > 1)
+        pm_error("This program takes at most 1 argument: the file name.  "
+                 "You specified %d", argc-1);
+    else if (argc-1 > 0) 
+        cmdlineP->inputFilespec = argv[1];
+    else
+        cmdlineP->inputFilespec = "-";
+}
+
+
+
+static void
+determinePalmFormat(unsigned int   const cols, 
+                    unsigned int   const rows, 
+                    xelval         const maxval, 
+                    int            const format, 
+                    xel **         const xels,
+                    unsigned int   const specified_bpp,
+                    unsigned int   const max_bpp, 
+                    bool           const custom_colormap,
+                    bool           const verbose,
+                    unsigned int * const bppP, 
+                    bool *         const directColorP, 
+                    Colormap *     const colormapP) {
+
+    if (PNM_FORMAT_TYPE(format) == PBM_TYPE) {
+        if (custom_colormap)
+            pm_error("You specified -colormap with a black and white input "
+                     "image.  -colormap is valid only with color.");
+        if (specified_bpp)
+            *bppP = specified_bpp;
+        else
+            *bppP = 1;    /* no point in wasting bits */
+        *directColorP = FALSE;
+        *colormapP = NULL;
+        if (verbose)
+            pm_message("output is black and white");
+    } else if (PNM_FORMAT_TYPE(format) == PGM_TYPE) {
+        /* we can usually handle this one, but may not have enough
+           pixels.  So check... */
+        if (custom_colormap)
+            pm_error("You specified -colormap with a black and white input"
+                     "image.  -colormap is valid only with color.");
+        if (specified_bpp)
+            *bppP = specified_bpp;
+        else if (max_bpp && (maxval >= (1 << max_bpp)))
+            *bppP = max_bpp;
+        else if (maxval > 16)
+            *bppP = 4;
+        else {
+            /* scale to minimum number of bpp needed */
+            for (*bppP = 1;  (1 << *bppP) < maxval;  *bppP *= 2)
+                ;
+        }
+        if (*bppP > 4)
+            *bppP = 4;
+        if (verbose)
+            pm_message("output is grayscale %d bits-per-pixel", *bppP);
+        *directColorP = FALSE;
+        *colormapP = NULL;
+    } else if (PNM_FORMAT_TYPE(format) == PPM_TYPE) {
+
+        /* We assume that we only get a PPM if the image cannot be
+           represented as PBM or PGM.  There are two options here: either
+           8-bit with a colormap, either the standard one or a custom one,
+           or 16-bit direct color.  In the 8-bit case, if "custom_colormap"
+           is specified (not recommended by Palm) we will put in our own
+           colormap; otherwise we will assume that the colors have been
+           mapped to the default Palm colormap by appropriate use of
+           pnmquant.  We try for 8-bit color first, since it works on
+           more PalmOS devices. 
+        */
+        if ((specified_bpp == 16) || 
+            (specified_bpp == 0 && max_bpp == 16)) {
+            /* we do the 16-bit direct color */
+            *directColorP = TRUE;
+            *colormapP = NULL;
+            *bppP = 16;
+        } else if (!custom_colormap) {
+            /* standard indexed 8-bit color */
+            *colormapP = palmcolor_build_default_8bit_colormap();
+            *bppP = 8;
+            if (((specified_bpp != 0) && (specified_bpp != 8)) ||
+                ((max_bpp != 0) && (max_bpp < 8)))
+                pm_error("Must use depth of 8 for color Palm Bitmap without "
+                         "custom color table.");
+            *directColorP = FALSE;
+            if (verbose)
+                pm_message("Output is color with default colormap at 8 bpp");
+        } else {
+            /* indexed 8-bit color with a custom colormap */
+            *colormapP = 
+                palmcolor_build_custom_8bit_colormap(rows, cols, xels);
+            for (*bppP = 1; (1 << *bppP) < (*colormapP)->ncolors; *bppP *= 2);
+            if (specified_bpp != 0) {
+                if (specified_bpp >= *bppP)
+                    *bppP = specified_bpp;
+                else
+                    pm_error("Too many colors for specified depth.  "
+                             "Use pnmquant to reduce.");
+            } else if ((max_bpp != 0) && (max_bpp < *bppP)) {
+                pm_error("Too many colors for specified max depth.  "
+                         "Use pnmquant to reduce.");
+            }
+            *directColorP = FALSE;
+            if (verbose)
+                pm_message("Output is color with custom colormap "
+                           "with %d colors at %d bpp", 
+                           (*colormapP)->ncolors, *bppP);
+        }
+    } else {
+        pm_error("unknown format 0x%x on input file", (unsigned) format);
+    }
+}
+
+
+
+static const char * 
+formatName(int const format) {
+    
+    const char * retval;
+
+    switch(PNM_FORMAT_TYPE(format)) {
+    case PBM_TYPE: retval = "black and white"; break;
+    case PGM_TYPE: retval = "grayscale";       break;
+    case PPM_TYPE: retval = "color";           break;
+    default:       retval = "???";             break;
+    }
+    return retval;
+}
+
+        
+
+static void
+findTransparentColor(char *         const colorSpec, 
+                     pixval         const newMaxval,
+                     bool           const directColor, 
+                     pixval         const maxval, 
+                     Colormap       const colormap,
+                     xel *          const transcolorP, 
+                     unsigned int * const transindexP) {
+
+    *transcolorP = ppm_parsecolor(colorSpec, maxval);
+    if (!directColor) {
+        Color_s const temp_color = 
+            ((((PPM_GETR(*transcolorP)*newMaxval) / maxval) << 16) 
+             | (((PPM_GETG(*transcolorP)*newMaxval) / maxval) << 8)
+             | ((PPM_GETB(*transcolorP)*newMaxval) / maxval));
+        Color const found = 
+            (bsearch(&temp_color,
+                     colormap->color_entries, colormap->ncolors,
+                     sizeof(Color_s), palmcolor_compare_colors));
+        if (!found) {
+            pm_error("Specified transparent color %s not found "
+                     "in colormap.", colorSpec);
+        } else
+            *transindexP = (*found >> 24) & 0xFF;
+    }
+}
+
+
+
+static unsigned int
+bitmapVersion(unsigned int         const bpp,
+              bool                 const colormap,
+              bool                 const transparent,
+              enum compressionType const compression,
+              unsigned int         const density) {
+/*----------------------------------------------------------------------------
+   Return the version number of the oldest version that can represent
+   the specified attributes.
+-----------------------------------------------------------------------------*/
+    unsigned int version;
+    /* we need Version 1 if we use more than 1 bpp,
+       Version 2 if we use compression or transparency,
+       Version 3 if density is 108 or higher
+    */
+    if (density > PALM_DENSITY_LOW)
+        version = 3;
+    else if (transparent || compression != COMP_NONE)
+        version = 2;
+    else if (bpp > 1 || colormap)
+        version = 1;
+    else
+        version = 0;
+
+    return version;
+}
+
+
+
+static void
+writeCommonHeader(unsigned int         const cols,
+                  unsigned int         const rows,
+                  unsigned int         const rowbytes,
+                  enum compressionType const compression,
+                  bool                 const colormap,
+                  bool                 const transparent,
+                  bool                 const directColor,
+                  unsigned int         const bpp,
+                  unsigned int         const version) {
+/*----------------------------------------------------------------------------
+   Write the first 10 bytes of the Palm Bitmap header.
+   These are common to all encodings (versions 0, 1, 2 and 3).
+-----------------------------------------------------------------------------*/
+    unsigned short flags;
+
+    if (cols > USHRT_MAX)
+        pm_error("Too many columns for Palm Bitmap: %u", cols);
+    pm_writebigshort(stdout, cols);    /* width */
+    if (rows > USHRT_MAX)
+        pm_error("Too many columns for Palm Bitmap: %u", rows);
+    pm_writebigshort(stdout, rows);    /* height */
+    if (rowbytes > USHRT_MAX)
+        pm_error("Too many bytes per row for Palm Bitmap: %u", rowbytes);
+    pm_writebigshort(stdout, rowbytes);
+
+    flags = 0;  /* initial value */
+    if (compression != COMP_NONE)
+        flags |= PALM_IS_COMPRESSED_FLAG;
+    if (colormap)
+        flags |= PALM_HAS_COLORMAP_FLAG;
+    if (transparent)
+        flags |= PALM_HAS_TRANSPARENCY_FLAG;
+    if (directColor)
+        flags |= PALM_DIRECT_COLOR_FLAG;
+    pm_writebigshort(stdout, flags);
+    assert(bpp <= UCHAR_MAX);
+    fputc(bpp, stdout);
+
+    fputc(version, stdout);
+}
+
+
+
+static unsigned char 
+compressionFieldValue(enum compressionType const compression) {
+
+    unsigned char retval;
+
+    switch (compression) {
+    case COMP_SCANLINE:
+        retval = PALM_COMPRESSION_SCANLINE;
+        break;
+    case COMP_RLE:
+        retval = PALM_COMPRESSION_RLE;
+        break;
+    case COMP_PACKBITS:
+        retval = PALM_COMPRESSION_PACKBITS;
+        break;
+    case COMP_NONE:
+        retval = 0x00;  /* empty */
+        break;
+    }
+    return retval;
+}
+
+
+
+static void
+writeRemainingHeaderLow(unsigned int         const nextDepthOffset,
+                        unsigned int         const transindex,
+                        enum compressionType const compression,
+                        unsigned int         const bpp) {
+/*----------------------------------------------------------------------------
+   Write last 6 bytes of a low density Palm Bitmap header. 
+-----------------------------------------------------------------------------*/
+    if (nextDepthOffset > USHRT_MAX)
+        pm_error("Image too large for Palm Bitmap");
+
+    pm_writebigshort(stdout, nextDepthOffset);
+
+    if (bpp != 16) {
+        assert(transindex <= UCHAR_MAX);
+        fputc(transindex, stdout);    /* transparent index */
+    } else
+        fputc(0, stdout);    /* the DirectInfoType will hold this info */
+    
+    fputc(compressionFieldValue(compression), stdout);
+
+    pm_writebigshort(stdout, 0);  /* reserved by Palm */
+}
+
+
+
+static void
+writeRemainingHeaderHigh(unsigned int         const bpp,
+                         enum compressionType const compression,
+                         unsigned int         const density,
+                         xelval               const maxval,
+                         bool                 const transparent,
+                         xel                  const transcolor,
+                         unsigned int         const transindex,
+                         unsigned int         const nextBitmapOffset) {
+/*----------------------------------------------------------------------------
+   Write last 16 bytes of a high density Palm Bitmap header. 
+-----------------------------------------------------------------------------*/
+    if ((nextBitmapOffset >> 31) > 1)
+        pm_error("Image too large for Palm Bitmap.  nextBitmapOffset "
+            "value doesn't fit in 4 bytes");
+
+    fputc(0x18, stdout); /* size of this high density header */
+
+    if (bpp != 16)
+        fputc(PALM_FORMAT_INDEXED, stdout);
+    else
+        fputc(PALM_FORMAT_565, stdout);
+
+    fputc(0x00, stdout); /* unused */
+
+    fputc(compressionFieldValue(compression), stdout);
+
+    pm_writebigshort(stdout, density);
+
+    if (transparent) {
+        if (bpp == 16) {
+            /* Blind guess here */
+            fputc(0, stdout);
+            fputc((PPM_GETR(transcolor) * 255) / maxval, stdout);
+            fputc((PPM_GETG(transcolor) * 255) / maxval, stdout);
+            fputc((PPM_GETB(transcolor) * 255) / maxval, stdout);
+        } else {
+            assert(transindex <= UCHAR_MAX);
+            fputc(0, stdout);
+            fputc(0, stdout);
+            fputc(0, stdout);
+            fputc(transindex, stdout);   /* transparent index */
+        }
+    } else
+        pm_writebiglong(stdout, 0);
+
+    pm_writebiglong(stdout, nextBitmapOffset);
+}
+
+
+
+static void
+writeDummy() {
+/*----------------------------------------------------------------------------
+   Write a dummy Palm Bitmap header.  This is a 16 byte header, of
+   type version 1 and with (only) pixelSize set to 0xFF.
+
+   An old viewer will see this as invalid due to the pixelSize, and stop
+   reading the stream.  A new viewer will recognize this for what it is
+   (a dummy header designed to stop old viewers from reading further in
+   the stream) and continue reading the stream.  Presumably, what follows
+   in the stream is understandable by a new viewer, but would confuse an
+   old one.
+-----------------------------------------------------------------------------*/
+    pm_writebiglong(stdout, 0x00);
+    pm_writebiglong(stdout, 0x00);
+    fputc(0xFF, stdout);               /* pixelSize */
+    fputc(0x01, stdout);               /* version */
+    pm_writebigshort(stdout, 0x00); 
+    pm_writebiglong(stdout, 0x00);
+}
+
+
+
+static void
+writeColormap(bool         const explicitColormap,
+              Colormap     const colormap,
+              bool         const directColor,
+              unsigned int const bpp,
+              bool         const transparent,
+              xel          const transcolor,
+              xelval       const maxval,
+              unsigned int const version) {
+              
+    /* if there's a colormap, write it out */
+    if (explicitColormap) {
+        unsigned int row;
+        if (!colormap)
+            pm_error("Internal error: user specified -colormap, but we did "
+                     "not generate a colormap.");
+        qsort (colormap->color_entries, colormap->ncolors,
+               sizeof(Color_s), palmcolor_compare_indices);
+        pm_writebigshort( stdout, colormap->ncolors );
+        for (row = 0;  row < colormap->ncolors; ++row)
+            pm_writebiglong (stdout, colormap->color_entries[row]);
+    }
+
+    if (directColor && (version < 3)) {
+        /* write the DirectInfoType (8 bytes) */
+        if (bpp == 16) {
+            fputc(5, stdout);   /* # of bits of red */
+            fputc(6, stdout);   /* # of bits of green */    
+            fputc(5, stdout);   /* # of bits of blue */
+            fputc(0, stdout);   /* reserved by Palm */
+        } else
+            pm_error("Don't know how to create %d bit DirectColor bitmaps.", 
+                     bpp);
+        if (transparent) {
+            fputc(0, stdout);
+            fputc((PPM_GETR(transcolor) * 255) / maxval, stdout);
+            fputc((PPM_GETG(transcolor) * 255) / maxval, stdout);
+            fputc((PPM_GETB(transcolor) * 255) / maxval, stdout);
+        } else
+            pm_writebiglong(stdout, 0);     /* no transparent color */
+    }
+}
+
+
+
+static void
+computeRawRowDirectColor(const xel *     const xelrow,
+                         unsigned int    const cols,
+                         xelval          const maxval,
+                         unsigned char * const rowdata) {
+
+    unsigned int col;
+    unsigned char *outptr;
+    
+    for (col = 0, outptr = rowdata; col < cols; ++col) {
+        unsigned int const color = 
+            ((((PPM_GETR(xelrow[col])*31)/maxval) << 11) |
+             (((PPM_GETG(xelrow[col])*63)/maxval) << 5) |
+             ((PPM_GETB(xelrow[col])*31)/maxval));
+        *outptr++ = (color >> 8) & 0xFF;
+        *outptr++ = color & 0xFF;
+    }
+}
+
+
+
+static void
+computeRawRowNonDirect(const xel *     const xelrow,
+                       unsigned int    const cols,
+                       xelval          const maxval,
+                       unsigned int    const bpp,
+                       Colormap        const colormap,
+                       unsigned int    const newMaxval,
+                       unsigned char * const rowdata) {
+
+    unsigned int col;
+    unsigned char *outptr;
+    unsigned char outbyte;
+        /* Accumulated bits to be output */
+    unsigned char outbit;
+        /* The lowest bit number we want to access for this pixel */
+
+    outbyte = 0x00;
+    outptr = rowdata;
+
+    for (outbit = 8 - bpp, col = 0; col < cols; ++col) {
+        unsigned int color;
+        if (!colormap) {
+            /* we assume grayscale, and use simple scaling */
+            color = (PNM_GET1(xelrow[col]) * newMaxval)/maxval;
+            if (color > newMaxval)
+                pm_error("oops.  Bug in color re-calculation code.  "
+                         "color of %u.", color);
+            color = newMaxval - color; /* note grayscale maps are inverted */
+        } else {
+            Color_s const temp_color =
+                ((((PPM_GETR(xelrow[col])*newMaxval)/maxval)<<16) 
+                 | (((PPM_GETG(xelrow[col])*newMaxval)/maxval)<<8)
+                 | (((PPM_GETB(xelrow[col])*newMaxval)/maxval)));
+            Color const found = (bsearch (&temp_color,
+                                          colormap->color_entries, 
+                                          colormap->ncolors,
+                                          sizeof(Color_s), 
+                                          palmcolor_compare_colors));
+            if (!found) {
+                pm_error("Color %d:%d:%d not found in colormap.  "
+                         "Try using pnmquant to reduce the "
+                         "number of colors.",
+                         PPM_GETR(xelrow[col]), 
+                         PPM_GETG(xelrow[col]), 
+                         PPM_GETB(xelrow[col]));
+            }
+            color = (*found >> 24) & 0xFF;
+        }
+
+        if (color > newMaxval)
+            pm_error("oops.  Bug in color re-calculation code.  "
+                     "color of %u.", color);
+        outbyte |= (color << outbit);
+        if (outbit == 0) {
+            /* Bit buffer is full.  Flush to to rowdata. */
+            *outptr++ = outbyte;
+            outbyte = 0x00;
+            outbit = 8 - bpp;
+        } else
+            outbit -= bpp;
+    }
+    if ((cols % (8 / bpp)) != 0) {
+        /* Flush bits remaining in the bit buffer to rowdata */
+        *outptr++ = outbyte;
+    }
+}
+
+
+struct seqBuffer {
+/*----------------------------------------------------------------------------
+   A buffer to which one can write bytes sequentially.
+-----------------------------------------------------------------------------*/
+    char * buffer;
+    unsigned int allocatedSize;
+    unsigned int occupiedSize;
+};
+
+
+static void
+createBuffer(struct seqBuffer ** const bufferPP) {
+
+    struct seqBuffer * bufferP;
+
+    MALLOCVAR_NOFAIL(bufferP);
+
+    bufferP->allocatedSize = 4096;
+    MALLOCARRAY(bufferP->buffer, bufferP->allocatedSize);
+    if (bufferP == NULL)
+        pm_error("Unable to allocate %u bytes of buffer", 
+                 bufferP->allocatedSize);
+    bufferP->occupiedSize = 0;
+
+    *bufferPP = bufferP;
+}
+
+
+
+static void
+destroyBuffer(struct seqBuffer * const bufferP) {
+
+    free(bufferP->buffer);
+    free(bufferP);
+}
+
+
+
+static void
+addByteToBuffer(struct seqBuffer * const bufferP,
+                unsigned char      const newByte) {
+
+    assert(bufferP->allocatedSize >= bufferP->occupiedSize);
+
+    if (bufferP->allocatedSize == bufferP->occupiedSize) {
+        bufferP->allocatedSize *= 2;
+        REALLOCARRAY(bufferP->buffer, bufferP->allocatedSize);
+        if (bufferP->buffer == NULL)
+            pm_error("Couldn't (re)allocate %u bytes of memory "
+                     "for buffer.", bufferP->allocatedSize);
+    }
+    bufferP->buffer[bufferP->occupiedSize++] = newByte;
+}
+
+
+
+static unsigned int
+bufferLength(struct seqBuffer * const bufferP) {
+    return bufferP->occupiedSize;
+}
+
+
+
+static void
+writeOutBuffer(struct seqBuffer * const bufferP,
+               FILE *             const fileP) {
+
+    size_t bytesWritten;
+
+    bytesWritten = fwrite(bufferP->buffer, sizeof(char), 
+                          bufferP->occupiedSize, fileP);
+
+    if (bytesWritten != bufferP->occupiedSize)
+        pm_error("fwrite() failed to write out the buffer.");
+}
+
+
+
+static void
+copyRowToBuffer(const unsigned char * const rowdata,
+                unsigned int          const rowbytes,
+                struct seqBuffer *    const rasterBufferP) {
+
+    unsigned int pos;
+    for (pos = 0; pos < rowbytes; ++pos)
+        addByteToBuffer(rasterBufferP, rowdata[pos]);
+} 
+
+
+
+static void
+scanlineCompressAndBufferRow(const unsigned char * const rowdata,
+                             unsigned int          const rowbytes,
+                             struct seqBuffer *    const rasterBufferP,
+                             const unsigned char * const lastrow) {
+/*----------------------------------------------------------------------------
+   Take the raw Palm Bitmap row 'rowdata', which is 'rowbytes'
+   columns, and add the scanline-compressed representation of it to
+   the buffer with handle 'rasterBufferP'.
+
+   'lastrow' is the raw contents of the row immediately before the one
+   we're compressing -- i.e. we compress with respect to that row.  This
+   function does not work on the first row of an image.
+-----------------------------------------------------------------------------*/
+    unsigned int pos;
+
+    for (pos = 0;  pos < rowbytes;  pos += 8) {
+        unsigned int const limit = MIN(rowbytes - pos, 8);
+
+        unsigned char map;
+            /* mask indicating which of the next 8 pixels are
+               different from the previous row, and therefore present
+               in the file immediately following the map byte.  
+            */
+        unsigned char differentPixels[8];
+        unsigned char *outptr;
+        unsigned char outbit;
+            
+        for (outbit = 0, map = 0x00, outptr = differentPixels;
+             outbit < limit;  
+             ++outbit) {
+            if (!lastrow 
+                || (lastrow[pos + outbit] != rowdata[pos + outbit])) {
+                map |= (1 << (7 - outbit));
+                *outptr++ = rowdata[pos + outbit];
+            }
+        }
+
+        addByteToBuffer(rasterBufferP, map);
+        {
+            unsigned int j;
+            for (j = 0; j < (outptr - differentPixels); ++j)
+                addByteToBuffer(rasterBufferP, differentPixels[j]);
+        }
+    }
+}
+
+
+
+static void
+rleCompressAndBufferRow(const unsigned char * const rowdata,
+                        unsigned int          const rowbytes,
+                        struct seqBuffer *    const rasterBufferP) {
+/*----------------------------------------------------------------------------
+   Take the raw Palm Bitmap row 'rowdata', which is 'rowbytes' bytes,
+   and add the rle-compressed representation of it to the buffer with
+   handle 'rasterBufferP'.
+-----------------------------------------------------------------------------*/
+    unsigned int pos;
+
+    /* we output a count of the number of bytes a value is
+       repeated, followed by that byte value 
+    */
+    pos = 0;
+    while (pos < rowbytes) {
+        unsigned int repeatcount;
+        for (repeatcount = 1;  
+             repeatcount < (rowbytes - pos) && repeatcount  < 255;  
+             ++repeatcount)
+            if (rowdata[pos + repeatcount] != rowdata[pos])
+                break;
+
+        addByteToBuffer(rasterBufferP, repeatcount);
+        addByteToBuffer(rasterBufferP, rowdata[pos]);
+        pos += repeatcount;
+    }
+}
+
+
+
+static void
+computeNextPackbitsRun(const unsigned char * const rowdata,
+                       unsigned int          const rowbytes,
+                       unsigned int          const startPos,
+                       unsigned int *        const nextPosP,
+                       unsigned char *       const output,
+                       int *                 const countP) {
+
+    unsigned int pos;
+    int count;
+    
+    pos = startPos;
+    count = 0;
+    
+    if (rowdata[pos] == rowdata[pos + 1]) {
+        ++pos;
+        --count;
+        while ((count > -127) && (pos < (rowbytes - 1)) &&
+               (rowdata[pos] == rowdata[pos + 1])) {
+            ++pos;
+            --count;
+        }
+        ++pos;  /* push pos past end of this run */
+    } else {
+        output[count] = rowdata[pos];
+        ++pos;
+        while ((count < 127) && (pos < (rowbytes - 1)) && 
+               (rowdata[pos] != rowdata[pos + 1])) {
+            ++count;
+            output[count] = rowdata[pos];
+            ++pos;
+        }
+        /* trailing literal */
+        if ((count < 127) && (pos == (rowbytes - 1)) &&
+            (rowdata[pos - 1] != rowdata[pos])) {
+            ++count;
+            output[count] = rowdata[pos];
+            ++pos;
+        }
+    }
+    *nextPosP = pos;
+    *countP = count;
+}
+
+
+
+static void
+addPackbitsRunToBuffer(const unsigned char * const rowdata,
+                       unsigned int          const rowbytes,
+                       unsigned int          const pos,
+                       unsigned char *       const output,
+                       int                   const count,
+                       struct seqBuffer *    const rasterBufferP) {
+
+    addByteToBuffer(rasterBufferP, (unsigned char)(signed char)count);
+    if (count < 0) {
+        addByteToBuffer(rasterBufferP, rowdata[pos - 1]);
+    } else {
+        unsigned int j;
+        for (j = 0; j <= count; j++)
+            addByteToBuffer(rasterBufferP, output[j]);
+    }
+    
+    if (pos == (rowbytes - 1) && (rowdata[pos - 1] != rowdata[pos])) {
+        /* orphaned byte, treat as literal */
+        addByteToBuffer(rasterBufferP, 0);
+        addByteToBuffer(rasterBufferP, rowdata[pos]);
+    }
+}
+
+
+
+static void
+packbitsCompressAndBufferRow(const unsigned char * const rowdata,
+                             unsigned int          const rowbytes,
+                             struct seqBuffer *    const rasterBufferP) {
+/*----------------------------------------------------------------------------
+   Take the raw Palm Bitmap row 'rowdata', which is 'rowbytess' bytes, and
+   add the packbits-compressed representation of it to the buffer 
+   with handle 'rasterBufferP'.
+-----------------------------------------------------------------------------*/
+    unsigned int position;
+        /* byte position within the row */
+
+    position = 0;  /* Start at beginning of row */
+
+    while (position < rowbytes - 1) {
+        unsigned char output[128];
+        int count;
+
+        computeNextPackbitsRun(rowdata, rowbytes, position, 
+                               &position, output, &count);
+
+        addPackbitsRunToBuffer(rowdata, rowbytes, position, output, count,
+                               rasterBufferP);
+    }
+}
+
+
+
+static void
+bufferRowFromRawRowdata(const unsigned char *  const rowdata,
+                        unsigned int           const rowbytes,
+                        enum compressionType   const compression,
+                        const unsigned char *  const lastrow,
+                        struct seqBuffer *     const rasterBufferP) {
+/*----------------------------------------------------------------------------
+   Starting with a raw (uncompressed) Palm raster line, do the
+   compression identified by 'compression' and add the compressed row
+   to the buffer with handle 'rasterBufferP'.
+
+   If 'compression' indicates scanline compression, 'lastrow' is the
+   row immediately preceding this one in the image (and this function
+   doesn't work on the first row of an image).  Otherwise, 'lastrow'
+   is meaningless.
+-----------------------------------------------------------------------------*/
+    switch (compression) {
+    case COMP_NONE:
+        copyRowToBuffer(rowdata, rowbytes, rasterBufferP);
+        break;
+    case COMP_SCANLINE:
+        scanlineCompressAndBufferRow(rowdata, rowbytes, rasterBufferP, 
+                                     lastrow);
+        break;
+    case COMP_RLE:
+        rleCompressAndBufferRow(rowdata, rowbytes, rasterBufferP);
+        break;
+    case COMP_PACKBITS:
+        packbitsCompressAndBufferRow(rowdata, rowbytes, rasterBufferP);
+        break;
+    }
+}
+
+
+
+static void
+bufferRow(const xel *          const xelrow,
+          unsigned int         const cols,
+          xelval               const maxval,
+          unsigned int         const rowbytes,
+          unsigned int         const bpp,
+          unsigned int         const newMaxval,
+          enum compressionType const compression,
+          bool                 const directColor,
+          Colormap             const colormap,
+          unsigned char *      const rowdata,
+          unsigned char *      const lastrow,
+          struct seqBuffer *   const rasterBufferP) {
+/*----------------------------------------------------------------------------
+   Add a row of the Palm Bitmap raster to buffer 'rasterBufferP'.
+   
+   'xelrow' is the image contents of row.  It is 'cols' columns wide.
+
+   If 'compression' indicates scanline compression, 'lastrow' is the
+   row immediately preceding this one in the image (and this function
+   doesn't work on the first row of an image).  Otherwise, 'lastrow'
+   is meaningless.
+
+   'rowdata' is a work buffer 'rowbytes' in size.
+-----------------------------------------------------------------------------*/
+    if (directColor)
+        computeRawRowDirectColor(xelrow, cols, maxval, rowdata);
+    else 
+        computeRawRowNonDirect(xelrow, cols, maxval, bpp, colormap, newMaxval,
+                               rowdata);
+
+    bufferRowFromRawRowdata(rowdata, rowbytes, compression,
+                            lastrow, rasterBufferP);
+}
+
+
+
+static void 
+bufferRaster(xel **               const xels,
+             unsigned int         const cols,
+             unsigned int         const rows,
+             xelval               const maxval,
+             unsigned int         const rowbytes,
+             unsigned int         const bpp,
+             unsigned int         const newMaxval,
+             enum compressionType const compression,
+             bool                 const directColor,
+             Colormap             const colormap,
+             struct seqBuffer **  const rasterBufferPP) {
+    
+    unsigned char * rowdata;
+    unsigned char * lastrow;
+    unsigned int row;
+
+    createBuffer(rasterBufferPP);
+    
+    MALLOCARRAY_NOFAIL(rowdata, rowbytes);
+    MALLOCARRAY_NOFAIL(lastrow, rowbytes);
+
+    /* And write out the data. */
+    for (row = 0; row < rows; ++row) {
+        bufferRow(xels[row], cols, maxval, rowbytes, bpp, newMaxval,
+                  compression,
+                  directColor, colormap, rowdata, row > 0 ? lastrow : NULL,
+                  *rasterBufferPP);
+
+        if (compression == COMP_SCANLINE)
+            memcpy(lastrow, rowdata, rowbytes);
+    }
+    free(lastrow);
+    free(rowdata);
+}
+
+
+
+static void
+computeOffsetStuff(bool                 const offsetWanted,
+                   unsigned int         const version,
+                   bool                 const directColor,
+                   enum compressionType const compression,
+                   bool                 const colormap,
+                   unsigned int         const colormapColorCount,
+                   unsigned int         const sizePlusRasterSize,
+                   unsigned int *       const nextDepthOffsetP,
+                   unsigned int *       const nextBitmapOffsetP,
+                   unsigned int *       const padBytesRequiredP) {
+    
+    if (offsetWanted) {
+        /* Offset is measured in 4-byte words (double words in
+           Intel/Microsoft terminology).  Account for header,
+           colormap, and raster size and round up 
+        */
+        unsigned int const headerSize = ((version < 3) ? 16 : 24);
+        unsigned int const colormapSize =
+            (colormap ? (2 + colormapColorCount * 4) : 0);
+        if (version < 3) {
+            unsigned int const directSize = 
+                (directColor && version < 3) ? 8 : 0; 
+            if (compression != COMP_NONE && sizePlusRasterSize > USHRT_MAX)
+                pm_error("Oversized compressed bitmap: %u bytes",
+                         sizePlusRasterSize);
+            *padBytesRequiredP = 4 - (sizePlusRasterSize + headerSize + 
+                                      directSize + colormapSize) % 4;
+            *nextDepthOffsetP = 
+                (sizePlusRasterSize + headerSize + 
+                 directSize + colormapSize + *padBytesRequiredP) / 4;
+        } else {
+            if (compression != COMP_NONE && (sizePlusRasterSize >> 31) > 1)
+                pm_error("Oversized compressed bitmap: %u bytes",
+                         sizePlusRasterSize);
+            /* Does version 3 need padding? Probably won't hurt */
+            *padBytesRequiredP = 4 - (sizePlusRasterSize + headerSize +
+                                      colormapSize) % 4;
+            *nextBitmapOffsetP = sizePlusRasterSize + headerSize + 
+                colormapSize + *padBytesRequiredP;
+        }
+    } else {
+        *padBytesRequiredP = 0;
+        *nextDepthOffsetP = 0;
+        *nextBitmapOffsetP = 0;
+    }
+}
+
+
+
+static void
+writeRasterSize(unsigned int const sizePlusRasterSize,
+                unsigned int const version,
+                FILE *       const fileP) {
+/*----------------------------------------------------------------------------
+   Write to file 'fileP' a raster size field for a Palm Bitmap version
+   'version' header, indicating 'sizePlusRasterSize' bytes.
+-----------------------------------------------------------------------------*/
+    if (version < 3) 
+        pm_writebigshort(fileP, sizePlusRasterSize);
+    else
+        pm_writebiglong(fileP, sizePlusRasterSize);
+}
+
+
+
+static void
+writeBitmap(xel **               const xels,
+            unsigned int         const cols,
+            unsigned int         const rows,
+            xelval               const maxval,
+            unsigned int         const rowbytes,
+            unsigned int         const bpp,
+            unsigned int         const newMaxval,
+            enum compressionType const compression,
+            bool                 const transparent,
+            bool                 const directColor,
+            bool                 const offsetWanted,
+            bool                 const hasColormap,
+            Colormap             const colormap,
+            unsigned int         const transindex,
+            xel                  const transcolor,
+            unsigned int         const version,
+            unsigned int         const density,
+            bool                 const withdummy) {
+    
+    unsigned int sizePlusRasterSize;
+    unsigned int nextDepthOffset;
+    unsigned int nextBitmapOffset;
+        /* Offset from the beginning of the image we write to the beginning
+           of the next one, assuming user writes another one following this
+           one.
+           nextDepthOffset is used in encodings 1, 2 and is in 4 byte words 
+           nextBitmapOffset is used in encoding 3, is in 4 bytes 
+        */
+    unsigned int padBytesRequired;
+        /* Number of bytes of padding we need to put after the image in
+           order to align properly for User to add the next image to the
+           stream.
+        */
+    struct seqBuffer * rasterBufferP;
+
+    writeCommonHeader(cols, rows, rowbytes, compression, hasColormap, 
+                      transparent, directColor, bpp, version);
+    
+    bufferRaster(xels, cols, rows, maxval, rowbytes, bpp, newMaxval,
+                 compression, directColor, colormap, &rasterBufferP);
+
+    /* rasterSize itself takes 2 or 4 bytes */
+    if (version < 3)
+        sizePlusRasterSize = 2 + bufferLength(rasterBufferP);
+    else
+        sizePlusRasterSize = 4 + bufferLength(rasterBufferP);
+    
+    computeOffsetStuff(offsetWanted, version, directColor, compression,
+                       hasColormap, hasColormap ? colormap->ncolors : 0, 
+                       sizePlusRasterSize,
+                       &nextDepthOffset, &nextBitmapOffset,
+                       &padBytesRequired);
+
+    if (version < 3)
+        writeRemainingHeaderLow(nextDepthOffset, transindex, compression, bpp);
+    else
+        writeRemainingHeaderHigh(bpp, compression, density,
+                                 maxval, transparent, transcolor,
+                                 transindex, nextBitmapOffset);
+
+    writeColormap(hasColormap, colormap, directColor, bpp, 
+                  transparent, transcolor, maxval, version);
+
+    if (compression != COMP_NONE)
+        writeRasterSize(sizePlusRasterSize, version, stdout);
+
+    writeOutBuffer(rasterBufferP, stdout);
+
+    destroyBuffer(rasterBufferP);
+
+    {
+        unsigned int i;
+        for (i = 0; i < padBytesRequired; ++i)
+            fputc(0x00, stdout);
+    }
+
+    if (withdummy)
+        writeDummy();
+}        
+
+
+
+int 
+main( int argc, char **argv ) {
+    struct cmdline_info cmdline;
+    unsigned int version;
+    FILE* ifP;
+    xel** xels;
+    xel transcolor;
+    unsigned int transindex;
+    int rows, cols;
+    unsigned int rowbytes;
+    xelval maxval;
+    int format;
+    unsigned int bpp;
+    bool directColor;
+    unsigned int newMaxval;
+    Colormap colormap;
+    
+    /* Parse default params */
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    xels = pnm_readpnm(ifP, &cols, &rows, &maxval, &format);
+    pm_close(ifP);
+
+    if (cmdline.verbose)
+        pm_message("Input is %dx%d %s, maxval %d", 
+                   cols, rows, formatName(format), maxval);
+    
+    determinePalmFormat(cols, rows, maxval, format, xels, cmdline.depth,
+                        cmdline.maxdepth, cmdline.colormap, cmdline.verbose,
+                        &bpp, &directColor, &colormap);
+
+    newMaxval = (1 << bpp) - 1;
+
+    if (cmdline.transparent) 
+        findTransparentColor(cmdline.transparent, newMaxval, directColor,
+                             maxval, colormap, &transcolor, &transindex);
+    else 
+        transindex = 0;
+
+    rowbytes = ((cols + (16 / bpp -1)) / (16 / bpp)) * 2;    
+        /* bytes per row - always a word boundary */
+
+    version = bitmapVersion(bpp, cmdline.colormap, !!cmdline.transparent, 
+                            cmdline.compression, cmdline.density);
+
+    writeBitmap(xels, cols, rows, maxval,
+                rowbytes, bpp, newMaxval, cmdline.compression,
+                !!cmdline.transparent, directColor, cmdline.offset, 
+                cmdline.colormap, colormap, transindex, transcolor,
+                version, cmdline.density, cmdline.withdummy);
+    
+    return 0;
+}
diff --git a/converter/other/pnmtopclxl.c b/converter/other/pnmtopclxl.c
new file mode 100644
index 00000000..79ee092c
--- /dev/null
+++ b/converter/other/pnmtopclxl.c
@@ -0,0 +1,1204 @@
+/*
+ * -------------------------------------------------------------
+ *
+ *  (C) 2002 Jochen Karrer, Linuxdata GbR
+ *
+ *      convert a pnm to PCL-XL image 
+ *
+ * -------------------------------------------------------------
+ */
+
+/* Engineering note: One PCL-XL printer prints an error message like
+   this when it doesn't like the PCL it sees:
+
+   PCL XL error
+      Subsystem:  IMAGE
+      Error:      IllegalAttributeValue
+      Operator:   ReadImage
+      Position:   8
+
+   "Position" is the sequence number of the PCL operator it was trying
+   to execute.
+*/
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <ctype.h>
+
+#include "pam.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+
+#include "pclxl.h"
+
+#define PAPERWIDTH(format) (xlPaperFormats[format].width)
+#define PAPERHEIGHT(format) (xlPaperFormats[format].height)
+
+
+
+typedef struct InputSource {
+    const char *         name; 
+    struct InputSource * next;
+} InputSource;
+
+
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    InputSource * sourceP;
+    int dpi;
+    enum MediaSize format;
+    unsigned int feederSpec;
+    int feeder;
+    unsigned int outtraySpec;
+    int outtray;
+    unsigned int duplexSpec;
+    enum DuplexPageMode duplex;
+    unsigned int copiesSpec;
+    int copies;
+    unsigned int center;
+    float xoffs;
+    float yoffs;
+    unsigned int colorok;
+    unsigned int verbose;
+    const char * jobsetup;  /* -jobsetup option value.  NULL if none */
+    unsigned int rendergray;
+};
+
+
+
+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 = malloc( 100*sizeof( optEntry ) );
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    char *formatOpt;
+    char *duplexOpt;
+    unsigned int dpiSpec, xoffsSpec, yoffsSpec, formatSpec, jobsetupSpec;
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "dpi",       OPT_UINT,    &cmdlineP->dpi,
+            &dpiSpec,         0);
+    OPTENT3(0, "xoffs",     OPT_FLOAT,   &cmdlineP->xoffs, 
+            &xoffsSpec,        0);
+    OPTENT3(0, "yoffs",     OPT_FLOAT,   &cmdlineP->yoffs, 
+            &yoffsSpec,        0);
+    OPTENT3(0, "format",    OPT_STRING,  &formatOpt, 
+            &formatSpec,        0);
+    OPTENT3(0, "duplex",    OPT_STRING,  &duplexOpt, 
+            &cmdlineP->duplexSpec,        0);
+    OPTENT3(0, "copies",    OPT_UINT,    &cmdlineP->copies,
+            &cmdlineP->copiesSpec,        0);
+    OPTENT3(0, "colorok",   OPT_FLAG,    NULL,                  
+            &cmdlineP->colorok, 0);
+    OPTENT3(0, "center",    OPT_FLAG,    NULL,                  
+            &cmdlineP->center, 0 );
+    OPTENT3(0, "feeder",    OPT_STRING,  &cmdlineP->feeder,
+            &cmdlineP->feederSpec,        0);
+    OPTENT3(0, "outtray",   OPT_STRING,  &cmdlineP->outtray,
+            &cmdlineP->outtraySpec,       0);
+    OPTENT3(0, "verbose",   OPT_FLAG,    NULL,                  
+            &cmdlineP->verbose, 0);
+    OPTENT3(0, "jobsetup",  OPT_STRING,  &cmdlineP->jobsetup,
+            &jobsetupSpec,      0);
+    OPTENT3(0, "rendergray", OPT_FLAG,    NULL,
+            &cmdlineP->rendergray, 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 (!dpiSpec)
+        cmdlineP->dpi = 300;
+    if (!xoffsSpec)
+        cmdlineP->xoffs = 0.0;
+    if (!yoffsSpec)
+        cmdlineP->yoffs = 0.0;
+
+    if (cmdlineP->duplexSpec) {
+        if (strncmp(duplexOpt, "vertical", strlen(duplexOpt)) == 0)
+            cmdlineP->duplex = eDuplexVerticalBinding;
+        else if (strncmp(duplexOpt, "horizontal", strlen(duplexOpt)) == 0)
+            cmdlineP->duplex = eDuplexHorizontalBinding;
+        else
+            pm_error("Invalid value '%s' for -duplex option", duplexOpt);
+    }
+
+    if (formatSpec) {
+        bool found;
+        int i;
+        for (i = 0, found=FALSE; xlPaperFormats[i].name && !found; ++i) {
+            if (STREQ(xlPaperFormats[i].name, formatOpt)) {
+                found = TRUE;
+                cmdlineP->format = xlPaperFormats[i].xl_nr;
+            }
+        }
+        if (!found) {
+            int i;
+            pm_message("Valid -format values:");
+            for (i = 0; xlPaperFormats[i].name; ++i) {
+                if (xlPaperFormats[i].width > 0)
+                    pm_message("   %s", xlPaperFormats[i].name);
+            }
+            pm_error("Invalid -format option '%s' specified.", formatOpt);
+        }
+    } else
+        cmdlineP->format = eLetterPaper;
+
+    if (!jobsetupSpec)
+        cmdlineP->jobsetup = NULL;
+
+    if (argc-1 < 1) {
+        MALLOCVAR(cmdlineP->sourceP);
+        cmdlineP->sourceP->name = "-";
+    } else {
+        int i;
+        InputSource ** nextLinkP;
+
+        nextLinkP = &cmdlineP->sourceP;
+        for (i = 1; i < argc; ++i) {
+            InputSource * sourceP;
+            MALLOCVAR(sourceP);
+            sourceP->name = argv[i];
+            *nextLinkP = sourceP;
+            nextLinkP = &sourceP->next;
+            *nextLinkP = NULL;
+        }
+    }
+}
+
+
+
+#define XY_RLE_FBUFSIZE (1024)
+typedef struct XY_rle {
+    int error;
+    unsigned char buf[128];
+    int bpos;
+    int state;  
+    unsigned char *fbuf;
+    int fbpos;
+    int fbufsize;
+    int fd;
+} XY_rle;
+
+
+static void 
+XY_RLEreset(XY_rle *rle) 
+{   
+    rle->state = eSTART;
+    rle->bpos = 0;
+    rle->fbpos=0;
+    rle->error=0;
+}
+static XY_rle * 
+XY_RLEnew(int size) {
+    XY_rle *rle;
+
+    MALLOCVAR(rle);
+    if(rle==NULL)
+        return rle;
+    if(size<1024)
+        size=1024;
+    rle->fbuf=malloc(size);
+    rle->fbufsize=size;
+    if(!rle->fbuf) {
+        free(rle);
+        return NULL;
+    }
+    return rle;
+}
+static void
+XY_RLEdelete(XY_rle *rle) {
+    free(rle->fbuf);
+    free(rle);
+}
+
+static int 
+out(XY_rle *rle,int count) {
+    if(rle->state==eRLE) {
+        rle->fbuf[rle->fbpos++]=-count+1;
+        rle->fbuf[rle->fbpos++]=rle->buf[0];
+    } else if(rle->bpos>0) {
+        rle->fbuf[rle->fbpos++]=count-1;
+        memcpy(rle->fbuf+rle->fbpos,rle->buf,count);
+        rle->fbpos+=count;
+    }
+    if(rle->fbpos+129>rle->fbufsize) {
+        rle->fbufsize*=1.2; 
+        rle->fbuf=realloc(rle->fbuf,rle->fbufsize);
+        if(rle->fbuf==NULL) {
+            rle->error=-1;
+            rle->fbpos=0;
+            return -1;
+        }
+    }
+    rle->bpos=0;
+    rle->state=eSTART;
+    return 0;
+}
+static int
+XY_RLEfinish (XY_rle *rle) {
+    out(rle,rle->bpos);
+    if(rle->error<0) 
+        return rle->error;
+    else
+        return rle->fbpos;
+}
+static  void
+rle_putbyte(XY_rle *rle,unsigned char u) 
+{
+    switch (rle->state) {
+        case eRLE:
+            if(u!=rle->buf[0]) {
+                out(rle,rle->bpos);
+            }   
+            break;
+        case eLIT:
+            if((u==rle->buf[rle->bpos-1])&&(u==rle->buf[rle->bpos-2])) {
+                out(rle,rle->bpos-2);
+                rle->buf[0]=u;
+                rle->bpos+=2;
+                rle->state=eRLE;
+            }   
+            break;
+        case eSTART:
+            if(rle->bpos==1) {
+                if(u==rle->buf[rle->bpos-1]) {
+                    rle->state=eRLE;
+                } else {
+                    rle->state=eLIT;
+                }   
+            }
+            break;
+    
+    }
+    rle->buf[rle->bpos++]=u;
+    if(rle->bpos==128) {
+        out(rle,rle->bpos);
+    }
+}
+
+
+
+static void
+XY_RLEput(XY_rle *rle,const unsigned char buf[],int count) 
+{
+    int i;
+    for(i=0;i<count;i++) {
+        rle_putbyte(rle,buf[i]);
+    }
+    
+}
+
+
+static int
+XY_Write(int fd, const void *buf,int cnt) {
+        int len=0;
+        while(len<cnt) {
+                int n = write(fd,(char*)buf+len,cnt-len);
+                if(n<=0)
+                        return n;
+                len+=n;
+        }
+        return len;
+}
+#define XY_Puts(fd,str)  XY_Write(fd,str,strlen(str))
+
+typedef struct pclGenerator {
+    enum ColorDepth colorDepth;
+    enum Colorspace colorSpace;
+    int width,height;
+    int linelen; /* bytes per line */
+    unsigned char *data;
+    void (*getnextrow)(const struct pclGenerator *, struct pam *);
+} pclGenerator;
+
+struct tPrinter { 
+    const char *name;
+    float topmargin;
+    float bottommargin;
+    float leftmargin;
+    float rightmargin;
+} xlPrinters[] = {
+    { "lj2200",0,0,0,0 }
+};
+
+
+
+static int
+out_ubyte(int fd,unsigned char data) {
+    return XY_Write(fd,&data,1);
+}
+static  int 
+XL_Operator(int fd,enum Operator const data)  {
+    return out_ubyte(fd,data);
+}
+static int
+out_uint16(int fd,unsigned short data ) {
+    unsigned char c[2];
+    c[0]=data&0xff; c[1]=data>>8;
+    return XY_Write(fd,c,2);
+}
+static int
+out_uint32(int fd,unsigned int data ) {
+    unsigned char c[4];
+    c[0] = data&0xff; c[1]=(data>>8)&0xff; c[2]=(data>>16)&0xff; c[3]=data>>24;
+    return XY_Write(fd,c,4);
+}
+static int
+out_sint16(int fd,signed short sdata ) {
+    unsigned short data=(unsigned short)sdata;    
+    unsigned char c[2];
+    c[0]=data&0xff; c[1]=data>>8;
+    return XY_Write(fd,c,2);
+}
+#if 0
+static int
+out_sint32(int fd,signed int sdata ) {
+    unsigned int data=(unsigned int)sdata;
+    unsigned char c[4];
+    c[0] = data&0xff; c[1]=(data>>8)&0xff; c[2]=(data>>16)&0xff; c[3]=data>>24;
+    return XY_Write(fd,c,4);
+}
+#endif
+
+static int
+xl_ubyte(int fd,unsigned char data) {
+    unsigned char const tag=0xc0;
+    XY_Write(fd,&tag,1);
+    return out_ubyte(fd,data);
+}
+static int
+xl_uint16(int fd,unsigned short data ) {
+    unsigned char const tag=0xc1;
+    XY_Write(fd,&tag,1);
+    return out_uint16(fd,data);
+}
+#if 0
+static int
+xl_uint32(int fd,unsigned int data ) {
+    unsigned char const c=0xc2;
+    XY_Write(fd,&c,1);
+    return out_uint32(fd,data);
+}
+static int
+xl_sint16(int fd,signed short data ) {
+    unsigned char const c=0xc3;
+    XY_Write(fd,&c,1);
+    return out_sint16(fd,data);
+}
+static int
+xl_sint32(int fd,signed int data ) {
+    unsigned char const c=0xc4;
+    XY_Write(fd,&c,1);
+    return out_sint32(fd,data);
+}
+#endif
+
+
+
+static int
+xl_ubyte_array(int                   const fd,
+               const unsigned char * const data,
+               int                   const len) {
+
+    unsigned int i;
+    unsigned char head[4];
+    
+    head[0] = 0xc8;
+    head[1] = 0xc1;
+    head[2] = len&0xff;
+    head[3] = (len>>8)&0xff;
+
+    XY_Write(fd, head, 4);
+
+    for (i = 0; i < len; ++i)
+        out_ubyte(fd, data[i]);
+
+    return 0;
+}
+
+
+
+#if 0
+static int
+xl_uint16_array(int fd,unsigned short *data,int len) {
+    int i;
+    unsigned char head[4];
+    head[0]=0xc9;head[1]=0xc1;head[2]=len&0xff;head[3]=(len>>8)&0xff;
+    XY_Write(fd,head,4);
+    for(i=0;i<len;i++) {
+        out_uint16(fd,data[i]);
+    }
+    return 0;
+}
+static int
+xl_uint32_array(int fd,unsigned int *data,int len) {
+    int i;
+    unsigned char head[4];
+    head[0]=0xca;head[1]=0xc1;head[2]=len&0xff;head[3]=(len>>8)&0xff;
+    XY_Write(fd,head,4);
+    for(i=0;i<len;i++) {
+        out_uint32(fd,data[i]);
+    }
+    return 0;
+}
+static int
+xl_sint16_array(int fd,signed short *data,int len) {
+    int i;
+    unsigned char head[4];
+    head[0]=0xcb;head[1]=0xc1;head[2]=len&0xff;head[3]=(len>>8)&0xff;
+    XY_Write(fd,head,4);
+    for(i=0;i<len;i++) {
+        out_sint16(fd,data[i]);
+    }
+    return 0;
+}
+static int
+xl_sint32_array(int fd,signed int *data,int len) {
+    int i;
+    unsigned char head[4];
+    head[0]=0xcc;head[1]=0xc1;head[2]=len&0xff;head[3]=(len>>8)&0xff;
+    XY_Write(fd,head,4);
+    for(i=0;i<len;i++) {
+        out_sint32(fd,data[i]);
+    }
+    return 0;
+}
+
+static int
+xl_ubyte_xy(int fd,unsigned char xdata,unsigned char ydata) {
+    unsigned char const tag=0xd0;
+    XY_Write(fd,&data,1);
+    out_ubyte(fd,ydata);
+    return out_ubyte(fd,xdata);
+}
+#endif
+static int
+xl_uint16_xy(int fd,unsigned short xdata,unsigned short ydata ) {
+    unsigned char const tag=0xd1;
+    XY_Write(fd,&tag,1);
+    out_uint16(fd,xdata);
+    return out_uint16(fd,ydata);
+}
+#if 0
+static int
+xl_uint32_xy(int fd,unsigned int xdata,unsigned int ydata ) {
+    unsigned char const tag=0xd2;
+    XY_Write(fd,&tag,1);
+    out_uint32(fd,xdata);
+    return out_uint32(fd,ydata);
+}
+#endif
+static int
+xl_sint16_xy(int fd,signed short xdata,signed short ydata ) {
+    unsigned char const tag=0xd3;
+    XY_Write(fd,&tag,1);
+    out_sint16(fd,xdata);
+    return out_sint16(fd,ydata);
+}
+
+#if 0
+static int
+xl_sint32_xy(int fd,signed int xdata,signed int ydata ) {
+    unsigned char const tag=0xd4;
+    XY_Write(fd,&tag,1);
+    out_sint32(fd,xdata);
+    return out_sint32(fd,ydata);
+}
+#endif
+
+static int
+xl_attr_ubyte(int fd,enum Attribute const data) {
+    unsigned char const tag=0xf8;
+    XY_Write(fd,&tag,1);
+    return out_ubyte(fd,data);
+}
+#if 0
+static int
+xl_attr_uint16(int fd,enum Attribute const data ) {
+    unsigned char const tag=0xf9;
+    XY_Write(fd,&tag,1);
+    return out_uint16(fd,data);
+}
+#endif
+static int
+xl_dataLength(int fd,unsigned int dataLength ) {
+    unsigned char const tag=0xfa;
+    XY_Write(fd,&tag,1);
+    return out_uint32(fd,dataLength);
+}
+
+#if 0
+static int
+xl_dataLengthbytes(int fd,unsigned char dataLengthBytes) {
+    unsigned char const tag=0xfb;
+    XY_Write(fd,&tag,1);
+    return out_ubyte(fd,dataLengthBytes);
+}
+#endif 
+
+
+
+static void
+copyFile(const char * const sourceFileName,
+         int          const destFd) {
+
+    FILE * sourceFileP;
+
+    sourceFileP = pm_openr(sourceFileName);
+
+    while (!feof(sourceFileP)) {
+        char buffer[1024];
+        size_t bytesRead;
+        size_t totalBytesWritten;
+
+        bytesRead = fread(buffer, 1, sizeof(buffer), sourceFileP);
+
+        if (ferror(sourceFileP))
+            pm_error("Read from file failed.  errno=%d (%s)",
+                     errno, strerror(errno));
+        
+        totalBytesWritten = 0;
+        
+        while (totalBytesWritten < bytesRead) {
+            ssize_t rc;
+
+            rc = write(destFd, buffer, bytesRead);
+
+            if (rc < 0)
+                pm_error("Write to file failed. errno=%d (%s)",
+                         errno, strerror(errno));
+            else
+                totalBytesWritten += rc;
+        }
+    }
+    pm_close(sourceFileP);
+}
+
+
+
+static void
+jobHead(int          const outFd,
+        bool         const renderGray,
+        const char * const userJobSetupFileName) {
+/*----------------------------------------------------------------------------
+   Start a PJL job.
+
+   Switch printer to PCL-XL mode.  This is called "entering a printer
+   language" in PCL terms.  In particular, we enter the PCL-XL language,
+   as opposed to e.g. Postscript.
+-----------------------------------------------------------------------------*/
+    /* Reset */
+    XY_Puts(outFd,"\033%-12345X");  
+
+    if (userJobSetupFileName)
+        copyFile(userJobSetupFileName, outFd);
+
+    if (renderGray)
+        XY_Puts(outFd,"@PJL SET RENDERMODE=GRAYSCALE\n");  
+
+    XY_Puts(outFd,"@PJL ENTER LANGUAGE=PCLXL\n");  
+    XY_Puts(outFd,") HP-PCL XL;1;1;Generated by Netpbm Pnmtopclxl\n");  
+}
+
+
+
+static void
+jobEnd(int const outFd) {
+/*----------------------------------------------------------------------------
+   End a PJL job.
+
+   Reset printer to quiescent mode.  Exit the printer language.
+-----------------------------------------------------------------------------*/
+    XY_Puts(outFd,"\033%-12345X");  
+}
+
+
+
+static void
+beginPage(int                 const outFd,
+          bool                const doDuplex,
+          enum DuplexPageMode const duplex,
+          bool                const doMediaSource,
+          int                 const mediaSource,
+          bool                const doMediaDestination,
+          int                 const mediaDestination,
+          enum MediaSize      const format) {
+/*----------------------------------------------------------------------------
+   Emit a BeginPage printer command.
+-----------------------------------------------------------------------------*/
+    if (doDuplex) {
+        xl_ubyte(outFd, duplex);  xl_attr_ubyte(outFd, aDuplexPageMode);
+    }
+
+    if (doMediaSource) {
+        /* if not included same as last time in same session is selected */
+        xl_ubyte(outFd, mediaSource);  xl_attr_ubyte(outFd, aMediaSource);
+    }
+
+    if (doMediaDestination) {
+        xl_ubyte(outFd, mediaDestination);  
+        xl_attr_ubyte(outFd, aMediaDestination);
+    }
+
+    xl_ubyte(outFd, ePortraitOrientation); xl_attr_ubyte(outFd, aOrientation);
+    xl_ubyte(outFd, format); xl_attr_ubyte(outFd, aMediaSize);
+
+    XL_Operator(outFd, oBeginPage);
+}
+
+
+
+static void
+openDataSource(int             const outFd,
+               enum DataOrg    const dataOrg,
+               enum DataSource const dataSource) {
+
+    xl_ubyte(outFd, dataOrg); xl_attr_ubyte(outFd,aDataOrg);
+    xl_ubyte(outFd, dataSource); xl_attr_ubyte(outFd,aSourceType);
+    XL_Operator(outFd,oOpenDataSource);
+}
+
+
+
+static void
+setColorSpace(int                   const outFd,
+              enum Colorspace       const colorSpace,
+              const unsigned char * const palette,
+              unsigned int          const paletteSize,
+              enum ColorDepth       const paletteDepth) {
+/*----------------------------------------------------------------------------
+   Emit printer control to set the color space.
+
+   'palette' == NULL means no palette (raster contains colors, not indexes
+   into a palette).
+
+   'paletteSize' is the number of bytes in the palette (undefined if
+   'palette' is NULL)
+
+   'paletteDepth' is the color depth of the entries in the palette.
+   e8Bit means the palette contains 8 bit values.
+
+   The palette is a "direct color" palette: A separate table for each
+   color component (i.e. one table for grayscale; three tables for
+   RGB).  Each table is indexed by a value from the raster and yields
+   a byte of color component value.
+
+   The palette has to be the right size to fit the number of color
+   components and raster color depth (bits per component in the raster).
+
+   E.g. with raster color depth of 1 bit (e1Bit) and RGB color (eRGB),
+   'paletteSize' would have to be 6 -- a table for each of R, G, and
+   B, of two elements each.
+
+   It is not clear from the documentation what the situation is when
+   paletteDepth is not e8Bit.  Is each palette entry still a byte and only
+   some of the byte gets used?  Or are there multiple entries per byte?
+-----------------------------------------------------------------------------*/
+    xl_ubyte(outFd, colorSpace); xl_attr_ubyte(outFd, aColorSpace);   
+    if (palette) {
+        xl_ubyte(outFd, paletteDepth); 
+        xl_attr_ubyte(outFd, aPaletteDepth);   
+        xl_ubyte_array(outFd, palette, paletteSize); 
+        xl_attr_ubyte(outFd, aPaletteData);
+    }
+    XL_Operator(outFd, oSetColorSpace);
+}
+
+
+
+static void
+positionCursor(int            const outFd,
+               bool           const center,
+               float          const xoffs,
+               float          const yoffs,
+               int            const imageWidth,
+               int            const imageHeight,
+               int            const dpi,
+               enum MediaSize const format) {
+/*----------------------------------------------------------------------------
+   Emit printer control to position the cursor to start the page.
+-----------------------------------------------------------------------------*/
+    float xpos, ypos;
+
+    if (center) {
+        float const width  = 1.0 * imageWidth/dpi;  
+        float const height = 1.0 * imageHeight/dpi;    
+        xpos = (PAPERWIDTH(format) - width)/2;
+        ypos = (PAPERHEIGHT(format) - height)/2;
+    } else {
+        xpos = xoffs;
+        ypos = yoffs;
+    }
+    /* cursor positioning */
+    xl_sint16_xy(outFd, xpos * dpi, ypos * dpi); xl_attr_ubyte(outFd, aPoint);
+    XL_Operator(outFd, oSetCursor);
+}
+
+
+
+static void
+convertAndWriteRleBlock(int                  const outFd,
+                        const pclGenerator * const pclGeneratorP,
+                        struct pam *         const pamP,
+                        int                  const firstLine,
+                        int                  const nlines,
+                        XY_rle *             const rle) {
+
+    unsigned char const pad[4] = {0,0,0,0};
+    unsigned int const paddedLinelen = ((pclGeneratorP->linelen+3)/4)*4;
+    int rlelen;
+    unsigned int line;
+    
+    XY_RLEreset(rle);
+
+    for (line = firstLine; line < firstLine + nlines; ++line) {
+        pclGeneratorP->getnextrow(pclGeneratorP, pamP);
+        XY_RLEput(rle, pclGeneratorP->data, pclGeneratorP->linelen);
+        XY_RLEput(rle, pad, paddedLinelen - pclGeneratorP->linelen);
+    }
+    rlelen = XY_RLEfinish(rle);
+    if (rlelen<0) 
+        pm_error("Error on Making rle");
+
+    xl_dataLength(outFd, rlelen); 
+    XY_Write(outFd, rle->fbuf, rlelen);
+}
+
+
+
+/*
+ * ------------------------------------------------------------
+ * XL_WriteImage
+ *  Write a PCL-XL image to the datastream 
+ * ------------------------------------------------------------
+ */
+static void 
+convertAndWriteImage(int                  const outFd,
+                     const pclGenerator * const pclGenP,
+                     struct pam *         const pamP) {
+
+    int blockStartLine;
+    XY_rle * rle;
+
+    xl_ubyte(outFd, eDirectPixel); xl_attr_ubyte(outFd, aColorMapping);
+    xl_ubyte(outFd, pclGenP->colorDepth); xl_attr_ubyte(outFd, aColorDepth);
+    xl_uint16(outFd, pclGenP->width); xl_attr_ubyte(outFd, aSourceWidth);  
+    xl_uint16(outFd, pclGenP->height); xl_attr_ubyte(outFd, aSourceHeight);    
+    xl_uint16_xy(outFd, pclGenP->width*1, pclGenP->height*1); 
+    xl_attr_ubyte(outFd, aDestinationSize);   
+    XL_Operator(outFd, oBeginImage);
+
+    rle = XY_RLEnew(pclGenP->linelen*20);
+    if (!rle) 
+        pm_error("Unable to allocate %d bytes for the RLE buffer",
+                 pclGenP->linelen * 20);
+
+    blockStartLine = 0;
+    while (blockStartLine < pclGenP->height) {
+        unsigned int const blockHeight =
+            MIN(20, pclGenP->height-blockStartLine);
+        xl_uint16(outFd, blockStartLine); xl_attr_ubyte(outFd, aStartLine); 
+        xl_uint16(outFd, blockHeight); xl_attr_ubyte(outFd, aBlockHeight);
+        xl_ubyte(outFd, eRLECompression); xl_attr_ubyte(outFd, aCompressMode);
+        /* In modern PCL-XL, we could use a PadBytesMultiple attribute
+           here to avoid having to pad the data to a multiple of 4
+           bytes.  But PCL-XL 1.1 didn't have PadBytesMultiple.
+           xl_ubyte(outFd, 1); xl_attr_ubyte(outFd, aPadBytesMultiple); 
+        */
+        XL_Operator(outFd, oReadImage);
+        convertAndWriteRleBlock(outFd, pclGenP, pamP,
+                                blockStartLine, blockHeight, rle);
+        blockStartLine += blockHeight;
+    }
+    XY_RLEdelete(rle);
+    XL_Operator(outFd, oEndImage);
+}
+
+
+
+static void
+endPage(int          const outFd,
+        bool         const doCopies,
+        unsigned int const copies) {
+/*----------------------------------------------------------------------------
+   Emit an EndPage printer command.
+-----------------------------------------------------------------------------*/
+    if (doCopies) {
+        /* wrong in example in PCL-XL manual. Type is uint16 ! */
+        xl_uint16(outFd, copies); xl_attr_ubyte(outFd, aPageCopies);
+    }
+    XL_Operator(outFd, oEndPage);
+}
+
+
+
+static void
+convertAndPrintPage(int                  const outFd,
+                    const pclGenerator * const pclGeneratorP,
+                    struct pam *         const pamP,
+                    enum MediaSize       const format,
+                    int                  const dpi,
+                    bool                 const center,
+                    float                const xoffs,
+                    float                const yoffs,
+                    bool                 const doDuplex,
+                    enum DuplexPageMode  const duplex,
+                    bool                 const doCopies,
+                    unsigned int         const copies,
+                    bool                 const doMediaSource,
+                    int                  const mediaSource,
+                    bool                 const doMediaDestination,
+                    int                  const mediaDestination) {
+
+    beginPage(outFd, doDuplex, duplex, doMediaSource, mediaSource,
+              doMediaDestination, mediaDestination, format);
+
+    /* Before Netpbm 10.27 (March 2005), we always set up a two-byte 8
+       bit deep palette: {0, 255}.  I don't know why, because this
+       works only for e1Bit color depth an eGray color space, and in
+       that case does the same thing as having no palette at all.  But
+       in other cases, it doesn't work.  E.g. with eRGB, e8Bit, we got
+       an IllegalArraySize error from the printer on the SetColorSpace
+       command.
+
+       So we don't use a palette at all now.  
+    */
+    setColorSpace(outFd, pclGeneratorP->colorSpace, NULL, 0, 0);
+
+    positionCursor(outFd, center, xoffs, yoffs, 
+                   pclGeneratorP->width, pclGeneratorP->height, dpi, format);
+
+    convertAndWriteImage(outFd, pclGeneratorP, pamP);
+
+    endPage(outFd, doCopies, copies);
+}
+
+
+
+static void
+beginSession(int              const outFd,
+             unsigned int     const xdpi,
+             unsigned int     const ydpi,
+             enum Measure     const measure,
+             bool             const noReporting,
+             enum ErrorReport const errorReport) {
+
+    xl_uint16_xy(outFd, xdpi, ydpi); xl_attr_ubyte(outFd, aUnitsPerMeasure); 
+    xl_ubyte(outFd, measure);  xl_attr_ubyte(outFd, aMeasure);
+    /* xl_ubyte(outFd,eNoReporting); xl_attr_ubyte(outFd,aErrorReport); */
+    xl_ubyte(outFd,errorReport); xl_attr_ubyte(outFd,aErrorReport);
+    XL_Operator(outFd,oBeginSession);
+}
+
+
+             
+static void 
+endSession(int outFd) {
+    XL_Operator(outFd,oEndSession);
+}
+
+
+
+static void
+pnmToPcllinePackbits(const pclGenerator * const pclGeneratorP,
+                     struct pam *         const pamP) {
+
+    tuple * tuplerow;
+    unsigned int pcl_cursor;
+    unsigned char accum;
+    unsigned char bitmask;
+    unsigned int col;
+        
+    tuplerow = pnm_allocpamrow(pamP);
+
+    pnm_readpamrow(pamP, tuplerow);
+
+    pcl_cursor = 0; bitmask = 0x80; accum = 0x00;
+    for (col = 0; col < pamP->width; ++col) {
+        if (tuplerow[col][0] == PAM_PBM_WHITE)
+            accum |= bitmask;
+        bitmask >>= 1;
+        if (bitmask == 0) {
+            pclGeneratorP->data[pcl_cursor++] = accum;
+            bitmask = 0x80; accum = 0x0;
+        } 
+    }
+    if (bitmask != 0x80)
+        pclGeneratorP->data[pcl_cursor++] = accum;
+
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+static void
+createPclGeneratorPackbits(struct pam *    const pamP,
+                           pclGenerator ** const pclGeneratorPP) {
+
+    /* Samples are black or white and packed 8 to a byte */
+
+    pclGenerator * pclGeneratorP;
+
+    MALLOCVAR_NOFAIL(pclGeneratorP);
+
+    pclGeneratorP->colorDepth = e1Bit;
+    pclGeneratorP->colorSpace = eGray;
+    pclGeneratorP->linelen = (pamP->width+7)/8;
+    pclGeneratorP->height = pamP->height;
+    pclGeneratorP->width = (pamP->width);
+
+    pclGeneratorP->data = malloc(pclGeneratorP->linelen);
+    
+    if (pclGeneratorP->data == NULL)
+        pm_error("Unable to allocate row buffer.");
+
+    pclGeneratorP->getnextrow = pnmToPcllinePackbits;
+
+    *pclGeneratorPP = pclGeneratorP;
+}
+
+
+
+static void
+pnmToPcllineWholebytes(const pclGenerator * const pclGeneratorP,
+                       struct pam *         const pamP) {
+
+    tuple * tuplerow;
+    unsigned int pcl_cursor;
+    unsigned int col;
+
+    tuplerow = pnm_allocpamrow(pamP);
+
+    pnm_readpamrow(pamP, tuplerow);
+
+    pcl_cursor = 0; /* initial value */
+    
+    for (col = 0; col < pamP->width; ++col) {
+        unsigned int plane;
+        for (plane = 0; plane < pamP->depth; ++plane) {
+            pclGeneratorP->data[pcl_cursor++] = 
+                pnm_scalesample(tuplerow[col][plane], pamP->maxval, 255);
+        }
+    }
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+static void
+createPclGeneratorWholebytes(struct pam *    const pamP,
+                             pclGenerator ** const pclGenPP) {
+    /* One sample per byte */
+
+    pclGenerator * pclGenP;
+
+    MALLOCVAR_NOFAIL(pclGenP);
+    
+    if (pamP->depth <  3)
+        pclGenP->colorSpace = eGray;
+    else
+        pclGenP->colorSpace = eRGB;
+
+    pclGenP->colorDepth = e8Bit;
+    pclGenP->height     = pamP->height;
+    pclGenP->width      = pamP->width;
+    pclGenP->linelen    = pamP->width * pamP->depth;
+
+    if (UINT_MAX / pamP->width < pamP->depth)
+        pm_error("Image to big to process");
+    else
+        pclGenP->data = malloc(pamP->width * pamP->depth);
+
+    if (pclGenP->data == NULL)
+        pm_error("Unable to allocate row buffer.");
+    
+    pclGenP->getnextrow = pnmToPcllineWholebytes;
+
+    *pclGenPP = pclGenP;
+}
+
+
+
+static void
+destroyPclGenerator(pclGenerator * const pclGenP) {
+
+    free(pclGenP->data);
+
+    free(pclGenP);
+}
+
+
+
+static void 
+createPclGenerator(struct pam *        const pamP,
+                   pclGenerator **     const pclGeneratorPP,
+                   bool                const colorok) {
+
+    if (pamP->depth > 1 && !colorok)
+        pm_message("WARNING: generating a color print stream because the "
+                   "input image is PPM.  "
+                   "To generate a black and white print stream, run the input "
+                   "through Ppmtopgm.  To suppress this warning, use the "
+                   "-colorok option.");
+
+    if (pamP->depth == 1 && pamP->maxval == 1) 
+        createPclGeneratorPackbits(pamP, pclGeneratorPP);
+    else 
+        createPclGeneratorWholebytes(pamP, pclGeneratorPP);
+}
+
+
+
+
+static void
+printPages(int                 const outFd,
+           InputSource *       const firstSourceP,
+           enum MediaSize      const format,
+           int                 const dpi,
+           bool                const center,
+           float               const xoffs,
+           float               const yoffs,
+           bool                const doDuplex,
+           enum DuplexPageMode const duplex,
+           bool                const doCopies,
+           unsigned int        const copies,
+           bool                const doMediaSource,
+           int                 const mediaSource,
+           bool                const doMediaDestination,
+           int                 const mediaDestination,
+           bool                const colorok) {
+/*----------------------------------------------------------------------------
+  Loop over all input files, and each file, all images.
+-----------------------------------------------------------------------------*/
+    InputSource * sourceP;
+    unsigned int sourceNum;
+
+    sourceP = firstSourceP;    
+
+    openDataSource(outFd, eBinaryLowByteFirst, eDefaultSource);
+
+    sourceNum = 0;   /* initial value */
+
+    while (sourceP) {
+        FILE * in_file;
+        struct pam pam;
+        bool eof;
+        unsigned int pageNum;
+
+        in_file = pm_openr(sourceP->name);
+
+        ++sourceNum;
+
+        pageNum = 0;  /* initial value */
+
+        eof = FALSE;
+        while(!eof) {
+            pnm_nextimage(in_file, &eof);
+            if (!eof) {
+                pclGenerator * pclGeneratorP;
+
+                ++pageNum;
+                pm_message("Processing File %u, Page %u", sourceNum, pageNum);
+
+                pnm_readpaminit(in_file, &pam, PAM_STRUCT_SIZE(tuple_type));
+                
+                createPclGenerator(&pam, &pclGeneratorP, colorok);
+                
+                convertAndPrintPage(
+                    outFd, pclGeneratorP, &pam,
+                    format, dpi, center, xoffs, yoffs, doDuplex, duplex,
+                    doCopies, copies, doMediaSource, mediaSource,
+                    doMediaDestination, mediaDestination);
+
+                destroyPclGenerator(pclGeneratorP);
+            }
+        }
+        pm_close(in_file);
+        sourceP = sourceP->next; 
+    }
+    XL_Operator(outFd, oCloseDataSource);
+}
+
+
+
+static void
+freeSource(InputSource * const firstSourceP) {
+    
+    InputSource * sourceP;
+
+    sourceP = firstSourceP;
+    while(sourceP) {
+        InputSource * const nextP = sourceP->next;
+        free(sourceP);
+        sourceP = nextP;
+    }
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    int const outFd = STDOUT_FILENO;
+
+    struct cmdlineInfo cmdline;
+    
+    /* In case you're wondering why we do direct file descriptor I/O
+       instead of stream (FILE *), it's because Jochen originally 
+       wrote this code for an embedded system with diet-libc.  Without
+       the stream library, the statically linked binary was only about
+       5K big.
+    */
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    jobHead(outFd, cmdline.rendergray, cmdline.jobsetup);
+
+    beginSession(outFd, cmdline.dpi, cmdline.dpi, eInch, 
+                 FALSE, eBackChAndErrPage);
+
+    printPages(outFd,cmdline.sourceP,
+               cmdline.format, cmdline.dpi, cmdline.center,
+               cmdline.xoffs, cmdline.yoffs,
+               cmdline.duplexSpec, cmdline.duplex,
+               cmdline.copiesSpec, cmdline.copies,
+               cmdline.feederSpec, cmdline.feeder,
+               cmdline.outtraySpec, cmdline.outtray,
+               cmdline.colorok
+        );
+    endSession(outFd);
+
+    jobEnd(outFd);
+
+    freeSource(cmdline.sourceP);
+
+    return 0;
+}
diff --git a/converter/other/pnmtoplainpnm b/converter/other/pnmtoplainpnm
new file mode 100755
index 00000000..87c58597
--- /dev/null
+++ b/converter/other/pnmtoplainpnm
@@ -0,0 +1,3 @@
+#! /bin/sh
+
+pnmtopnm -plain $@
diff --git a/converter/other/pnmtopng.README b/converter/other/pnmtopng.README
new file mode 100644
index 00000000..bfa524dc
--- /dev/null
+++ b/converter/other/pnmtopng.README
@@ -0,0 +1,101 @@
+Pnmtopng and Pngtopnm are based on programs of the same name in the 
+Pnmtopng package owned by Alexander Lehmann and Willem Van Schaik,
+available at http://www.libpng.org/pub/png/src on 2001.07.14.
+
+I added it to and adapted it to Netpbm on 2000.03.02 to make it more
+easily available to people.  I applied a patch on 2000.06.03 to bring
+it up the 2.37.4 release of that package.  I updated it again on
+2001.07.14 to bring it up to Release 2.37.5.  There is no process in
+place to bring improvements to the base package into the Netpbm
+version, but there hasn't been a lot of update activity anyway.
+
+Attached below is the file README from Release 2.37.5 of the base
+package.
+
+Here are the differences between the base and the Netpbm version:
+
+  I added an "unsigned" to make formal and actual arguments to png_sig_cmp()
+  match and quiet a compiler warning.
+
+  I fixed an "include" statement so the dependencies work out right.
+
+  I removed the BIGGRAYS stuff, which became obsolete in Netpbm 9.0
+
+  I replaced a PPM_MAXVAL with PPM_OVERALLMAXAL to handle the new 16 bits
+  formats.
+
+  macro VERSION is defined directly in pngtopnm.c and pnmtopng.c instead
+  of being included via file version.h.
+
+  Pnmtopng, since June 2001, reads one row at a time instead of holding 
+  the entire image in memory.  That makes it work with large bitmaps
+  where it would otherwise run out of memory.  It also works faster with
+  bitmaps since a bit takes up only a bit of memory in a cached input 
+  file, but 96 bits of memory after reading it into a Netpbm data 
+  structure.
+
+  The base Pnmtopng ignores -transparent if it specifies a color that
+  isn't in the image.  Netpbm's Pnmtopng selects a nearby color that _is_
+  in the image, which is what base Pnmtopng did before October 2000.
+  Netpbm's Pnmtopng lets you put an '=' sign before the color to specify
+  that you don't want a nearby color to be chosen, i.e. you want the
+  base Pnmtopng function.  This is consistent with Pnmtogif.
+
+There were some other changes necessary before Netpbm 9.0, but the change
+of the xelval type from 1 byte to 4 made them unnecessary.
+
+
+** PNMTOPNG / PNGTOPNM
+** version 2.37.5 - 24 October 2000
+
+[This is a semi-official bug-fix and enhancement release; I sort of took over
+ maintenance of this package while Willem was on an extended bike trip, and
+ for now I'm continuing with periodic, small updates.  Version 2.37 (March
+ 1998) was never publicly released, partly because Willem had hoped to quiet
+ gcc's "<var> might be clobbered by `longjmp'" warnings.  Those are fixed in
+ 2.37.2; under Solaris, they resulted in stack corruption even when there was
+ no error in the image files or libraries.  Version 2.37.3 fixes a minor bug
+ w.r.t. error exits and generally does cleaner error exits (close files, etc.)
+ Version 2.37.4 fixes a bug that caused 16-shade grayscale images to be written
+ as 8-bit grayscale instead of (smaller) 4-bit colormapped images (bug report,
+ analysis and fix by Rafal Rzeczkowski), and it supports the new/upcoming
+ pbmplus release.  Version 2.37.5 fixes a bug in -transparent handling (pnmtopng
+ no longer chooses an approximate color if the specified one isn't present) and
+ quiets a gcc warning in the non-16-bit version.
+ --Greg Roelofs]
+
+The utilities pnmtopng and pngtopnm are based on other pbm tools and require
+the libraries included in the pbmplus/netpbm package. Also required are the
+png library and the zlib compression library.
+
+These can be found at:
+	ftp://swrinde.nde.swri.edu/pub/png/src/libpng-*
+	ftp://swrinde.nde.swri.edu/pub/png/src/zlib-*
+	ftp://ftp.x.org/contrib/utilities/netpbm-1mar1994*
+or see
+	http://www.libpng.org/pub/png/apps/pnmtopng.html
+	http://netpbm.sourceforge.net/
+	http://www.acme.com/software/pbmplus/		[update coming soon?]
+
+To compile and install a makefile is provided. Do check the directories
+where you have put the required libraries. Then either accommodate the 
+makefile or make links from generic names (e.g., zlib) to version-specific
+directories (e.g., zlib-1.1.3), which is the recommended way.
+
+For testing purposes, have a look at the test-set PngSuite.tar.gz, which
+contains a small test-image for every PNG color type and for most PNG chunk
+types. It can be found at:
+	http://www.schaik.com/pngsuite/pngsuite.html
+	ftp://swrinde.nde.swri.edu/pub/png/images/suite/
+
+Other web pages with PNG images are at:
+	http://www.libpng.org/pub/png/png-textures.html
+	http://www.libpng.org/pub/png/pngs-img.html
+	http://www.libpng.org/pub/png/pngpic2.html
+	http://www.libpng.org/pub/png/colorcube/
+	http://www.libpng.org/pub/png/pngmisc.html#images
+
+------
+Alexander Lehmann <lehmann@usa.net>
+Willem van Schaik <willem@schaik.com>
+Greg Roelofs <newt@pobox.com>
diff --git a/converter/other/pnmtopng.c b/converter/other/pnmtopng.c
new file mode 100644
index 00000000..9287d0ee
--- /dev/null
+++ b/converter/other/pnmtopng.c
@@ -0,0 +1,2745 @@
+/*
+** pnmtopng.c -
+** read a portable anymap and produce a Portable Network Graphics file
+**
+** derived from pnmtorast.c (c) 1990,1991 by Jef Poskanzer and some
+** parts derived from ppmtogif.c by Marcel Wijkstra <wijkstra@fwi.uva.nl>
+**
+** Copyright (C) 1995-1998 by Alexander Lehmann <alex@hal.rhein-main.de>
+**                        and Willem van Schaik <willem@schaik.com>
+** Copyright (C) 1999,2001 by Greg Roelofs <newt@pobox.com>
+**
+** 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 Netpbm version of Pnmtopng was derived from the independently
+   distributed program of the same name, Version 2.37.6 (21 July 2001).
+*/
+
+/* A performance note: This program reads one row at a time because
+   the whole image won't fit in memory always.  When you realize that
+   in a Netpbm xel array a one bit pixel can take 96 bits of memory,
+   it's easy to see that an ordinary fax could deplete your virtual
+   memory and even if it didn't, it might deplete your real memory and
+   iterating through the array would cause thrashing.  This program
+   iterates through the image multiple times.  
+
+   So instead, we read the image into memory one row at a time, into a
+   single row buffer.  We use Netpbm's pm_openr_seekable() facility to
+   access the file.  That facility copies the file into a temporary
+   file if it isn't seekable, so we always end up with a file that we
+   can rewind and reread multiple times.
+
+   This shouldn't cause I/O delays because the entire image ought to fit
+   in the system's I/O cache (remember that the file is a lot smaller than
+   the xel array you'd get by doing a pnm_readpnm() of it).
+
+   However, it does introduce some delay because of all the system calls 
+   required to read the file.  A future enhancement might read the entire
+   file into an xel array in some cases, and read one row at a time in 
+   others, depending on the needs of the particular use.
+
+   We do still read the entire alpha mask (if there is one) into a
+   'gray' array, rather than access it one row at a time.  
+
+   Before May 2001, we did in fact read the whole image into an xel array,
+   and we got complaints.  Before April 2000, it wasn't as big a problem
+   because xels were only 24 bits.  Now they're 96.
+*/
+   
+#define GRR_GRAY_PALETTE_FIX
+
+#ifndef PNMTOPNG_WARNING_LEVEL
+#  define PNMTOPNG_WARNING_LEVEL 0   /* use 0 for backward compatibility, */
+#endif                               /*  2 for warnings (1 == error) */
+
+#include <assert.h>
+#include <string.h> /* strcat() */
+#include <limits.h>
+#include <png.h>    /* includes zlib.h and setjmp.h */
+#include "pnm.h"
+#include "pngtxt.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "version.h"
+
+struct zlibCompression {
+    /* These are parameters that describe a form of zlib compression.
+       Values have the same meaning as the similarly named arguments to
+       zlib's deflateInit2().  See zlib.h.
+    */
+    unsigned int levelSpec;
+    unsigned int level;
+    unsigned int memLevelSpec;
+    unsigned int mem_level;
+    unsigned int strategySpec;
+    unsigned int strategy;
+    unsigned int windowBitsSpec;
+    unsigned int window_bits;
+    unsigned int methodSpec;
+    unsigned int method;
+    unsigned int bufferSizeSpec;
+    unsigned int buffer_size;
+};
+
+struct chroma {
+    float wx;
+    float wy;
+    float rx;
+    float ry;
+    float gx;
+    float gy;
+    float bx;
+    float by;
+};
+
+struct phys {
+    int x;
+    int y;
+    int unit;
+};
+
+typedef struct cahitem {
+    xel color;
+    gray alpha;
+    int value;
+    struct cahitem * next;
+} cahitem;
+
+typedef cahitem ** coloralphahash_table;
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *  inputFilename;  /* '-' if stdin */
+    const char *  alpha;
+    unsigned int  verbose;
+    unsigned int  downscale;
+    unsigned int  interlace;
+    const char *  transparent;  /* NULL if none */
+    const char *  background;   /* NULL if none */
+    unsigned int  gammaSpec;
+    float         gamma;        /* Meaningless if !gammaSpec */
+    unsigned int  hist;
+    unsigned int  rgbSpec;
+    struct chroma rgb;          /* Meaningless if !rgbSpec */
+    unsigned int  sizeSpec;
+    struct phys   size;         /* Meaningless if !sizeSpec */
+    const char *  text;         /* NULL if none */
+    const char *  ztxt;         /* NULL if none */
+    unsigned int  modtimeSpec;
+    time_t        modtime;      /* Meaningless if !modtimeSpec */
+    const char *  palette;      /* NULL if none */
+    int           filterSet;
+    unsigned int  force;
+    unsigned int  libversion;
+    unsigned int  compressionSpec;
+    struct zlibCompression zlibCompression;
+};
+
+
+
+typedef struct _jmpbuf_wrapper {
+  jmp_buf jmpbuf;
+} jmpbuf_wrapper;
+
+#ifndef TRUE
+#  define TRUE 1
+#endif
+#ifndef FALSE
+#  define FALSE 0
+#endif
+#ifndef NONE
+#  define NONE 0
+#endif
+#define MAXCOLORS 256
+#define MAXPALETTEENTRIES 256
+
+/* PALETTEMAXVAL is the maxval used in a PNG palette */
+#define PALETTEMAXVAL 255
+
+#define PALETTEOPAQUE 255
+#define PALETTETRANSPARENT 0
+
+static bool verbose;
+
+static jmpbuf_wrapper pnmtopng_jmpbuf_struct;
+static int errorlevel;
+
+
+
+static void
+parseSizeOpt(const char *  const sizeOpt,
+             struct phys * const sizeP) {
+
+    int count;
+    
+    count = sscanf(sizeOpt, "%d %d %d", &sizeP->x, &sizeP->y, &sizeP->unit);
+
+    if (count != 3)
+        pm_error("Invalid syntax for the -size option value '%s'.  "
+                 "Should be 3 integers: x, y, and unit code", sizeOpt);
+}
+
+
+
+static void
+parseRgbOpt(const char *    const rgbOpt,
+            struct chroma * const rgbP) {
+
+    int count;
+    
+    count = sscanf(rgbOpt, "%f %f %f %f %f %f %f %f",
+                   &rgbP->wx, &rgbP->wy,
+                   &rgbP->rx, &rgbP->ry,
+                   &rgbP->gx, &rgbP->gy,
+                   &rgbP->bx, &rgbP->by);
+
+    if (count != 6)
+        pm_error("Invalid syntax for the -rgb option value '%s'.  "
+                 "Should be 6 floating point number: "
+                 "x and y for each of white, red, green, and blue",
+                 rgbOpt);
+}
+
+
+
+static void
+parseModtimeOpt(const char * const modtimeOpt,
+                time_t *     const modtimeP) {
+
+    struct tm brokenTime;
+    int year;
+    int month;
+    int count;
+
+    count = sscanf(modtimeOpt, "%d-%d-%d %d:%d:%d",
+                   &year,
+                   &month,
+                   &brokenTime.tm_mday,
+                   &brokenTime.tm_hour,
+                   &brokenTime.tm_min,
+                   &brokenTime.tm_sec);
+
+    if (count != 6)
+        pm_error("Invalid value for -modtime '%s'.   It should have "
+                 "the form [yy]yy-mm-dd hh:mm:ss.", modtimeOpt);
+    
+    if (year < 0)
+        pm_error("Year is negative in -modtime value '%s'", modtimeOpt);
+    if (year > 9999)
+        pm_error("Year is more than 4 digits in -modtime value '%s'",
+                 modtimeOpt);
+    if (month < 0)
+        pm_error("Month is negative in -modtime value '%s'", modtimeOpt);
+    if (month > 12)
+        pm_error("Month is >12 in -modtime value '%s'", modtimeOpt);
+    if (brokenTime.tm_mday < 0)
+        pm_error("Day of month is negative in -modtime value '%s'",
+                 modtimeOpt);
+    if (brokenTime.tm_mday > 31)
+        pm_error("Day of month is >31 in -modtime value '%s'", modtimeOpt);
+    if (brokenTime.tm_hour < 0)
+        pm_error("Hour is negative in -modtime value '%s'", modtimeOpt);
+    if (brokenTime.tm_hour > 23)
+        pm_error("Hour is >23 in -modtime value '%s'", modtimeOpt);
+    if (brokenTime.tm_min < 0)
+        pm_error("Minute is negative in -modtime value '%s'", modtimeOpt);
+    if (brokenTime.tm_min > 59)
+        pm_error("Minute is >59 in -modtime value '%s'", modtimeOpt);
+    if (brokenTime.tm_sec < 0)
+        pm_error("Second is negative in -modtime value '%s'", modtimeOpt);
+    if (brokenTime.tm_sec > 59)
+        pm_error("Second is >59 in -modtime value '%s'", modtimeOpt);
+
+    brokenTime.tm_mon = month - 1;
+    if (year >= 1900)
+        brokenTime.tm_year = year - 1900;
+    else
+        brokenTime.tm_year = year;
+
+    /* Note that mktime() considers brokeTime to be in local time.
+       This is what we want, since we got it from a user.  User should
+       set his local time zone to UTC if he wants absolute time.
+    */
+    *modtimeP = mktime(&brokenTime);
+}
+
+
+
+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 alphaSpec, transparentSpec, backgroundSpec;
+    unsigned int textSpec, ztxtSpec, paletteSpec;
+    unsigned int filterSpec;
+
+    unsigned int nofilter, sub, up, avg, paeth, filter;
+    unsigned int chroma, phys, time;
+    const char * size;
+    const char * rgb;
+    const char * modtime;
+    const char * compMethod;
+    const char * compStrategy;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "alpha",            OPT_STRING,    &cmdlineP->alpha,
+            &alphaSpec,            0);
+    OPTENT3(0, "transparent",      OPT_STRING,    &cmdlineP->transparent,
+            &transparentSpec,      0);
+    OPTENT3(0, "background",       OPT_STRING,    &cmdlineP->background,
+            &backgroundSpec,       0);
+    OPTENT3(0, "rgb",              OPT_STRING,    &rgb,
+            &cmdlineP->rgbSpec,    0);
+    OPTENT3(0, "size",             OPT_STRING,    &size,
+            &cmdlineP->sizeSpec,   0);
+    OPTENT3(0, "text",             OPT_STRING,    &cmdlineP->text,
+            &textSpec,             0);
+    OPTENT3(0, "ztxt",             OPT_STRING,    &cmdlineP->ztxt,
+            &ztxtSpec,             0);
+    OPTENT3(0, "modtime",          OPT_STRING,    &modtime,
+            &cmdlineP->modtimeSpec,0);
+    OPTENT3(0, "palette",          OPT_STRING,    &cmdlineP->palette,
+            &paletteSpec,          0);
+    OPTENT3(0, "compression",      OPT_UINT,
+            &cmdlineP->zlibCompression.level,
+            &cmdlineP->zlibCompression.levelSpec,            0);
+    OPTENT3(0, "comp_mem_level",   OPT_UINT,
+            &cmdlineP->zlibCompression.mem_level,
+            &cmdlineP->zlibCompression.memLevelSpec,         0);
+    OPTENT3(0, "comp_strategy",    OPT_STRING,    &compStrategy,
+            &cmdlineP->zlibCompression.strategySpec,         0);
+    OPTENT3(0, "comp_window_bits", OPT_UINT,
+            &cmdlineP->zlibCompression.window_bits,
+            &cmdlineP->zlibCompression.windowBitsSpec,       0);
+    OPTENT3(0, "comp_method",      OPT_STRING,    &compMethod,
+            &cmdlineP->zlibCompression.methodSpec,           0);
+    OPTENT3(0, "comp_buffer_size", OPT_UINT,
+            &cmdlineP->zlibCompression.buffer_size,
+            &cmdlineP->zlibCompression.bufferSizeSpec,       0);
+    OPTENT3(0, "gamma",            OPT_FLOAT,     &cmdlineP->gamma,
+            &cmdlineP->gammaSpec,  0);
+    OPTENT3(0, "hist",             OPT_FLAG,      NULL,
+            &cmdlineP->hist,       0);
+    OPTENT3(0, "downscale",        OPT_FLAG,      NULL,
+            &cmdlineP->downscale,  0);
+    OPTENT3(0, "interlace",        OPT_FLAG,      NULL,
+            &cmdlineP->interlace,  0);
+    OPTENT3(0, "force",            OPT_FLAG,      NULL,
+            &cmdlineP->force,      0);
+    OPTENT3(0, "libversion",       OPT_FLAG,      NULL,
+            &cmdlineP->libversion, 0);
+    OPTENT3(0, "verbose",          OPT_FLAG,      NULL,
+            &cmdlineP->verbose,    0);
+    OPTENT3(0, "nofilter",         OPT_FLAG,      NULL,
+            &nofilter,             0);
+    OPTENT3(0, "sub",              OPT_FLAG,      NULL,
+            &sub,                  0);
+    OPTENT3(0, "up",               OPT_FLAG,      NULL,
+            &up,                   0);
+    OPTENT3(0, "avg",              OPT_FLAG,      NULL,
+            &avg,                  0);
+    OPTENT3(0, "paeth",            OPT_FLAG,      NULL,
+            &paeth,                0);
+    OPTENT3(0, "filter",           OPT_INT,       &filter,
+            &filterSpec,           0);
+    OPTENT3(0, "verbose",          OPT_FLAG,      NULL,
+            &cmdlineP->verbose,    0);
+    OPTENT3(0, "chroma",           OPT_FLAG,      NULL,
+            &chroma,               0);
+    OPTENT3(0, "phys",             OPT_FLAG,      NULL,
+            &phys,                 0);
+    OPTENT3(0, "time",             OPT_FLAG,      NULL,
+            &time,                 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 (chroma)
+        pm_error("The -chroma option no longer exists.  Use -rgb instead.");
+    if (phys)
+        pm_error("The -phys option no longer exists.  Use -size instead.");
+    if (time)
+        pm_error("The -time option no longer exists.  Use -modtime instead.");
+
+    if (alphaSpec + transparentSpec > 1)
+        pm_error("You may not specify both -alpha and -transparent");
+    if (!alphaSpec)
+        cmdlineP->alpha = NULL;
+    if (!transparentSpec)
+        cmdlineP->transparent = NULL;
+    if (!backgroundSpec)
+        cmdlineP->background = NULL;
+    if (!textSpec)
+        cmdlineP->text = NULL;
+    if (!ztxtSpec)
+        cmdlineP->ztxt = NULL;
+    if (!paletteSpec)
+        cmdlineP->palette = NULL;
+    
+    if (filterSpec + nofilter + sub + up + avg + paeth > 1)
+        pm_error("You may specify at most one of "
+                 "-nofilter, -sub, -up, -avg, -paeth, and -filter");
+    
+    if (filterSpec) {
+        if (filter < 0 || filter > 4)
+            pm_error("-filter is obsolete.  Use -nofilter, -sub, -up, -avg, "
+                     "and -paeth options instead.");
+        else
+            switch (filter) {
+            case 0: cmdlineP->filterSet = PNG_FILTER_NONE;  break;
+            case 1: cmdlineP->filterSet = PNG_FILTER_SUB;   break;
+            case 2: cmdlineP->filterSet = PNG_FILTER_UP;    break;
+            case 3: cmdlineP->filterSet = PNG_FILTER_AVG;   break;
+            case 4: cmdlineP->filterSet = PNG_FILTER_PAETH; break;
+            }
+    } else {
+        if (nofilter)
+            cmdlineP->filterSet = PNG_FILTER_NONE;
+        else if (sub)
+            cmdlineP->filterSet = PNG_FILTER_SUB;
+        else if (up)
+            cmdlineP->filterSet = PNG_FILTER_UP;
+        else if (avg)
+            cmdlineP->filterSet = PNG_FILTER_AVG;
+        else if (paeth)
+            cmdlineP->filterSet = PNG_FILTER_PAETH;
+        else
+            cmdlineP->filterSet = PNG_FILTER_NONE;
+    }
+    
+    if (cmdlineP->sizeSpec)
+        parseSizeOpt(size, &cmdlineP->size);
+
+    if (cmdlineP->rgbSpec)
+        parseRgbOpt(rgb, &cmdlineP->rgb);
+    
+    if (cmdlineP->modtimeSpec)
+        parseModtimeOpt(modtime, &cmdlineP->modtime);
+
+    if (cmdlineP->zlibCompression.levelSpec &&
+        cmdlineP->zlibCompression.level > 9)
+        pm_error("-compression value must be from 0 (no compression) "
+                 "to 9 (maximum compression).  You specified %u",
+                 cmdlineP->zlibCompression.level);
+
+    if (cmdlineP->zlibCompression.memLevelSpec) {
+        if (cmdlineP->zlibCompression.mem_level  < 1 ||
+            cmdlineP->zlibCompression.mem_level > 9)
+        pm_error("-comp_mem_level value must be from 1 (minimum memory usage) "
+                 "to 9 (maximum memory usage).  You specified %u",
+                 cmdlineP->zlibCompression.mem_level);
+    }
+
+    if (cmdlineP->zlibCompression.methodSpec) {
+        if (STREQ(compMethod, "deflated"))
+            cmdlineP->zlibCompression.method = Z_DEFLATED;
+        else
+            pm_error("The only valid value for -method is 'deflated'.  "
+                     "You specified '%s'", compMethod);
+    }
+
+    if (cmdlineP->zlibCompression.strategySpec) {
+        if (STREQ(compStrategy, "huffman_only"))
+            cmdlineP->zlibCompression.strategy = Z_HUFFMAN_ONLY;
+        else if (STREQ(compStrategy, "filtered"))
+            cmdlineP->zlibCompression.strategy = Z_FILTERED;
+        else
+            pm_error("Valid values for -strategy are 'huffman_only' and "
+                     "filtered.  You specified '%s'", compStrategy);
+    }
+
+
+    if (argc-1 < 1)
+        cmdlineP->inputFilename = "-";
+    else if (argc-1 == 1)
+        cmdlineP->inputFilename = argv[1];
+    else
+        pm_error("Program takes at most one argument:  input file name");
+}
+
+
+
+static png_color_16
+xelToPngColor_16(xel const input, 
+                 xelval const maxval, 
+                 xelval const pngMaxval) {
+    png_color_16 retval;
+
+    xel scaled;
+    
+    PPM_DEPTH(scaled, input, maxval, pngMaxval);
+
+    retval.red   = PPM_GETR(scaled);
+    retval.green = PPM_GETG(scaled);
+    retval.blue  = PPM_GETB(scaled);
+    retval.gray  = PNM_GET1(scaled);
+
+    return retval;
+}
+
+
+
+static void
+closestColorInPalette(pixel          const targetColor, 
+                      pixel                palette_pnm[],
+                      unsigned int   const paletteSize,
+                      unsigned int * const bestIndexP,
+                      unsigned int * const bestMatchP) {
+    
+    unsigned int paletteIndex;
+    unsigned int bestIndex;
+    unsigned int bestMatch;
+
+    assert(paletteSize > 0);
+
+    bestMatch = UINT_MAX;
+    for (paletteIndex = 0; paletteIndex < paletteSize; ++paletteIndex) {
+        unsigned int const dist = 
+            PPM_DISTANCE(palette_pnm[paletteIndex], targetColor);
+
+        if (dist < bestMatch) {
+            bestMatch = dist;
+            bestIndex = paletteIndex;
+        }
+    }
+    if (bestIndexP != NULL)
+        *bestIndexP = bestIndex;
+    if (bestMatchP != NULL)
+        *bestMatchP = bestMatch;
+}
+
+
+
+/* We really ought to make this hash function actually depend upon
+   the "a" argument; we just don't know a decent prime number off-hand.
+*/
+#define HASH_SIZE 20023
+#define hashpixelalpha(p,a) ((((long) PPM_GETR(p) * 33023 + \
+                               (long) PPM_GETG(p) * 30013 + \
+                               (long) PPM_GETB(p) * 27011 ) \
+                              & 0x7fffffff ) % HASH_SIZE )
+
+static coloralphahash_table
+alloccoloralphahash(void)  {
+    coloralphahash_table caht;
+    int i;
+
+    MALLOCARRAY(caht,HASH_SIZE);
+    if (caht == NULL)
+        pm_error( "out of memory allocating hash table" );
+
+    for (i = 0; i < HASH_SIZE; ++i)
+        caht[i] = NULL;
+
+    return caht;
+}
+
+
+static void
+freecoloralphahash(coloralphahash_table const caht) {
+    int i;
+
+    for (i = 0; i < HASH_SIZE; ++i) {
+        cahitem * p;
+        cahitem * next;
+        for (p = caht[i]; p; p = next) {
+            next = p->next;
+            free(p);
+        }
+    }
+    free(caht);
+}
+
+
+
+static void
+addtocoloralphahash(coloralphahash_table const caht,
+                    pixel *              const colorP,
+                    gray *               const alphaP,
+                    int                  const value) {
+
+    int hash;
+    cahitem * itemP;
+
+    MALLOCVAR(itemP);
+    if (itemP == NULL)
+        pm_error("Out of memory building hash table");
+    hash = hashpixelalpha(*colorP, *alphaP);
+    itemP->color = *colorP;
+    itemP->alpha = *alphaP;
+    itemP->value = value;
+    itemP->next = caht[hash];
+    caht[hash] = itemP;
+}
+
+
+
+static int
+lookupColorAlpha(coloralphahash_table const caht,
+                 const pixel *        const colorP,
+                 const gray *         const alphaP) {
+
+    int hash;
+    cahitem * p;
+
+    hash = hashpixelalpha(*colorP, *alphaP);
+    for (p = caht[hash]; p; p = p->next)
+        if (PPM_EQUAL(p->color, *colorP) && p->alpha == *alphaP)
+            return p->value;
+
+    return -1;
+}
+
+
+
+static void
+pnmtopng_error_handler(png_structp     const png_ptr,
+                       png_const_charp const msg) {
+
+  jmpbuf_wrapper  *jmpbuf_ptr;
+
+  /* this function, aside from the extra step of retrieving the "error
+   * pointer" (below) and the fact that it exists within the application
+   * rather than within libpng, is essentially identical to libpng's
+   * default error handler.  The second point is critical:  since both
+   * setjmp() and longjmp() are called from the same code, they are
+   * guaranteed to have compatible notions of how big a jmp_buf is,
+   * regardless of whether _BSD_SOURCE or anything else has (or has not)
+   * been defined. */
+
+  fprintf(stderr, "pnmtopng:  fatal libpng error: %s\n", msg);
+  fflush(stderr);
+
+  jmpbuf_ptr = png_get_error_ptr(png_ptr);
+  if (jmpbuf_ptr == NULL) {         /* we are completely hosed now */
+    fprintf(stderr,
+      "pnmtopng:  EXTREMELY fatal error: jmpbuf unrecoverable; terminating.\n");
+    fflush(stderr);
+    exit(99);
+  }
+
+  longjmp(jmpbuf_ptr->jmpbuf, 1);
+}
+
+
+/* The following variables belong to getChv() and freeChv() */
+static bool getChv_computed = FALSE;
+static colorhist_vector getChv_chv;
+
+
+
+static void
+getChv(FILE *             const ifP, 
+       pm_filepos         const rasterPos,
+       int                const cols, 
+       int                const rows, 
+       xelval             const maxval,
+       int                const format, 
+       int                const maxColors, 
+       colorhist_vector * const chvP,
+       unsigned int *     const colorsP) {
+/*----------------------------------------------------------------------------
+   Return a list of all the colors in a libnetpbm image and the number of
+   times they occur.  The image is in the seekable file 'ifP', whose
+   raster starts at position 'rasterPos' of the file.  The image's properties
+   are 'cols', 'rows', 'maxval', and 'format'.
+
+   Return the number of colors as *colorsP.  Return the details of the 
+   colors in newly malloc'ed storage, and its address as *chvP.  If
+   there are more than 'maxColors' colors, though, just return NULL as
+   *chvP and leave *colorsP undefined.
+
+   Don't spend the time to read the file if this subroutine has been called
+   before.  In that case, just assume the inputs are all the same and return
+   the previously computed information.  Ick.
+
+   *chvP is in static program storage.
+-----------------------------------------------------------------------------*/
+    static unsigned int getChv_colors;
+
+    if (!getChv_computed) {
+        int colorCount;
+        if (verbose) 
+            pm_message ("Finding colors in input image...");
+
+        pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
+        getChv_chv = ppm_computecolorhist2(ifP, cols, rows, maxval, format, 
+                                           maxColors, &colorCount);
+        
+        getChv_colors = colorCount;
+
+        if (verbose) {
+            if (getChv_chv)
+                pm_message("%u colors found", getChv_colors);
+            else
+                pm_message("Too many colors (more than %u) found", maxColors);
+        }
+        getChv_computed = TRUE;
+    }
+    *chvP = getChv_chv;
+    *colorsP = getChv_colors;
+}
+
+
+
+static void freeChv(void) {
+
+    if (getChv_computed)
+        if (getChv_chv)
+            ppm_freecolorhist(getChv_chv);
+
+    getChv_computed = FALSE;
+}
+
+
+
+static bool
+pgmBitsAreRepeated(unsigned int const repeatedSize,
+                   FILE *       const ifP,
+                   pm_filepos   const rasterPos, 
+                   int          const cols,
+                   int          const rows,
+                   xelval       const maxval,
+                   int          const format) {
+/*----------------------------------------------------------------------------
+   Return TRUE iff all the samples in the image in file 'ifP',
+   described by 'cols', 'rows', 'maxval', and 'format', consist in the
+   rightmost 'repeatedSize' * 2 bits of two identical sets of
+   'repeatedSize' bits.
+
+   The file has arbitrary position, but the raster is at file position
+   'rasterPos'.
+
+   E.g. for repeatedSize = 2, a sample value of 0xaa would qualify.
+   So would 0x0a.
+
+   Leave the file positioned where we found it.
+-----------------------------------------------------------------------------*/
+    unsigned int const mask2 = (1 << repeatedSize*2) - 1;
+    unsigned int const mask1 = (1 << repeatedSize) - 1;
+
+    bool mayscale;
+    unsigned int row;
+    xel * xelrow;
+
+    xelrow = pnm_allocrow(cols);
+    
+    pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
+
+    mayscale = TRUE;  /* initial assumption */
+
+    for (row = 0; row < rows && mayscale; ++row) {
+        unsigned int col;
+        pnm_readpnmrow(ifP, xelrow, cols, maxval, format);
+        for (col = 0; col < cols && mayscale; ++col) {
+            xelval const testbits2 = PNM_GET1(xelrow[col]) & mask2;
+                /* The bits of interest in the sample */
+            xelval const testbits1 = testbits2 & mask1;
+                /* The lower half of the bits of interest in the sample */
+            if (((testbits1 << repeatedSize) | testbits1) != testbits2)
+                mayscale = FALSE;
+        }
+    }
+    pnm_freerow(xelrow);
+
+    return mayscale;
+}
+
+
+
+static void
+meaningful_bits_pgm(FILE *         const ifP, 
+                    pm_filepos     const rasterPos, 
+                    int            const cols,
+                    int            const rows,
+                    xelval         const maxval,
+                    int            const format,
+                    unsigned int * const retvalP) {
+/*----------------------------------------------------------------------------
+   In the PGM raster with maxval 'maxval' at file offset 'rasterPos'
+   in file 'ifp', the samples may be composed of groups of 1, 2, 4, or 8
+   bits repeated.  This would be the case if the image were converted
+   at some point from a 2 bits-per-pixel image to an 8-bits-per-pixel
+   image, for example.
+
+   If this is the case, we find out and find out how small these repeated
+   groups of bits are and return the number of bits.
+-----------------------------------------------------------------------------*/
+    unsigned int maxMeaningfulBits;
+        /* progressive estimate of the maximum number of meaningful
+           (nonrepeated) bits in the samples.
+        */
+
+    maxMeaningfulBits = pm_maxvaltobits(maxval);  /* initial value */
+
+    if (maxval == 0xffff || maxval == 0xff || maxval == 0xf || maxval == 0x3) {
+        if (maxMeaningfulBits == 16) {
+            if (pgmBitsAreRepeated(8,
+                                   ifP, rasterPos, cols, rows, maxval, format))
+                maxMeaningfulBits = 8;
+        }
+        if (maxMeaningfulBits == 8) {
+            if (pgmBitsAreRepeated(4,
+                                   ifP, rasterPos, cols, rows, maxval, format))
+                maxMeaningfulBits = 4;
+        }
+        if (maxMeaningfulBits == 4) {
+            if (pgmBitsAreRepeated(2,
+                                   ifP, rasterPos, cols, rows, maxval, format))
+                maxMeaningfulBits = 2;
+        }
+        if (maxMeaningfulBits == 2) {
+            if (pgmBitsAreRepeated(1,
+                                   ifP, rasterPos, cols, rows, maxval, format))
+                maxMeaningfulBits = 1;
+        }
+    }
+    *retvalP = maxMeaningfulBits;
+}
+
+
+
+static void
+meaningful_bits_ppm(FILE *         const ifp, 
+                    pm_filepos     const rasterPos, 
+                    int            const cols,
+                    int            const rows,
+                    xelval         const maxval,
+                    int            const format,
+                    unsigned int * const retvalP) {
+/*----------------------------------------------------------------------------
+   In the PPM raster with maxval 'maxval' at file offset 'rasterPos'
+   in file 'ifp', the samples may be composed of groups of 8
+   bits repeated twice.  This would be the case if the image were converted
+   at some point from a 8 bits-per-pixel image to an 16-bits-per-pixel
+   image, for example.
+
+   We return the smallest number of bits we can take from the right of
+   a sample without losing information (8 or all).
+-----------------------------------------------------------------------------*/
+    int mayscale;
+    unsigned int row;
+    xel * xelrow;
+    unsigned int maxMeaningfulBits;
+        /* progressive estimate of the maximum number of meaningful
+           (nonrepeated) bits in the samples.
+        */
+
+    xelrow = pnm_allocrow(cols);
+
+    maxMeaningfulBits = pm_maxvaltobits(maxval);
+
+    if (maxval == 65535) {
+        mayscale = TRUE;   /* initial assumption */
+        pm_seek2(ifp, &rasterPos, sizeof(rasterPos));
+        for (row = 0; row < rows && mayscale; ++row) {
+            unsigned int col;
+            pnm_readpnmrow(ifp, xelrow, cols, maxval, format);
+            for (col = 0; col < cols && mayscale; ++col) {
+                xel const p = xelrow[col];
+                if ((PPM_GETR(p) & 0xff) * 0x101 != PPM_GETR(p) ||
+                    (PPM_GETG(p) & 0xff) * 0x101 != PPM_GETG(p) ||
+                    (PPM_GETB(p) & 0xff) * 0x101 != PPM_GETB(p))
+                    mayscale = FALSE;
+            }
+        }
+        if (mayscale)
+            maxMeaningfulBits = 8;
+    }
+    pnm_freerow(xelrow);
+
+    *retvalP = maxMeaningfulBits;
+}
+
+
+
+static void
+tryTransparentColor(FILE *     const ifp, 
+                    pm_filepos const rasterPos, 
+                    int        const cols, 
+                    int        const rows, 
+                    xelval     const maxval,
+                    int        const format, 
+                    gray **    const alphaMask,
+                    gray       const alphaMaxval,
+                    pixel      const transcolor,
+                    bool *     const singleColorIsTransP) {
+
+    int const pnm_type = PNM_FORMAT_TYPE(format);
+
+    xel * xelrow;
+    bool singleColorIsTrans;
+        /* So far, it looks like a single color is uniquely transparent */
+    int row;
+
+    xelrow = pnm_allocrow(cols);
+
+    pm_seek2(ifp, &rasterPos, sizeof(rasterPos));
+
+    singleColorIsTrans = TRUE;  /* initial assumption */
+        
+    for (row = 0; row < rows && singleColorIsTrans; ++row) {
+        int col;
+        pnm_readpnmrow(ifp, xelrow, cols, maxval, format);
+        for (col = 0 ; col < cols && singleColorIsTrans; ++col) {
+            if (alphaMask[row][col] == 0) { /* transparent */
+                /* If we have a second transparent color, we're
+                   disqualified
+                */
+                if (pnm_type == PPM_TYPE) {
+                    if (!PPM_EQUAL(xelrow[col], transcolor))
+                        singleColorIsTrans = FALSE;
+                } else {
+                    if (PNM_GET1(xelrow[col]) != PNM_GET1(transcolor))
+                        singleColorIsTrans = FALSE;
+                }
+            } else if (alphaMask[row][col] != alphaMaxval) {
+                /* Here's an area of the mask that is translucent.  That
+                   disqualified us.
+                */
+                singleColorIsTrans = FALSE;
+            } else {
+                /* Here's an area of the mask that is opaque.  If it's
+                   the same color as our candidate transparent color,
+                   that disqualifies us.
+                */
+                if (pnm_type == PPM_TYPE) {
+                    if (PPM_EQUAL(xelrow[col], transcolor))
+                        singleColorIsTrans = FALSE;
+                } else {
+                    if (PNM_GET1(xelrow[col]) == PNM_GET1(transcolor))
+                        singleColorIsTrans = FALSE;
+                }
+            }
+        }
+    }  
+    pnm_freerow(xelrow);
+}
+
+
+
+static void
+analyzeAlpha(FILE *     const ifp, 
+             pm_filepos const rasterPos, 
+             int        const cols, 
+             int        const rows, 
+             xelval     const maxval,
+             int        const format, 
+             gray **    const alphaMask,
+             gray       const alphaMaxval,
+             bool *     const allOpaqueP,
+             bool *     const singleColorIsTransP, 
+             pixel*     const alphaTranscolorP) {
+/*----------------------------------------------------------------------------
+  Get information about the alpha mask, in combination with the masked
+  image, that Caller can use to choose the most efficient way to
+  represent the information in the alpha mask in a PNG.  Simply
+  putting the alpha mask in the PNG is a last resort.  But if the mask
+  says all opaque, we can simply omit any mention of transparency
+  instead -- default is opaque.  And if the mask makes all the pixels
+  of a certain color fully transparent and every other pixel opaque,
+  we can simply identify that color in the PNG.
+
+  We have to do this before any scaling occurs, since alpha is only
+  possible with 8 and 16-bit.
+-----------------------------------------------------------------------------*/
+    xel * xelrow;
+    bool foundTransparentPixel;
+        /* We found a pixel in the image where the alpha mask says it is
+           transparent.
+        */
+    pixel transcolor;
+        /* Color of the transparent pixel mentioned above. */
+    
+    xelrow = pnm_allocrow(cols);
+
+    {
+        int row;
+        /* Find a candidate transparent color -- the color of any pixel in the
+           image that the alpha mask says should be transparent.
+        */
+        foundTransparentPixel = FALSE;  /* initial assumption */
+        pm_seek2(ifp, &rasterPos, sizeof(rasterPos));
+        for (row = 0 ; row < rows && !foundTransparentPixel ; ++row) {
+            int col;
+            pnm_readpnmrow(ifp, xelrow, cols, maxval, format);
+            for (col = 0; col < cols && !foundTransparentPixel; ++col) {
+                if (alphaMask[row][col] == 0) {
+                    foundTransparentPixel = TRUE;
+                    transcolor = xeltopixel(xelrow[col]);
+                }
+            }
+        }
+    }
+
+    pnm_freerow(xelrow);
+
+    if (foundTransparentPixel) {
+        *allOpaqueP = FALSE;
+        tryTransparentColor(ifp, rasterPos, cols, rows, maxval, format,
+                            alphaMask, alphaMaxval, transcolor,
+                            singleColorIsTransP);
+        *alphaTranscolorP = transcolor;
+    } else {
+        *allOpaqueP   = TRUE;
+        *singleColorIsTransP = FALSE;
+    }
+}
+
+
+
+static void
+findRedundantBits(FILE *         const ifp, 
+                  int            const rasterPos, 
+                  int            const cols,
+                  int            const rows,
+                  xelval         const maxval,
+                  int            const format,
+                  bool           const alpha,
+                  bool           const force,
+                  unsigned int * const meaningfulBitsP) {
+/*----------------------------------------------------------------------------
+   Find out if we can use just a subset of the bits from each input
+   sample.  Often, people create an image with e.g. 8 bit samples from
+   one that has e.g. only 4 bit samples by scaling by 256/16, which is
+   the same as repeating the bits.  E.g.  1011 becomes 10111011.  We
+   detect this case.  We return as *meaningfulBitsP the minimum number
+   of bits, starting from the least significant end, that contain
+   original information.
+-----------------------------------------------------------------------------*/
+  if (!alpha && PNM_FORMAT_TYPE(format) == PGM_TYPE && !force) 
+      meaningful_bits_pgm(ifp, rasterPos, cols, rows, maxval, format,
+                          meaningfulBitsP);
+  else if (PNM_FORMAT_TYPE(format) == PPM_TYPE && !force)
+      meaningful_bits_ppm(ifp, rasterPos, cols, rows, maxval, format,
+                          meaningfulBitsP);
+  else 
+      *meaningfulBitsP = pm_maxvaltobits(maxval);
+
+  if (verbose && *meaningfulBitsP != pm_maxvaltobits(maxval))
+      pm_message("Using only %d rightmost bits of input samples.  The "
+                 "rest are redundant.", *meaningfulBitsP);
+}
+
+
+
+static void
+readOrderedPalette(FILE *         const pfp,
+                   xel                  ordered_palette[], 
+                   unsigned int * const ordered_palette_size_p) {
+
+    xel ** xels;
+    int cols, rows;
+    xelval maxval;
+    int format;
+    
+    if (verbose)
+        pm_message("reading ordered palette (colormap)...");
+
+    xels = pnm_readpnm(pfp, &cols, &rows, &maxval, &format);
+    
+    if (PNM_FORMAT_TYPE(format) != PPM_TYPE) 
+        pm_error("ordered palette must be a PPM file, not type %d", format);
+
+    *ordered_palette_size_p = rows * cols;
+    if (*ordered_palette_size_p > MAXCOLORS) 
+        pm_error("ordered-palette image contains %d pixels.  Maximum is %d",
+                 *ordered_palette_size_p, MAXCOLORS);
+    if (verbose)
+        pm_message("%u colors found", *ordered_palette_size_p);
+
+    {
+        unsigned int j;
+        unsigned int row;
+        j = 0;  /* initial value */
+        for (row = 0; row < rows; ++row) {
+            int col;
+            for (col = 0; col < cols; ++col) 
+                ordered_palette[j++] = xels[row][col];
+        }
+    }
+    pnm_freearray(xels, rows);
+}        
+
+
+
+static void
+compute_nonalpha_palette(colorhist_vector const chv,
+                         int              const colors,
+                         pixval           const maxval,
+                         FILE *           const pfp,
+                         pixel                  palette_pnm[],
+                         unsigned int *   const paletteSizeP,
+                         gray                   trans_pnm[],
+                         unsigned int *   const transSizeP) {
+/*----------------------------------------------------------------------------
+   Compute the palette corresponding to the color set 'chv'
+   (consisting of 'colors' distinct colors) assuming a pure-color (no
+   transparency) palette.
+
+   If 'pfp' is non-null, assume it's a PPM file and read the palette
+   from that.  Make sure it contains the same colors as the palette
+   we computed ourself would have.  Caller supplied the file because he
+   wants the colors in a particular order in the palette.
+-----------------------------------------------------------------------------*/
+    unsigned int colorIndex;
+    
+    xel ordered_palette[MAXCOLORS];
+    unsigned int ordered_palette_size;
+
+    if (pfp) {
+        readOrderedPalette(pfp, ordered_palette, &ordered_palette_size);
+
+        if (colors != ordered_palette_size) 
+            pm_error("sizes of ordered palette (%d) "
+                     "and existing palette (%d) differ",
+                     ordered_palette_size, colors);
+        
+        /* Make sure the ordered palette contains all the colors in
+           the image 
+        */
+        for (colorIndex = 0; colorIndex < colors; colorIndex++) {
+            int j;
+            bool found;
+            
+            found = FALSE;
+            for (j = 0; j < ordered_palette_size && !found; ++j) {
+                if (PNM_EQUAL(ordered_palette[j], chv[colorIndex].color)) 
+                    found = TRUE;
+            }
+            if (!found) 
+                pm_error("failed to find color (%d, %d, %d), which is in the "
+                         "input image, in the ordered palette",
+                         PPM_GETR(chv[colorIndex].color),
+                         PPM_GETG(chv[colorIndex].color),
+                         PPM_GETB(chv[colorIndex].color));
+        }
+        /* OK, the ordered palette passes muster as a palette; go ahead
+           and return it as the palette.
+        */
+        for (colorIndex = 0; colorIndex < colors; ++colorIndex)
+            palette_pnm[colorIndex] = ordered_palette[colorIndex];
+    } else {
+        for (colorIndex = 0; colorIndex < colors; ++colorIndex) 
+            palette_pnm[colorIndex] = chv[colorIndex].color;
+    }
+    *paletteSizeP = colors;
+    *transSizeP = 0;
+}
+
+
+
+static void
+computeUnsortedAlphaPalette(FILE *           const ifP,
+                            int              const cols,
+                            int              const rows,
+                            xelval           const maxval,
+                            int              const format,
+                            pm_filepos       const rasterPos,
+                            gray **          const alpha_mask,
+                            unsigned int     const maxPaletteEntries,
+                            colorhist_vector const chv,
+                            int              const colors,
+                            gray *                 alphas_of_color[],
+                            unsigned int           alphas_first_index[],
+                            unsigned int           alphas_of_color_cnt[]) {
+/*----------------------------------------------------------------------------
+   Read the image at position 'rasterPos' in file *ifP, which is a PNM
+   described by 'cols', 'rows', 'maxval', and 'format'.
+
+   Using the alpha mask 'alpha_mask' and color map 'chv' (of size 'colors')
+   for the image, construct a palette of (color index, alpha) ordered pairs 
+   for the image, as follows.
+
+   The alpha/color palette is the set of all ordered pairs of
+   (color,alpha) in the PNG, including the background color.  The
+   actual palette is an array with up to 'maxPaletteEntries elements.  Each
+   array element contains a color index from the color palette and
+   an alpha value.  All the elements with the same color index are
+   contiguous.  alphas_first_index[x] is the index in the
+   alpha/color palette of the first element that has color index x.
+   alphas_of_color_cnt[x] is the number of elements that have color
+   index x.  alphas_of_color[x][y] is the yth alpha value that
+   appears with color index x (in order of appearance).
+   alpha_color_pair_count is the total number of elements, i.e. the
+   total number of combinations color and alpha.
+-----------------------------------------------------------------------------*/
+    colorhash_table cht;
+    int color_index;
+    int row;
+    xel * xelrow;
+
+    cht = ppm_colorhisttocolorhash (chv, colors);
+
+    for (color_index = 0 ; color_index < colors + 1 ; ++color_index) {
+        /* TODO: It sure would be nice if we didn't have to allocate
+           256 words here for what is normally only 0 or 1 different
+           alpha values!  Maybe we should do some sophisticated reallocation.
+        */
+        MALLOCARRAY(alphas_of_color[color_index], maxPaletteEntries);
+        if (alphas_of_color[color_index] == NULL)
+            pm_error ("out of memory allocating alpha/palette entries");
+        alphas_of_color_cnt[color_index] = 0;
+    }
+ 
+    pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
+
+    xelrow = pnm_allocrow(cols);
+
+    for (row = 0 ; row < rows ; ++row) {
+        int col;
+        pnm_readpnmrow(ifP, xelrow, cols, maxval, format);
+        pnm_promoteformatrow(xelrow, cols, maxval, format, maxval, PPM_TYPE);
+        for (col = 0 ; col < cols ; ++col) {
+            int i;
+            int const color = ppm_lookupcolor(cht, &xelrow[col]);
+            for (i = 0 ; i < alphas_of_color_cnt[color] ; ++i) {
+                if (alpha_mask[row][col] == alphas_of_color[color][i])
+                    break;
+            }
+            if (i == alphas_of_color_cnt[color]) {
+                alphas_of_color[color][i] = alpha_mask[row][col];
+                alphas_of_color_cnt[color]++;
+            }
+        }
+    }
+    {
+        int i;
+        alphas_first_index[0] = 0;
+        for (i = 1 ; i < colors ; i++)
+            alphas_first_index[i] = alphas_first_index[i-1] +
+                alphas_of_color_cnt[i-1];
+    }
+    pnm_freerow(xelrow);
+    ppm_freecolorhash(cht);
+}
+
+
+
+static void
+sortAlphaPalette(gray *alphas_of_color[],
+                 unsigned int alphas_first_index[],
+                 unsigned int alphas_of_color_cnt[],
+                 unsigned int const colors,
+                 unsigned int mapping[],
+                 unsigned int * const transSizeP) {
+/*----------------------------------------------------------------------------
+   Remap the palette indices so opaque entries are last.
+
+   alphas_of_color[], alphas_first_index[], and alphas_of_color_cnt[]
+   describe an unsorted PNG (alpha/color) palette.  We generate
+   mapping[] such that mapping[x] is the index into the sorted PNG
+   palette of the alpha/color pair whose index is x in the unsorted
+   PNG palette.  This mapping sorts the palette so that opaque entries
+   are last.
+-----------------------------------------------------------------------------*/
+    unsigned int bot_idx;
+    unsigned int top_idx;
+    unsigned int colorIndex;
+    
+    /* We start one index at the bottom of the palette index range
+       and another at the top.  We run through the unsorted palette,
+       and when we see an opaque entry, we map it to the current top
+       cursor and bump it down.  When we see a non-opaque entry, we map 
+       it to the current bottom cursor and bump it up.  Because the input
+       and output palettes are the same size, the two cursors should meet
+       right when we process the last entry of the unsorted palette.
+    */    
+    bot_idx = 0;
+    top_idx = alphas_first_index[colors-1] + alphas_of_color_cnt[colors-1] - 1;
+    
+    for (colorIndex = 0;  colorIndex < colors;  ++colorIndex) {
+        unsigned int j;
+        for (j = 0; j < alphas_of_color_cnt[colorIndex]; ++j) {
+            unsigned int const paletteIndex = 
+                alphas_first_index[colorIndex] + j;
+            if (alphas_of_color[colorIndex][j] == PALETTEOPAQUE)
+                mapping[paletteIndex] = top_idx--;
+                else
+                    mapping[paletteIndex] = bot_idx++;
+        }
+    }
+    /* indices should have just crossed paths */
+    if (bot_idx != top_idx + 1) {
+        pm_error ("internal inconsistency: "
+                  "remapped bot_idx = %u, top_idx = %u",
+                  bot_idx, top_idx);
+    }
+    *transSizeP = bot_idx;
+}
+
+
+
+static void
+compute_alpha_palette(FILE *         const ifP, 
+                      int            const cols,
+                      int            const rows,
+                      xelval         const maxval,
+                      int            const format,
+                      pm_filepos     const rasterPos,
+                      gray **        const alpha_mask,
+                      pixel                palette_pnm[],
+                      gray                 trans_pnm[],
+                      unsigned int * const paletteSizeP,
+                      unsigned int * const transSizeP,
+                      bool *         const tooBigP) {
+/*----------------------------------------------------------------------------
+   Return the palette of color/alpha pairs for the image indicated by
+   'ifP', 'cols', 'rows', 'maxval', 'format', and 'rasterPos'.
+   alpha_mask[] is the Netpbm-style alpha mask for the image.
+
+   Return the palette as the arrays palette_pnm[] and trans_pnm[].
+   The ith entry in the palette is the combination of palette[i],
+   which defines the color, and trans[i], which defines the
+   transparency.
+
+   Return the number of entries in the palette as *paletteSizeP.
+
+   The palette is sorted so that the opaque entries are last, and we return
+   *transSizeP as the number of non-opaque entries.
+
+   palette[] and trans[] are allocated by the caller to at least 
+   MAXPALETTEENTRIES elements.
+
+   If there are more than MAXPALETTEENTRIES color/alpha pairs in the image, 
+   don't return any palette information -- just return *tooBigP == TRUE.
+-----------------------------------------------------------------------------*/
+    colorhist_vector chv;
+    unsigned int colors;
+
+    gray *alphas_of_color[MAXPALETTEENTRIES];
+    unsigned int alphas_first_index[MAXPALETTEENTRIES];
+    unsigned int alphas_of_color_cnt[MAXPALETTEENTRIES];
+ 
+    getChv(ifP, rasterPos, cols, rows, maxval, format, MAXCOLORS, 
+           &chv, &colors);
+
+    computeUnsortedAlphaPalette(ifP, cols, rows, maxval, format, rasterPos,
+                                alpha_mask, MAXPALETTEENTRIES, chv, colors,
+                                alphas_of_color,
+                                alphas_first_index,
+                                alphas_of_color_cnt);
+
+    *paletteSizeP = 
+        alphas_first_index[colors-1] + alphas_of_color_cnt[colors-1];
+    if (*paletteSizeP > MAXPALETTEENTRIES) {
+        *tooBigP = TRUE;
+    } else {
+        unsigned int mapping[MAXPALETTEENTRIES];
+            /* Sorting of the alpha/color palette.  mapping[x] is the
+               index into the sorted PNG palette of the alpha/color
+               pair whose index is x in the unsorted PNG palette.
+               This mapping sorts the palette so that opaque entries
+               are last.  
+            */
+
+        *tooBigP = FALSE;
+
+        /* Make the opaque palette entries last */
+        sortAlphaPalette(alphas_of_color, alphas_first_index,
+                         alphas_of_color_cnt, colors,
+                         mapping, transSizeP);
+
+        {
+            unsigned int colorIndex;
+
+            for (colorIndex = 0; colorIndex < colors; ++colorIndex) {
+                unsigned int j;
+                for (j = 0; j < alphas_of_color_cnt[colorIndex]; ++j) {
+                    unsigned int const paletteIndex = 
+                        alphas_first_index[colorIndex] + j;
+                    palette_pnm[mapping[paletteIndex]] = chv[colorIndex].color;
+                    trans_pnm[mapping[paletteIndex]] = 
+                    alphas_of_color[colorIndex][j];
+                }
+            }
+        }
+    }
+    { 
+        unsigned int colorIndex;
+        for (colorIndex = 0; colorIndex < colors + 1; ++colorIndex)
+            free(alphas_of_color[colorIndex]);
+    }
+} 
+
+
+
+static void
+makeOneColorTransparentInPalette(xel            const transColor, 
+                                 bool           const exact,
+                                 pixel                palette_pnm[],
+                                 unsigned int   const paletteSize,
+                                 gray                 trans_pnm[],
+                                 unsigned int * const transSizeP) {
+/*----------------------------------------------------------------------------
+   Find the color 'transColor' in the color/alpha palette defined by
+   palette_pnm[], paletteSize, trans_pnm[] and *transSizeP.  
+
+   Make that entry fully transparent.
+
+   Rearrange the palette so that that entry is first.  (The PNG compressor
+   can do a better job when the opaque entries are all last in the 
+   color/alpha palette).
+
+   If the specified color is not there and exact == TRUE, return
+   without changing anything, but issue a warning message.  If it's
+   not there and exact == FALSE, just find the closest color.
+
+   We assume every entry in the palette is opaque upon entry.
+
+   A valid palette has at least one color.
+-----------------------------------------------------------------------------*/
+    unsigned int transparentIndex;
+    unsigned int distance;
+
+    assert(paletteSize > 0);
+    
+    if (*transSizeP != 0)
+        pm_error("Internal error: trying to make a color in the palette "
+                 "transparent where there already is one.");
+
+    closestColorInPalette(transColor, palette_pnm, paletteSize, 
+                          &transparentIndex, &distance);
+
+    if (distance != 0 && exact) {
+        pm_message("specified transparent color not present in palette; "
+                   "ignoring -transparent");
+        errorlevel = PNMTOPNG_WARNING_LEVEL;
+    } else {        
+        /* Swap this with the first entry in the palette */
+        pixel tmp;
+    
+        tmp = palette_pnm[transparentIndex];
+        palette_pnm[transparentIndex] = palette_pnm[0];
+        palette_pnm[0] = tmp;
+        
+        /* Make it transparent */
+        trans_pnm[0] = PGM_TRANSPARENT;
+        *transSizeP = 1;
+        if (verbose) {
+            pixel const p = palette_pnm[0];
+            pm_message("Making all occurences of color (%u, %u, %u) "
+                       "transparent.",
+                       PPM_GETR(p), PPM_GETG(p), PPM_GETB(p));
+        }
+    }
+}
+
+
+
+static void
+findOrAddBackgroundInPalette(pixel          const backColor, 
+                             pixel                palette_pnm[], 
+                             unsigned int * const paletteSizeP,
+                             unsigned int * const backgroundIndexP) {
+/*----------------------------------------------------------------------------
+  Add the background color 'backColor' to the palette, unless
+  it's already in there.  If it's not present and there's no room to
+  add it, choose a background color that's already in the palette,
+  as close to 'backColor' as possible.
+
+  If we add an entry to the palette, make it opaque.  But in searching the 
+  existing palette, ignore transparency.
+
+  Note that PNG specs say that transparency of the background is meaningless;
+  i.e. a viewer must ignore the transparency of the palette entry when 
+  using the background color.
+
+  Return the palette index of the background color as *backgroundIndexP.
+-----------------------------------------------------------------------------*/
+    int backgroundIndex;  /* negative means not found */
+    unsigned int paletteIndex;
+
+    backgroundIndex = -1;
+    for (paletteIndex = 0; 
+         paletteIndex < *paletteSizeP; 
+         ++paletteIndex) 
+        if (PPM_EQUAL(palette_pnm[paletteIndex], backColor))
+            backgroundIndex = paletteIndex;
+
+    if (backgroundIndex >= 0) {
+        /* The background color is already in the palette. */
+        *backgroundIndexP = backgroundIndex;
+        if (verbose) {
+            pixel const p = palette_pnm[*backgroundIndexP];
+            pm_message("background color (%u, %u, %u) appears in image.",
+                       PPM_GETR(p), PPM_GETG(p), PPM_GETB(p));
+        }
+    } else {
+        /* Try to add the background color, opaque, to the palette. */
+        if (*paletteSizeP < MAXCOLORS) {
+            /* There's room, so just add it to the end of the palette */
+
+            /* Because we're not expanding the transparency palette, this
+               entry is not in it, and is thus opaque.
+            */
+            *backgroundIndexP = (*paletteSizeP)++;
+            palette_pnm[*backgroundIndexP] = backColor;
+            if (verbose) {
+                pixel const p = palette_pnm[*backgroundIndexP];
+                pm_message("added background color (%u, %u, %u) to palette.",
+                           PPM_GETR(p), PPM_GETG(p), PPM_GETB(p));
+            }
+        } else {
+            closestColorInPalette(backColor, palette_pnm, *paletteSizeP,
+                                  backgroundIndexP, NULL);
+            errorlevel = PNMTOPNG_WARNING_LEVEL;
+            {
+                pixel const p = palette_pnm[*backgroundIndexP];
+                pm_message("no room in palette for background color; "
+                           "using closest match (%u, %u, %u) instead",
+                           PPM_GETR(p), PPM_GETG(p), PPM_GETB(p));
+            }
+        }
+    }
+}
+
+
+
+static void 
+buildColorLookup(pixel                   palette_pnm[], 
+                 unsigned int      const paletteSize,
+                 colorhash_table * const chtP) {
+/*----------------------------------------------------------------------------
+   Create a colorhash_table out of the palette described by
+   palette_pnm[] (which has 'paletteSize' entries) so one can look up
+   the palette index of a given color.
+
+   Where the same color appears twice in the palette, the lookup table
+   finds an arbitrary one of them.  We don't consider transparency of
+   palette entries, so if the same color appears in the palette once
+   transparent and once opaque, the lookup table finds an arbitrary one
+   of those two.
+-----------------------------------------------------------------------------*/
+    colorhash_table const cht = ppm_alloccolorhash();
+    unsigned int paletteIndex;
+
+    for (paletteIndex = 0; paletteIndex < paletteSize; ++paletteIndex) {
+        ppm_addtocolorhash(cht, &palette_pnm[paletteIndex], paletteIndex);
+    }
+    *chtP = cht;
+}
+
+
+static void 
+buildColorAlphaLookup(pixel              palette_pnm[], 
+                      unsigned int const paletteSize,
+                      gray               trans_pnm[], 
+                      unsigned int const transSize,
+                      gray         const alphaMaxval,
+                      coloralphahash_table * const cahtP) {
+    
+    coloralphahash_table const caht = alloccoloralphahash();
+
+    unsigned int paletteIndex;
+
+    for (paletteIndex = 0; paletteIndex < paletteSize; ++paletteIndex) {
+        gray paletteTrans;
+
+        if (paletteIndex < transSize)
+            paletteTrans = alphaMaxval;
+        else
+            paletteTrans = trans_pnm[paletteIndex];
+
+
+        addtocoloralphahash(caht, &palette_pnm[paletteIndex],
+                            &trans_pnm[paletteIndex], paletteIndex);
+    }
+    *cahtP = caht;
+}
+
+
+
+static void
+tryAlphaPalette(FILE *         const ifP,
+                int            const cols,
+                int            const rows,
+                xelval         const maxval,
+                int            const format,
+                pm_filepos     const rasterPos,
+                gray **        const alpha_mask,
+                FILE *         const pfP,
+                pixel *        const palette_pnm,
+                unsigned int * const paletteSizeP,
+                gray *         const trans_pnm,
+                unsigned int * const transSizeP,
+                const char **  const impossibleReasonP) {
+/*----------------------------------------------------------------------------
+   Try to make an alpha palette as 'trans_pnm', size *transSizeP.
+
+   If it's impossible, return as *impossibleReasonP newly malloced storage
+   containing text that tells why.  But if we succeed, return
+   *impossibleReasonP == NULL.
+-----------------------------------------------------------------------------*/
+    bool tooBig;
+    if (pfP)
+        pm_error("This program is not capable of generating "
+                 "a PNG with transparency when you specify "
+                 "the palette with -palette.");
+
+    compute_alpha_palette(ifP, cols, rows, maxval, format, 
+                          rasterPos,  alpha_mask, palette_pnm, trans_pnm, 
+                          paletteSizeP, transSizeP, &tooBig);
+    if (tooBig) {
+        asprintfN(impossibleReasonP,
+                  "too many color/transparency pairs "
+                  "(more than the PNG maximum of %u", 
+                  MAXPALETTEENTRIES);
+    } else
+        *impossibleReasonP = NULL;
+} 
+
+
+
+static void
+computePixelWidth(int            const pnm_type,
+                  unsigned int   const pnm_meaningful_bits,
+                  bool           const alpha,
+                  unsigned int * const bitsPerSampleP,
+                  unsigned int * const bitsPerPixelP) {
+
+    unsigned int bitsPerSample, bitsPerPixel;
+
+    if (pnm_type == PPM_TYPE || alpha) {
+        /* PNG allows only depths of 8 and 16 for a truecolor image 
+           and for a grayscale image with an alpha channel.
+          */
+        if (pnm_meaningful_bits > 8)
+            bitsPerSample = 16;
+        else 
+            bitsPerSample = 8;
+    } else {
+        /* A grayscale, non-colormapped, no-alpha PNG may have any 
+             bit depth from 1 to 16
+          */
+        if (pnm_meaningful_bits > 8)
+            bitsPerSample = 16;
+        else if (pnm_meaningful_bits > 4)
+            bitsPerSample = 8;
+        else if (pnm_meaningful_bits > 2)
+            bitsPerSample = 4;
+        else if (pnm_meaningful_bits > 1)
+            bitsPerSample = 2;
+        else
+            bitsPerSample = 1;
+    }
+    if (alpha) {
+        if (pnm_type == PPM_TYPE)
+            bitsPerPixel = 4 * bitsPerSample;
+        else
+            bitsPerPixel = 2 * bitsPerSample;
+    } else {
+        if (pnm_type == PPM_TYPE)
+            bitsPerPixel = 3 * bitsPerSample;
+        else
+            bitsPerPixel = bitsPerSample;
+    }
+    if (bitsPerPixelP)
+        *bitsPerPixelP = bitsPerPixel;
+    if (bitsPerSampleP)
+        *bitsPerSampleP = bitsPerSample;
+}
+
+
+
+static unsigned int
+paletteIndexBits(unsigned int const nColors) {
+/*----------------------------------------------------------------------------
+  Return the number of bits that a palette index in the PNG will
+  occupy given that the palette has 'nColors' colors in it.  It is 1,
+  2, 4, or 8 bits.
+  
+  If 'nColors' is not a valid PNG palette size, return 0.
+-----------------------------------------------------------------------------*/
+    unsigned int retval;
+
+    if (nColors < 1)
+        retval = 0;
+    else if (nColors <= 2)
+        retval = 1;
+    else if (nColors <= 4)
+        retval = 2;
+    else if (nColors <= 16)
+        retval = 4;
+    else if (nColors <= 256)
+        retval = 8;
+    else
+        retval = 0;
+
+    return retval;
+}
+
+
+
+static void
+computeColorMap(FILE *         const ifP,
+                pm_filepos     const rasterPos,
+                int            const cols,
+                int            const rows,
+                xelval         const maxval,
+                int            const format,
+                bool           const force,
+                FILE *         const pfP,
+                bool           const alpha,
+                bool           const transparent,
+                pixel          const transcolor,
+                bool           const transexact,
+                bool           const background,
+                pixel          const backcolor,
+                gray **        const alpha_mask,
+                unsigned int   const pnm_meaningful_bits,
+                /* Outputs */
+                pixel *        const palette_pnm,
+                unsigned int * const paletteSizeP,
+                gray *         const trans_pnm,
+                unsigned int * const transSizeP,
+                unsigned int * const backgroundIndexP,
+                const char **  const noColormapReasonP) {
+/*---------------------------------------------------------------------------
+  Determine whether to do a colormapped or truecolor PNG and if
+  colormapped, compute the full PNG palette -- both color and
+  transparency.
+
+  If we decide to do truecolor, we return as *noColormapReasonP a text
+  description of why, in newly malloced memory.  If we decide to go
+  with colormapped, we return *noColormapReasonP == NULL.
+
+  In the colormapped case, we return the palette as arrays
+  palette_pnm[] and trans_pnm[], allocated by Caller, with sizes
+  *paletteSizeP and *transSizeP.
+
+  'background' means the image is to have a background color, and that
+  color is 'backcolor'.  'backcolor' is meaningless when 'background'
+  is false.
+
+  If the image is to have a background color, we return the palette index
+  of that color as *backgroundIndexP.
+-------------------------------------------------------------------------- */
+    if (force)
+        asprintfN(noColormapReasonP, "You requested no color map");
+    else if (maxval > PALETTEMAXVAL)
+        asprintfN(noColormapReasonP, "The maxval of the input image (%u) "
+                  "exceeds the PNG palette maxval (%u)", 
+                  maxval, PALETTEMAXVAL);
+    else {
+        unsigned int bitsPerPixel;
+        computePixelWidth(PNM_FORMAT_TYPE(format), pnm_meaningful_bits, alpha,
+                          NULL, &bitsPerPixel);
+
+        if (!pfP && bitsPerPixel == 1)
+            /* No palette can beat 1 bit per pixel -- no need to waste time
+               counting the colors.
+            */
+            asprintfN(noColormapReasonP, "pixel is already only 1 bit");
+        else {
+            /* We'll have to count the colors ('colors') to know if a
+               palette is possible and desirable.  Along the way, we'll
+               compute the actual set of colors (chv) too, and then create
+               the palette itself if we decide we want one.
+            */
+            colorhist_vector chv;
+            unsigned int colors;
+            
+            getChv(ifP, rasterPos, cols, rows, maxval, format, MAXCOLORS, 
+                   &chv, &colors);
+
+            if (chv == NULL) {
+                asprintfN(noColormapReasonP, 
+                          "More than %u colors found -- too many for a "
+                          "colormapped PNG", MAXCOLORS);
+            } else {
+                /* There are few enough colors that a palette is possible */
+                if (bitsPerPixel <= paletteIndexBits(colors) && !pfP)
+                    asprintfN(noColormapReasonP, 
+                              "palette index for %u colors would be "
+                              "no smaller than the indexed value (%u bits)", 
+                              colors, bitsPerPixel);
+                else {
+                    unsigned int paletteSize;
+                    unsigned int transSize;
+                    if (alpha)
+                        tryAlphaPalette(ifP, cols, rows, maxval, format,
+                                        rasterPos, alpha_mask, pfP,
+                                        palette_pnm, &paletteSize, 
+                                        trans_pnm, &transSize,
+                                        noColormapReasonP);
+
+                    else {
+                        *noColormapReasonP = NULL;
+
+                        compute_nonalpha_palette(chv, colors, maxval, pfP,
+                                                 palette_pnm, &paletteSize, 
+                                                 trans_pnm, &transSize);
+    
+                        if (transparent)
+                            makeOneColorTransparentInPalette(
+                                transcolor, transexact, 
+                                palette_pnm, paletteSize, trans_pnm, 
+                                &transSize);
+                    }
+                    if (!*noColormapReasonP) {
+                        if (background)
+                            findOrAddBackgroundInPalette(
+                                backcolor, palette_pnm, &paletteSize,
+                                backgroundIndexP);
+                        *paletteSizeP = paletteSize;
+                        *transSizeP   = transSize;
+                    }
+                }
+            }
+            freeChv();
+        }
+    }
+}
+
+
+
+static void computeColorMapLookupTable(
+    bool                   const colorMapped,
+    pixel                        palette_pnm[],
+    unsigned int           const palette_size,
+    gray                         trans_pnm[],
+    unsigned int           const trans_size,
+    bool                   const alpha,
+    xelval                 const alpha_maxval,
+    colorhash_table *      const chtP,
+    coloralphahash_table * const cahtP) {
+/*----------------------------------------------------------------------------
+   Compute applicable lookup tables for the palette index.  If there's no
+   alpha mask, this is just a standard Netpbm colorhash_table.  If there's
+   an alpha mask, it is the slower Pnmtopng-specific 
+   coloralphahash_table.
+
+   If a lookup table is not applicable to the image, return NULL as
+   its address.  (If the image is not colormapped, both will be NULL).
+-----------------------------------------------------------------------------*/
+    if (colorMapped) {
+        if (alpha) {
+            buildColorAlphaLookup(palette_pnm, palette_size, 
+                                  trans_pnm, trans_size, alpha_maxval, cahtP);
+            *chtP = NULL;
+        } else { 
+            buildColorLookup(palette_pnm, palette_size, chtP);
+            *cahtP = NULL;
+        }
+        if (verbose)
+            pm_message("PNG palette has %u entries, %u of them non-opaque",
+                       palette_size, trans_size);
+    } else {
+        *chtP = NULL;
+        *cahtP = NULL;
+    }
+}
+
+
+
+static void
+computeRasterWidth(bool           const colorMapped,
+                   unsigned int   const palette_size,
+                   int            const pnm_type,
+                   unsigned int   const pnm_meaningful_bits,
+                   bool           const alpha,
+                   unsigned int * const bitsPerSampleP,
+                   unsigned int * const bitsPerPixelP) {
+/*----------------------------------------------------------------------------
+   Compute the number of bits per raster sample and per raster pixel:
+   *bitsPerSampleP and *bitsPerPixelP.  Note that a raster element may be a
+   palette index, or a gray value or color with or without alpha mask.
+-----------------------------------------------------------------------------*/
+    if (colorMapped) {
+        /* The raster element is a palette index */
+        if (palette_size <= 2)
+            *bitsPerSampleP = 1;
+        else if (palette_size <= 4)
+            *bitsPerSampleP = 2;
+        else if (palette_size <= 16)
+            *bitsPerSampleP = 4;
+        else
+            *bitsPerSampleP = 8;
+        *bitsPerPixelP = *bitsPerSampleP;
+        if (verbose)
+            pm_message("Writing %d-bit color indexes", *bitsPerSampleP);
+    } else {
+        /* The raster element is an explicit pixel -- color and transparency */
+        computePixelWidth(pnm_type, pnm_meaningful_bits, alpha,
+                          bitsPerSampleP, bitsPerPixelP);
+
+        if (verbose)
+            pm_message("Writing %d bits per component per pixel", 
+                       *bitsPerSampleP);
+    }
+}
+
+
+static void
+createPngPalette(pixel              palette_pnm[], 
+                 unsigned int const paletteSize, 
+                 pixval       const maxval,
+                 gray               trans_pnm[],
+                 unsigned int const transSize,
+                 gray               alpha_maxval,
+                 png_color          palette[],
+                 png_byte           trans[]) {
+/*----------------------------------------------------------------------------
+   Create the data structure to be passed to the PNG compressor to represent
+   the palette -- the whole palette, color + transparency.
+
+   This is basically just a maxval conversion from the Netpbm-format
+   equivalents we get as input.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+
+    for (i = 0; i < paletteSize; ++i) {
+        pixel p;
+        PPM_DEPTH(p, palette_pnm[i], maxval, PALETTEMAXVAL);
+        palette[i].red   = PPM_GETR(p);
+        palette[i].green = PPM_GETG(p);
+        palette[i].blue  = PPM_GETB(p);
+    }
+
+    for (i = 0; i < transSize; ++i) {
+        unsigned int const newmv = PALETTEMAXVAL;
+        unsigned int const oldmv = alpha_maxval;
+        trans[i] = (trans_pnm[i] * newmv + (oldmv/2)) / oldmv;
+    }
+}
+
+
+
+static void
+setCompressionSize(png_struct * const png_ptr,
+                   int const    buffer_size) {
+
+#if PNG_LIBPNG_VER >= 10009
+    png_set_compression_buffer_size(png_ptr, buffer_size);
+#else
+    pm_error("Your PNG library cannot set the compression buffer size.  "
+             "You need at least Version 1.0.9 of Libpng; you have Version %s",
+             PNG_LIBPNG_VER_STRING);
+#endif
+}
+
+
+
+static void
+setZlibCompression(png_struct *           const png_ptr,
+                   struct zlibCompression const zlibCompression) {
+
+    if (zlibCompression.levelSpec)
+        png_set_compression_level(png_ptr, zlibCompression.level);
+
+    if (zlibCompression.memLevelSpec)
+        png_set_compression_mem_level(png_ptr, zlibCompression.mem_level);
+
+    if (zlibCompression.strategySpec)
+        png_set_compression_strategy(png_ptr, zlibCompression.strategy);
+
+    if (zlibCompression.windowBitsSpec)
+        png_set_compression_window_bits(png_ptr, zlibCompression.window_bits);
+
+    if (zlibCompression.methodSpec)
+        png_set_compression_method(png_ptr, zlibCompression.method);
+
+    if (zlibCompression.bufferSizeSpec) {
+        setCompressionSize(png_ptr, zlibCompression.buffer_size);
+    }
+}
+                  
+
+
+static void
+makePngLine(png_byte *           const line,
+            const xel *          const xelrow,
+            unsigned int         const cols,
+            xelval               const maxval,
+            bool                 const alpha,
+            gray *               const alpha_mask,
+            colorhash_table      const cht,
+            coloralphahash_table const caht,
+            png_info *           const info_ptr,
+            xelval               const png_maxval,
+            unsigned int         const depth) {
+            
+    unsigned int col;
+    png_byte *pp;
+
+    pp = line;  /* start at beginning of line */
+    for (col = 0; col < cols; ++col) {
+        xel p_png;
+        xel const p = xelrow[col];
+        PPM_DEPTH(p_png, p, maxval, png_maxval);
+        if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
+            info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+            if (depth == 16)
+                *pp++ = PNM_GET1(p_png) >> 8;
+            *pp++ = PNM_GET1(p_png) & 0xff;
+        } else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
+            unsigned int paletteIndex;
+            if (alpha)
+                paletteIndex = lookupColorAlpha(caht, &p, &alpha_mask[col]);
+            else
+                paletteIndex = ppm_lookupcolor(cht, &p);
+            *pp++ = paletteIndex;
+        } else if (info_ptr->color_type == PNG_COLOR_TYPE_RGB ||
+                   info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
+            if (depth == 16)
+                *pp++ = PPM_GETR(p_png) >> 8;
+            *pp++ = PPM_GETR(p_png) & 0xff;
+            if (depth == 16)
+                *pp++ = PPM_GETG(p_png) >> 8;
+            *pp++ = PPM_GETG(p_png) & 0xff;
+            if (depth == 16)
+                *pp++ = PPM_GETB(p_png) >> 8;
+            *pp++ = PPM_GETB(p_png) & 0xff;
+        } else
+            pm_error("INTERNAL ERROR: undefined color_type");
+                
+        if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) {
+            int const png_alphaval = (int)
+                alpha_mask[col] * (float) png_maxval / maxval + 0.5;
+            if (depth == 16)
+                *pp++ = png_alphaval >> 8;
+            *pp++ = png_alphaval & 0xff;
+        }
+    }
+}
+
+
+
+static void
+writeRaster(png_struct *         const png_ptr,
+            png_info *           const info_ptr,
+            FILE *               const ifP,
+            pm_filepos           const rasterPos,
+            unsigned int         const cols,
+            unsigned int         const rows,
+            xelval               const maxval,
+            int                  const format,
+            xelval               const png_maxval,
+            unsigned             const int depth,
+            bool                 const alpha,
+            gray **              const alpha_mask,
+            colorhash_table      const cht,
+            coloralphahash_table const caht
+            ) {
+/*----------------------------------------------------------------------------
+   Write the PNG raster via compressor *png_ptr, reading the PNM raster
+   from file *ifP, position 'rasterPos'.
+
+   The PNG raster consists of IDAT chunks.
+
+   'alpha_mask' is defined only if 'alpha' is true.
+-----------------------------------------------------------------------------*/
+    xel * xelrow;
+    png_byte * line;
+    unsigned int pass;
+
+    xelrow = pnm_allocrow(cols);
+
+    /* max: 3 color channels, one alpha channel, 16-bit */
+    MALLOCARRAY(line, cols * 8);
+    if (line == NULL)
+        pm_error("out of memory allocating PNG row buffer");
+
+    for (pass = 0; pass < png_set_interlace_handling(png_ptr); ++pass) {
+        unsigned int row;
+        pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
+        for (row = 0; row < rows; ++row) {
+            pnm_readpnmrow(ifP, xelrow, cols, maxval, format);
+            pnm_promoteformatrow(xelrow, cols, maxval, format, maxval,
+                                 PPM_TYPE);
+            
+            makePngLine(line, xelrow, cols, maxval,
+                        alpha, alpha ? alpha_mask[row] : NULL,
+                        cht, caht, info_ptr, png_maxval, depth);
+
+            png_write_row(png_ptr, line);
+        }
+    }
+    pnm_freerow(xelrow);
+}
+
+
+
+static void
+doGamaChunk(struct cmdlineInfo const cmdline,
+            png_info *         const info_ptr) {
+            
+    if (cmdline.gammaSpec) {
+        /* gAMA chunk */
+        info_ptr->valid |= PNG_INFO_gAMA;
+        info_ptr->gamma = cmdline.gamma;
+    }
+}
+
+
+
+static void
+doChrmChunk(struct cmdlineInfo const cmdline,
+            png_info *         const info_ptr) {
+
+    if (cmdline.rgbSpec) {
+        /* cHRM chunk */
+        info_ptr->valid |= PNG_INFO_cHRM;
+
+        info_ptr->x_white = cmdline.rgb.wx;
+        info_ptr->y_white = cmdline.rgb.wy;
+        info_ptr->x_red   = cmdline.rgb.rx;
+        info_ptr->y_red   = cmdline.rgb.ry;
+        info_ptr->x_green = cmdline.rgb.gx;
+        info_ptr->y_green = cmdline.rgb.gy;
+        info_ptr->x_blue  = cmdline.rgb.bx;
+        info_ptr->y_blue  = cmdline.rgb.by;
+    }
+}
+
+
+
+static void
+doPhysChunk(struct cmdlineInfo const cmdline,
+            png_info *         const info_ptr) {
+
+    if (cmdline.sizeSpec) {
+        /* pHYS chunk */
+        info_ptr->valid |= PNG_INFO_pHYs;
+
+        info_ptr->x_pixels_per_unit = cmdline.size.x;
+        info_ptr->y_pixels_per_unit = cmdline.size.y;
+        info_ptr->phys_unit_type    = cmdline.size.unit;
+    }
+}
+
+
+
+
+static void
+doTimeChunk(struct cmdlineInfo const cmdline,
+            png_info *         const info_ptr) {
+
+    if (cmdline.modtimeSpec) {
+        /* tIME chunk */
+        info_ptr->valid |= PNG_INFO_tIME;
+
+        png_convert_from_time_t(&info_ptr->mod_time, cmdline.modtime);
+    }
+}
+
+
+
+static void
+doSbitChunk(png_info * const pngInfoP,
+            xelval     const pngMaxval,
+            xelval     const maxval,
+            bool       const alpha,
+            xelval     const alphaMaxval) {
+
+    if (pngInfoP->color_type != PNG_COLOR_TYPE_PALETTE &&
+        (pngMaxval > maxval || (alpha && pngMaxval > alphaMaxval))) {
+
+        /* We're writing in a bit depth that doesn't match the maxval
+           of the input image and the alpha mask.  So we write an sBIT
+           chunk to tell what the original image's maxval was.  The
+           sBit chunk doesn't let us specify any maxval -- only powers
+           of two minus one.  So we pick the power of two minus one
+           which is greater than or equal to the actual input maxval.
+           
+           PNG also doesn't let an sBIT chunk indicate a maxval
+           _greater_ than the the PNG maxval.  The designers probably
+           did not conceive of the case where that would happen.  The
+           case is this: We detected redundancy in the bits so were
+           able to store fewer bits than the user provided.  But since
+           PNG doesn't allow it, we don't attempt to create such an
+           sBIT chunk.
+        */
+
+        pngInfoP->valid |= PNG_INFO_sBIT;
+
+        {
+            int const sbitval = pm_maxvaltobits(MIN(maxval, pngMaxval));
+
+            if (pngInfoP->color_type & PNG_COLOR_MASK_COLOR) {
+                pngInfoP->sig_bit.red   = sbitval;
+                pngInfoP->sig_bit.green = sbitval;
+                pngInfoP->sig_bit.blue  = sbitval;
+            } else
+                pngInfoP->sig_bit.gray = sbitval;
+            
+            if (verbose)
+                pm_message("Writing sBIT chunk with bits = %d", sbitval);
+        }
+        if (pngInfoP->color_type & PNG_COLOR_MASK_ALPHA) {
+            pngInfoP->sig_bit.alpha =
+                pm_maxvaltobits(MIN(alphaMaxval, pngMaxval));
+            if (verbose)
+                pm_message("  alpha bits = %d", pngInfoP->sig_bit.alpha);
+        }
+    }
+}
+
+
+
+static void 
+convertpnm(struct cmdlineInfo const cmdline,
+           FILE *             const ifp,
+           FILE *             const afp,
+           FILE *             const pfp,
+           FILE *             const tfp,
+           int *              const errorLevelP
+    ) {
+/*----------------------------------------------------------------------------
+   Design note:  It's is really a modularity violation that we have
+   all the command line parameters as an argument.  We do it because we're
+   lazy -- it takes a great deal of work to carry all that information as
+   separate arguments -- and it's only a very small violation.
+-----------------------------------------------------------------------------*/
+  xel p;
+  int rows, cols, format;
+  xelval maxval;
+      /* The maxval of the input image */
+  xelval png_maxval;
+      /* The maxval of the samples in the PNG output 
+         (must be 1, 3, 7, 15, 255, or 65535)
+      */
+  pixel transcolor;
+      /* The color that is to be transparent, with maxval equal to that
+         of the input image.
+      */
+  int transexact;  
+    /* boolean: the user wants only the exact color he specified to be
+       transparent; not just something close to it.
+    */
+  int transparent;
+  bool alpha;
+    /* There will be an alpha mask */
+  unsigned int pnm_meaningful_bits;
+  pixel backcolor;
+      /* The background color, with maxval equal to that of the input
+         image.
+      */
+  png_struct *png_ptr;
+  png_info *info_ptr;
+
+  bool colorMapped;
+  pixel palette_pnm[MAXCOLORS];
+  png_color palette[MAXCOLORS];
+      /* The color part of the color/alpha palette passed to the PNG
+         compressor 
+      */
+  unsigned int palette_size;
+
+  gray trans_pnm[MAXCOLORS];
+  png_byte  trans[MAXCOLORS];
+      /* The alpha part of the color/alpha palette passed to the PNG
+         compressor 
+      */
+  unsigned int trans_size;
+
+  colorhash_table cht;
+  coloralphahash_table caht;
+
+  unsigned int background_index;
+      /* Index into palette[] of the background color. */
+
+  png_uint_16 histogram[MAXCOLORS];
+  gray alpha_maxval;
+  int alpha_rows;
+  int alpha_cols;
+  const char * noColormapReason;
+      /* The reason that we shouldn't make a colormapped PNG, or NULL if
+         we should.  malloc'ed null-terminated string.
+      */
+  unsigned int depth;
+      /* The number of bits per sample in the (uncompressed) png 
+         raster -- if the raster contains palette indices, this is the
+         number of bits in the index.
+      */
+  unsigned int fulldepth;
+      /* The total number of bits per pixel in the (uncompressed) png
+         raster, including all channels 
+      */
+  pm_filepos rasterPos;  
+      /* file position in input image file of start of image (i.e. after
+         the header)
+      */
+  xel *xelrow;    /* malloc'ed */
+      /* The row of the input image currently being processed */
+
+  int pnm_type;
+  xelval maxmaxval;
+  gray ** alpha_mask;
+
+  /* these guys are initialized to quiet compiler warnings: */
+  maxmaxval = 255;
+  alpha_mask = NULL;
+  depth = 0;
+  errorlevel = 0;
+
+  png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
+    &pnmtopng_jmpbuf_struct, pnmtopng_error_handler, NULL);
+  if (png_ptr == NULL) {
+    pm_closer (ifp);
+    pm_error ("cannot allocate main libpng structure (png_ptr)");
+  }
+
+  info_ptr = png_create_info_struct (png_ptr);
+  if (info_ptr == NULL) {
+    png_destroy_write_struct (&png_ptr, (png_infopp)NULL);
+    pm_closer (ifp);
+    pm_error ("cannot allocate libpng info structure (info_ptr)");
+  }
+
+  if (setjmp (pnmtopng_jmpbuf_struct.jmpbuf)) {
+    png_destroy_write_struct (&png_ptr, &info_ptr);
+    pm_closer (ifp);
+    pm_error ("setjmp returns error condition (1)");
+  }
+
+  pnm_readpnminit (ifp, &cols, &rows, &maxval, &format);
+  pm_tell2(ifp, &rasterPos, sizeof(rasterPos));
+  pnm_type = PNM_FORMAT_TYPE (format);
+
+  xelrow = pnm_allocrow(cols);
+
+  if (verbose) {
+    if (pnm_type == PBM_TYPE)    
+      pm_message ("reading a PBM file (maxval=%d)", maxval);
+    else if (pnm_type == PGM_TYPE)    
+      pm_message ("reading a PGM file (maxval=%d)", maxval);
+    else if (pnm_type == PPM_TYPE)    
+      pm_message ("reading a PPM file (maxval=%d)", maxval);
+  }
+
+  if (pnm_type == PGM_TYPE)
+    maxmaxval = PGM_OVERALLMAXVAL;
+  else if (pnm_type == PPM_TYPE)
+    maxmaxval = PPM_OVERALLMAXVAL;
+
+  if (cmdline.transparent) {
+      const char * transstring2;  
+          /* The -transparent value, but with possible leading '=' removed */
+      if (cmdline.transparent[0] == '=') {
+          transexact = 1;
+          transstring2 = &cmdline.transparent[1];
+      } else {
+          transexact = 0;
+          transstring2 = cmdline.transparent;
+      }  
+      /* We do this funny PPM_DEPTH thing instead of just passing 'maxval'
+         to ppm_parsecolor() because ppm_parsecolor() does a cheap maxval
+         scaling, and this is more precise.
+      */
+      PPM_DEPTH (transcolor, ppm_parsecolor(transstring2, maxmaxval),
+                 maxmaxval, maxval);
+  }
+  if (cmdline.alpha) {
+    pixel alpha_transcolor;
+    bool alpha_can_be_transparency_index;
+    bool all_opaque;
+
+    if (verbose)
+      pm_message ("reading alpha-channel image...");
+    alpha_mask = pgm_readpgm (afp, &alpha_cols, &alpha_rows, &alpha_maxval);
+
+    if (alpha_cols != cols || alpha_rows != rows) {
+      png_destroy_write_struct (&png_ptr, &info_ptr);
+      pm_closer (ifp);
+      pm_error ("dimensions for image and alpha mask do not agree");
+    }
+    analyzeAlpha(ifp, rasterPos, cols, rows, maxval, format, 
+                 alpha_mask, alpha_maxval, &all_opaque,
+                 &alpha_can_be_transparency_index, &alpha_transcolor);
+
+    if (alpha_can_be_transparency_index && !cmdline.force) {
+      if (verbose)
+        pm_message ("converting alpha mask to transparency index");
+      alpha = FALSE;
+      transparent = 2;
+      transcolor = alpha_transcolor;
+    } else if (all_opaque) {
+        alpha = FALSE;
+        transparent = -1;
+    } else {
+      alpha = TRUE;
+      transparent = -1;
+    }
+  } else {
+      /* Though there's no alpha_mask, we still need an alpha_maxval for
+         use with trans[], which can have stuff in it if the user specified
+         a transparent color.
+      */
+      alpha = FALSE;
+      alpha_maxval = 255;
+      transparent = cmdline.transparent ? 1 : -1;
+  }
+  if (cmdline.background) 
+      PPM_DEPTH(backcolor, ppm_parsecolor(cmdline.background, maxmaxval), 
+                maxmaxval, maxval);;
+
+  /* first of all, check if we have a grayscale image written as PPM */
+
+  if (pnm_type == PPM_TYPE && !cmdline.force) {
+      unsigned int row;
+      bool isgray;
+
+      isgray = TRUE;  /* initial assumption */
+      pm_seek2(ifp, &rasterPos, sizeof(rasterPos));
+      for (row = 0; row < rows && isgray; ++row) {
+          unsigned int col;
+          pnm_readpnmrow(ifp, xelrow, cols, maxval, format);
+          for (col = 0; col < cols && isgray; ++col) {
+              p = xelrow[col];
+              if (PPM_GETR(p) != PPM_GETG(p) || PPM_GETG(p) != PPM_GETB(p))
+                  isgray = FALSE;
+          }
+      }
+      if (isgray)
+          pnm_type = PGM_TYPE;
+  }
+
+  /* handle `odd' maxvalues */
+
+  if (maxval > 65535 && !cmdline.downscale) {
+      png_destroy_write_struct(&png_ptr, &info_ptr);
+      pm_closer(ifp);
+      pm_error("can only handle files up to 16-bit "
+               "(use -downscale to override");
+  }
+
+  findRedundantBits(ifp, rasterPos, cols, rows, maxval, format, alpha,
+                    cmdline.force, &pnm_meaningful_bits);
+  
+  computeColorMap(ifp, rasterPos, cols, rows, maxval, format,
+                  cmdline.force, pfp,
+                  alpha, transparent >= 0, transcolor, transexact, 
+                  !!cmdline.background, backcolor,
+                  alpha_mask, pnm_meaningful_bits,
+                  palette_pnm, &palette_size, trans_pnm, &trans_size,
+                  &background_index, &noColormapReason);
+
+  if (noColormapReason) {
+      if (pfp)
+          pm_error("You specified a particular palette, but this image "
+                   "cannot be represented by any palette.  %s",
+                   noColormapReason);
+      if (verbose)
+          pm_message("Not using color map.  %s", noColormapReason);
+      strfree(noColormapReason);
+      colorMapped = FALSE;
+  } else
+      colorMapped = TRUE;
+  
+  computeColorMapLookupTable(colorMapped, palette_pnm, palette_size,
+                             trans_pnm, trans_size, alpha, alpha_maxval,
+                             &cht, &caht);
+
+  computeRasterWidth(colorMapped, palette_size, pnm_type, 
+                     pnm_meaningful_bits, alpha,
+                     &depth, &fulldepth);
+  if (verbose)
+    pm_message ("writing a%s %d-bit %s%s file%s",
+                fulldepth == 8 ? "n" : "", fulldepth,
+                colorMapped ? "palette": 
+                (pnm_type == PPM_TYPE ? "RGB" : "gray"),
+                alpha ? (colorMapped ? "+transparency" : "+alpha") : "",
+                cmdline.interlace ? " (interlaced)" : "");
+
+  /* now write the file */
+
+  png_maxval = pm_bitstomaxval(depth);
+
+  if (setjmp (pnmtopng_jmpbuf_struct.jmpbuf)) {
+    png_destroy_write_struct (&png_ptr, &info_ptr);
+    pm_closer (ifp);
+    pm_error ("setjmp returns error condition (2)");
+  }
+
+  png_init_io (png_ptr, stdout);
+  info_ptr->width = cols;
+  info_ptr->height = rows;
+  info_ptr->bit_depth = depth;
+
+  if (colorMapped)
+    info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
+  else if (pnm_type == PPM_TYPE)
+    info_ptr->color_type = PNG_COLOR_TYPE_RGB;
+  else
+    info_ptr->color_type = PNG_COLOR_TYPE_GRAY;
+
+  if (alpha && info_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
+    info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
+
+  info_ptr->interlace_type = cmdline.interlace;
+
+  doGamaChunk(cmdline, info_ptr);
+
+  doChrmChunk(cmdline, info_ptr);
+
+  doPhysChunk(cmdline, info_ptr);
+
+  if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
+
+    /* creating PNG palette  (PLTE and tRNS chunks) */
+
+    createPngPalette(palette_pnm, palette_size, maxval,
+                     trans_pnm, trans_size, alpha_maxval, 
+                     palette, trans);
+    info_ptr->valid |= PNG_INFO_PLTE;
+    info_ptr->palette = palette;
+    info_ptr->num_palette = palette_size;
+    if (trans_size > 0) {
+        info_ptr->valid |= PNG_INFO_tRNS;
+        info_ptr->trans = trans;
+        info_ptr->num_trans = trans_size;   /* omit opaque values */
+    }
+    /* creating hIST chunk */
+    if (cmdline.hist) {
+        colorhist_vector chv;
+        unsigned int colors;
+        colorhash_table cht;
+        
+        getChv(ifp, rasterPos, cols, rows, maxval, format, MAXCOLORS, 
+               &chv, &colors);
+
+        cht = ppm_colorhisttocolorhash (chv, colors);
+                
+        { 
+            unsigned int i;
+            for (i = 0 ; i < MAXCOLORS; ++i) {
+                int const chvIndex = ppm_lookupcolor(cht, &palette_pnm[i]);
+                if (chvIndex == -1)
+                    histogram[i] = 0;
+                else
+                    histogram[i] = chv[chvIndex].value;
+            }
+        }
+
+        ppm_freecolorhash(cht);
+
+        info_ptr->valid |= PNG_INFO_hIST;
+        info_ptr->hist = histogram;
+        if (verbose)
+            pm_message("histogram created");
+    }
+  } else { /* color_type != PNG_COLOR_TYPE_PALETTE */
+    if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
+        info_ptr->color_type == PNG_COLOR_TYPE_RGB) {
+        if (transparent > 0) {
+            info_ptr->valid |= PNG_INFO_tRNS;
+            info_ptr->trans_values = 
+                xelToPngColor_16(transcolor, maxval, png_maxval);
+        }
+    } else {
+        /* This is PNG_COLOR_MASK_ALPHA.  Transparency will be handled
+           by the alpha channel, not a transparency color.
+        */
+    }
+    if (verbose) {
+        if (info_ptr->valid && PNG_INFO_tRNS) 
+            pm_message("Transparent color {gray, red, green, blue} = "
+                       "{%d, %d, %d, %d}",
+                       info_ptr->trans_values.gray,
+                       info_ptr->trans_values.red,
+                       info_ptr->trans_values.green,
+                       info_ptr->trans_values.blue);
+        else
+            pm_message("No transparent color");
+    }
+  }
+
+  /* bKGD chunk */
+  if (cmdline.background) {
+      info_ptr->valid |= PNG_INFO_bKGD;
+      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
+          info_ptr->background.index = background_index;
+      } else {
+          info_ptr->background = 
+              xelToPngColor_16(backcolor, maxval, png_maxval);
+          if (verbose)
+              pm_message("Writing bKGD chunk with background color "
+                         " {gray, red, green, blue} = {%d, %d, %d, %d}",
+                         info_ptr->background.gray, 
+                         info_ptr->background.red, 
+                         info_ptr->background.green, 
+                         info_ptr->background.blue ); 
+      }
+  }
+
+  doSbitChunk(info_ptr, png_maxval, maxval, alpha, alpha_maxval);
+
+  /* tEXT and zTXT chunks */
+  if (cmdline.text || cmdline.ztxt)
+      pnmpng_read_text(info_ptr, tfp, !!cmdline.ztxt, cmdline.verbose);
+
+  doTimeChunk(cmdline, info_ptr);
+
+  if (cmdline.filterSet != 0)
+      png_set_filter(png_ptr, 0, cmdline.filterSet);
+
+  setZlibCompression(png_ptr, cmdline.zlibCompression);
+
+  /* write the png-info struct */
+  png_write_info(png_ptr, info_ptr);
+
+  if (cmdline.text || cmdline.ztxt)
+      /* prevent from being written twice with png_write_end */
+      info_ptr->num_text = 0;
+
+  if (cmdline.modtime)
+      /* prevent from being written twice with png_write_end */
+      info_ptr->valid &= ~PNG_INFO_tIME;
+
+  /* let libpng take care of, e.g., bit-depth conversions */
+  png_set_packing (png_ptr);
+
+  writeRaster(png_ptr, info_ptr, ifp, rasterPos, cols, rows, maxval, format,
+              png_maxval, depth, alpha, alpha_mask, cht, caht);
+
+  png_write_end (png_ptr, info_ptr);
+
+
+#if 0
+  /* The following code may be intended to solve some segfault problem
+     that arises with png_destroy_write_struct().  The latter is the
+     method recommended in the libpng documentation and this program 
+     will not compile under Cygwin because the Windows DLL for libpng
+     does not contain png_write_destroy() at all.  Since the author's
+     comment below does not make it clear what the segfault issue is,
+     we cannot consider it.  -Bryan 00.09.15
+*/
+
+  png_write_destroy (png_ptr);
+  /* flush first because free(png_ptr) can segfault due to jmpbuf problems
+     in png_write_destroy */
+  fflush (stdout);
+  free (png_ptr);
+  free (info_ptr);
+#else
+  png_destroy_write_struct(&png_ptr, &info_ptr);
+#endif
+
+  pnm_freerow(xelrow);
+
+  if (cht)
+      ppm_freecolorhash(cht);
+  if (caht)
+      freecoloralphahash(caht);
+
+  *errorLevelP = errorlevel;
+}
+
+
+
+static void
+displayVersion() {
+
+    fprintf(stderr,"Pnmtopng version %s.\n", NETPBM_VERSION);
+
+    /* We'd like to display the version of libpng with which we're
+       linked, as we do for zlib, but it isn't practical.
+       While libpng is capable of telling you what it's level
+       is, different versions of it do it two different ways: with
+       png_libpng_ver or with png_get_header_ver.  So we have to be
+       compiled for a particular version just to find out what
+       version it is! It's not worth having a link failure, much
+       less a compile failure, if we choose wrong.
+       png_get_header_ver is not in anything older than libpng 1.0.2a
+       (Dec 1998).  png_libpng_ver is not there in libraries built
+       without USE_GLOBAL_ARRAYS.  Cygwin versions are normally built
+       without USE_GLOBAL_ARRAYS.  -bjh 2002.06.17.
+    */
+    fprintf(stderr, "   Compiled with libpng %s.\n",
+            PNG_LIBPNG_VER_STRING);
+    fprintf(stderr, "   Compiled with zlib %s; using zlib %s.\n",
+            ZLIB_VERSION, zlib_version);
+    fprintf(stderr, "\n");
+}
+
+
+
+int 
+main(int argc, char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    FILE * afP;
+    FILE * pfP;
+    FILE * tfP;
+
+    int errorlevel;
+    
+    pnm_init (&argc, argv);
+    
+    parseCommandLine(argc, argv, &cmdline);
+    
+    if (cmdline.libversion) {
+        displayVersion();
+        return 0;
+    }
+    verbose = cmdline.verbose;
+    
+    ifP = pm_openr_seekable(cmdline.inputFilename);
+    
+    if (cmdline.alpha)
+        afP = pm_openr(cmdline.alpha);
+    else
+        afP = NULL;
+    
+    if (cmdline.palette)
+        pfP = pm_openr(cmdline.palette);
+    else
+        pfP = NULL;
+    
+    if (cmdline.text)
+        tfP = pm_openr(cmdline.text);
+    else if (cmdline.ztxt)
+        tfP = pm_openr(cmdline.ztxt);
+    else
+        tfP = NULL;
+
+    convertpnm(cmdline, ifP, afP, pfP, tfP, &errorlevel);
+    
+    if (afP)
+        pm_close(afP);
+    if (pfP)
+        pm_close(pfP);
+    if (tfP)
+        pm_close(tfP);
+
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return errorlevel;
+}
diff --git a/converter/other/pnmtops.c b/converter/other/pnmtops.c
new file mode 100644
index 00000000..9f6de526
--- /dev/null
+++ b/converter/other/pnmtops.c
@@ -0,0 +1,1323 @@
+/* pnmtops.c - read a PNM image and produce a PostScript program.
+
+   Copyright information is at end of file.
+
+   We produce two main kinds of Postscript program:
+
+      1) Use built in Postscript filters /ASCII85Decode, /ASCIIHexDecode,
+         /RunLengthDecode, and /FlateDecode;
+
+         We use methods we learned from Dirk Krause's program Bmeps and
+         raster encoding code copied almost directly from Bmeps.
+
+      2) Use our own filters and redefine /readstring .  This is aboriginal
+         Netpbm code, from when Postscript was young.
+
+   (2) is the default, because it's been working for ages and we have
+   more confidence in it.  But (1) gives more options.  The user
+   selects (1) with the -psfilter option.
+
+   We also do a few other bold new things only when the user specifies
+   -psfilter, because we're not sure they work for everyone.
+
+   (I actually don't know Postscript, so some of this description, not to
+   mention the code, may be totally bogus.)
+
+   NOTE: it is possible to put transparency information in an
+   encapsulated Postscript program.  Bmeps does this.  We don't.  It
+   might be hard to do, because in Postscript, the transparency information
+   goes in separate from the rest of the raster.
+*/
+
+#define _BSD_SOURCE  /* Make sure string.h contains strdup() */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include <string.h>
+#include <assert.h>
+#include "pam.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "nstring.h"
+#include "bmepsoe.h"
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;  /* Filespecs of input file */
+    float scale;
+    unsigned int dpiX;     /* horiz component of DPI option */
+    unsigned int dpiY;     /* vert component of DPI option */
+    unsigned int width;              /* in 1/72 inch */
+    unsigned int height;             /* in 1/72 inch */
+    unsigned int mustturn;
+    bool         canturn;
+    unsigned int rle;
+    bool         center;
+    unsigned int imagewidth;         /* in 1/72 inch; zero if unspec */
+    unsigned int imageheight;        /* in 1/72 inch; zero if unspec */
+    unsigned int equalpixels;
+    unsigned int setpage;
+    bool         showpage;
+    unsigned int level;
+    unsigned int levelSpec;
+    unsigned int psfilter;
+    unsigned int flate;
+    unsigned int ascii85;
+    unsigned int dict;
+    unsigned int vmreclaim;
+    unsigned int verbose;
+};
+
+
+static bool verbose;
+
+
+static void
+parseDpi(const char *   const dpiOpt, 
+         unsigned int * const dpiXP, 
+         unsigned int * const dpiYP) {
+
+    char *dpistr2;
+    unsigned int dpiX, dpiY;
+
+    dpiX = strtol(dpiOpt, &dpistr2, 10);
+    if (dpistr2 == dpiOpt) 
+        pm_error("Invalid value for -dpi: '%s'.  Must be either number "
+                 "or NxN ", dpiOpt);
+    else {
+        if (*dpistr2 == '\0') {
+            *dpiXP = dpiX;
+            *dpiYP = dpiX;
+        } else if (*dpistr2 == 'x') {
+            char * dpistr3;
+
+            dpistr2++;  /* Move past 'x' */
+            dpiY = strtol(dpistr2, &dpistr3, 10);        
+            if (dpistr3 != dpistr2 && *dpistr3 == '\0') {
+                *dpiXP = dpiX;
+                *dpiYP = dpiY;
+            } else {
+                pm_error("Invalid value for -dpi: '%s'.  Must be either "
+                         "number or NxN", dpiOpt);
+            }
+        }
+    }
+}
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+
+    unsigned int imagewidthSpec, imageheightSpec;
+    float imagewidth, imageheight;
+    unsigned int center, nocenter;
+    unsigned int nosetpage;
+    float width, height;
+    unsigned int noturn;
+    unsigned int showpage, noshowpage;
+    const char *dpiOpt;
+    unsigned int dpiSpec;
+
+    optStruct3 opt;
+    unsigned int option_def_index = 0;
+    optEntry *option_def;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    OPTENT3(0, "scale",       OPT_FLOAT, &cmdlineP->scale, NULL,         0);
+    OPTENT3(0, "dpi",         OPT_STRING, &dpiOpt,         &dpiSpec,     0);
+    OPTENT3(0, "width",       OPT_FLOAT, &width,           NULL,         0);
+    OPTENT3(0, "height",      OPT_FLOAT, &height,          NULL,         0);
+    OPTENT3(0, "psfilter",    OPT_FLAG,  NULL, &cmdlineP->psfilter,      0);
+    OPTENT3(0, "turn",        OPT_FLAG,  NULL, &cmdlineP->mustturn,      0);
+    OPTENT3(0, "noturn",      OPT_FLAG,  NULL, &noturn,                  0);
+    OPTENT3(0, "rle",         OPT_FLAG,  NULL, &cmdlineP->rle,           0);
+    OPTENT3(0, "runlength",   OPT_FLAG,  NULL, &cmdlineP->rle,           0);
+    OPTENT3(0, "ascii85",     OPT_FLAG,  NULL, &cmdlineP->ascii85,       0);
+    OPTENT3(0, "center",      OPT_FLAG,  NULL, &center,                  0);
+    OPTENT3(0, "nocenter",    OPT_FLAG,  NULL, &nocenter,                0);
+    OPTENT3(0, "equalpixels", OPT_FLAG,  NULL, &cmdlineP->equalpixels,   0);
+    OPTENT3(0, "imagewidth",  OPT_FLOAT, &imagewidth,  &imagewidthSpec,  0);
+    OPTENT3(0, "imageheight", OPT_FLOAT, &imageheight, &imageheightSpec, 0);
+    OPTENT3(0, "nosetpage",   OPT_FLAG,  NULL, &nosetpage,               0);
+    OPTENT3(0, "setpage",     OPT_FLAG,  NULL, &cmdlineP->setpage,       0);
+    OPTENT3(0, "noshowpage",  OPT_FLAG,  NULL, &noshowpage,              0);
+    OPTENT3(0, "flate",       OPT_FLAG,  NULL, &cmdlineP->flate,         0);
+    OPTENT3(0, "dict",        OPT_FLAG,  NULL, &cmdlineP->dict,          0);
+    OPTENT3(0, "vmreclaim",   OPT_FLAG,  NULL, &cmdlineP->vmreclaim,     0);
+    OPTENT3(0, "showpage",    OPT_FLAG,  NULL, &showpage,                0);
+    OPTENT3(0, "verbose",     OPT_FLAG,  NULL, &cmdlineP->verbose,       0);
+    OPTENT3(0, "level",       OPT_UINT, &cmdlineP->level, 
+            &cmdlineP->levelSpec,              0);
+    
+    /* DEFAULTS */
+    cmdlineP->scale = 1.0;
+    width = 8.5;
+    height = 11.0;
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;
+    opt.allowNegNum = FALSE;
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+
+    if (cmdlineP->mustturn && noturn)
+        pm_error("You cannot specify both -turn and -noturn");
+    if (center && nocenter)
+        pm_error("You cannot specify both -center and -nocenter");
+    if (showpage && noshowpage)
+        pm_error("You cannot specify both -showpage and -noshowpage");
+    if (cmdlineP->setpage && nosetpage)
+        pm_error("You cannot specify both -setpage and -nosetpage");
+
+    if (dpiSpec)
+        parseDpi(dpiOpt, &cmdlineP->dpiX, &cmdlineP->dpiY);
+    else {
+        cmdlineP->dpiX = 300;
+        cmdlineP->dpiY = 300;
+    }
+
+    cmdlineP->center  =  !nocenter;
+    cmdlineP->canturn =  !noturn;
+    cmdlineP->showpage = !noshowpage;
+    
+    cmdlineP->width  = width * 72;
+    cmdlineP->height = height * 72;
+
+    if (imagewidthSpec)
+        cmdlineP->imagewidth = imagewidth * 72;
+    else
+        cmdlineP->imagewidth = 0;
+    if (imageheightSpec)
+        cmdlineP->imageheight = imageheight * 72;
+    else
+        cmdlineP->imageheight = 0;
+
+    if (!cmdlineP->psfilter &&
+        (cmdlineP->flate || cmdlineP->ascii85))
+        pm_error("You must specify -psfilter in order to specify "
+                 "-flate or -ascii85");
+
+    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];
+
+}
+
+
+/*===========================================================================
+  The native output encoder.  This is archaic and uses global variables.
+  It is probably obsoleted by the bmeps output encoder; we just haven't
+  had a chance to verify that yet.
+===========================================================================*/
+
+/*----------------------------------------------------------------------------
+   The following global variables are the native output encoder state.
+-----------------------------------------------------------------------------*/
+static unsigned int itemsinline;
+    /* The number of items in the line we are currently building */
+static unsigned int bitsinitem;
+    /* The number of bits filled so far in the item we are currently
+       building 
+    */
+static unsigned int rlebitsinitem;
+    /* The number of bits filled so far in the item we are currently
+       building 
+    */
+static unsigned int bitspersample;
+static unsigned int item, bitshift, items;
+static unsigned int rleitem, rlebitshift;
+static unsigned int repeat, itembuf[128], count, repeatitem, repeatcount;
+
+
+
+static void
+initNativeOutputEncoder(bool const rle, unsigned int const bitspersample) {
+/*----------------------------------------------------------------------------
+   Initialize the native output encoder.  Call this once per
+   Postscript image that you will write with putitem(), before for the
+   first putitem().
+
+   We initialize the item putter state variables, which are the
+   global variable defined above.
+-----------------------------------------------------------------------------*/
+    itemsinline = 0;
+    items = 0;
+
+    if (rle) {
+        rleitem = 0;
+        rlebitsinitem = 0;
+        rlebitshift = 8 - bitspersample;
+        repeat = 1;
+        count = 0;
+    } else {
+        item = 0;
+        bitsinitem = 0;
+        bitshift = 8 - bitspersample;
+    }
+
+}
+
+
+
+static void
+putitem(void) {
+    const char* const hexits = "0123456789abcdef";
+
+    if (itemsinline == 30) {
+        putchar('\n');
+        itemsinline = 0;
+    }
+    putchar(hexits[item >> 4]);
+    putchar(hexits[item & 15]);
+    ++itemsinline;
+    ++items;
+    item = 0;
+    bitsinitem = 0;
+    bitshift = 8 - bitspersample;
+}
+
+
+
+static void
+flushitem() {
+    if (bitsinitem > 0)
+        putitem();
+}
+
+
+
+static void 
+putxelval(xelval const xv) {
+    if (bitsinitem == 8)
+        putitem();
+    item += xv << bitshift;
+    bitsinitem += bitspersample;
+    bitshift -= bitspersample;
+}
+
+
+
+static void
+rleputbuffer() {
+    if (repeat) {
+        item = 256 - count;
+        putitem();
+        item = repeatitem;
+        putitem();
+    } else {
+        unsigned int i;
+    
+        item = count - 1;
+        putitem();
+        for (i = 0; i < count; ++i) {
+            item = itembuf[i];
+            putitem();
+        }
+    }
+    repeat = 1;
+    count = 0;
+}
+
+
+
+static void
+rleputitem() {
+    int i;
+
+    if ( count == 128 )
+        rleputbuffer();
+
+    if ( repeat && count == 0 )
+    { /* Still initializing a repeat buf. */
+        itembuf[count] = repeatitem = rleitem;
+        ++count;
+    }
+    else if ( repeat )
+    { /* Repeating - watch for end of run. */
+        if ( rleitem == repeatitem )
+        { /* Run continues. */
+            itembuf[count] = rleitem;
+            ++count;
+        }
+        else
+        { /* Run ended - is it long enough to dump? */
+            if ( count > 2 )
+            { /* Yes, dump a repeat-mode buffer and start a new one. */
+                rleputbuffer();
+                itembuf[count] = repeatitem = rleitem;
+                ++count;
+            }
+            else
+            { /* Not long enough - convert to non-repeat mode. */
+                repeat = 0;
+                itembuf[count] = repeatitem = rleitem;
+                ++count;
+                repeatcount = 1;
+            }
+        }
+    }
+    else
+    { /* Not repeating - watch for a run worth repeating. */
+        if ( rleitem == repeatitem )
+        { /* Possible run continues. */
+            ++repeatcount;
+            if ( repeatcount > 3 )
+            { /* Long enough - dump non-repeat part and start repeat. */
+                count = count - ( repeatcount - 1 );
+                rleputbuffer();
+                count = repeatcount;
+                for ( i = 0; i < count; ++i )
+                    itembuf[i] = rleitem;
+            }
+            else
+            { /* Not long enough yet - continue as non-repeat buf. */
+                itembuf[count] = rleitem;
+                ++count;
+            }
+        }
+        else
+        { /* Broken run. */
+            itembuf[count] = repeatitem = rleitem;
+            ++count;
+            repeatcount = 1;
+        }
+    }
+
+    rleitem = 0;
+    rlebitsinitem = 0;
+    rlebitshift = 8 - bitspersample;
+}
+
+
+
+static void 
+rleputxelval(xelval const xv) {
+    if (rlebitsinitem == 8)
+        rleputitem();
+    rleitem += xv << rlebitshift;
+    rlebitsinitem += bitspersample;
+    rlebitshift -= bitspersample;
+}
+
+
+
+static void
+rleflush() {
+    if (rlebitsinitem > 0)
+        rleputitem();
+    if (count > 0)
+        rleputbuffer();
+}
+
+
+static void
+flushNativeOutput(bool const rle) {
+    if (rle)
+        rleflush();
+    else
+        flushitem();
+    printf("\n");
+}
+        
+/*===========================================================================
+  The BMEPS output encoder.
+===========================================================================*/
+
+/* This code is just a wrapper around the output encoder that is part of
+   Bmeps, to give it better modularity.
+*/
+
+struct bmepsoe {
+    Output_Encoder * oeP;
+    int * rleBuffer;
+    Byte * flateInBuffer;
+    Byte * flateOutBuffer;
+};
+
+
+
+static void
+createBmepsOutputEncoder(struct bmepsoe ** const bmepsoePP,
+                         FILE *            const ofP,
+                         bool              const rle,
+                         bool              const flate,
+                         bool              const ascii85) {
+
+    unsigned int const FLATE_IN_SIZE = 16384;
+    unsigned int const FLATE_OUT_SIZE = 17408;
+
+    struct bmepsoe * bmepsoeP;
+    int mode;
+
+    MALLOCVAR_NOFAIL(bmepsoeP);
+    MALLOCVAR_NOFAIL(bmepsoeP->oeP);
+    MALLOCARRAY_NOFAIL(bmepsoeP->rleBuffer, 129);
+    MALLOCARRAY_NOFAIL(bmepsoeP->flateInBuffer, FLATE_IN_SIZE);
+    MALLOCARRAY_NOFAIL(bmepsoeP->flateOutBuffer, FLATE_OUT_SIZE);
+
+    mode = 0;
+    if (rle)
+        mode |= OE_RL;
+    if (flate)
+        mode |= OE_FLATE;
+    if (ascii85)
+        mode |= OE_ASC85;
+
+    oe_init(bmepsoeP->oeP, ofP, mode, 9, 
+            bmepsoeP->rleBuffer, 
+            bmepsoeP->flateInBuffer, FLATE_IN_SIZE,
+            bmepsoeP->flateOutBuffer, FLATE_OUT_SIZE);
+
+    *bmepsoePP = bmepsoeP;
+}
+
+
+
+static void
+destroyBmepsOutputEncoder(struct bmepsoe * const bmepsoeP) {
+    
+    free(bmepsoeP->rleBuffer);
+    free(bmepsoeP->flateInBuffer);
+    free(bmepsoeP->flateOutBuffer);
+    
+    free(bmepsoeP);
+}
+
+
+
+static void
+outputBmepsSample(struct bmepsoe * const bmepsoeP,
+                  unsigned int     const sampleValue,
+          unsigned int     const bitsPerSample) {
+
+    if (bitsPerSample == 8)
+        oe_byte_add(bmepsoeP->oeP, sampleValue);
+    else {
+        unsigned int m;
+
+        for (m = 1 << (bitsPerSample-1); m != 0; m >>= 1)
+            /* depends on oe_bit_add accepting any value !=0 as 1 */
+            oe_bit_add(bmepsoeP->oeP, sampleValue & m); 
+    }
+}
+
+
+
+static void
+flushBmepsOutput(struct bmepsoe * const bmepsoeP) {
+    oe_byte_flush(bmepsoeP->oeP);
+}
+
+
+/*============================================================================
+   END OF OUTPUT ENCODERS
+============================================================================*/
+
+
+static void
+computeImagePosition(int     const dpiX, 
+                     int     const dpiY, 
+                     int     const icols, 
+                     int     const irows,
+                     bool    const mustturn,
+                     bool    const canturn,
+                     bool    const center,
+                     int     const pagewid, 
+                     int     const pagehgt, 
+                     float   const requestedScale,
+                     float   const imagewidth,
+                     float   const imageheight,
+                     bool    const equalpixels,
+                     float * const scolsP,
+                     float * const srowsP,
+                     float * const llxP, 
+                     float * const llyP,
+                     bool *  const turnedP ) {
+/*----------------------------------------------------------------------------
+   Determine where on the page the image is to go.  This means position,
+   dimensions, and orientation.
+
+   icols/irows are the dimensions of the PNM input in xels.
+
+   'mustturn' means we are required to rotate the image.
+
+   'canturn' means we may rotate the image if it fits better, but don't
+   have to.
+
+   *scolsP, *srowsP are the dimensions of the image in 1/72 inch.
+
+   *llxP, *llyP are the coordinates, in 1/72 inch, of the lower left
+   corner of the image on the page.
+
+   *turnedP is true iff the image is to be rotated 90 degrees on the page.
+
+   imagewidth/imageheight are the requested dimensions of the image on
+   the page, in 1/72 inch.  Image will be as large as possible within
+   those dimensions.  Zero means unspecified, so 'scale', 'pagewid',
+   'pagehgt', 'irows', and 'icols' determine image size.
+
+   'equalpixels' means the user wants one printed pixel per input pixel.
+   It is inconsistent with imagewidth or imageheight != 0
+
+   'requestedScale' is meaningful only when imageheight/imagewidth == 0
+   and equalpixels == FALSE.  It tells how many inches the user wants
+   72 pixels of input to occupy, if it fits on the page.
+-----------------------------------------------------------------------------*/
+    int cols, rows;
+        /* Number of columns, rows of input xels in the output, as
+           rotated if applicable
+        */
+    bool shouldturn;  /* The image fits the page better if we turn it */
+    
+    if (icols > irows && pagehgt > pagewid)
+        shouldturn = TRUE;
+    else if (irows > icols && pagewid > pagehgt)
+        shouldturn = TRUE;
+    else
+        shouldturn = FALSE;
+
+    if (mustturn || (canturn && shouldturn)) {
+        *turnedP = TRUE;
+        cols = irows;
+        rows = icols;
+    } else {
+        *turnedP = FALSE;
+        cols = icols;
+        rows = irows;
+    }
+    if (equalpixels) {
+        *scolsP = (72.0/dpiX)*cols;
+        *srowsP = (72.0/dpiY)*rows;
+    } else if (imagewidth > 0 || imageheight > 0) {
+        float scale;
+
+        if (imagewidth == 0)
+            scale = (float) imageheight/rows;
+        else if (imageheight == 0)
+            scale = (float) imagewidth/cols;
+        else
+            scale = MIN((float)imagewidth/cols, (float)imageheight/rows);
+        
+        *scolsP = cols*scale;
+        *srowsP = rows*scale;
+    } else {
+        /* He didn't give us a bounding box for the image so figure
+           out output image size from other inputs.
+        */
+        const int devpixX = dpiX / 72.0 + 0.5;        
+        const int devpixY = dpiY / 72.0 + 0.5;        
+            /* How many device pixels make up 1/72 inch, rounded to
+               nearest integer */
+        const float pixfacX = 72.0 / dpiX * devpixX;  /* 1, approx. */
+        const float pixfacY = 72.0 / dpiY * devpixY;  /* 1, approx. */
+        float scale;
+
+        scale = MIN(requestedScale, 
+                    MIN((float)pagewid/cols, (float)pagehgt/rows));
+
+        *scolsP = scale * cols * pixfacX;
+        *srowsP = scale * rows * pixfacY;
+        
+        if (scale != requestedScale)
+            pm_message("warning, image too large for page, rescaling to %g", 
+                       scale );
+
+        /* Before May 2001, Pnmtops enforced a 5% margin around the page.
+           If the image would be too big to leave a 5% margin, Pnmtops would
+           scale it down.  But people have images that are exactly the size
+           of a page, e.g. because they created them with Sane's 'scanimage'
+           program from a full page of input.  So we removed the gratuitous
+           5% margin.  -Bryan.
+        */
+    }
+    *llxP = (center) ? ( pagewid - *scolsP ) / 2 : 0;
+    *llyP = (center) ? ( pagehgt - *srowsP ) / 2 : 0;
+
+
+    if (verbose)
+        pm_message("Image will be %3.2f points wide by %3.2f points high, "
+                   "left edge %3.2f points from left edge of page, "
+                   "bottom edge %3.2f points from top of page; "
+                   "%sturned to landscape orientation",
+                   *scolsP, *srowsP, *llxP, *llyP, *turnedP ? "" : "NOT ");
+}
+
+
+
+static void
+determineDictionaryRequirement(bool           const userWantsDict,
+                               bool           const psFilter,
+                               unsigned int * const dictSizeP) {
+
+    if (userWantsDict) {
+        if (psFilter) {
+            /* The Postscript this program generates to use built-in
+               Postscript filters does not define any variables.
+            */
+            *dictSizeP = 0;
+        } else
+            *dictSizeP = 8;
+    } else
+        *dictSizeP = 0;
+}
+
+
+
+static void
+defineReadstring(bool const rle) {
+/*----------------------------------------------------------------------------
+   Write to Standard Output Postscript statements to define /readstring.
+-----------------------------------------------------------------------------*/
+    if (rle) {
+        printf("/rlestr1 1 string def\n");
+        printf("/readrlestring {\n");             /* s -- nr */
+        printf("  /rlestr exch def\n");           /* - */
+        printf("  currentfile rlestr1 readhexstring pop\n");  /* s1 */
+        printf("  0 get\n");                  /* c */
+        printf("  dup 127 le {\n");               /* c */
+        printf("    currentfile rlestr 0\n");         /* c f s 0 */
+        printf("    4 3 roll\n");             /* f s 0 c */
+        printf("    1 add  getinterval\n");           /* f s */
+        printf("    readhexstring pop\n");            /* s */
+        printf("    length\n");               /* nr */
+        printf("  } {\n");                    /* c */
+        printf("    256 exch sub dup\n");         /* n n */
+        printf("    currentfile rlestr1 readhexstring pop\n");/* n n s1 */
+        printf("    0 get\n");                /* n n c */
+        printf("    exch 0 exch 1 exch 1 sub {\n");       /* n c 0 1 n-1*/
+        printf("      rlestr exch 2 index put\n");
+        printf("    } for\n");                /* n c */
+        printf("    pop\n");                  /* nr */
+        printf("  } ifelse\n");               /* nr */
+        printf("} bind def\n");
+        printf("/readstring {\n");                /* s -- s */
+        printf("  dup length 0 {\n");             /* s l 0 */
+        printf("    3 copy exch\n");              /* s l n s n l*/
+        printf("    1 index sub\n");              /* s l n s n r*/
+        printf("    getinterval\n");              /* s l n ss */
+        printf("    readrlestring\n");            /* s l n nr */
+        printf("    add\n");                  /* s l n */
+        printf("    2 copy le { exit } if\n");        /* s l n */
+        printf("  } loop\n");                 /* s l l */
+        printf("  pop pop\n");                /* s */
+        printf("} bind def\n");
+    } else {
+        printf("/readstring {\n");                /* s -- s */
+        printf("  currentfile exch readhexstring pop\n");
+        printf("} bind def\n");
+    }
+}
+
+
+
+static void
+setupReadstringNative(bool         const rle,
+                      bool         const color,
+                      unsigned int const icols, 
+                      unsigned int const padright, 
+                      unsigned int const bps) {
+/*----------------------------------------------------------------------------
+   Write to Standard Output statements to define /readstring and also
+   arguments for it (/picstr or /rpicstr, /gpicstr, and /bpicstr).
+-----------------------------------------------------------------------------*/
+    unsigned int const bytesPerRow = (icols + padright) * bps / 8;
+
+    defineReadstring(rle);
+    
+    if (color) {
+        printf("/rpicstr %d string def\n", bytesPerRow);
+        printf("/gpicstr %d string def\n", bytesPerRow);
+        printf("/bpicstr %d string def\n", bytesPerRow);
+    } else
+        printf("/picstr %d string def\n", bytesPerRow);
+}
+
+
+
+static void
+putFilters(unsigned int const postscriptLevel,
+           bool         const rle,
+           bool         const flate,
+           bool         const ascii85,
+           bool         const color) {
+
+    assert(postscriptLevel > 1);
+    
+    if (ascii85)
+        printf("/ASCII85Decode filter ");
+    else 
+        printf("/ASCIIHexDecode filter ");
+    if (flate)
+        printf("/FlateDecode filter ");
+    if (rle) /* bmeps encodes rle before flate, so must decode after! */
+        printf("/RunLengthDecode filter ");
+}
+
+
+
+static void
+putReadstringNative(bool const color) {
+
+    if (color) {
+        printf("{ rpicstr readstring }\n");
+        printf("{ gpicstr readstring }\n");
+        printf("{ bpicstr readstring }\n");
+    } else
+        printf("{ picstr readstring }\n");
+}
+
+
+
+static void
+putSetup(unsigned int const dictSize,
+         bool         const psFilter,
+         bool         const rle,
+         bool         const color,
+         unsigned int const icols,
+         unsigned int const padright,
+         unsigned int const bps) {
+/*----------------------------------------------------------------------------
+   Put the setup section in the Postscript program on Standard Output.
+-----------------------------------------------------------------------------*/
+    printf("%%%%BeginSetup\n");
+
+    if (dictSize > 0)
+        /* inputf {r,g,b,}pictsr readstring readrlestring rlestring */
+        printf("%u dict begin\n", dictSize);
+    
+    if (!psFilter)
+        setupReadstringNative(rle, color, icols, padright, bps);
+
+    printf("%%%%EndSetup\n");
+}
+
+
+
+static void
+putImage(bool const psFilter,
+         bool const color) {
+/*----------------------------------------------------------------------------
+   Put the image/colorimage statement in the Postscript program on
+   Standard Output.
+-----------------------------------------------------------------------------*/
+    if (color) {
+        if (psFilter)
+            printf("false 3\n");
+        else
+            printf("true 3\n");
+        printf("colorimage");
+    } else
+        printf("image");
+}
+
+
+
+static void
+putInitPsFilter(unsigned int const postscriptLevel,
+                bool         const rle,
+                bool         const flate,
+                bool         const ascii85,
+                bool         const color) {
+
+    bool const filterTrue = TRUE;
+
+    printf("{ currentfile ");
+
+    putFilters(postscriptLevel, rle, flate, ascii85, color);
+
+    putImage(filterTrue, color);
+    
+    printf(" } exec");
+}
+
+
+
+static void
+putInitReadstringNative(bool const color) {
+
+    bool const filterFalse = FALSE;
+
+    putReadstringNative(color);
+    
+    putImage(filterFalse, color);
+}
+
+
+
+static void
+putInit(unsigned int const postscriptLevel,
+        char         const name[], 
+        int          const icols, 
+        int          const irows, 
+        float        const scols, 
+        float        const srows,
+        float        const llx, 
+        float        const lly,
+        int          const padright, 
+        int          const bps,
+        int          const pagewid, 
+        int          const pagehgt,
+        bool         const color, 
+        bool         const turned, 
+        bool         const rle,
+        bool         const flate,
+        bool         const ascii85,
+        bool         const setpage,
+        bool         const psFilter,
+        unsigned int const dictSize) {
+/*----------------------------------------------------------------------------
+   Write out to Standard Output the headers stuff for the Postscript
+   program (everything up to the raster).
+-----------------------------------------------------------------------------*/
+    /* The numbers in the %! line often confuse people. They are NOT the
+       PostScript language level.  The first is the level of the DSC comment
+       spec being adhered to, the second is the level of the EPSF spec being
+       adhered to.  It is *incorrect* to claim EPSF compliance if the file
+       contains a setpagedevice.
+    */
+    printf("%%!PS-Adobe-3.0%s\n", setpage ? "" : " EPSF-3.0");
+    printf("%%%%LanguageLevel: %u\n", postscriptLevel);
+    printf("%%%%Creator: pnmtops\n");
+    printf("%%%%Title: %s.ps\n", name);
+    printf("%%%%Pages: 1\n");
+    printf(
+        "%%%%BoundingBox: %d %d %d %d\n",
+        (int) llx, (int) lly,
+        (int) (llx + scols + 0.5), (int) (lly + srows + 0.5));
+    printf("%%%%EndComments\n");
+
+    putSetup(dictSize, psFilter, rle, color, icols, padright, bps);
+
+    printf("%%%%Page: 1 1\n");
+    if (setpage)
+        printf("<< /PageSize [ %d %d ] /ImagingBBox null >> setpagedevice\n",
+               pagewid, pagehgt);
+    printf("gsave\n");
+    printf("%g %g translate\n", llx, lly);
+    printf("%g %g scale\n", scols, srows);
+    if (turned)
+        printf("0.5 0.5 translate  90 rotate  -0.5 -0.5 translate\n");
+    printf("%d %d %d\n", icols, irows, bps);
+    printf("[ %d 0 0 -%d 0 %d ]\n", icols, irows, irows);
+
+    if (psFilter)
+        putInitPsFilter(postscriptLevel, rle, flate, ascii85, color);
+    else
+        putInitReadstringNative(color);
+
+    printf("\n");
+}
+
+
+
+static void
+putEnd(bool         const showpage, 
+       bool         const psFilter,
+       bool         const ascii85,
+       unsigned int const dictSize,
+       bool         const vmreclaim) {
+
+    if (psFilter) {
+        if (ascii85)
+            printf("%s\n", "~>");
+        else
+            printf("%s\n", ">");
+    } else {
+        printf("currentdict /inputf undef\n");
+        printf("currentdict /picstr undef\n");
+        printf("currentdict /rpicstr undef\n");
+        printf("currentdict /gpicstr undef\n");
+        printf("currentdict /bpicstr undef\n");
+    }
+
+    if (dictSize > 0)
+        printf("end\n");
+
+    if (vmreclaim)
+        printf("1 vmreclaim\n");
+
+    printf("grestore\n");
+
+    if (showpage)
+        printf("showpage\n");
+    printf("%%%%Trailer\n");
+}
+
+
+
+static void
+computeDepth(xelval         const inputMaxval,
+             unsigned int   const postscriptLevel, 
+             unsigned int * const bitspersampleP,
+             unsigned int * const psMaxvalP) {
+/*----------------------------------------------------------------------------
+   Figure out how many bits will represent each sample in the Postscript
+   program, and the maxval of the Postscript program samples.  The maxval
+   is just the maximum value allowable in the number of bits.
+-----------------------------------------------------------------------------*/
+    unsigned int const bitsRequiredByMaxval = pm_maxvaltobits(inputMaxval);
+
+    if (bitsRequiredByMaxval <= 1)
+        *bitspersampleP = 1;
+    else if (bitsRequiredByMaxval <= 2)
+        *bitspersampleP = 2;
+    else if (bitsRequiredByMaxval <= 4)
+        *bitspersampleP = 4;
+    else if (bitsRequiredByMaxval <= 8 || postscriptLevel < 2)
+        *bitspersampleP = 8;
+    else
+        *bitspersampleP = 12;
+
+    if (*bitspersampleP < bitsRequiredByMaxval)
+        pm_message("Maxval of input requires %u bit samples for full "
+                   "resolution, but we are using the Postscript level %u "
+                   "maximum of %u",
+                   bitsRequiredByMaxval, postscriptLevel, *bitspersampleP);
+
+    *psMaxvalP = pm_bitstomaxval(*bitspersampleP);
+
+    if (verbose)
+        pm_message("Input maxval is %u.  Postscript raster will have "
+                   "%u bits per sample, so maxval = %u",
+                   inputMaxval, *bitspersampleP, *psMaxvalP);
+}    
+
+
+
+static void
+convertRowNative(struct pam * const pamP, 
+                 tuple *      const tuplerow, 
+                 unsigned int const psMaxval, 
+                 bool         const rle, 
+                 unsigned int const padright) {
+
+    unsigned int plane;
+
+    for (plane = 0; plane < pamP->depth; ++plane) {
+        unsigned int col;
+        for (col= 0; col < pamP->width; ++col) {
+            sample const scaledSample = 
+                pnm_scalesample(tuplerow[col][plane], pamP->maxval, psMaxval);
+
+            if (rle)
+                rleputxelval(scaledSample);
+            else
+                putxelval(scaledSample);
+        }
+        for (col = 0; col < padright; ++col)
+            if (rle)
+                rleputxelval(0);
+        else
+            putxelval(0);
+        if (rle)
+            rleflush();
+    }
+}
+
+
+
+static void
+convertRowPsFilter(struct pam *     const pamP,
+                   tuple *          const tuplerow,
+                   struct bmepsoe * const bmepsoeP,
+                   unsigned int     const psMaxval) {
+
+    unsigned int const bitsPerSample = pm_maxvaltobits(psMaxval);
+    unsigned int const stragglers =
+        (((bitsPerSample * pamP->depth) % 8) * pamP->width) % 8;
+        /* Number of bits at the right edge that don't fill out a
+           whole byte
+        */
+
+    unsigned int col;
+    tuple scaledTuple;
+    
+    scaledTuple = pnm_allocpamtuple(pamP);
+
+    for (col = 0; col < pamP->width; ++col) {
+        unsigned int plane;
+        pnm_scaletuple(pamP, scaledTuple, tuplerow[col], psMaxval);
+        
+        for (plane = 0; plane < pamP->depth; ++plane)
+            outputBmepsSample(bmepsoeP, scaledTuple[plane], bitsPerSample);
+    }
+    if (stragglers > 0) {
+        unsigned int i;
+        for (i = stragglers; i < 8; ++i)
+            oe_bit_add(bmepsoeP->oeP, 0);
+    }
+    pnm_freepamtuple(scaledTuple);
+}
+
+
+
+static void
+selectPostscriptLevel(bool           const levelIsGiven,
+                      unsigned int   const levelGiven,
+                      bool           const color,
+                      bool           const dict,
+                      bool           const flate,
+                      bool           const ascii85,
+                      bool           const psFilter,
+                      unsigned int * const postscriptLevelP) {
+
+    unsigned int const maxPermittedLevel = 
+        levelIsGiven ? levelGiven : UINT_MAX;
+    unsigned int minPossibleLevel;
+
+    /* Until we know, later in this function, that we needs certain
+       features, we assume we can get by with classic Postscript Level 1:
+    */
+    minPossibleLevel = 1;
+
+    /* Now we increase 'minPossibleLevel' as we notice that each of
+       various features are required:
+    */
+    if (color) {
+        minPossibleLevel = MAX(minPossibleLevel, 2);
+        if (2 > maxPermittedLevel)
+            pm_error("Color requires at least Postscript level 2");
+    }
+    if (flate) {
+        minPossibleLevel = MAX(minPossibleLevel, 3);
+        if (2 > maxPermittedLevel)
+            pm_error("flate compression requires at least Postscript level 3");
+    }
+    if (ascii85) {
+        minPossibleLevel = MAX(minPossibleLevel, 2);
+        if (2 > maxPermittedLevel)
+            pm_error("ascii85 encoding requires at least Postscript level 2");
+    }
+    if (psFilter) {
+        minPossibleLevel = MAX(minPossibleLevel, 2);
+        if (2 > maxPermittedLevel)
+            pm_error("-psfilter requires at least Postscript level 2");
+    }
+    if (levelIsGiven)
+        *postscriptLevelP = levelGiven;
+    else
+        *postscriptLevelP = minPossibleLevel;
+}
+
+
+
+static void
+convertPage(FILE * const ifP, 
+            int    const turnflag, 
+            int    const turnokflag, 
+            bool   const psFilter,
+            bool   const rle, 
+            bool   const flate,
+            bool   const ascii85,
+            bool   const setpage,
+            bool   const showpage,
+            bool   const center, 
+            float  const scale,
+            int    const dpiX, 
+            int    const dpiY, 
+            int    const pagewid, 
+            int    const pagehgt,
+            int    const imagewidth, 
+            int    const imageheight, 
+            bool   const equalpixels,
+            char   const name[],
+            bool   const dict,
+            bool   const vmreclaim,
+            bool   const levelIsGiven,
+            bool   const levelGiven) {
+    
+    struct pam inpam;
+    tuple* tuplerow;
+    unsigned int padright;
+        /* Number of bits we must add to the right end of each Postscript
+           output line in order to have an integral number of bytes of output.
+           E.g. at 2 bits per sample with 10 columns, this would be 4.
+        */
+    int row;
+    unsigned int psMaxval;
+        /* The maxval of the Postscript program */
+    float scols, srows;
+    float llx, lly;
+    bool turned;
+    bool color;
+    unsigned int postscriptLevel;
+    struct bmepsoe * bmepsoeP;
+    unsigned int dictSize;
+        /* Size of Postscript dictionary we should define */
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+    
+    if (!STRSEQ(inpam.tuple_type, PAM_PBM_TUPLETYPE) &&
+        !STRSEQ(inpam.tuple_type, PAM_PGM_TUPLETYPE) &&
+        !STRSEQ(inpam.tuple_type, PAM_PPM_TUPLETYPE))
+        pm_error("Unrecognized tuple type %s.  This program accepts only "
+                 "PBM, PGM, PPM, and equivalent PAM input images", 
+                 inpam.tuple_type);
+
+    color = STRSEQ(inpam.tuple_type, PAM_PPM_TUPLETYPE);
+    
+    selectPostscriptLevel(levelIsGiven, levelGiven, color, 
+                          dict, flate, ascii85, psFilter, &postscriptLevel);
+    
+    if (color)
+        pm_message("generating color Postscript program.");
+
+    computeDepth(inpam.maxval, postscriptLevel, &bitspersample, &psMaxval);
+    {
+        unsigned int const realBitsPerLine = inpam.width * bitspersample;
+        unsigned int const paddedBitsPerLine = ((realBitsPerLine + 7) / 8) * 8;
+        padright = (paddedBitsPerLine - realBitsPerLine) / bitspersample;
+    }
+    /* In positioning/scaling the image, we treat the input image as if
+       it has a density of 72 pixels per inch.
+    */
+    computeImagePosition(dpiX, dpiY, inpam.width, inpam.height, 
+                         turnflag, turnokflag, center,
+                         pagewid, pagehgt, scale, imagewidth, imageheight,
+                         equalpixels,
+                         &scols, &srows, &llx, &lly, &turned);
+
+    determineDictionaryRequirement(dict, psFilter, &dictSize);
+    
+    putInit(postscriptLevel, name, inpam.width, inpam.height, 
+            scols, srows, llx, lly, padright, bitspersample, 
+            pagewid, pagehgt, color,
+            turned, rle, flate, ascii85, setpage, psFilter, dictSize);
+
+    createBmepsOutputEncoder(&bmepsoeP, stdout, rle, flate, ascii85);
+    initNativeOutputEncoder(rle, bitspersample);
+
+    tuplerow = pnm_allocpamrow(&inpam);
+
+    for (row = 0; row < inpam.height; ++row) {
+        pnm_readpamrow(&inpam, tuplerow);
+        if (psFilter)
+            convertRowPsFilter(&inpam, tuplerow, bmepsoeP, psMaxval);
+        else
+            convertRowNative(&inpam, tuplerow, psMaxval, rle, padright);
+    }
+
+    pnm_freepamrow(tuplerow);
+
+    if (psFilter)
+        flushBmepsOutput(bmepsoeP);
+    else
+        flushNativeOutput(rle);
+
+    destroyBmepsOutputEncoder(bmepsoeP);
+
+    putEnd(showpage, psFilter, ascii85, dictSize, vmreclaim);
+}
+
+
+
+static const char *
+basebasename(const char * const filespec) {
+/*----------------------------------------------------------------------------
+    Return filename up to first period
+-----------------------------------------------------------------------------*/
+    char const dirsep = '/';
+    const char * const lastSlashPos = strrchr(filespec, dirsep);
+
+    char * name;
+    const char * filename;
+
+    if (lastSlashPos)
+        filename = lastSlashPos + 1;
+    else
+        filename = filespec;
+
+    name = strdup(filename);
+    if (name != NULL) {
+        char * const dotPosition = strchr(name, '.');
+
+        if (dotPosition)
+            *dotPosition = '\0';
+    }
+    return name;
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    FILE* ifp;
+    const char *name;  /* malloc'ed */
+    struct cmdlineInfo cmdline;
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    verbose = cmdline.verbose;
+
+    ifp = pm_openr(cmdline.inputFileName);
+
+    if (STREQ(cmdline.inputFileName, "-"))
+        name = strdup("noname");
+    else
+        name = basebasename(cmdline.inputFileName);
+    {
+        int eof;  /* There are no more images in the input file */
+        unsigned int imageSeq;
+
+        /* I don't know if this works at all for multi-image PNM input.
+           Before July 2000, it ignored everything after the first image,
+           so this probably is at least as good -- it should be identical
+           for a single-image file, which is the only kind which was legal
+           before July 2000.
+
+           Maybe there needs to be some per-file header and trailers stuff
+           in the Postscript program, with some per-page header and trailer
+           stuff inside.  I don't know Postscript.  - Bryan 2000.06.19.
+        */
+
+        eof = FALSE;  /* There is always at least one image */
+        for (imageSeq = 0; !eof; ++imageSeq) {
+            convertPage(ifp, cmdline.mustturn, cmdline.canturn, 
+                        cmdline.psfilter,
+                        cmdline.rle, cmdline.flate, cmdline.ascii85, 
+                        cmdline.setpage, cmdline.showpage,
+                        cmdline.center, cmdline.scale,
+                        cmdline.dpiX, cmdline.dpiY,
+                        cmdline.width, cmdline.height, 
+                        cmdline.imagewidth, cmdline.imageheight, 
+                        cmdline.equalpixels, name, 
+                        cmdline.dict, cmdline.vmreclaim,
+                        cmdline.levelSpec, cmdline.level);
+            pnm_nextimage(ifp, &eof);
+        }
+    }
+    strfree(name);
+
+    pm_close(ifp);
+    
+    return 0;
+}
+
+
+
+/*
+** 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.
+**
+**
+** -nocenter option added November 1993 by Wolfgang Stuerzlinger,
+**  wrzl@gup.uni-linz.ac.at.
+**
+*/
diff --git a/converter/other/pnmtorast.c b/converter/other/pnmtorast.c
new file mode 100644
index 00000000..7d1ae05a
--- /dev/null
+++ b/converter/other/pnmtorast.c
@@ -0,0 +1,310 @@
+/* pnmtorast.c - read a portable anymap and produce a Sun rasterfile
+**
+** 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.
+*/
+
+#include "pnm.h"
+#include "rast.h"
+#include "mallocvar.h"
+
+#define MAXCOLORS 256
+static colormap_t* make_pr_colormap ARGS(( colorhist_vector chv, int colors ));
+static colormap_t* make_gray_pr_colormap ARGS(( void ));
+static colormap_t* alloc_pr_colormap ARGS(( void ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+{
+    FILE* ifp;
+    xel** xels;
+    xel* xelrow;
+    xel p;
+    register xel* xP;
+    colorhist_vector chv;
+    colorhash_table cht;
+    colormap_t* pr_colormapP;
+    int argn, pr_type, rows, cols, format, i;
+    int depth, colors, linesize, row;
+    register int col, bitcount;
+    xelval maxval;
+    struct pixrect* pr;
+    unsigned char* data;
+    register unsigned char* byteP;
+    const char* const usage = "[-standard|-rle] [pnmfile]";
+
+    pnm_init( &argc, argv );
+
+    argn = 1;
+    pr_type = RT_BYTE_ENCODED;
+
+    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
+    {
+        if ( pm_keymatch( argv[argn], "-standard", 2 ) )
+            pr_type = RT_STANDARD;
+        else if ( pm_keymatch( argv[argn], "-rle", 2 ) )
+            pr_type = RT_BYTE_ENCODED;
+        else
+            pm_usage( usage );
+        ++argn;
+    }
+
+    if ( argn != argc )
+    {
+        ifp = pm_openr( argv[argn] );
+        ++argn;
+    }
+    else
+        ifp = stdin;
+
+    if ( argn != argc )
+        pm_usage( usage );
+
+    xels = pnm_readpnm( ifp, &cols, &rows, &maxval, &format );
+
+    pm_close( ifp );
+
+    /* Figure out the proper depth and colormap. */
+    switch ( PNM_FORMAT_TYPE(format) )
+    {
+    case PPM_TYPE:
+        pm_message( "computing colormap..." );
+        chv = ppm_computecolorhist( xels, cols, rows, MAXCOLORS, &colors );
+        if ( chv == (colorhist_vector) 0 )
+        {
+            pm_message(
+                "Too many colors - proceeding to write a 24-bit non-mapped" );
+            pm_message(
+                "rasterfile.  If you want 8 bits, try doing a 'pnmquant %d'.",
+                MAXCOLORS );
+            depth = 24;
+            pr_type = RT_STANDARD;
+            pr_colormapP = (colormap_t*) 0;
+        }
+        else
+        {
+            pm_message( "%d colors found", colors );
+
+            if ( maxval != 255 )
+                for ( i = 0; i < colors; ++i )
+                    PPM_DEPTH( chv[i].color, chv[i].color, maxval, 255 );
+
+            /* Force white to slot 0 and black to slot 1, if possible. */
+            PPM_ASSIGN( p, 255, 255, 255 );
+            ppm_addtocolorhist( chv, &colors, MAXCOLORS, &p, 0, 0 );
+            PPM_ASSIGN( p, 0, 0, 0 );
+            ppm_addtocolorhist( chv, &colors, MAXCOLORS, &p, 0, 1 );
+
+            if ( colors == 2 )
+            {
+                /* Monochrome. */
+                depth = 1;
+                pr_colormapP = (colormap_t*) 0;
+            }
+            else
+            {
+                /* Turn the ppm colormap into the appropriate Sun colormap. */
+                depth = 8;
+                pr_colormapP = make_pr_colormap( chv, colors );
+            }
+            cht = ppm_colorhisttocolorhash( chv, colors );
+            ppm_freecolorhist( chv );
+        }
+
+        break;
+
+    case PGM_TYPE:
+        depth = 8;
+        pr_colormapP = make_gray_pr_colormap( );
+        break;
+
+    default:
+        depth = 1;
+        pr_colormapP = (colormap_t*) 0;
+        break;
+    }
+
+    if ( maxval > 255 && depth != 1 )
+        pm_message(
+            "maxval is not 255 - automatically rescaling colors" );
+    
+    /* Allocate space for the Sun-format image. */
+    if ( (pr = mem_create(cols, rows, depth)) == (struct pixrect*) 0 )
+        pm_error( "unable to create new pixrect" );
+    data = ( (struct mpr_data*) pr->pr_data )->md_image;
+    linesize = ( (struct mpr_data*) pr->pr_data )->md_linebytes;
+
+    /* And compute the Sun image.  The variables at this point are:
+    **   cht is null or not
+    **   depth is 1, 8, or 24
+    */
+    for ( row = 0; row < rows; ++row )
+    {
+        xelrow = xels[row];
+        byteP = data;
+        switch ( depth )
+        {
+        case 1:
+            *byteP = 0;
+            bitcount = 7;
+            for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
+            {
+                register int color;
+
+                switch ( PNM_FORMAT_TYPE(format) )
+                {
+                case PPM_TYPE:
+                    if ( maxval != 255 )
+                        PPM_DEPTH( *xP, *xP, maxval, 255 );
+                    color = ppm_lookupcolor( cht, xP );
+                    if ( color == -1 )
+                        pm_error(
+                            "color not found?!?  row=%d col=%d  r=%d g=%d b=%d",
+                            row, col, PPM_GETR(*xP), PPM_GETG(*xP),
+                            PPM_GETB(*xP) );
+                    if ( color )
+                        *byteP |= 1 << bitcount;
+                    break;
+
+                default:
+                    color = PNM_GET1( *xP );
+                    if ( ! color )
+                        *byteP |= 1 << bitcount;
+                    break;
+                }
+                --bitcount;
+                if ( bitcount < 0 )
+                {
+                    ++byteP;
+                    *byteP = 0;
+                    bitcount = 7;
+                }
+            }
+            break;
+
+        case 8:
+            for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
+            {
+                register int color;
+
+                switch ( PNM_FORMAT_TYPE(format) )
+                {
+                case PPM_TYPE:
+                    if ( maxval != 255 )
+                        PPM_DEPTH( *xP, *xP, maxval, 255 );
+                    color = ppm_lookupcolor( cht, xP );
+                    if ( color == -1 )
+                        pm_error(
+                            "color not found?!?  row=%d col=%d  r=%d g=%d b=%d",
+                            row, col, PPM_GETR(*xP), PPM_GETG(*xP),
+                            PPM_GETB(*xP) );
+                    break;
+
+                case PGM_TYPE:
+                    color = PNM_GET1( *xP );
+                    if ( maxval != 255 )
+                        color = color * 255 / maxval;
+                    break;
+
+                default:
+                    color = PNM_GET1( *xP );
+                }
+                *byteP++ = color;
+            }
+            break;
+
+        case 24:
+            /* If depth is 24, we do NOT have a valid cht. */
+            for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
+            {
+                if ( maxval != 255 )
+                    PPM_DEPTH( *xP, *xP, maxval, 255 );
+                *byteP++ = PPM_GETB( *xP );
+                *byteP++ = PPM_GETG( *xP );
+                *byteP++ = PPM_GETR( *xP );
+            }
+            break;
+
+        default:
+            pm_error( "can't happen" );
+        }
+        data += linesize;
+    }
+    pnm_freearray( xels, rows );
+
+    /* Finally, write the sucker out. */
+    if ( pr_dump( pr, stdout, pr_colormapP, pr_type, 0 ) == PIX_ERR )
+        pm_error( "error writing rasterfile" );
+
+    exit( 0 );
+}
+
+static colormap_t*
+make_pr_colormap( chv, colors )
+    colorhist_vector chv;
+    int colors;
+{
+    colormap_t* pr_colormapP;
+    int i;
+
+    pr_colormapP = alloc_pr_colormap( );
+
+    for ( i = 0; i < colors; ++i )
+    {
+        pr_colormapP->map[0][i] = PPM_GETR( chv[i].color );
+        pr_colormapP->map[1][i] = PPM_GETG( chv[i].color );
+        pr_colormapP->map[2][i] = PPM_GETB( chv[i].color );
+    }
+    for ( ; i < MAXCOLORS; ++i )
+        pr_colormapP->map[0][i] = pr_colormapP->map[1][i] =
+            pr_colormapP->map[2][i] = 0;
+
+    return pr_colormapP;
+}
+
+static colormap_t*
+make_gray_pr_colormap( )
+{
+    colormap_t* pr_colormapP;
+    int i;
+
+    pr_colormapP = alloc_pr_colormap( );
+
+    for ( i = 0; i < MAXCOLORS; ++i )
+    {
+        pr_colormapP->map[0][i] = i;
+        pr_colormapP->map[1][i] = i;
+        pr_colormapP->map[2][i] = i;
+    }
+
+    return pr_colormapP;
+}
+
+static colormap_t*
+alloc_pr_colormap( )
+{
+    colormap_t* pr_colormapP;
+
+    MALLOCVAR(pr_colormapP);
+    if ( pr_colormapP == NULL )
+        pm_error( "out of memory" );
+    pr_colormapP->type = RMT_EQUAL_RGB;
+    pr_colormapP->length = MAXCOLORS;
+    MALLOCARRAY(pr_colormapP->map[0], MAXCOLORS);
+    MALLOCARRAY(pr_colormapP->map[1], MAXCOLORS);
+    MALLOCARRAY(pr_colormapP->map[2], MAXCOLORS);
+    if ( pr_colormapP->map[0] == NULL || 
+         pr_colormapP->map[1] == NULL ||
+         pr_colormapP->map[2] == NULL )
+        pm_error( "out of memory" );
+
+    return pr_colormapP;
+}
diff --git a/converter/other/pnmtorle.c b/converter/other/pnmtorle.c
new file mode 100644
index 00000000..8c6dc589
--- /dev/null
+++ b/converter/other/pnmtorle.c
@@ -0,0 +1,267 @@
+/*
+ * This is derived from the file of the same name dated June 5, 1995,
+ * copied from the Army High Performance Computing Research Center's
+ * media-tools.tar.gz package, received from 
+ * http://www.arc.umn.edu/gvl-software/media-tools.tar.gz on 2000.04.13.
+ *
+ * This software is copyrighted as noted below.  It may be freely copied,
+ * modified, and redistributed, provided that the copyright notice is
+ * preserved on all copies.
+ *
+ * There is no warranty or other guarantee of fitness for this software,
+ * it is provided solely "as is".  Bug reports or fixes may be sent
+ * to the author, who may or may not act on them as he desires.
+ *
+ * You may not include this software in a program or other software product
+ * without supplying the source, or without informing the end-user that the
+ * source is available for no extra charge.
+ *
+ * If you modify this software, you should include a notice giving the
+ * name of the person performing the modification, the date of modification,
+ * and the reason for such modification.
+ */
+/*
+ * pnmtorle - A program which will convert pbmplus (ppm or pgm) images
+ *            to Utah's "rle" image format.
+ *
+ * Author:      Wes Barris (wes@msc.edu)
+ *              AHPCRC
+ *              Minnesota Supercomputer Center, Inc.
+ * Date:        March 30, 1994
+ * Copyright (c) Minnesota Supercomputer Center, Inc.
+ * 
+ * 2000.04.13 adapted for Netpbm by Bryan Henderson.  Quieted compiler 
+ *            warnings.
+ *
+ */
+/*-----------------------------------------------------
+ * System includes.
+ */
+#include <string.h>
+#include <stdio.h>
+#include "pnm.h"
+#include "mallocvar.h"
+#include "rle.h"
+
+#define VPRINTF if (verbose || header) fprintf
+
+typedef unsigned char U_CHAR;
+/*
+ * Global variables.
+ */
+static FILE    *fp;
+static rle_hdr hdr;
+static int  format;
+static int width, height;
+static int verbose = 0, header = 0, do_alpha = 0;
+static gray    maxval;
+/*-----------------------------------------------------------------------------
+ *                                        Read the pnm image file header.
+ */
+static void 
+read_pnm_header()
+{
+    pnm_readpnminit(fp, &width, &height, &maxval, &format);
+    switch (format) {
+    case PBM_FORMAT:
+        VPRINTF(stderr, "Image type: plain pbm format\n");
+        break;
+    case RPBM_FORMAT:
+        VPRINTF(stderr, "Image type: raw pbm format\n");
+        break;
+    case PGM_FORMAT:
+        VPRINTF(stderr, "Image type: plain pgm format\n");
+        break;
+    case RPGM_FORMAT:
+        VPRINTF(stderr, "Image type: raw pgm format\n");
+        break;
+    case PPM_FORMAT:
+        VPRINTF(stderr, "Image type: plain ppm format\n");
+        break;
+    case RPPM_FORMAT:
+        VPRINTF(stderr, "Image type: raw ppm format\n");
+        break;
+    }
+    VPRINTF(stderr, "Full image: %dx%d\n", width, height);
+    VPRINTF(stderr, "Maxval:     %d\n", maxval);
+    if (do_alpha)
+        VPRINTF(stderr, "Computing alpha channel...\n");
+}
+/*-----------------------------------------------------------------------------
+ *                                             Write the rle image file header.
+ */
+static void 
+write_rle_header()
+{
+    hdr.xmin    = 0;
+    hdr.xmax    = width-1;
+    hdr.ymin    = 0;
+    hdr.ymax    = height-1;
+    hdr.background = 0;
+    switch (format) {
+    case PBM_FORMAT:
+    case RPBM_FORMAT:
+    case PGM_FORMAT:
+    case RPGM_FORMAT:
+        hdr.ncolors = 1;
+        RLE_SET_BIT(hdr, RLE_RED);
+        break;
+    case PPM_FORMAT:
+    case RPPM_FORMAT:
+        hdr.ncolors = 3;
+        RLE_SET_BIT(hdr, RLE_RED);
+        RLE_SET_BIT(hdr, RLE_GREEN);
+        RLE_SET_BIT(hdr, RLE_BLUE);
+        break;
+    }
+    if (do_alpha) {
+        hdr.alpha = 1;
+        RLE_SET_BIT(hdr, RLE_ALPHA);
+    }
+    rle_put_setup(&hdr);
+}
+/*-----------------------------------------------------------------------------
+ *                                      Write the rle data portion of the file.
+ */
+static void 
+write_rle_data()
+{
+    register int     x;
+    register int     scan;
+    register xel     *xelrow, *pP;
+    rle_pixel        ***scanlines, **scanline;
+/*
+ * Allocate some memory.
+ */
+    /*xelrow = pnm_allowcrow(width);*/
+    xelrow = (xel*) pm_allocrow( width, sizeof(xel) );
+    MALLOCARRAY(scanlines, height);
+    RLE_CHECK_ALLOC( hdr.cmd, scanlines, "scanline pointers" );
+
+    for ( scan = 0; scan < height; scan++ )
+        RLE_CHECK_ALLOC( hdr.cmd, (rle_row_alloc(&hdr, &scanlines[scan]) >= 0),
+                         "pixel memory" );
+/*
+ * Loop through the pnm files image window, read data and flip vertically.
+ */
+    switch (format) {
+    case PBM_FORMAT:
+    case RPBM_FORMAT:
+        for (scan = 0; scan < height; scan++) {
+            scanline = scanlines[height - scan - 1];
+            pnm_readpnmrow(fp, xelrow, width, maxval, format);
+            for (x = 0, pP = xelrow; x < width; x++, pP++) {
+                scanline[RLE_RED][x]   = (PNM_GET1(*pP) ? 255 : 0);
+                if (do_alpha) {
+                    scanline[RLE_ALPHA][x] = scanline[RLE_RED][x];
+                }
+            }
+        }
+        break;
+    case PGM_FORMAT:
+    case RPGM_FORMAT:
+        for (scan = 0; scan < height; scan++) {
+            scanline = scanlines[height - scan - 1];
+            pnm_readpnmrow(fp, xelrow, width, maxval, format);
+            for (x = 0, pP = xelrow; x < width; x++, pP++) {
+                scanline[RLE_RED][x]   = PNM_GET1(*pP);
+                if (do_alpha) {
+                    scanline[RLE_ALPHA][x] = (scanline[RLE_RED][x] ? 255 : 0);
+                }
+            }
+        }
+        break;
+    case PPM_FORMAT:
+    case RPPM_FORMAT:
+        for (scan = 0; scan < height; scan++) {
+            scanline = scanlines[height - scan - 1];
+            pnm_readpnmrow(fp, xelrow, width, maxval, format);
+            for (x = 0, pP = xelrow; x < width; x++, pP++) {
+                scanline[RLE_RED][x]   = PPM_GETR(*pP);
+                scanline[RLE_GREEN][x] = PPM_GETG(*pP);
+                scanline[RLE_BLUE][x]  = PPM_GETB(*pP);
+                if (do_alpha) {
+                    scanline[RLE_ALPHA][x] = (scanline[RLE_RED][x] ||
+                                              scanline[RLE_GREEN][x] ||
+                                              scanline[RLE_BLUE][x] ? 255 : 0);
+                }
+            }
+        }
+        break;
+    }
+/*
+ * Write out data in URT order (bottom to top).
+ */
+    for ( scan = 0; scan < height; scan++ ) {
+        rle_putrow(scanlines[scan], width, &hdr);
+        rle_row_free( &hdr, scanlines[scan] );
+    }
+    free( scanlines );
+
+    VPRINTF(stderr, "Done -- write eof to RLE data.\n");
+    rle_puteof(&hdr);
+}
+
+int
+main(argc, argv)
+    int argc;
+    char **argv;
+{
+    char     *pnmname = NULL, *outname = NULL;
+    static char  filename[BUFSIZ];
+    int      oflag, c;
+
+    pnm_init(&argc, argv);
+
+/*
+ * Get those options.
+ */
+    if (!scanargs(argc,argv,
+                  "% v%- h%- a%- o%-outfile!s pnmfile%s\n(\
+\tConvert a PNM file to URT RLE format.\n\
+\t-a\tFake an alpha channel.  Alpha=0 when input=0, 255 otherwise.\n\
+\t-h\tPrint header of PNM file and exit.\n\
+\t-v\tVerbose mode.)",
+                  &verbose,
+                  &header,
+                  &do_alpha,
+                  &oflag, &outname,
+                  &pnmname))
+        exit(-1);
+
+    hdr = *rle_hdr_init( (rle_hdr *)NULL );
+    rle_names( &hdr, cmd_name( argv ), outname, 0 );
+/*
+ * Open the file.
+ */
+    if (pnmname == NULL) {
+        strcpy(filename, "stdin");
+        fp = stdin;
+    }
+    else {
+        strcpy(filename, pnmname);
+        fp = pm_openr(filename);
+    }
+
+    hdr.rle_file = rle_open_f( hdr.cmd, outname, "wb" );
+    while ( (c = getc( fp )) != EOF ) {
+        ungetc( c, fp );
+/*
+ * Read the PPM file header.
+ */
+        read_pnm_header();
+        if (header)
+            break;
+/*
+ * Write the rle file header.
+ */
+        rle_addhist(argv, (rle_hdr *)NULL, &hdr);
+        write_rle_header();
+/*
+ * Write the rle file data.
+ */
+        write_rle_data();
+    }
+    fclose(fp);
+    return 0;
+}
diff --git a/converter/other/pnmtosgi.c b/converter/other/pnmtosgi.c
new file mode 100644
index 00000000..472b5197
--- /dev/null
+++ b/converter/other/pnmtosgi.c
@@ -0,0 +1,354 @@
+/* pnmtosgi.c - convert portable anymap to SGI image
+**
+** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
+**
+** Based on the SGI image description v0.9 by Paul Haeberli (paul@sgi.comp)
+** Available via ftp from sgi.com:graphics/SGIIMAGESPEC
+**
+** 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.
+**
+** 29Jan94: first version
+*/
+#include "pnm.h"
+#include "sgi.h"
+#include "mallocvar.h"
+
+/*#define DEBUG*/
+
+typedef short       ScanElem;
+typedef struct {
+    ScanElem *  data;
+    long        length;
+} ScanLine;
+
+/* prototypes */
+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 put_short_as_byte ARGS((short s));
+static void write_table  ARGS((long *table, int tabsize));
+static void write_channels ARGS((int cols, int rows, int channels, void (*put) ARGS((short)) ));
+static long * build_channels ARGS((FILE *ifp, int cols, int rows, xelval maxval, int format, int bpc, int channels));
+static ScanElem *compress ARGS((ScanElem *temp, int row, int rows, int cols, int chan_no, long *table, int bpc));
+static int rle_compress ARGS((ScanElem *inbuf, int cols));
+
+#define WORSTCOMPR(x)   (2*(x) + 2)
+
+
+#define MAXVAL_BYTE     255
+#define MAXVAL_WORD     65535
+
+static char storage = STORAGE_RLE;
+static ScanLine * channel[3];
+static ScanElem * rletemp;
+static xel * pnmrow;
+
+
+static void
+write_header(int const cols, 
+             int const rows, 
+             xelval const maxval, 
+             int const bpc, 
+             int const dimensions, 
+             int const channels, 
+             const char * const imagename)
+{
+    int i;
+
+#ifdef DEBUG
+    pm_message("writing header");
+#endif
+
+    put_big_short(SGI_MAGIC);
+    put_byte(storage);
+    put_byte((char)bpc);
+    put_big_short(dimensions);
+    put_big_short(cols);
+    put_big_short(rows);
+    put_big_short(channels);
+    put_big_long(0);                /* PIXMIN */
+    put_big_long(maxval);           /* PIXMAX */
+    for( i = 0; i < 4; i++ )
+        put_byte(0);
+    for( i = 0; i < 79 && imagename[i] != '\0'; i++ )
+        put_byte(imagename[i]);
+    for(; i < 80; i++ )
+        put_byte(0);
+    put_big_long(CMAP_NORMAL);
+    for( i = 0; i < 404; i++ )
+        put_byte(0);
+}
+
+
+
+int
+main(argc, argv)
+    int argc;
+    char *argv[];
+{
+    FILE *ifp;
+    int argn;
+    const char * const usage = "[-verbatim|-rle] [-imagename <name>] [pnmfile]";
+    int cols, rows, format;
+    xelval maxval, newmaxval;
+    const char *imagename = "no name";
+    int bpc, dimensions, channels;
+    long *table = NULL;
+
+    pnm_init(&argc, argv);
+
+    argn = 1;
+    while( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) {
+        if( pm_keymatch(argv[argn], "-verbatim", 2) )
+            storage = STORAGE_VERBATIM;
+        else
+        if( pm_keymatch(argv[argn], "-rle", 2) )
+            storage = STORAGE_RLE;
+        else
+        if( pm_keymatch(argv[argn], "-imagename", 2) ) {
+            if( ++argn >= argc )
+                pm_usage(usage);
+            imagename = argv[argn];
+        }
+        else
+            pm_usage(usage);
+        ++argn;
+    }
+
+    if( argn < argc ) {
+        ifp = pm_openr( argv[argn] );
+        argn++;
+    }
+    else
+        ifp = stdin;
+
+    if( argn != argc )
+        pm_usage(usage);
+
+    pnm_readpnminit(ifp, &cols, &rows, &maxval, &format);
+    pnmrow = pnm_allocrow(cols);
+
+    switch( PNM_FORMAT_TYPE(format) ) {
+        case PBM_TYPE:
+            newmaxval = PGM_MAXMAXVAL;
+            pm_message("promoting PBM to PGM");
+        case PGM_TYPE:
+            newmaxval = maxval;
+            dimensions = 2; channels = 1;
+            break;
+        case PPM_TYPE:
+            newmaxval = maxval;
+            dimensions = 3; channels = 3;
+            break;
+        default:
+            pm_error("can\'t happen");
+    }
+    if( newmaxval <= MAXVAL_BYTE )
+        bpc = 1;
+    else if( newmaxval <= MAXVAL_WORD )
+        bpc = 2;
+    else
+        pm_error("maxval too large - try using \"pnmdepth %d\"", MAXVAL_WORD);
+
+    table = build_channels(ifp, cols, rows, newmaxval, format, bpc, channels);
+    pnm_freerow(pnmrow);
+    pm_close(ifp);
+
+    write_header(cols, rows, newmaxval, bpc, dimensions, channels, imagename);
+    if( table )
+        write_table(table, rows * channels);
+    if( bpc == 1 )
+        write_channels(cols, rows, channels, put_short_as_byte);
+    else
+        write_channels(cols, rows, channels, put_big_short);
+
+    exit(0);
+}
+
+
+static void
+write_table(table, tabsize)
+    long *table;
+    int tabsize;
+{
+    int i;
+    long offset;
+
+#ifdef DEBUG
+    pm_message("writing table");
+#endif
+
+    offset = HeaderSize + tabsize * 8;
+    for( i = 0; i < tabsize; i++ ) {
+        put_big_long(offset);
+        offset += table[i];
+    }
+    for( i = 0; i < tabsize; i++ )
+        put_big_long(table[i]);
+}
+
+
+static void
+write_channels(cols, rows, channels, put)
+    int cols, rows, channels;
+    void (*put) ARGS((short));
+{
+    int i, row, col;
+
+#ifdef DEBUG
+    pm_message("writing image data");
+#endif
+
+    for( i = 0; i < channels; i++ ) {
+        for( row = 0; row < rows; row++ ) {
+            for( col = 0; col < channel[i][row].length; col++ ) {
+                (*put)(channel[i][row].data[col]);
+            }
+        }
+    }
+}
+
+static void
+put_big_short(short s)
+{
+    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
+put_short_as_byte(short s)
+{
+    put_byte((unsigned char)s);
+}
+
+
+static long *
+build_channels(FILE *ifp, int cols, int rows, xelval maxval, 
+               int format, int bpc, int channels)
+{
+    int i, row, col, sgirow;
+    long *table = NULL;
+    ScanElem *temp;
+
+#ifdef DEBUG
+    pm_message("building channels");
+#endif
+
+    if( storage != STORAGE_VERBATIM ) {
+        MALLOCARRAY_NOFAIL(table, channels * rows);
+        MALLOCARRAY_NOFAIL(rletemp, WORSTCOMPR(cols));
+    }
+    MALLOCARRAY_NOFAIL(temp, cols);
+
+    for( i = 0; i < channels; i++ )
+        MALLOCARRAY_NOFAIL(channel[i], rows);
+
+    for( row = 0, sgirow = rows-1; row < rows; row++, sgirow-- ) {
+        pnm_readpnmrow(ifp, pnmrow, cols, maxval, format);
+        if( channels == 1 ) {
+            for( col = 0; col < cols; col++ )
+                temp[col] = (ScanElem)PNM_GET1(pnmrow[col]);
+            temp = compress(temp, sgirow, rows, cols, 0, table, bpc);
+        }
+        else {
+            for( col = 0; col < cols; col++ )
+                temp[col] = (ScanElem)PPM_GETR(pnmrow[col]);
+            temp = compress(temp, sgirow, rows, cols, 0, table, bpc);
+            for( col = 0; col < cols; col++ )
+                temp[col] = (ScanElem)PPM_GETG(pnmrow[col]);
+            temp = compress(temp, sgirow, rows, cols, 1, table, bpc);
+            for( col = 0; col < cols; col++ )
+                temp[col] = (ScanElem)PPM_GETB(pnmrow[col]);
+            temp = compress(temp, sgirow, rows, cols, 2, table, bpc);
+        }
+    }
+
+    free(temp);
+    if( table )
+        free(rletemp);
+    return table;
+}
+
+
+static ScanElem *
+compress(temp, row, rows, cols, chan_no, table, bpc)
+    ScanElem *temp;
+    int row, rows, cols, chan_no;
+    long *table;
+    int bpc;
+{
+    int len, i, tabrow;
+    ScanElem *p;
+
+    switch( storage ) {
+        case STORAGE_VERBATIM:
+            channel[chan_no][row].length = cols;
+            channel[chan_no][row].data = temp;
+            MALLOCARRAY_NOFAIL(temp, cols);
+            break;
+        case STORAGE_RLE:
+            tabrow = chan_no * rows + row;
+            len = rle_compress(temp, cols);    /* writes result into rletemp */
+            channel[chan_no][row].length = len;
+            MALLOCARRAY(p, len);
+            channel[chan_no][row].data = p;
+            for( i = 0; i < len; i++, p++ )
+                *p = rletemp[i];
+            table[tabrow] = len * bpc;
+            break;
+        default:
+            pm_error("unknown storage type - can\'t happen");
+    }
+    return temp;
+}
+
+
+/*
+slightly modified RLE algorithm from ppmtoilbm.c
+written by Robert A. Knop (rknop@mop.caltech.edu)
+*/
+static int
+rle_compress(inbuf, size)
+    ScanElem *inbuf;
+    int size;
+{
+    int in, out, hold, count;
+    ScanElem *outbuf = rletemp;
+
+    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<127; in++,count++)
+                ;
+            outbuf[out++]=(ScanElem)(count);
+            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>=127 )
+                    break;
+            }
+            outbuf[hold]=(ScanElem)(count | 0x80);
+        }
+    }
+    outbuf[out++] = (ScanElem)0;     /* terminator */
+    return(out);
+}
+
diff --git a/converter/other/pnmtosir.c b/converter/other/pnmtosir.c
new file mode 100644
index 00000000..c8dec5b6
--- /dev/null
+++ b/converter/other/pnmtosir.c
@@ -0,0 +1,146 @@
+/* pnmtosir.c - read a portable anymap and produce a Solitaire Image Recorder
+**		file (MGI TYPE 11 or MGI TYPE 17)
+**
+** Copyright (C) 1991 by Marvin Landis
+**
+** 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 "pnm.h"
+
+#define MAXCOLORS 256
+
+int main(int argc, char * argv[]) {
+    FILE* ifp;
+    xel** xels;
+    register xel* xP;
+    const char* dumpname;
+    int rows, cols, format, row, col;
+    int m, n;
+    int grayscale;
+    xelval maxval;
+    const char* const usage = "[pnmfile]";
+    unsigned char ub;
+    unsigned short Header[16];
+    unsigned short LutHeader[16];
+    unsigned short Lut[2048];
+
+    pnm_init( &argc, argv );
+
+    if ( argc > 2 )
+        pm_usage( usage );
+
+    if ( argc == 2 )
+	{
+        dumpname = argv[1];
+        ifp = pm_openr( argv[1] );
+	}
+    else
+	{
+        dumpname = "Standard Input";
+        ifp = stdin;
+	}
+    
+    xels = pnm_readpnm( ifp, &cols, &rows, &maxval, &format );
+    pm_close( ifp );
+    
+    /* Figure out the colormap. */
+    switch ( PNM_FORMAT_TYPE(format) )
+	{
+	case PPM_TYPE:
+        grayscale = 0;
+        pm_message( "Writing a 24-bit SIR format (MGI TYPE 11)" );
+        break;
+
+    case PGM_TYPE:
+        grayscale = 1;
+        pm_message( "Writing a grayscale SIR format (MGI TYPE 17)" );
+        break;
+
+	default:
+        grayscale = 1;
+        pm_message( "Writing a monochrome SIR format (MGI TYPE 17)" );
+        break;
+	}
+
+    /* Set up the header. */
+    Header[0] = 0x3a4f;
+    Header[1] = 0;
+    if (grayscale)
+        Header[2] = 17;
+    else
+        Header[2] = 11;
+    Header[3] = cols;
+    Header[4] = rows;
+    Header[5] = 0;
+    Header[6] = 1;
+    Header[7] = 6;
+    Header[8] = 0;
+    Header[9] = 0;
+    for (n = 0; n < 10; n++)
+        pm_writelittleshort(stdout,Header[n]);
+    for (n = 10; n < 256; n++)
+        pm_writelittleshort(stdout,0);
+
+    /* Create color map */
+    LutHeader[0] = 0x1524;
+    LutHeader[1] = 0;
+    LutHeader[2] = 5;
+    LutHeader[3] = 256;
+    LutHeader[4] = 256;
+    for (n = 0; n < 5; n++)
+        pm_writelittleshort(stdout,LutHeader[n]);
+    for (n = 5; n < 256; n++)
+        pm_writelittleshort(stdout,0);
+ 
+    for(n = 0; n < 3; n ++)
+        for (m = 0; m < 256; m++)
+            Lut[m * 4 + n] = m << 8;
+    for (n = 0; n < 1024; n++)
+        pm_writelittleshort(stdout,Lut[n]);
+ 
+    /* Finally, write out the data. */
+    switch ( PNM_FORMAT_TYPE(format) )
+    {
+	case PPM_TYPE:
+	    for ( row = 0; row < rows; ++row )
+            for ( col = 0, xP = xels[row]; col < cols; ++col, ++xP )
+            {
+                ub = (char) ( PPM_GETR( *xP ) * ( 255 / maxval ) ); 
+                fputc( ub, stdout );
+            }
+        for ( row = 0; row < rows; ++row )
+            for ( col = 0, xP = xels[row]; col < cols; ++col, ++xP )
+            {  
+                ub = (char) ( PPM_GETG( *xP ) * ( 255 / maxval ) );
+                fputc( ub, stdout );
+            }
+        for ( row = 0; row < rows; ++row )
+            for ( col = 0, xP = xels[row]; col < cols; ++col, ++xP )
+            {  
+                ub = (char) ( PPM_GETB( *xP ) * ( 255 / maxval ) );
+                fputc( ub, stdout );
+            }
+	    break;
+
+    default:
+        for ( row = 0; row < rows; ++row )
+            for ( col = 0, xP = xels[row]; col < cols; ++col, ++xP )
+            {
+                register unsigned long val;
+
+                val = PNM_GET1( *xP );
+                ub = (char) ( val * ( 255 / maxval ) );
+                fputc( ub, stdout );
+            }
+        break;
+    }
+
+    exit( 0 );
+}
+
diff --git a/converter/other/pnmtotiffcmyk.c b/converter/other/pnmtotiffcmyk.c
new file mode 100644
index 00000000..0fda6b08
--- /dev/null
+++ b/converter/other/pnmtotiffcmyk.c
@@ -0,0 +1,1000 @@
+/*
+
+CMYKTiff - pnmtotiffcmyk - conversion from a pnm file to a CMYK
+encoded tiff file.
+
+Copyright (C) 1999 Andrew Cooke  (Jara Software)
+
+Comments to jara@andrewcooke.free-online.co.uk
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA
+
+These conditions also apply to any other files distributed with the
+program that describe / document the CMYKTiff package.
+
+
+Much of the code uses ideas from other pnm programs, written by Jef
+Poskanzer (thanks go to him and libtiff maintainer Sam Leffler).  A
+small section of the code - some of the tiff tag settings - is derived
+directly from pnmtotiff, by Jef Poskanzer, which, in turn,
+acknowledges Patrick Naughton with the following text:
+
+** Derived by Jef Poskanzer from ras2tif.c, which is:
+**
+** Copyright (c) 1990 by Sun Microsystems, Inc.
+**
+** Author: Patrick J. Naughton
+** naughton@wind.sun.com
+**
+** 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.
+
+Software copyrights will soon need family trees... :-)
+
+*/
+
+#define _XOPEN_SOURCE
+
+#include <string.h>
+#include <math.h>
+/* float.h used to be included only if __osf__ was defined.  On some
+   platforms, float.h is also included automatically by math.h.  But at
+   least on Solaris, it isn't.  00.04.25.
+*/
+#include <float.h>
+#include <limits.h>
+#include <fcntl.h>
+/* See warning about tiffio.h in pamtotiff.c */
+#include <tiffio.h>
+
+#include "pm_c_util.h"
+#include "nstring.h"
+#include "pnm.h"
+
+/* first release after testing */
+/* second release after testing - small mods to netpbm package */
+#define VERSION 1.01
+
+/* only support 8 bit values */
+#define MAXTIFFBITS 8
+#define MAXTIFFVAL 255 
+
+
+/* definitions for error values */
+
+typedef int Err ;
+
+#define ERR_OK       0  /* no error */
+#define ERR_PNM      1  /* thrown by pnm library */
+#define ERR_MEMORY   2  /* could not allocate memory */
+#define ERR_ARG      3  /* unexpected argument */
+#define ERR_TIFF     4  /* failure in tiff library */
+#define ERR_HELP     5  /* terminate with -help */
+
+/* definitions for command line options */
+
+#define UNSET 2 /* Simple->remove value when gammap not set */
+#define K_NORMAL 0
+#define K_REMOVE 1
+#define K_ONLY 2
+
+
+/* definitions for conversion calculations */
+
+/* catch rounding errors to outside 0-1 range */
+#define LIMIT01(x) MAX( 0.0, MIN( 1.0, (x) ) )
+/* convert from 0-1 to 0-maxOut */
+#define TOMAX(x) MAX( 0, MIN( rt->maxOut, (int)(rt->maxOut * (x)) ) )
+#define TINY FLT_EPSILON
+#ifndef PI
+#define PI 3.1415926
+#endif
+#define ONETWENTY ( 2.0 * PI / 3.0 ) 
+#define TWOFORTY ( 4.0 * PI / 3.0 )
+#define THREESIXTY ( 2.0 * PI ) 
+/* expand from 0-90 to 0-120 degrees */
+#define EXPAND(x) ( 4.0 * ( x ) / 3.0 )
+/* contract from 0-120 to 0-90 degrees */
+#define CONTRACT(x) ( 3.0 * ( x ) / 4.0 )
+
+
+
+/* --- main structure for the process ----------------- */
+
+struct tagIn ;
+struct tagConv ;
+struct tagOut ;
+
+typedef struct {
+  struct tagIn *in ;
+  struct tagConv *conv ;
+  struct tagOut *out ;
+  int nCols ;
+  int nRows ;
+  int maxOut ;
+  const char *name ;
+} Root ;
+
+
+
+/* --- utils ------------------------------------------ */
+
+
+/* parse an arg with a float value.  name should be the full key name,
+   preceded by '-' */
+static Err 
+floatArg( float *arg, const char *name, int size, float lo, float hi,
+          int *argn, int argc, char **argv ) {
+
+  char extra ;
+  int count ;
+
+  if ( pm_keymatch( argv[*argn], name, size ) ) {
+    if ( ++(*argn) == argc ) {
+      fprintf( stderr, "no value for %s\n", name ) ;
+      return ERR_ARG ;
+    }
+    if ( ! (count = sscanf( argv[*argn], "%f%1c", arg, &extra )) ) {
+      fprintf( stderr, "cannot parse %s for %s\n", argv[*argn], name ) ;
+      return ERR_ARG ;
+    }
+    if ( count > 1 ) {
+      fprintf( stderr, "warning: ignored %c... in value for %s\n",
+               extra, name ) ;
+    }
+    if ( *arg > hi || *arg < lo ) {
+      fprintf( stderr, "%s (%f) must be in range %f to %f\n", 
+               name, *arg, lo, hi ) ;
+      return ERR_ARG ;
+    }
+    ++(*argn) ;
+  }
+
+  return ERR_OK ;
+}
+
+
+/* parse an arg with a long value.  name should be the full key name,
+   preceded by '-' */
+static Err 
+longArg( long *arg, const char *name, int size, long lo, long hi,
+         int *argn, int argc, char **argv ) {
+
+  char extra ;
+  int count ;
+
+  if ( pm_keymatch( argv[*argn], name, size ) ) {
+    if ( ++(*argn) == argc ) {
+      fprintf( stderr, "no value for %s\n", name ) ;
+      return ERR_ARG ;
+    }
+    if ( ! (count = sscanf( argv[*argn], "%ld%1c", arg, &extra )) ) {
+      fprintf( stderr, "cannot parse %s for %s\n", argv[*argn], name ) ;
+      return ERR_ARG ;
+    }
+    if ( count > 1 ) {
+      fprintf( stderr, "warning: ignored %c... in value for %s\n",
+               extra, name ) ;
+    }
+    if ( *arg > hi || *arg < lo ) {
+      fprintf( stderr, "%s (%ld) must be in range %ld to %ld\n", 
+               name, *arg, lo, hi ) ;
+      return ERR_ARG ;
+    }
+    ++(*argn) ;
+  }
+
+  return ERR_OK ;
+}
+
+
+/* print usage.  for simplicity this routine is *not* split amongst
+   the various components - when you add a component (eg a new
+   conversion algorithm, or maybe new input or output code), you must
+   also change this routine.  by keeping all the options in one place
+   it is also easier to calculate the minimum key name length (passed
+   to pnm_keymatch) */
+static void 
+printUsage( ) {
+  fprintf( stderr, "\nusage: pnmtocmyk [Compargs] [Tiffargs] [Convargs] [pnmfile]\n" ) ;
+  fprintf( stderr, " Compargs: [-none|-packbits|-lzw [-predictor 1|-predictor 2]]\n" ) ;
+  fprintf( stderr, " Tiffargs: [-msb2lsb|-lsb2msb] [-rowsperstrip n]\n" ) ;
+  fprintf( stderr, "           [-lowdotrange lo] [-highdotrange hi] [-knormal|-konly|-kremove]\n" ) ;
+  fprintf( stderr, " Convargs: [[-default] [Defargs]|-negative]\n" ) ;
+  fprintf( stderr, " Defargs:  [-theta deg] [-gamma g] [-gammap -1|-gammap g]\n" ) ;
+  fprintf( stderr, "where 0 <= lo < hi <= 255; -360 < deg < 360; 0.1 < g < 10; 0 < n < INT_MAX\n\n" ) ;
+  fprintf( stderr, "returns: 0 OK; 1 pnm library error ; 2 memory error ; 3 unexpected arg ;\n" ) ;
+  fprintf( stderr, "         4 tiff library error ; 5 -help key used\n\n" ) ;
+  fprintf( stderr, "Convert a pnm file to a CMYK tiff file.  Version %5.2f\n", VERSION ) ;
+  fprintf( stderr, "CMY under K will be removed unless -gammap -1 is used.\n" ) ;
+  fprintf( stderr, "(c) 1999 Andrew Cooke - Beta version, not for public use.\n" ) ;
+  fprintf( stderr, "No warranty.\n\n" ) ;
+}
+
+/* list of key args and number of significant letters required
+-default      2
+-gamma        6
+-gammap       7
+-highdotrange 2
+-knormal      3
+-konly        3
+-kremove      3
+-lowdotrange  3
+-lsb2msb      3
+-lzw          3
+-msb2lsb      2
+-negative     3
+-none         3
+-packbits     3
+-predictor    3
+-rowsperstrip 2
+-theta        2
+*/
+
+
+
+/* --- reading the input ------------------------------ */
+
+
+/* encapsulate the file reader - uses pnm library at the moment, but
+   could be changed if we move to libtiff */
+
+typedef Err OptIn( struct tagIn *conv, Root *r, 
+                   int *argn, int argc, char **argv ) ;
+typedef int HasMore( struct tagIn *in ) ;
+typedef Err Next( struct tagIn *in, float *r, float *g, float *b ) ;
+typedef Err OpenIn( struct tagIn *in, Root *r ) ; 
+typedef void CloseIn( struct tagIn *in ) ;
+
+typedef struct tagIn {
+  OptIn *opt ;
+  HasMore *hasMore ;
+  Next *next ;
+  OpenIn *open ;
+  CloseIn *close ;
+  void *private ;
+} In ;
+
+
+/* implementation for pnm files */
+
+typedef struct {
+  FILE *in ;
+  int type ;
+  xelval maxVal ;
+  int maxPix ;
+  int iPix ;
+  xel *row ;
+  int maxRow ;
+  int iRow ;
+} PnmIn ;
+
+
+/* the only output option is the filename, which will be the last
+   argument, if it doesn't start with '-' */
+static Err 
+pnmOpt( In *in, Root *r, int *argn, int argc, char **argv ) {
+  PnmIn *p = (PnmIn*)in->private ;
+  if ( *argn + 1 == argc && argv[*argn][0] != '\0' &&
+       argv[*argn][0] != '-' ) {
+    r->name = argv[(*argn)++] ;
+    p->in = pm_openr( r->name ) ;
+  }
+  return ERR_OK ;
+}
+
+
+/* free the row buffer when closing the input */
+static void 
+pnmClose( In *in ) {
+  if ( in ) {
+    PnmIn *p = (PnmIn*)in->private ;
+    if ( p ) {
+      if ( p->row ) pnm_freerow( p->row ) ;
+      if ( p->in ) pm_close( p->in ) ;
+      free( p ) ;
+    }
+  }
+}
+
+
+/* open the file, storing dimensions both locally and in the global
+   root structure */
+static Err 
+pnmOpen( In *in, Root *r ) {
+
+  PnmIn *p = (PnmIn*)in->private ;
+
+  if ( ! p->in ) p->in = stdin ;
+
+  pnm_readpnminit( p->in, &(r->nCols), &(r->nRows), &(p->maxVal), 
+                   &(p->type) ) ;
+  p->maxPix = r->nCols * r->nRows ;
+  p->iPix = 0 ;
+  p->maxRow = r->nCols ;
+  p->iRow = 0 ;
+  p->row = pnm_allocrow( p->maxRow ) ;
+
+  return ERR_OK ;
+}
+
+
+/* more data available? */
+static int 
+pnmHasMore( In *in ) {
+  PnmIn *p = (PnmIn*)in->private ;
+  return p->iPix < p->maxPix ;
+}
+
+
+/* read next pixel - buffered by row.  return values in range 0 to 1 */
+static Err 
+pnmNext( In *in, float *r, float *g, float *b ) {
+
+  PnmIn *p = (PnmIn*)in->private ;
+  float m = (float)p->maxVal ;
+
+  if ( p->iPix == 0 || p->iRow == p->maxRow ) {
+    p->iRow = 0 ;
+    pnm_readpnmrow( p->in, p->row, p->maxRow, p->maxVal, p->type ) ;
+  }
+  if ( PNM_FORMAT_TYPE( p->type ) == PPM_TYPE ) {
+    *r = (float)PPM_GETR( p->row[p->iRow] ) / m ;
+    *g = (float)PPM_GETG( p->row[p->iRow] ) / m ;
+    *b = (float)PPM_GETB( p->row[p->iRow] ) / m ;
+  } else {
+    *r = *g = *b = (float)PNM_GET1( p->row[p->iRow] ) / m ;
+  }
+  ++(p->iRow) ;
+  ++(p->iPix) ;
+
+  return ERR_OK ;
+}
+
+
+/* build the input struct */
+static Err 
+newPnmInput( In **in ) {
+  if ( ! (*in = (In*)calloc( 1, sizeof( In ) )) ) {
+    fprintf( stderr, "cannot allocate memory\n" ) ;
+    return ERR_MEMORY ;
+  }
+  (*in)->private = calloc( 1, sizeof( PnmIn ) ) ;
+  (*in)->opt = pnmOpt ;
+  (*in)->open = pnmOpen ;
+  (*in)->hasMore = pnmHasMore ;
+  (*in)->next = pnmNext ;
+  (*in)->close = pnmClose ;
+  return ERR_OK ;
+}
+
+
+
+/* --- writing the output ----------------------------- */
+
+
+/* encapsulate file writing (will probably always use libtiff, but may
+   as well be consistent) */
+
+typedef Err OptOut( struct tagOut *conv, Root *r,
+                    int *argn, int argc, char **argv ) ;
+typedef Err Write( struct tagOut *out, int c, int m, int y, int k ) ;
+typedef Err OpenOut( struct tagOut *out, Root *r ) ;
+typedef void CloseOut( struct tagOut *out ) ;
+
+typedef struct tagOut {
+  OptOut *opt ;
+  Write *write ;
+  OpenOut *open ;
+  CloseOut *close ;
+  void *private ;
+} Out ;
+
+
+/* implementation for tiff files */
+
+typedef struct {
+  tdata_t buffer ;
+  tsize_t maxBuffer ;
+  tsize_t iBuffer ;
+  uint32 iRow ;
+  TIFF *tiff ;
+  uint32 rowsperstrip ;
+  uint16 compression ;
+  uint16 fillorder ;
+  uint16 predictor ;
+  uint16 lowdotrange ;
+  uint16 highdotrange ;
+  int kFlag ;
+} TiffOut ;
+
+
+/* these options come from either the tiff 6.0 spec or the pnmtotiff
+   code */
+static Err 
+tiffOpt( Out *out, Root* rt, int *argn, int argc, char **argv ) {
+
+  Err err ;
+  int oldn ;
+  long lVal ;
+  TiffOut *t = (TiffOut*)out->private ;
+
+  if ( pm_keymatch( argv[*argn], "-none", 3 ) ) {
+    t->compression = COMPRESSION_NONE ;
+    ++(*argn) ;
+  } else if ( pm_keymatch( argv[*argn], "-packbits", 3 ) ) {
+    t->compression = COMPRESSION_PACKBITS ;
+    ++(*argn) ;
+  } else if ( pm_keymatch( argv[*argn], "-lzw", 3 ) ) {
+    t->compression = COMPRESSION_LZW ;
+    ++(*argn) ;
+  } else if ( pm_keymatch( argv[*argn], "-msb2lsb", 2 ) ) {
+    t->fillorder = FILLORDER_MSB2LSB ;
+    ++(*argn) ;
+  } else if ( pm_keymatch( argv[*argn], "-lsb2msb", 3 ) ) {
+    t->fillorder = FILLORDER_LSB2MSB ;
+    ++(*argn) ;
+  } else if ( pm_keymatch( argv[*argn], "-knormal", 3 ) ) {
+    t->kFlag = K_NORMAL ;
+    ++(*argn) ;
+  } else if ( pm_keymatch( argv[*argn], "-kremove", 3 ) ) {
+    t->kFlag = K_REMOVE ;
+    ++(*argn) ;
+  } else if ( pm_keymatch( argv[*argn], "-konly", 3 ) ) {
+    t->kFlag = K_ONLY ;
+    ++(*argn) ;
+  } else {
+    oldn = *argn ;
+    if ( (err = longArg( &lVal, "-predictor", 3, 1, 2,
+                         argn, argc, argv )) ) return err ;
+    if ( oldn != *argn ) {
+      t->predictor = (uint16)lVal ;
+    } else {
+      if ( (err = longArg( &lVal, "-rowsperstrip", 2, 1, INT_MAX,
+                           argn, argc, argv )) ) return err ;
+    }
+    if ( oldn != *argn ) {
+      t->rowsperstrip = (uint32)lVal ;
+    } else {
+      if ( (err = longArg( &lVal, "-lowdotrange", 3, 0, 
+                           (int)(t->highdotrange - 1),
+                           argn, argc, argv )) ) return err ;
+    }
+    if ( oldn != *argn ) {
+      t->lowdotrange = (uint16)lVal ;
+    } else {
+      if ( (err = longArg( &lVal, "-highdotrange", 2, 
+                           (int)(t->lowdotrange + 1), MAXTIFFVAL,
+                           argn, argc, argv )) ) return err ;
+    }
+    if ( oldn != *argn ) {
+      t->highdotrange = (uint16)lVal ;
+    }
+  }
+
+  return ERR_OK ;
+}
+
+
+/* helper routine - writes individual bytes */
+static Err 
+tiffWriteByte( TiffOut *t, int b ) {
+  ((unsigned char*)(t->buffer))[t->iBuffer] = (unsigned char)b ;
+  if ( ++(t->iBuffer) == t->maxBuffer ) {
+    if ( TIFFWriteScanline( t->tiff, t->buffer, t->iRow, 0 ) == -1 ) {
+      return ERR_TIFF ;
+    }
+    ++(t->iRow) ;
+    t->iBuffer = 0 ;
+  }
+  return ERR_OK ;
+}
+
+
+/* write the pixel to the tiff file */
+static Err 
+tiffWrite( Out *out, int c, int m, int y, int k ) {
+  Err err ;
+  TiffOut *t = (TiffOut*)out->private ;
+  if ( t->kFlag == K_ONLY ) {
+    c = m = y = k ;
+  } else if ( t->kFlag == K_REMOVE ) {
+    k = 0 ;
+  }
+  if ( (err = tiffWriteByte( t, c )) ) return err ;
+  if ( (err = tiffWriteByte( t, m )) ) return err ;
+  if ( (err = tiffWriteByte( t, y )) ) return err ;
+  if ( (err = tiffWriteByte( t, k )) ) return err ;
+  return ERR_OK ;
+}
+
+
+/* open output to stdout - see warning below */
+static Err 
+tiffOpen( Out* out, Root *r ) {
+
+  TiffOut *t = (TiffOut*)out->private ;
+
+  short samplesperpixel = 4 ; /* cmyk has four values */
+  uint16 bitspersample = MAXTIFFBITS ;
+  short photometric = PHOTOMETRIC_SEPARATED ; /* ie cmyk */
+  int bytesperrow = r->nCols ;
+
+  /* if i don't set stdout non-blocking on my machine then the read
+     that is called inside TIFFFdOpen hangs until the users types ^D.
+     this is also true for pnmtotiff */
+  fcntl( 1, F_SETFL, O_NONBLOCK ) ;
+  t->tiff = TIFFFdOpen( 1, "Standard Output", "w" ) ;
+  if ( ! t->tiff ) {
+    fprintf( stderr, "cannot open tiff stream to standard output\n" ) ;
+    return ERR_TIFF ;
+  }
+
+  /* from pnmtotiff - default is to have 8kb strips */
+  if ( ! t->rowsperstrip ) {
+    t->rowsperstrip = ( 8 * 1024 ) / bytesperrow ;
+  }
+
+  TIFFSetField( t->tiff, TIFFTAG_DOTRANGE, t->lowdotrange, t->highdotrange ) ;
+  TIFFSetField( t->tiff, TIFFTAG_IMAGEWIDTH, (uint32)r->nCols ) ;
+  TIFFSetField( t->tiff, TIFFTAG_IMAGELENGTH, (uint32)r->nRows ) ;
+  TIFFSetField( t->tiff, TIFFTAG_BITSPERSAMPLE, bitspersample ) ;
+  TIFFSetField( t->tiff, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT ) ;
+  TIFFSetField( t->tiff, TIFFTAG_COMPRESSION, t->compression ) ;
+  if ( t->compression == COMPRESSION_LZW && t->predictor ) {
+	TIFFSetField( t->tiff, TIFFTAG_PREDICTOR, t->predictor ) ;
+  }
+  TIFFSetField( t->tiff, TIFFTAG_PHOTOMETRIC, photometric ) ;
+  TIFFSetField( t->tiff, TIFFTAG_FILLORDER, t->fillorder ) ;
+  TIFFSetField( t->tiff, TIFFTAG_DOCUMENTNAME, r->name ) ;
+  TIFFSetField( t->tiff, TIFFTAG_IMAGEDESCRIPTION, "PNM -> CMYK tiff" ) ;
+  TIFFSetField( t->tiff, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel ) ;
+  TIFFSetField( t->tiff, TIFFTAG_ROWSPERSTRIP, t->rowsperstrip ) ;
+  TIFFSetField( t->tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG ) ;
+
+  t->maxBuffer = TIFFScanlineSize( t->tiff ) ;
+  t->buffer = _TIFFmalloc( t->maxBuffer ) ;
+  t->iBuffer = 0 ;
+  t->iRow = 0 ;
+  if ( ! t->buffer ) {
+    fprintf( stderr, "cannot allocate memory\n" ) ;
+    return ERR_MEMORY ;
+  }
+  
+  return ERR_OK ;
+}
+
+
+/* close file and tidy memory */
+static void 
+tiffClose( Out *out ) {
+  if ( out ) {
+    TiffOut *t = (TiffOut*)out->private ;
+    if ( t ) {
+      if ( t->buffer ) _TIFFfree( t->buffer ) ;
+      if ( t->tiff ) TIFFClose( t->tiff ) ;
+      free( t ) ;
+    }
+    free( out ) ;
+  }
+}
+
+
+/* assemble the routines above into a single struct/object */
+static Err 
+newTiffOutput( Out **out ) {
+
+  TiffOut *t ;
+
+  if ( ! (*out = (Out*)calloc( 1, sizeof( Out ) )) ) goto error ;
+  if ( ! (t = (TiffOut*)calloc( 1, sizeof( TiffOut ) )) ) goto error ;
+  t->compression = COMPRESSION_LZW ;
+  t->fillorder = FILLORDER_MSB2LSB ;
+  t->predictor = 0 ;
+  t->rowsperstrip = 0 ;
+  t->lowdotrange = 0 ;
+  t->highdotrange = MAXTIFFVAL ;
+  t->kFlag = K_NORMAL ;
+  (*out)->private = t ;
+  (*out)->opt = tiffOpt ;
+  (*out)->open = tiffOpen ;
+  (*out)->write = tiffWrite ;
+  (*out)->close = tiffClose ;
+  return ERR_OK ;
+
+ error:
+  fprintf( stderr, "cannot allocate memory\n" ) ;
+  return ERR_MEMORY ;
+}
+
+
+
+/* --- conversion ------------------------------------- */
+
+
+/* encapsulate conversion - allow easy extension with other conversion
+   algorithms.  if selection of conversion routine is by command line
+   flag then that flag must precede conversion parameters (for
+   simplicity in argument parsing) */
+
+typedef Err Opt( struct tagConv *conv, Root *r,
+                 int *argn, int argc, char **argv ) ;
+typedef Err Convert( struct tagConv *conv, Root *rt,
+                     float r, float g, float b, 
+                     int *c, int *m, int *y, int *k ) ;
+typedef void Close( struct tagConv *conv ) ;
+
+typedef struct tagConv {
+  Opt *opt ;
+  Convert *convert ;
+  Close *close ;
+  void *private ;
+} Conv ;
+
+
+/* include two conversion routines to show how the code can be
+   extended.  first, the standard converter, as outlined in the
+   proposal, then a simple replacement that produces colour 
+   negatives */
+
+
+/* implementation with hue rotation, black gamma correction and
+   removal of cmy under k */
+
+typedef struct {
+  int initialized ;
+  float theta ;
+  float gamma ;
+  float gammap ;
+  int remove ;
+} Standard ;
+
+
+/* represent an rgb colour as a hue (angle), white level and colour
+   strength */
+static void 
+rgbToHueWhiteColour( float r, float g, float b, 
+                     double *phi, float *white, float *colour ) {
+  *white = MIN( r, MIN( g, b ) ) ;
+  r -= *white ;
+  g -= *white ;
+  b -= *white ;
+  *colour = sqrt( r * r + g * g + b * b ) ;
+  if ( r > TINY || g > TINY || b > TINY ) {
+    if ( b < r && b <= g ) { 
+      *phi = EXPAND( atan2( g, r ) ) ;
+    } else if ( r < g && r <= b ) {
+      *phi = ONETWENTY + EXPAND( atan2( b, g ) ) ;
+    } else {
+      *phi = TWOFORTY + EXPAND( atan2( r, b ) ) ;
+    }
+  } 
+}
+
+
+/* represent hue, white and colour values as rgb */
+static void 
+hueWhiteColourToRgb( double phi, float white, float colour,
+                     float *r, float *g, float *b ) {
+  while ( phi < 0 ) { phi += THREESIXTY ; }
+  while ( phi > THREESIXTY ) { phi -= THREESIXTY ; }
+  if ( phi < ONETWENTY ) {
+    phi = CONTRACT( phi ) ;
+    *r = colour * cos( phi ) ;
+    *g = colour * sin( phi ) ;
+    *b = 0.0 ;
+  } else if ( phi < TWOFORTY ) {
+    phi = CONTRACT( phi - ONETWENTY ) ;
+    *r = 0.0 ;
+    *g = colour * cos( phi ) ;
+    *b = colour * sin( phi ) ;
+  } else {
+    phi = CONTRACT( phi - TWOFORTY ) ;
+    *r = colour * sin( phi ) ;
+    *g = 0.0 ;
+    *b = colour * cos( phi ) ;
+  }
+  *r += white ;
+  *g += white ;
+  *b += white ;
+}
+
+
+/* for details, see the proposal.  it's pretty simple - a rotation
+   before conversion, with colour removal */
+static Err 
+standardConvert( Conv *conv, Root *rt, float r, float g, float b, 
+                 int *c, int *m, int *y, int *k ) {
+
+  float c0, m0, y0, k0 ; /* CMYK before colour removal */
+  float gray, white, colour ;
+  double phi ;
+
+  Standard *s ; /* private conversion data */
+
+  s = (Standard*)(conv->private) ;
+
+  if ( ! s->initialized ) {
+    s->theta *= 2.0 * PI / 360.0 ; /* to radians */
+    /* if gammap never specified in options, set to gamma now */
+    if ( s->remove == UNSET ) {
+      s->remove = 1 ;
+      s->gammap = s->gamma ;
+    }
+    s->initialized = 1 ;
+  }
+
+  /* rotate in rgb */
+  if ( fabs( s->theta ) > TINY ) {
+    rgbToHueWhiteColour( r, g, b, &phi, &white, &colour ) ;
+    hueWhiteColourToRgb( phi + s->theta, white, colour, &r, &g, &b ) ;
+  }
+
+  c0 = LIMIT01( 1.0 - r ) ;
+  m0 = LIMIT01( 1.0 - g ) ;
+  y0 = LIMIT01( 1.0 - b ) ;
+  k0 = MIN( c0, MIN( y0, m0 ) ) ;
+
+  /* apply gamma corrections to modify black levels */
+  *k = TOMAX( pow( k0, s->gamma ) ) ;
+
+  /* remove colour under black and convert to integer range 0 - rt->maxOut */
+  if ( s->remove ) {
+    gray = pow( k0, s->gammap ) ;
+  } else {
+    gray = 0.0 ;
+  }
+  *c = TOMAX( c0 - gray ) ;
+  *m = TOMAX( m0 - gray ) ;
+  *y = TOMAX( y0 - gray ) ;
+
+  return ERR_OK ;
+}
+
+
+/* parse options for this conversion - note the ugly use of -1 for
+   gammap to indicate no removal of cmy under k */
+static Err 
+standardOpt( Conv *conv, Root *r, int *argn, int argc, char **argv ) {
+
+  Err err ;
+  int oldn ;
+  Standard *p = (Standard*)conv->private ;
+
+  oldn = *argn ;
+  if ( (err = floatArg( &(p->theta), "-theta", 2, -360.0, 360.0,
+                        argn, argc, argv )) ) return err ;
+  if ( oldn == *argn ) {
+    if ( (err = floatArg( &(p->gamma), "-gamma", 6, 0.1, 10.0,
+                          argn, argc, argv )) ) return err ;
+    /* gammap traces gamma unless set separately */
+    if ( oldn != *argn && p->remove == UNSET ) p->gammap = p->gamma ;
+  }
+  /* handle the special case of -1 (no removal) */
+  if ( oldn == *argn && pm_keymatch( argv[*argn], "-gammap", 7 ) &&
+       *argn + 1 < argc && STREQ(argv[*argn + 1], "-1") ) {
+    p->remove = 0 ;
+    *argn = (*argn) + 2 ;
+  } 
+  if ( oldn == *argn ) {
+    if ( (err = floatArg( &(p->gammap), "-gammap", 7, 0.1, 10.0,
+                               argn, argc, argv )) ) return err ;
+    if ( oldn != *argn ) p->remove = 1 ;
+  }
+
+  return ERR_OK ;
+}
+
+
+/* free conversion structure */
+static void 
+standardClose( Conv *conv ) {
+  if ( conv ) {
+    if ( conv->private ) { free( conv->private ) ; }
+    free( conv ) ; 
+  }
+}
+
+
+/* build new conversion structure */
+static Err 
+newStandardMap( Conv **conv ) {
+
+  Standard *s ;
+
+  if ( ! (*conv = (Conv*)calloc( 1, sizeof( Conv ) )) ) goto error ;
+  if ( ! (s = (Standard*)calloc( 1, sizeof( Standard ) )) ) goto error ;
+  s->initialized = 0 ;
+  s->remove = UNSET ; /* gammap unset */
+  s->gamma = 1.0 ;
+  (*conv)->private = s ;
+  (*conv)->opt = standardOpt ;
+  (*conv)->convert = standardConvert ;
+  (*conv)->close = standardClose ;
+
+  return ERR_OK ;
+
+ error:
+  fprintf( stderr, "cannot allocate memory\n" ) ;
+  return ERR_MEMORY ;
+}
+
+
+
+/* next a simple demonstration of how to implement other conversion
+   algorithms - in this case, something that produces a "colour
+   negative" (just to be different) */
+
+
+/* the conversion routine must match the Convert typedef */
+static Err 
+negativeConvert( Conv *conv, Root *rt, float r, float g, float b,
+                 int *c, int *m, int *y, int *k ) {
+
+  /* the simple conversion is c=1-r, but for negatives we will use
+     c=r, so only need to convert from rgb 0-1 floats to cmyk
+     0-MAXTIFFVAL ints */
+  *c = TOMAX( r ) ;
+  *m = TOMAX( g ) ;
+  *y = TOMAX( b ) ;
+  *k = MIN( *c, MIN( *m, *y ) ) ;
+
+  return ERR_OK ;
+}
+
+
+/* since this simple conversion takes no parameters, we don't need to
+   parse them - this routine must match the Opt typedef */
+static Err 
+negativeOpt( Conv *conv, Root *r, int *argn, int argc, char **argv ) {
+  return ERR_OK ;
+}
+
+
+/* with no parameters we haven't needed the private data structure, so
+   closing is trivial - this routine must match the Close typedef */
+static void 
+negativeClose( Conv *conv ) { }
+
+
+/* and that's it, apart from assembling the routines above into a
+   single struct/object (and adding code in parseOpts to select the
+   algorithm and printUsage to help the user) */
+static Err 
+newNegativeMap( Conv **conv ) {
+
+  if ( ! (*conv = (Conv*)calloc( 1, sizeof( Conv ) )) ) goto error ;
+  (*conv)->opt = negativeOpt ;
+  (*conv)->convert = negativeConvert ;
+  (*conv)->close = negativeClose ;
+
+  return ERR_OK ;
+
+ error:
+  fprintf( stderr, "cannot allocate memory\n" ) ;
+  return ERR_MEMORY ;
+}
+
+
+
+/* --- general routines ------------------------------- */
+
+
+/* run through args, passing to sub components */
+static Err 
+parseOpts( int argc, char **argv, Root *r ) {
+
+  int argn = 1 ;
+  int oldn ;
+  Err err ;
+
+  /* set default reader, writer and converter */
+  if ( (err = newPnmInput( &(r->in) )) ) return err ;
+  if ( (err = newTiffOutput( &(r->out) )) ) return err ;
+  if ( (err = newStandardMap( &(r->conv) )) ) return err ;
+
+  /* minimum number of chars from inspection - this is standard netpbm
+     approach - so must check when adding more options */
+  while ( argn < argc ) {
+
+    /* first, high-level options that select components - currently
+       just the default or negative converter */
+    if ( pm_keymatch( argv[argn], "-default", 2 ) ) {
+      if ( r->conv ) { r->conv->close( r->conv ) ; }
+      if ( (err = newStandardMap( &(r->conv) )) ) return err ;
+      ++argn ;
+    } else if ( pm_keymatch( argv[argn], "-negative", 3 ) ) {
+      if ( r->conv ) { r->conv->close( r->conv ) ; }
+      if ( (err = newNegativeMap( &(r->conv) )) ) return err ;
+      ++argn ;
+    } else if ( pm_keymatch( argv[argn], "-help", 2 ) ) {
+      printUsage( ) ;
+      return ERR_HELP ;
+    } else {
+      /* next, try passing the option to each subcomponent */
+      oldn = argn ;
+      if ( (err = r->in->opt( r->in, r, &argn, argc, argv )) ) {
+        return err ;
+      } else if ( oldn == argn &&
+                  (err = r->out->opt( r->out, r, &argn, argc, argv )) ) {
+        return err ;
+      } else if ( oldn == argn &&
+                  (err = r->conv->opt( r->conv, r, &argn, argc, argv )) ) {
+        return err ;
+      } else if ( oldn == argn ) {
+        fprintf( stderr, "unexpected arg: %s\n", argv[argn] ) ;
+        return ERR_ARG ;
+      }
+    }
+  }
+
+  return ERR_OK ;
+}
+
+
+/* drive the reading, conversion, and writing */
+int main( int argc, char **argv ) {
+
+  Root *rt ;
+  float r, g, b ;
+  int c, m, y, k ;
+  Err err = ERR_OK ;
+
+  pnm_init(&argc, argv);
+
+  if ( ! (rt = (Root*)calloc( 1, sizeof( Root ) )) ) {
+    err = ERR_MEMORY ;
+    fprintf( stderr, "cannot allocate memory\n" ) ;
+    goto exit ;
+  }
+  rt->name = "Standard input" ;
+  rt->maxOut = MAXTIFFVAL ;
+
+  if ( (err = parseOpts( argc, argv, rt )) ) goto exit ;
+  
+  if ( (err = rt->in->open( rt->in, rt )) ) goto exit ;
+  if ( (err = rt->out->open( rt->out, rt )) ) goto exit ;
+
+  while ( rt->in->hasMore( rt->in ) ) {
+    if ( (err = rt->in->next( rt->in, &r, &g, &b )) ) goto exit ;
+    if ( (err = rt->conv->convert( rt->conv, rt, r, g, b, &c, &m, &y, &k )) )
+      goto exit ;
+    if ( (err = rt->out->write( rt->out, c, m, y, k )) ) goto exit ;
+  }
+
+ exit:
+
+  if ( rt && rt->out && rt->out->close ) rt->out->close( rt->out ) ;
+  if ( rt && rt->conv && rt->conv->close ) rt->conv->close( rt->conv ) ;
+  if ( rt && rt->in && rt->in->close ) rt->in->close( rt->in ) ;
+  if ( rt ) free( rt ) ;
+
+  if ( err == ERR_ARG ) printUsage( ) ;
+
+  return err ;
+}
+
+
+
diff --git a/converter/other/pnmtoxwd.c b/converter/other/pnmtoxwd.c
new file mode 100644
index 00000000..32fb8a7b
--- /dev/null
+++ b/converter/other/pnmtoxwd.c
@@ -0,0 +1,505 @@
+/* pnmtoxwd.c - read a portable anymap and produce a color X11 window dump
+**
+** 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.
+*/
+
+#include <string.h>
+#include "pnm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "x11wd.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;  /* Filespecs of input file */
+    unsigned int pseudodepth;
+    unsigned int directcolor;
+};
+
+
+
+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 depthSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+  
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "directcolor", OPT_FLAG,    NULL,   &cmdlineP->directcolor,  0);
+    OPTENT3(0, "pseudodepth",    OPT_UINT,  &cmdlineP->pseudodepth,    
+            &depthSpec,          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 (!depthSpec)
+        cmdlineP->pseudodepth = 8;
+    else {
+        if (cmdlineP->pseudodepth < 1)
+            pm_error("-pseudodepth option value must be at least 1.  "
+                     "You specified %u", cmdlineP->pseudodepth);
+        else if (cmdlineP->pseudodepth > 16)
+            pm_error("-pseudodepth option value must be at most 16.  "
+                     "You specified %u", cmdlineP->pseudodepth);
+    }
+
+    if (argc-1 == 0) 
+        cmdlineP->inputFilespec = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
+    else
+        cmdlineP->inputFilespec = argv[1];
+}
+
+
+
+static void
+setupX11Header(X11WDFileHeader * const h11P,
+               const char * const dumpname,
+               unsigned int const cols,
+               unsigned int const rows,
+               int          const format,
+               bool         const direct,
+               bool         const grayscale,
+               unsigned int const colors,
+               unsigned int const pseudodepth) {
+
+    /* Set up the header. */
+
+    h11P->header_size = sizeof(*h11P) + (xwdval) strlen(dumpname) + 1;
+    h11P->file_version = X11WD_FILE_VERSION;
+    h11P->pixmap_format = ZPixmap;
+    h11P->pixmap_width = cols;
+    h11P->pixmap_height = rows;
+    h11P->xoffset = 0;
+    h11P->byte_order = MSBFirst;
+    h11P->bitmap_bit_order = MSBFirst;
+    h11P->window_width = cols;
+    h11P->window_height = rows;
+    h11P->window_x = 0;
+    h11P->window_y = 0;
+    h11P->window_bdrwidth = 0;
+
+    if (direct) {
+        h11P->pixmap_depth = 24;
+        h11P->bitmap_unit = 32;
+        h11P->bitmap_pad = 32;
+        h11P->bits_per_pixel = 32;
+        h11P->visual_class = DirectColor;
+        h11P->colormap_entries = 256;
+        h11P->ncolors = 256;
+        h11P->red_mask = 0xff0000;
+        h11P->green_mask = 0xff00;
+        h11P->blue_mask = 0xff;
+        h11P->bytes_per_line = cols * 4;
+    } else {  /* pseudocolor -- i.e. regular paletted raster */
+        if (grayscale) {
+            if (PNM_FORMAT_TYPE(format) == PBM_TYPE) {
+                h11P->pixmap_depth = 1;
+                h11P->bits_per_pixel = 1;
+                h11P->colormap_entries = colors;
+                h11P->bytes_per_line = (cols + 7) / 8;
+            } else {
+                h11P->pixmap_depth = pseudodepth;
+                h11P->bits_per_pixel = pseudodepth;
+                h11P->colormap_entries = colors;
+                h11P->bytes_per_line = cols;
+            }
+            h11P->bitmap_unit = 8;
+            h11P->bitmap_pad = 8;
+            h11P->visual_class = StaticGray;
+            h11P->red_mask = 0;
+            h11P->green_mask = 0;
+            h11P->blue_mask = 0;
+        } else {
+            h11P->pixmap_depth = pseudodepth;
+            h11P->bits_per_pixel = pseudodepth;
+            h11P->visual_class = PseudoColor;
+            h11P->colormap_entries = 1 << pseudodepth;
+            h11P->red_mask = 0;
+            h11P->green_mask = 0;
+            h11P->blue_mask = 0;
+            h11P->bytes_per_line = cols;
+            h11P->bitmap_unit = 8;
+            h11P->bitmap_pad = 8;
+        }
+        h11P->ncolors = colors;
+    }
+    h11P->bits_per_rgb = h11P->pixmap_depth;
+}
+
+
+
+
+static void
+writeX11Header(X11WDFileHeader const h11,
+               FILE *          const ofP) {
+
+    /* Write out the header in big-endian order. */
+
+    pm_writebiglong(ofP, h11.header_size);
+    pm_writebiglong(ofP, h11.file_version);
+    pm_writebiglong(ofP, h11.pixmap_format);
+    pm_writebiglong(ofP, h11.pixmap_depth);
+    pm_writebiglong(ofP, h11.pixmap_width);
+    pm_writebiglong(ofP, h11.pixmap_height);
+    pm_writebiglong(ofP, h11.xoffset);
+    pm_writebiglong(ofP, h11.byte_order);
+    pm_writebiglong(ofP, h11.bitmap_unit);
+    pm_writebiglong(ofP, h11.bitmap_bit_order);
+    pm_writebiglong(ofP, h11.bitmap_pad);
+    pm_writebiglong(ofP, h11.bits_per_pixel);
+    pm_writebiglong(ofP, h11.bytes_per_line);
+    pm_writebiglong(ofP, h11.visual_class);
+    pm_writebiglong(ofP, h11.red_mask);
+    pm_writebiglong(ofP, h11.green_mask);
+    pm_writebiglong(ofP, h11.blue_mask);
+    pm_writebiglong(ofP, h11.bits_per_rgb);
+    pm_writebiglong(ofP, h11.colormap_entries);
+    pm_writebiglong(ofP, h11.ncolors);
+    pm_writebiglong(ofP, h11.window_width);
+    pm_writebiglong(ofP, h11.window_height);
+    pm_writebiglong(ofP, h11.window_x);
+    pm_writebiglong(ofP, h11.window_y);
+    pm_writebiglong(ofP, h11.window_bdrwidth);
+}
+
+
+
+static void
+writePseudoColormap(FILE *           const ofP,
+                    colorhist_vector const chv,
+                    unsigned int     const colors,
+                    bool             const grayscale,
+                    bool             const backwardMap,
+                    xelval           const maxval) {
+    /* Write out the colormap, big-endian order. */
+    
+    X11XColor color;
+    unsigned int i;
+
+    color.flags = 7;
+    color.pad = 0;
+    for (i = 0; i < colors; ++i) {
+        color.num = i;
+        if (grayscale) {
+            /* Stupid hack because xloadimage and xwud disagree on
+               how to interpret bitmaps. 
+            */
+            if (backwardMap)
+                color.red = (long) (colors-1-i) * 65535L / (colors - 1);
+            else
+                color.red = (long) i * 65535L / (colors - 1);
+            
+            color.green = color.red;
+            color.blue = color.red;
+        } else {
+            color.red = PPM_GETR(chv[i].color);
+            color.green = PPM_GETG(chv[i].color);
+            color.blue = PPM_GETB(chv[i].color);
+            if (maxval != 65535L) {
+                color.red = (long) color.red * 65535L / maxval;
+                color.green = (long) color.green * 65535L / maxval;
+                color.blue = (long) color.blue * 65535L / maxval;
+            }
+        }
+        pm_writebiglong( ofP, color.num);
+        pm_writebigshort(ofP, color.red);
+        pm_writebigshort(ofP, color.green);
+        pm_writebigshort(ofP, color.blue);
+        putc(color.flags, ofP);
+        putc(color.pad,   ofP);
+    }
+}
+
+
+
+static void
+writeDirectColormap(FILE * const ofP) {
+/*----------------------------------------------------------------------------
+   Write the XWD colormap.
+
+   We use a constant (independent of input) color map which simply has
+   each of the values 0-255, scaled to 16 bits, for each of the three
+   maps
+-----------------------------------------------------------------------------*/
+    X11XColor color;
+    unsigned int i;
+
+    color.flags = 7;
+    color.pad = 0;
+
+    for (i = 0; i < 256; ++i) {
+        color.red   = (short)(i << 8 | i);
+        color.green = (short)(i << 8 | i);
+        color.blue  = (short)(i << 8 | i);
+        color.num   = i << 16 | i << 8 | i;
+        
+        pm_writebiglong( ofP, color.num);
+        pm_writebigshort(ofP, color.red);
+        pm_writebigshort(ofP, color.green);
+        pm_writebigshort(ofP, color.blue);
+        putc(color.flags, ofP);
+        putc(color.pad,   ofP);
+    }
+}
+
+
+
+static void
+writeRowDirect(FILE *       const ofP,
+               xel *        const xelrow,
+               unsigned int const cols,
+               int          const format,
+               long         const xmaxval,
+               xelval       const maxval) {
+    
+    switch (PNM_FORMAT_TYPE(format)) {
+    case PPM_TYPE: {
+        unsigned int col;
+        for (col = 0; col < cols; ++col) {
+            unsigned long const ul = 
+                ((PPM_GETR(xelrow[col]) * xmaxval / maxval) << 16) |
+                ((PPM_GETG(xelrow[col]) * xmaxval / maxval) << 8) |
+                (PPM_GETB(xelrow[col]) * xmaxval / maxval);
+            pm_writebiglong(ofP, ul);
+        }
+    }
+    break;
+
+    default: {
+        unsigned int col;
+        for (col = 0; col < cols; ++col) {
+            unsigned long const val = PNM_GET1(xelrow[col]);
+            unsigned long const ul =
+                ((val * xmaxval / maxval) << 16) |
+                ((val * xmaxval / maxval) << 8) |
+                (val * xmaxval / maxval);
+            pm_writebiglong(ofP, ul);
+        }
+        break;
+    }
+    }
+}
+
+
+
+static void
+writeRowGrayscale(FILE *       const ofP,
+                  xel *        const xelrow,
+                  unsigned int const cols,
+                  xelval       const maxval,
+                  bool         const backwardMap,
+                  unsigned int const bitsPerPixel) {
+
+    xelval bigger_maxval;
+    int bitshift;
+    unsigned char byte;
+    unsigned int col;
+    
+    bigger_maxval = pm_bitstomaxval(bitsPerPixel);
+    bitshift = 8 - bitsPerPixel;
+    byte = 0;
+    for (col = 0; col < cols; ++col) {
+        xelval s;
+        s = PNM_GET1(xelrow[col]);
+        if (backwardMap)
+            s = 1 - s;
+        
+        if (maxval != bigger_maxval)
+            s = (long) s * bigger_maxval / maxval;
+        byte |= s << bitshift;
+        bitshift -= bitsPerPixel;
+        if (bitshift < 0) {
+            putc(byte, stdout);
+            bitshift = 8 - bitsPerPixel;
+            byte = 0;
+        }
+    }
+    if (bitshift < 8 - bitsPerPixel)
+        putc(byte, ofP);
+}
+
+
+
+static void
+writeRowPseudoColor(FILE *          const ofP,
+                    xel *           const xelrow,
+                    unsigned int    const cols,
+                    colorhash_table const cht) {
+                       
+    unsigned int col;
+
+    for (col = 0; col < cols; ++col)
+        putc(ppm_lookupcolor(cht, &xelrow[col]), ofP);
+}
+
+
+
+static void
+writeRaster(FILE *           const ofP,
+            xel **           const xels,
+            unsigned int     const cols,
+            unsigned int     const rows,
+            int              const format,
+            xelval           const maxval,
+            long             const xmaxval,
+            colorhist_vector const chv,
+            unsigned int     const colors,
+            bool             const direct,
+            bool             const grayscale,
+            bool             const backwardMap,
+            unsigned int     const bitsPerPixel) {
+
+    unsigned int row;
+    colorhash_table cht;
+
+    if (chv)
+        cht = ppm_colorhisttocolorhash(chv, colors);
+            
+    for (row = 0; row < rows; ++row) {
+        if (direct)
+            writeRowDirect(ofP, xels[row], cols, format, xmaxval, maxval);
+        else {
+            if (grayscale)
+                writeRowGrayscale(ofP, xels[row], cols, maxval, backwardMap,
+                                  bitsPerPixel);
+            else
+                writeRowPseudoColor(ofP, xels[row], cols, cht);
+        }
+    }
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE* ifP;
+    xel ** xels;
+    int rows, cols, format, colors;
+    xelval maxval;
+    long xmaxval;
+    colorhist_vector chv;
+    X11WDFileHeader h11;
+    bool direct, grayscale;
+    const char * dumpname;
+    bool backwardMap;
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    xels = pnm_readpnm(ifP, &cols, &rows, &maxval, &format);
+    xmaxval = (1 << cmdline.pseudodepth) - 1;
+    pm_close(ifP);
+    
+    if (cmdline.directcolor) {
+        direct = TRUE;
+        grayscale = FALSE;
+        chv = NULL;
+    } else {
+        /* Figure out the colormap. */
+        switch (PNM_FORMAT_TYPE(format)) {
+        case PPM_TYPE:
+            pm_message("computing colormap...");
+            chv = ppm_computecolorhist(xels, cols, rows, xmaxval+1, &colors);
+            if (!chv) {
+                pm_message("Too many colors - "
+                           "proceeding to write a 24-bit DirectColor "
+                           "dump file.  If you want PseudoColor, "
+                           "try doing a 'pnmquant %ld'.",
+                           xmaxval);
+                direct = TRUE;
+            } else {
+                pm_message("%d colors found", colors);
+                direct = FALSE;
+            }
+            grayscale = FALSE;
+            break;
+
+        case PBM_TYPE:
+            chv = NULL;
+            direct = FALSE;
+            grayscale = TRUE;
+            colors = 2;
+            break;
+        case PGM_TYPE:
+            chv = NULL;
+            direct = FALSE;
+            grayscale = TRUE;
+            colors = xmaxval + 1;
+            break;
+        default:
+            pm_error("INTERNAL ERROR: impossible format type");
+        }
+    }
+
+    if (STREQ(cmdline.inputFilespec, "-"))
+        dumpname = "stdin";
+    else {
+        if (strlen(cmdline.inputFilespec) > XWDVAL_MAX - sizeof(h11) - 1)
+            pm_error("Input file name is ridiculously long.");
+        else
+            dumpname = cmdline.inputFilespec;
+    }
+
+    setupX11Header(&h11, dumpname, cols, rows, format, 
+                   direct, grayscale, colors,
+                   cmdline.pseudodepth);
+
+    writeX11Header(h11, stdout);
+
+    /* Write out the dump name. */
+    fwrite(dumpname, 1, strlen(dumpname) + 1, stdout);
+
+    backwardMap = PNM_FORMAT_TYPE(format) == PBM_TYPE;
+
+    if (direct)
+        writeDirectColormap(stdout);
+    else
+        writePseudoColormap(stdout, chv, colors, 
+                            grayscale, backwardMap, maxval);
+    
+    writeRaster(stdout, xels, cols, rows, format, maxval, xmaxval, 
+                chv, colors, direct, grayscale,
+                backwardMap, h11.bits_per_pixel);
+
+    return 0;
+}
diff --git a/converter/other/ppmtopgm.c b/converter/other/ppmtopgm.c
new file mode 100644
index 00000000..86e7ae6a
--- /dev/null
+++ b/converter/other/ppmtopgm.c
@@ -0,0 +1,95 @@
+/* ppmtopgm.c - convert a portable pixmap to a portable graymap
+**
+** 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 "pm_c_util.h"
+#include "ppm.h"
+#include "pgm.h"
+#include "lum.h"
+
+static void
+convertRaster(FILE *       const ifP,
+              unsigned int const cols,
+              unsigned int const rows,
+              pixval       const maxval,
+              int          const format, 
+              pixel *      const inputRow,
+              gray *       const outputRow, 
+              FILE *       const ofP) {
+
+    unsigned int row;
+
+    for (row = 0; row < rows; ++row) {
+        ppm_readppmrow( ifP, inputRow, cols, maxval, format );
+        if (maxval <= 255) {
+            /* Use fast approximation to 0.299 r + 0.587 g + 0.114 b */
+            unsigned int col;
+            for (col = 0; col < cols; ++col)
+                outputRow[col] = (gray) ppm_fastlumin(inputRow[col]);
+        } else {
+            /* Can't use fast approximation, so fall back on floats. */
+            int col;
+            for (col = 0; col < cols; ++col) 
+                outputRow[col] = (gray) (PPM_LUMIN(inputRow[col]) + 0.5);
+        }
+        pgm_writepgmrow(ofP, outputRow, cols, maxval, 0);
+    }
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    FILE* ifP;
+    const char * inputFilespec;
+    int eof;
+    
+    ppm_init( &argc, argv );
+
+    if (argc-1 > 1)
+        pm_error("The only argument is the (optional) input filename");
+
+    if (argc == 2)
+        inputFilespec = argv[1];
+    else
+        inputFilespec = "-";
+    
+    ifP = pm_openr(inputFilespec);
+
+    eof = FALSE;  /* initial assumption */
+
+    while (!eof) {
+        ppm_nextimage(ifP, &eof);
+        if (!eof) {
+            int rows, cols, format;
+            pixval maxval;
+            pixel* inputRow;
+            gray* outputRow;
+
+            ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
+            pgm_writepgminit(stdout, cols, rows, maxval, 0);
+
+            inputRow = ppm_allocrow(cols);
+            outputRow = pgm_allocrow(cols);
+
+            convertRaster(ifP, cols, rows, maxval, format, 
+                          inputRow, outputRow, stdout);
+
+            ppm_freerow(inputRow);
+            pgm_freerow(outputRow);
+        }
+    }
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
+}
diff --git a/converter/other/pstopnm.c b/converter/other/pstopnm.c
new file mode 100644
index 00000000..a31c3f64
--- /dev/null
+++ b/converter/other/pstopnm.c
@@ -0,0 +1,899 @@
+/*----------------------------------------------------------------------------
+                                 pstopnm
+------------------------------------------------------------------------------
+  Use Ghostscript to convert a Postscript file into a PBM, PGM, or PNM
+  file.
+
+  Implementation note: This program feeds the input file to Ghostcript
+  directly (with possible statements preceding it), and uses
+  Ghostscript's PNM output device drivers.  As an alternative,
+  Ghostscript also comes with the Postscript program pstoppm.ps which
+  we could run and it would read the input file and produce PNM
+  output.  It isn't clear to me what pstoppm.ps adds to what you get
+  from just feeding your input directly to Ghostscript as the main program.
+
+-----------------------------------------------------------------------------*/
+
+#define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  
+    /* Make sure fdopen() is in stdio.h and strdup() is in string.h */
+
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sys/wait.h>  
+#include <sys/stat.h>
+
+#include "pnm.h"
+#include "shhopt.h"
+#include "nstring.h"
+
+enum orientation {PORTRAIT, LANDSCAPE, UNSPECIFIED};
+struct box {
+    /* Description of a rectangle within an image; all coordinates 
+       measured in points (1/72") with lower left corner of page being the 
+       origin.
+    */
+    int llx;  /* lower left X coord */
+        /* -1 for llx means whole box is undefined. */
+    int lly;  /* lower left Y coord */
+    int urx;  /* upper right X coord */
+    int ury;  /* upper right Y coord */
+};
+
+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;  /* Filespecs of input files */
+    unsigned int forceplain;
+    struct box extract_box;
+    unsigned int nocrop;
+    unsigned int format_type;
+    unsigned int verbose;
+    float xborder;
+    unsigned int xmax;
+    unsigned int xsize;  /* zero means unspecified */
+    float yborder;
+    unsigned int ymax;
+    unsigned int ysize;  /* zero means unspecified */
+    unsigned int dpi;    /* zero means unspecified */
+    enum orientation orientation;
+    unsigned int goto_stdout;
+};
+
+
+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 = malloc( 100*sizeof( optEntry ) );
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int pbm_opt, pgm_opt, ppm_opt;
+    unsigned int portrait_opt, landscape_opt;
+    float llx, lly, urx, ury;
+    unsigned int llxSpec, llySpec, urxSpec, urySpec;
+    unsigned int xmaxSpec, ymaxSpec, xsizeSpec, ysizeSpec, dpiSpec;
+    
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "forceplain", OPT_FLAG,  NULL, &cmdlineP->forceplain,     0);
+    OPTENT3(0, "llx",        OPT_FLOAT, &llx, &llxSpec,                  0);
+    OPTENT3(0, "lly",        OPT_FLOAT, &lly, &llySpec,                  0);
+    OPTENT3(0, "urx",        OPT_FLOAT, &urx, &urxSpec,                  0);
+    OPTENT3(0, "ury",        OPT_FLOAT, &ury, &urySpec,                  0);
+    OPTENT3(0, "nocrop",     OPT_FLAG,  NULL, &cmdlineP->nocrop,         0);
+    OPTENT3(0, "pbm",        OPT_FLAG,  NULL, &pbm_opt,                  0);
+    OPTENT3(0, "pgm",        OPT_FLAG,  NULL, &pgm_opt,                  0);
+    OPTENT3(0, "ppm",        OPT_FLAG,  NULL, &ppm_opt,                  0);
+    OPTENT3(0, "verbose",    OPT_FLAG,  NULL, &cmdlineP->verbose,        0);
+    OPTENT3(0, "xborder",    OPT_FLOAT, &cmdlineP->xborder, NULL,        0);
+    OPTENT3(0, "xmax",       OPT_UINT,  &cmdlineP->xmax, &xmaxSpec,      0);
+    OPTENT3(0, "xsize",      OPT_UINT,  &cmdlineP->xsize, &xsizeSpec,    0);
+    OPTENT3(0, "yborder",    OPT_FLOAT, &cmdlineP->yborder, NULL,        0);
+    OPTENT3(0, "ymax",       OPT_UINT,  &cmdlineP->ymax, &ymaxSpec,      0);
+    OPTENT3(0, "ysize",      OPT_UINT,  &cmdlineP->ysize, &ysizeSpec,    0);
+    OPTENT3(0, "dpi",        OPT_UINT,  &cmdlineP->dpi, &dpiSpec,        0);
+    OPTENT3(0, "portrait",   OPT_FLAG,  NULL, &portrait_opt,             0);
+    OPTENT3(0, "landscape",  OPT_FLAG,  NULL, &landscape_opt,            0);
+    OPTENT3(0, "stdout",     OPT_FLAG,  NULL, &cmdlineP->goto_stdout,    0);
+
+    /* Set the defaults */
+    cmdlineP->xborder = cmdlineP->yborder = 0.1;
+
+    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 (xmaxSpec) {
+        if (cmdlineP->xmax == 0)
+            pm_error("zero is not a valid value for -xmax");
+    } else
+        cmdlineP->xmax = 612;
+
+    if (ymaxSpec) {
+        if (cmdlineP->ymax == 0)
+            pm_error("zero is not a valid value for -ymax");
+    } else 
+        cmdlineP->ymax = 792;
+
+    if (xsizeSpec) {
+        if (cmdlineP->xsize == 0)
+            pm_error("zero is not a valid value for -xsize");
+    } else
+        cmdlineP->xsize = 0;
+
+    if (ysizeSpec) {
+        if (cmdlineP->ysize == 0)
+            pm_error("zero is not a valid value for -ysize");
+    } else 
+        cmdlineP->ysize = 0;
+
+    if (portrait_opt & !landscape_opt)
+        cmdlineP->orientation = PORTRAIT;
+    else if (!portrait_opt & landscape_opt)
+        cmdlineP->orientation = LANDSCAPE;
+    else if (!portrait_opt & !landscape_opt)
+        cmdlineP->orientation = UNSPECIFIED;
+    else
+        pm_error("Cannot specify both -portrait and -landscape options");
+
+    if (pbm_opt)
+        cmdlineP->format_type = PBM_TYPE;
+    else if (pgm_opt)
+        cmdlineP->format_type = PGM_TYPE;
+    else if (ppm_opt)
+        cmdlineP->format_type = PPM_TYPE;
+    else
+        cmdlineP->format_type = PPM_TYPE;
+
+    /* If any one of the 4 bounding box coordinates is given on the
+       command line, we default any of the 4 that aren't.  
+    */
+    if (llxSpec || llySpec || urxSpec || urySpec) {
+        if (!llxSpec) cmdlineP->extract_box.llx = 72;
+        else cmdlineP->extract_box.llx = llx * 72;
+        if (!llySpec) cmdlineP->extract_box.lly = 72;
+        else cmdlineP->extract_box.lly = lly * 72;
+        if (!urxSpec) cmdlineP->extract_box.urx = 540;
+        else cmdlineP->extract_box.urx = urx * 72;
+        if (!urySpec) cmdlineP->extract_box.ury = 720;
+        else cmdlineP->extract_box.ury = ury * 72;
+    } else {
+        cmdlineP->extract_box.llx = -1;
+    }
+
+    if (dpiSpec) {
+        if (cmdlineP->dpi == 0)
+            pm_error("Zero is not a valid value for -dpi");
+    } else
+        cmdlineP->dpi = 0;
+
+    if (dpiSpec && xsizeSpec + ysizeSpec + xmaxSpec + ymaxSpec > 0)
+        pm_error("You may not specify both size options and -dpi");
+
+    if (argc-1 == 0)
+        cmdlineP->input_filespec = "-";  /* stdin */
+    else if (argc-1 == 1)
+        cmdlineP->input_filespec = argv[1];
+    else 
+        pm_error("Too many arguments (%d).  "
+                 "Only need one: the Postscript filespec", argc-1);
+}
+
+
+
+static void
+add_ps_to_filespec(const char orig_filespec[], char ** const new_filespec_p,
+                   const int verbose) {
+/*----------------------------------------------------------------------------
+   If orig_filespec[] does not name an existing file, but the same
+   name with ".ps" added to the end does, return the name with the .ps
+   attached.  Otherwise, just return orig_filespec[].
+
+   Return the name in newly malloc'ed storage, pointed to by
+   *new_filespec_p.
+-----------------------------------------------------------------------------*/
+    struct stat statbuf;
+    int stat_rc;
+
+    stat_rc = lstat(orig_filespec, &statbuf);
+    
+    if (stat_rc == 0)
+        *new_filespec_p = strdup(orig_filespec);
+    else {
+        const char *filespec_plus_ps;
+
+        asprintfN(&filespec_plus_ps, "%s.ps", orig_filespec);
+
+        stat_rc = lstat(filespec_plus_ps, &statbuf);
+        if (stat_rc == 0)
+            *new_filespec_p = strdup(filespec_plus_ps);
+        else
+            *new_filespec_p = strdup(orig_filespec);
+        strfree(filespec_plus_ps);
+    }
+    if (verbose)
+        pm_message("Input file is %s", *new_filespec_p);
+}
+
+
+
+static void
+computeSizeResFromSizeSpec(unsigned int   const requestedXsize,
+                           unsigned int   const requestedYsize,
+                           unsigned int   const imageWidth,
+                           unsigned int   const imageHeight,
+                           unsigned int * const xsizeP,
+                           unsigned int * const ysizeP,
+                           unsigned int * const xresP,
+                           unsigned int * const yresP) {
+
+    if (requestedXsize) {
+        *xsizeP = requestedXsize;
+        *xresP = (unsigned int) (requestedXsize * 72 / imageWidth + 0.5);
+        if (!requestedYsize) {
+            *yresP = *xresP;
+            *ysizeP = (unsigned int) (imageHeight * (float)*yresP/72 + 0.5);
+            }
+        }
+
+    if (requestedYsize) {
+        *ysizeP = requestedYsize;
+        *yresP = (unsigned int) (requestedYsize * 72 / imageHeight + 0.5);
+        if (!requestedXsize) {
+            *xresP = *yresP;
+            *xsizeP = (unsigned int) (imageWidth * (float)*xresP/72 + 0.5);
+        }
+    } 
+}
+
+
+
+static void
+computeSizeResBlind(unsigned int   const xmax,
+                    unsigned int   const ymax,
+                    unsigned int   const imageWidth,
+                    unsigned int   const imageHeight,
+                    bool           const nocrop,
+                    unsigned int * const xsizeP,
+                    unsigned int * const ysizeP,
+                    unsigned int * const xresP,
+                    unsigned int * const yresP) {
+
+    *xresP = *yresP = MIN(xmax * 72 / imageWidth, 
+                          ymax * 72 / imageHeight);
+    
+    if (nocrop) {
+        *xsizeP = xmax;
+        *ysizeP = ymax;
+    } else {
+        *xsizeP = (unsigned int) (imageWidth * (float)*xresP / 72 + 0.5);
+        *ysizeP = (unsigned int) (imageHeight * (float)*yresP / 72 + 0.5);
+    }
+}
+
+
+
+static void
+compute_size_res(struct cmdlineInfo const cmdline, 
+                 enum orientation   const orientation, 
+                 struct box         const bordered_box,
+                 unsigned int *     const xsizeP, 
+                 unsigned int *     const ysizeP,
+                 unsigned int *     const xresP, 
+                 unsigned int *     const yresP) {
+/*----------------------------------------------------------------------------
+  Figure out how big the output image should be (return as
+  *xsizeP and *ysizeP) and what output device resolution Ghostscript
+  should assume (return as *xresP, *yresP).
+
+  A resolution number is the number of pixels per inch that the a
+  printer prints.  Since we're emulating a printed page with a PNM
+  image, and a PNM image has no spatial dimension (you can't say how
+  many inches wide a PNM image is), it's kind of confusing.  
+
+  If the user doesn't select a resolution, we choose the resolution
+  that causes the image to be a certain number of pixels, knowing how
+  big (in inches) Ghostscript wants the printed picture to be.  For
+  example, the part of the Postscript image we are going to print is 2
+  inches wide.  We want the PNM image to be 1000 pixels wide.  So we
+  tell Ghostscript that our horizontal output device resolution is 500
+  pixels per inch.
+  
+  *xresP and *yresP are in dots per inch.
+-----------------------------------------------------------------------------*/
+    unsigned int sx, sy;
+        /* The horizontal and vertical sizes of the input image, in points
+           (1/72 inch)
+        */
+
+    if (orientation == LANDSCAPE) {
+        sx = bordered_box.ury - bordered_box.lly;
+        sy = bordered_box.urx - bordered_box.llx;
+    } else {
+        sx = bordered_box.urx - bordered_box.llx;
+        sy = bordered_box.ury - bordered_box.lly;
+    }
+
+    if (cmdline.dpi) {
+        /* User gave resolution; we figure out output image size */
+        *xresP = *yresP = cmdline.dpi;
+        *xsizeP = (int) (cmdline.dpi * sx / 72 + 0.5);
+        *ysizeP = (int) (cmdline.dpi * sy / 72 + 0.5);
+    } else  if (cmdline.xsize || cmdline.ysize)
+        computeSizeResFromSizeSpec(cmdline.xsize, cmdline.ysize, sx, sy,
+                                   xsizeP, ysizeP, xresP, yresP);
+    else 
+        computeSizeResBlind(cmdline.xmax, cmdline.ymax, sx, sy, cmdline.nocrop,
+                            xsizeP, ysizeP, xresP, yresP);
+
+    if (cmdline.verbose) {
+        pm_message("output is %u pixels wide X %u pixels high",
+                   *xsizeP, *ysizeP);
+        pm_message("output device resolution is %u dpi horiz, %u dpi vert",
+                   *xresP, *yresP);
+    }
+}
+
+
+
+enum postscript_language {COMMON_POSTSCRIPT, ENCAPSULATED_POSTSCRIPT};
+
+static enum postscript_language
+language_declaration(const char input_filespec[], int const verbose) {
+/*----------------------------------------------------------------------------
+  Return the Postscript language in which the file declares it is written.
+  (Except that if the file is on Standard Input or doesn't validly declare
+  a languages, just say it is Common Postscript).
+-----------------------------------------------------------------------------*/
+    enum postscript_language language;
+
+    if (STREQ(input_filespec, "-"))
+        /* Can't read stdin, because we need it to remain positioned for the 
+           Ghostscript interpreter to read it.
+        */
+        language = COMMON_POSTSCRIPT;
+    else {
+        FILE *infile;
+        char line[80];
+
+        infile = pm_openr(input_filespec);
+
+        if (fgets(line, sizeof(line), infile) == NULL)
+            language = COMMON_POSTSCRIPT;
+        else {
+            const char eps_header[] = " EPSF-";
+
+            if (strstr(line, eps_header))
+                language = ENCAPSULATED_POSTSCRIPT;
+            else
+                language = COMMON_POSTSCRIPT;
+        }
+        fclose(infile);
+    }
+    if (verbose)
+        pm_message("language is %s",
+                   language == ENCAPSULATED_POSTSCRIPT ?
+                   "encapsulated postscript" :
+                   "not encapsulated postscript");
+    return language;
+}
+
+
+
+static struct box
+compute_box_to_extract(struct box const cmdline_extract_box,
+                       char       const input_filespec[],
+                       bool       const verbose) {
+
+    struct box retval;
+
+    if (cmdline_extract_box.llx != -1)
+        /* User told us what box to extract, so that's what we'll do */
+        retval = cmdline_extract_box;
+    else {
+        /* Try to get the bounding box from the DSC %%BoundingBox
+           statement (A Postscript comment) in the input.
+        */
+        struct box ps_bb;  /* Box described by %%BoundingBox stmt in input */
+
+        if (STREQ(input_filespec, "-"))
+            /* Can't read stdin, because we need it to remain
+               positioned for the Ghostscript interpreter to read it.  
+            */
+            ps_bb.llx = -1;
+        else {
+            FILE *infile;
+            int found_BB, eof;  /* logical */
+            infile = pm_openr(input_filespec);
+            
+            found_BB = FALSE;
+            eof = FALSE;
+            while (!eof && !found_BB) {
+                char line[200];
+                
+                if (fgets(line, sizeof(line), infile) == NULL)
+                    eof = TRUE;
+                else {
+                    int rc;
+                    rc = sscanf(line, "%%%%BoundingBox: %d %d %d %d",
+                                &ps_bb.llx, &ps_bb.lly, 
+                                &ps_bb.urx, &ps_bb.ury);
+                    if (rc == 4) 
+                        found_BB = TRUE;
+                }
+            }
+            fclose(infile);
+
+            if (!found_BB) {
+                ps_bb.llx = -1;
+                pm_message("Warning: no %%%%BoundingBox statement "
+                           "in the input or command line.\n"
+                           "Will use defaults");
+            }
+        }
+        if (ps_bb.llx != -1) {
+            if (verbose)
+                pm_message("Using %%%%BoundingBox statement from input.");
+            retval = ps_bb;
+        } else { 
+            /* Use the center of an 8.5" x 11" page with 1" border all around*/
+            retval.llx = 72;
+            retval.lly = 72;
+            retval.urx = 540;
+            retval.ury = 720;
+        }
+    }
+    if (verbose)
+        pm_message("Extracting the box ((%d,%d),(%d,%d))",
+                   retval.llx, retval.lly, retval.urx, retval.ury);
+    return retval;
+}
+
+
+
+static enum orientation
+compute_orientation(struct cmdlineInfo const cmdline, 
+                    struct box         const extract_box) {
+
+    enum orientation retval;
+    unsigned int const input_width  = extract_box.urx - extract_box.llx;
+    unsigned int const input_height = extract_box.ury - extract_box.lly;
+
+    if (cmdline.orientation != UNSPECIFIED)
+        retval = cmdline.orientation;
+    else {
+        if ((!cmdline.xsize || !cmdline.ysize) &
+            (cmdline.xsize || cmdline.ysize)) {
+            /* User specified one output dimension, but not the other,
+               so we can't use output dimensions to make the decision.  So
+               just use the input dimensions.
+            */
+            if (input_height > input_width) retval = PORTRAIT;
+            else retval = LANDSCAPE;
+        } else {
+            int output_width, output_height;
+            if (cmdline.xsize) {
+                /* He gave xsize and ysize, so that's the output size */
+                output_width = cmdline.xsize;
+                output_height = cmdline.ysize;
+            } else {
+                /* Well then we'll just use his (or default) xmax, ymax */
+                output_width = cmdline.xmax;
+                output_height = cmdline.ymax;
+            }
+
+            if (input_height > input_width && output_height > output_width)
+                retval = PORTRAIT;
+            else if (input_height < input_width && 
+                     output_height < output_width)
+                retval = PORTRAIT;
+            else 
+                retval = LANDSCAPE;
+        }
+    }
+    return retval;
+}
+
+
+
+static struct box
+add_borders(const struct box input_box, 
+            const float xborder_scale, float yborder_scale,
+            const int verbose) {
+/*----------------------------------------------------------------------------
+   Return a box which is 'input_box' plus some borders.
+
+   Add left and right borders that are the fraction 'xborder_scale' of the
+   width of the input box; likewise for top and bottom borders with 
+   'yborder_scale'.
+-----------------------------------------------------------------------------*/
+    struct box retval;
+
+    const int left_right_border_size = 
+        (int) ((input_box.urx - input_box.llx) * xborder_scale + 0.5);
+    const int top_bottom_border_size = 
+        (int) ((input_box.ury - input_box.lly) * yborder_scale + 0.5);
+
+    retval.llx = input_box.llx - left_right_border_size;
+    retval.lly = input_box.lly - top_bottom_border_size;
+    retval.urx = input_box.urx + left_right_border_size;
+    retval.ury = input_box.ury + top_bottom_border_size;
+
+    if (verbose)
+        pm_message("With borders, extracted box is ((%d,%d),(%d,%d))",
+                   retval.llx, retval.lly, retval.urx, retval.ury);
+
+    return retval;
+}
+
+
+
+static const char *
+compute_pstrans(const struct box box, const enum orientation orientation,
+                const int xsize, const int ysize, 
+                const int xres, const int yres) {
+
+    const char * retval;
+
+    if (orientation == PORTRAIT) {
+        int llx, lly;
+        llx = box.llx - (xsize * 72 / xres - (box.urx - box.llx)) / 2;
+        lly = box.lly - (ysize * 72 / yres - (box.ury - box.lly)) / 2;
+        asprintfN(&retval, "%d neg %d neg translate", llx, lly);
+    } else {
+        int llx, ury;
+        llx = box.llx - (ysize * 72 / yres - (box.urx - box.llx)) / 2;
+        ury = box.ury + (xsize * 72 / xres - (box.ury - box.lly)) / 2;
+        asprintfN(&retval, "90 rotate %d neg %d neg translate", llx, ury);
+    }
+
+    if (retval == NULL)
+        pm_error("Unable to allocate memory for pstrans");
+
+    return retval;
+}
+
+
+
+static const char *
+compute_outfile_arg(const struct cmdlineInfo cmdline) {
+
+    const char *retval;  /* malloc'ed */
+
+    if (cmdline.goto_stdout)
+        retval = strdup("-");
+    else if (STREQ(cmdline.input_filespec, "-"))
+        retval = strdup("-");
+    else {
+        char * basename;
+        const char * suffix;
+        
+        basename  = strdup(cmdline.input_filespec);
+        if (strlen(basename) > 3 && 
+            STREQ(basename+strlen(basename)-3, ".ps")) 
+            /* The input filespec ends in ".ps".  Chop it off. */
+            basename[strlen(basename)-3] = '\0';
+
+        switch (cmdline.format_type) {
+        case PBM_TYPE: suffix = "pbm"; break;
+        case PGM_TYPE: suffix = "pgm"; break;
+        case PPM_TYPE: suffix = "ppm"; break;
+        default: pm_error("Internal error: invalid value for format_type: %d",
+                          cmdline.format_type);
+        }
+        asprintfN(&retval, "%s%%03d.%s", basename, suffix);
+
+        strfree(basename);
+    }
+    return(retval);
+}
+
+
+
+static const char *
+compute_gs_device(const int format_type, const int forceplain) {
+
+    const char * basetype;
+    const char * retval;
+
+    switch (format_type) {
+    case PBM_TYPE: basetype = "pbm"; break;
+    case PGM_TYPE: basetype = "pgm"; break;
+    case PPM_TYPE: basetype = "ppm"; break;
+    default: pm_error("Internal error: invalid value format_type");
+    }
+    if (forceplain)
+        retval = strdup(basetype);
+    else
+        asprintfN(&retval, "%sraw", basetype);
+
+    if (retval == NULL)
+        pm_error("Unable to allocate memory for gs device");
+
+    return(retval);
+}
+
+
+
+static void
+findGhostscriptProg(const char ** const retvalP) {
+    
+    *retvalP = NULL;  /* initial assumption */
+    if (getenv("GHOSTSCRIPT"))
+        *retvalP = strdup(getenv("GHOSTSCRIPT"));
+    if (*retvalP == NULL) {
+        if (getenv("PATH") != NULL) {
+            char *pathwork;  /* malloc'ed */
+            const char * candidate;
+
+            pathwork = strdup(getenv("PATH"));
+            
+            candidate = strtok(pathwork, ":");
+
+            *retvalP = NULL;
+            while (!*retvalP && candidate) {
+                struct stat statbuf;
+                const char * filename;
+                int rc;
+
+                asprintfN(&filename, "%s/gs", candidate);
+                rc = stat(filename, &statbuf);
+                if (rc == 0) {
+                    if (S_ISREG(statbuf.st_mode))
+                        *retvalP = strdup(filename);
+                } else if (errno != ENOENT)
+                    pm_error("Error looking for Ghostscript program.  "
+                             "stat(\"%s\") returns errno %d (%s)",
+                             filename, errno, strerror(errno));
+                strfree(filename);
+
+                candidate = strtok(NULL, ":");
+            }
+            free(pathwork);
+        }
+    }
+    if (*retvalP == NULL)
+        *retvalP = strdup("/usr/bin/gs");
+}
+
+
+
+static void
+execGhostscript(int const inputPipeFd,
+                const char ghostscript_device[],
+                const char outfile_arg[], 
+                int const xsize, int const ysize, 
+                int const xres, int const yres,
+                const char input_filespec[], int const verbose) {
+    
+    const char *arg0;
+    const char *ghostscriptProg;
+    const char *deviceopt;
+    const char *outfileopt;
+    const char *gopt;
+    const char *ropt;
+    int rc;
+
+    findGhostscriptProg(&ghostscriptProg);
+
+    /* Put the input pipe on Standard Input */
+    rc = dup2(inputPipeFd, STDIN_FILENO);
+    close(inputPipeFd);
+
+    asprintfN(&arg0, "gs");
+    asprintfN(&deviceopt, "-sDEVICE=%s", ghostscript_device);
+    asprintfN(&outfileopt, "-sOutputFile=%s", outfile_arg);
+    asprintfN(&gopt, "-g%dx%d", xsize, ysize);
+    asprintfN(&ropt, "-r%dx%d", xres, yres);
+
+    /* -dSAFER causes Postscript to disable %pipe and file operations,
+       which are almost certainly not needed here.  This prevents our
+       Postscript program from doing crazy unexpected things, possibly
+       as a result of a malicious booby trapping of our Postscript file.
+    */
+
+    if (verbose) {
+        pm_message("execing '%s' with args '%s' (arg 0), "
+                   "'%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s'",
+                   ghostscriptProg, arg0,
+                   deviceopt, outfileopt, gopt, ropt, "-q", "-dNOPAUSE", 
+                   "-dSAFER", "-");
+    }
+
+    execl(ghostscriptProg, arg0, deviceopt, outfileopt, gopt, ropt, "-q",
+          "-dNOPAUSE", "-dSAFER", "-", NULL);
+    
+    pm_error("execl() of Ghostscript ('%s') failed, errno=%d (%s)",
+             ghostscriptProg, errno, strerror(errno));
+}
+
+
+
+
+static void
+execute_ghostscript(const char pstrans[], const char ghostscript_device[],
+                    const char outfile_arg[], 
+                    const int xsize, const int ysize, 
+                    const int xres, const int yres,
+                    const char input_filespec[], 
+                    const enum postscript_language language,
+                    const int verbose) {
+
+    int gs_exit;  /* wait4 exit code from Ghostscript */
+    FILE *gs;  /* Pipe to Ghostscript's standard input */
+    FILE *infile;
+    int rc;
+    int eof;  /* End of file on input */
+    int pipefd[2];
+
+    if (strlen(outfile_arg) > 80)
+        pm_error("output file spec too long.");
+    
+    rc = pipe(pipefd);
+    if (rc < 0)
+        pm_error("Unable to create pipe to talk to Ghostscript process.  "
+                 "errno = %d (%s)", errno, strerror(errno));
+    
+    rc = fork();
+    if (rc < 0)
+        pm_error("Unable to fork a Ghostscript process.  errno=%d (%s)",
+                 errno, strerror(errno));
+    else if (rc == 0) {
+        /* Child process */
+        close(pipefd[1]);
+        execGhostscript(pipefd[0], ghostscript_device, outfile_arg,
+                        xsize, ysize, xres, yres, input_filespec, verbose);
+    } else {
+        pid_t const ghostscriptPid = rc;
+        int const pipeToGhostscriptFd = pipefd[1];
+        /* parent process */
+        close(pipefd[0]);
+
+        gs = fdopen(pipeToGhostscriptFd, "w");
+        if (gs == NULL) 
+            pm_error("Unable to open stream on pipe to Ghostscript process.");
+    
+        infile = pm_openr(input_filespec);
+        /*
+          In encapsulated Postscript, we the encapsulator are supposed to
+          handle showing the page (which we do by passing a showpage
+          statement to Ghostscript).  Any showpage statement in the 
+          input must be defined to have no effect.
+          
+          See "Enscapsulated PostScript Format File Specification",
+          v. 3.0, 1 May 1992, in particular Example 2, p. 21.  I found
+          it at 
+          http://partners.adobe.com/asn/developer/pdfs/tn/5002.EPSF_Spec.pdf
+          The example given is a much fancier solution than we need
+          here, I think, so I boiled it down a bit.  JM 
+        */
+        if (language == ENCAPSULATED_POSTSCRIPT)
+            fprintf(gs, "\n/b4_Inc_state save def /showpage { } def\n");
+ 
+        if (verbose) 
+            pm_message("Postscript prefix command: '%s'", pstrans);
+
+        fprintf(gs, "%s\n", pstrans);
+
+        /* If our child dies, it closes the pipe and when we next write to it,
+           we get a SIGPIPE.  We must survive that signal in order to report
+           on the fate of the child.  So we ignore SIGPIPE:
+        */
+        signal(SIGPIPE, SIG_IGN);
+
+        eof = FALSE;
+        while (!eof) {
+            char buffer[4096];
+            int bytes_read;
+            
+            bytes_read = fread(buffer, 1, sizeof(buffer), infile);
+            if (bytes_read == 0) 
+                eof = TRUE;
+            else 
+                fwrite(buffer, 1, bytes_read, gs);
+        }
+        pm_close(infile);
+
+        if (language == ENCAPSULATED_POSTSCRIPT)
+            fprintf(gs, "\nb4_Inc_state restore showpage\n");
+
+        fclose(gs);
+        
+        waitpid(ghostscriptPid, &gs_exit, 0);
+        if (rc < 0)
+            pm_error("Wait for Ghostscript process to terminated failed.  "
+                     "errno = %d (%s)", errno, strerror(errno));
+
+        if (gs_exit != 0) {
+            if (WIFEXITED(gs_exit))
+                pm_error("Ghostscript failed.  Exit code=%d\n", 
+                         WEXITSTATUS(gs_exit));
+            else if (WIFSIGNALED(gs_exit))
+                pm_error("Ghostscript process died due to a signal %d.",
+                         WTERMSIG(gs_exit));
+            else 
+                pm_error("Ghostscript process died with exit code %d", 
+                         gs_exit);
+        }
+    }
+}
+
+
+
+int
+main(int argc, char **argv) {
+
+    struct cmdlineInfo cmdline;
+    char *input_filespec;  /* malloc'ed */
+        /* The file specification of our Postscript input file */
+    unsigned int xres, yres;    /* Resolution in pixels per inch */
+    unsigned int xsize, ysize;  /* output image size in pixels */
+    struct box extract_box;
+        /* coordinates of the box within the input we are to extract; i.e.
+           that will become the output. 
+           */
+    struct box bordered_box;
+        /* Same as above, but expanded to include borders */
+
+    enum postscript_language language;
+    enum orientation orientation;
+    const char *ghostscript_device;
+    const char *outfile_arg;
+    const char *pstrans;
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    add_ps_to_filespec(cmdline.input_filespec, &input_filespec,
+                       cmdline.verbose);
+
+    extract_box = compute_box_to_extract(cmdline.extract_box, input_filespec, 
+                                         cmdline.verbose);
+
+    language = language_declaration(input_filespec, cmdline.verbose);
+    
+    orientation = compute_orientation(cmdline, extract_box);
+
+    bordered_box = add_borders(extract_box, cmdline.xborder, cmdline.yborder,
+                               cmdline.verbose);
+
+    compute_size_res(cmdline, orientation, bordered_box, 
+                     &xsize, &ysize, &xres, &yres);
+    
+    pstrans = compute_pstrans(bordered_box, orientation,
+                              xsize, ysize, xres, yres);
+
+    outfile_arg = compute_outfile_arg(cmdline);
+
+    ghostscript_device = 
+        compute_gs_device(cmdline.format_type, cmdline.forceplain);
+    
+    pm_message("Writing %s file", ghostscript_device);
+    
+    execute_ghostscript(pstrans, ghostscript_device, outfile_arg, 
+                        xsize, ysize, xres, yres, input_filespec,
+                        language, cmdline.verbose);
+
+    strfree(ghostscript_device);
+    strfree(outfile_arg);
+    strfree(pstrans);
+    
+    return 0;
+}
+
+
diff --git a/converter/other/pstopnm.csh b/converter/other/pstopnm.csh
new file mode 100755
index 00000000..adde3e6f
--- /dev/null
+++ b/converter/other/pstopnm.csh
@@ -0,0 +1,301 @@
+#!/bin/csh -f
+#
+#	Uses ghostscript to translate an Encapsulated PostScript file to
+#	Portable Anymap format file(s).
+#	pstopnm will create as many files as the number of pages in 
+#	the Postscript document.  The name of the files will be 
+#	psfile001.ppm, psfile002.ppm, etc.
+#	The ouput files will contain the area inside the BoundingBox.
+#	If BoundingBox parameters are not found in the PostScript
+#	document, default values are used.
+#
+#
+#       Usage: pstopnm [-forceplain] [-help] [-llx s] [-lly s] 
+#		       [-urx s] [-ury s] [-nocrop] [-pbm|-pgm|-ppm] 
+#		       [-verbose] [-xborder n] [-xmax n] [-xsize n] 
+#		       [-yborder n] [-ymax n] [-ysize n] 
+#		       [-portrait] [-landscape] psfile[.ps]
+# 
+# 	Copyright (C) 1992 by Alberto Accomazzi, Smithsonian Astrophysical
+#	Observatory (alberto@cfa.harvard.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.
+#
+set noglob
+
+set progname = $0
+set progname = $progname:t
+set filtertail = "raw"
+set filterhead = "ppm"
+set xsize = 0
+set ysize = 0
+set xres = ""
+set yres = ""
+
+# default values: max image x and y sizes
+set xmax = 612
+set ymax = 792
+# default values: image area fits in a 8.5x11 sheet with 1 inch border
+set llx = 72
+set lly = 72
+set urx = 540
+set ury = 720
+# default values: x and y borders are 10% of x and y size
+set xborder = "0.1"
+set yborder = "0.1"
+# default values: orientation is unknown
+set orient = 0
+
+set psfile = ""
+set USAGE = "Usage: $progname [-forceplain] [-help] [-llx s] [-lly s]\
+[-urx s] [-ury s] [-landscape] [-portrait]\
+[-nocrop] [-pbm|-pgm|-ppm] [-verbose] [-xborder s] [-xmax s]\
+[-xsize s] [-yborder s] [-ymax s] [-ysize s] psfile[.ps]"
+alias usage 'echo $USAGE; exit 1'
+
+while ($#argv > 0)
+    switch ($argv[1])
+    case -h*:   # -help
+        usage
+        breaksw
+    case -pbm:
+    case -pgm:
+    case -ppm:
+    	set filterhead = `echo "$argv[1]" | sed "s/-//1"`
+	breaksw
+    case -llx:
+        shift argv
+        if ($#argv == 0) eval usage
+    	set llx = `(echo "scale=4";echo "$argv[1] * 72")|bc -l`
+	set nobb
+	breaksw
+    case -lly:
+        shift argv
+        if ($#argv == 0) eval usage
+	set lly = `(echo "scale=4";echo "$argv[1] * 72")|bc -l`
+	set nobb
+	breaksw
+    case -urx:
+        shift argv
+        if ($#argv == 0) eval usage
+	set urx = `(echo "scale=4";echo "$argv[1] * 72")|bc -l`
+	set nobb
+	breaksw
+    case -ury:
+        shift argv
+        if ($#argv == 0) eval usage
+	set ury = `(echo "scale=4";echo "$argv[1] * 72")|bc -l`
+	set nobb
+	breaksw
+    case -no*:	# -nocrop
+	set nocrop
+	breaksw
+    case -xs*:	# -xsize
+        shift argv
+        if ($#argv == 0) eval usage
+	@ xsize = $argv[1]
+	breaksw
+    case -ys*:	# -ysize
+        shift argv
+        if ($#argv == 0) eval usage
+	@ ysize = $argv[1]
+	breaksw
+    case -xm*:	# -xmax
+        shift argv
+        if ($#argv == 0) eval usage
+	@ xmax = $argv[1]
+	breaksw
+    case -ym*:	# -ymax
+        shift argv
+        if ($#argv == 0) eval usage
+	@ ymax = $argv[1]
+	breaksw
+    case -xb*:	# -xborder
+        shift argv
+        if ($#argv == 0) eval usage
+	set xborder = $argv[1]
+	breaksw
+    case -yb*:	# -yborder
+        shift argv
+        if ($#argv == 0) eval usage
+	set yborder = $argv[1]
+	breaksw
+    case -f*:	# -forceplain
+	set filtertail = ""
+	breaksw
+    case -s*:	# -stdout
+	set goto_stdout
+	breaksw
+    case -v*:	# -verbose
+	set verb
+	breaksw
+    case -po*:	# -portrait
+	set orient = 1
+	breaksw
+    case -la*:	# -landscape
+	set orient = 2
+	breaksw
+    case -*:
+        echo "${progname}: Unknown option $argv[1]"
+        usage
+        breaksw
+    default:	# input file
+	set psfile = $argv[1]
+	set ppmfile = `basename $argv[1] .ps`
+        breaksw
+    endsw
+    shift argv
+end
+
+if ($psfile =~ "") eval usage
+if (! -f $psfile) then
+    echo "${progname}: file $psfile not found"
+    usage
+endif
+
+set bb = `grep "%%BoundingBox" $psfile`
+if ($?nobb == 0 && $#bb == 5) then
+    set llx = $bb[2]
+    set lly = $bb[3]
+    set urx = $bb[4]
+    set ury = $bb[5]
+else
+    if ($?nobb == 0) \
+    	echo "${progname}: warning: BoundingBox not found in input file"
+endif
+
+set tmpsx = `(echo "scale=4";echo "$urx - $llx")|bc -l`
+set tmpsy = `(echo "scale=4";echo "$ury - $lly")|bc -l`
+
+# see if orientation was specified 
+if ($orient == 0) then
+    # no orientation was specified; compute default orientation
+    set tmpx = 0
+    set tmpy = 0
+    set tmpsx1 = $tmpsx:r
+    set tmpsy1 = $tmpsy:r
+    # default is landscape mode
+    set orient = 2
+    if ($xsize == 0 && $ysize == 0) then
+	set tmpx = $xmax
+	set tmpy = $ymax
+    else
+	if ($xsize != 0) set tmpx = $xsize
+	if ($ysize != 0) set tmpy = $ysize
+    endif
+    if ($tmpx == 0 || $tmpy == 0) then
+	# only one size was specified
+	if ($tmpsy1 > $tmpsx1) set orient = 1
+    else
+	# no size or both sizes were specified
+	if ($tmpsy1 > $tmpsx1 && $tmpy > $tmpx) set orient = 1
+	if ($tmpsx1 > $tmpsy1 && $tmpx > $tmpy) set orient = 1
+    endif
+endif
+
+# now reset BoundingBox llc and total size to take into account margin
+set llx = `(echo "scale=4";echo "$llx - $tmpsx * $xborder")|bc -l`
+set lly = `(echo "scale=4";echo "$lly - $tmpsy * $yborder")|bc -l`
+set urx = `(echo "scale=4";echo "$urx + $tmpsx * $xborder")|bc -l`
+set ury = `(echo "scale=4";echo "$ury + $tmpsy * $yborder")|bc -l`
+# compute image area size 
+set sx = `(echo "scale=4";echo "$tmpsx + 2 * $xborder * $tmpsx")|bc -l`
+set sy = `(echo "scale=4";echo "$tmpsy + 2 * $yborder * $tmpsy")|bc -l`
+    
+if ($orient != 1) then
+    # render image in landscape mode
+    set tmpsx = $sx
+    set sx = $sy
+    set sy = $tmpsx
+endif
+
+# if xsize or ysize was specified, compute resolution from them
+if ($xsize != 0) set xres = `(echo "scale=4";echo "$xsize *72 / $sx")|bc -l`
+if ($ysize != 0) set yres = `(echo "scale=4";echo "$ysize *72 / $sy")|bc -l`
+
+if ($xres =~ "" && $yres !~ "") then
+    # ysize was specified, xsize was not; compute xsize based on ysize
+    set xres = $yres 
+    set xsize = `(echo "scale=4";echo "$sx * $xres /72 + 0.5")|bc -l`
+    set xsize = $xsize:r
+else 
+    if ($yres =~ "" && $xres !~ "") then
+	# xsize was specified, ysize was not; compute ysize based on xsize
+        set yres = $xres
+    	set ysize = `(echo "scale=4";echo "$sy * $yres /72 + 0.5")|bc -l`
+    	set ysize = $ysize:r
+    else
+	if ($xres =~ "" && $yres =~ "") then
+    	    # neither xsize nor ysize was specified; compute them from
+	    # xmax and ymax
+	    set xres = `(echo "scale=4";echo "$xmax *72/$sx")|bc -l`
+	    set yres = `(echo "scale=4";echo "$ymax *72/$sy")|bc -l`
+	    set xres = `(echo "scale=4";echo "if($xres>$yres)$yres";echo "if($yres>$xres)$xres";echo "if($xres==$yres)$xres")|bc -l`
+	    set yres = $xres
+	    if ($?nocrop) then
+		# keep output file dimensions equal to xmax and ymax
+		set xsize = $xmax
+		set ysize = $ymax
+	    else
+    	    	set xsize = `(echo "scale=4";echo "$sx * $xres /72+0.5")|bc -l`
+    	    	set ysize = `(echo "scale=4";echo "$sy * $yres /72+0.5")|bc -l`
+	    endif
+    	    set xsize = $xsize:r
+    	    set ysize = $ysize:r
+	endif
+    endif
+endif
+
+# translate + rotate image, if necessary
+if ($orient == 1) then
+    # portrait mode
+    # adjust offsets
+    set llx = `(echo "scale=4";echo "$llx - ($xsize *72/$xres - $sx)/2")|bc -l`
+    set lly = `(echo "scale=4";echo "$lly - ($ysize *72/$yres - $sy)/2")|bc -l`
+    set pstrans = "$llx neg $lly neg translate"
+else
+    # landscape mode
+    # adjust offsets
+    set llx = `(echo "scale=4";echo "$llx - ($ysize *72/$yres - $sy)/2")|bc -l`
+    set ury = `(echo "scale=4";echo "$ury + ($xsize *72/$xres - $sx)/2")|bc -l`
+    set pstrans = "90 rotate $llx neg $ury neg translate"
+endif
+   
+if ($?goto_stdout) then
+    set outfile = "-"
+else
+    set outfile = $ppmfile%03d.$filterhead 
+endif
+
+if ($?verb) then
+    echo "sx = $sx" 
+    echo "sy = $sy"
+    echo "xres  = $xres"
+    echo "yres  = $yres"
+    echo "xsize = $xsize"
+    echo "ysize = $ysize"
+    echo -n "orientation "
+    if ($orient == 1) then 
+	echo "portrait"
+    else
+	echo "landscape"
+    endif
+    echo "PS header: $pstrans"
+endif
+
+echo "${progname}: writing $filterhead file(s)" 1>&2
+
+echo $pstrans | \
+	gs -sDEVICE=${filterhead}${filtertail} \
+	   -sOutputFile=$outfile \
+	   -g${xsize}x${ysize} \
+	   -r${xres}x${yres} \
+	   -q - $psfile 
+
+
+
diff --git a/converter/other/rast.c b/converter/other/rast.c
new file mode 100644
index 00000000..91c50ccd
--- /dev/null
+++ b/converter/other/rast.c
@@ -0,0 +1,465 @@
+/* libpnm4.c - pnm utility library part 4
+**
+** Copyright (C) 1988 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 "pnm.h"
+#include "rast.h"
+#include "mallocvar.h"
+
+/*
+** Semi-work-alike versions of some Sun pixrect routines.  Just enough
+** for rasterfile reading and writing to work.
+*/
+
+struct pixrect*
+mem_create( w, h, depth )
+    int w, h, depth;
+{
+    struct pixrect* p;
+    struct mpr_data* m;
+
+    MALLOCVAR(p);
+    if ( p == NULL )
+        return NULL;
+    p->pr_ops = NULL;
+    p->pr_size.x = w;
+    p->pr_size.y = h;
+    p->pr_depth = depth;
+    MALLOCVAR(m);
+    if ( m == NULL )
+    {
+        free( p );
+        return NULL;
+    }
+    p->pr_data = m;
+
+    /* According to the documentation, linebytes is supposed to be rounded
+    ** up to a longword (except on 386 boxes).  However, this turns out
+    ** not to be the case.  In reality, all of Sun's code rounds up to
+    ** a short, not a long.
+    */
+    m->md_linebytes = ( w * depth + 15 ) / 16 * 2;
+    m->md_offset.x = 0;
+    m->md_offset.y = 0;
+    m->md_flags = 0;
+    MALLOCARRAY(m->md_image, m->md_linebytes * h );
+    if ( m->md_image == NULL )
+    {
+        free( m );
+        free( p );
+        return NULL;
+    }
+
+    return p;
+}
+
+void
+mem_free( p )
+    struct pixrect* p;
+{
+    free( p->pr_data->md_image );
+    free( p->pr_data );
+    free( p );
+}
+
+int
+pr_dump( p, out, colormap, type, copy_flag )
+    struct pixrect* p;
+    FILE* out;
+    colormap_t* colormap;
+    int type, copy_flag;
+{
+    struct rasterfile h;
+    int size, besize, count;
+    unsigned char* beimage;
+    unsigned char* bp;
+    unsigned char c, pc;
+    int i, j;
+
+    h.ras_magic = RAS_MAGIC;
+    h.ras_width = p->pr_size.x;
+    h.ras_height = p->pr_size.y;
+    h.ras_depth = p->pr_depth;
+
+    h.ras_type = type;
+    switch ( type )
+    {
+    case RT_OLD:
+        pm_error( "This program does not know the Old rasterfile type" );
+
+    case RT_FORMAT_TIFF:
+        pm_error( "This program does not know the TIFF rasterfile type" );
+
+    case RT_FORMAT_IFF:
+        pm_error( "This program does not know the IFF rasterfile type" );
+
+    case RT_EXPERIMENTAL:
+        pm_error( "This program does not know the Experimental "
+                  "rasterfile type" );
+
+    case RT_STANDARD:
+    case RT_FORMAT_RGB:
+        /* Ignore hP->ras_length. */
+        h.ras_length = p->pr_size.y * p->pr_data->md_linebytes;
+        break;
+
+    case RT_BYTE_ENCODED:
+        size = p->pr_size.y * p->pr_data->md_linebytes;
+        bp = p->pr_data->md_image;
+        MALLOCARRAY(beimage, size * 3 / 2);  /* worst case */
+        if ( beimage == NULL )
+            return PIX_ERR;
+        besize = 0;
+        count = 0;
+        for ( i = 0; i < size; ++i )
+        {
+            c = *bp++;
+            if ( count > 0 )
+            {
+                if ( pc != c )
+                {
+                    if ( count == 1 && pc == 128 )
+                    {
+                        beimage[besize++] = 128;
+                        beimage[besize++] = 0;
+                        count = 0;
+                    }
+                    else if ( count > 2 || pc == 128 )
+                    {
+                        beimage[besize++] = 128;
+                        beimage[besize++] = count - 1;
+                        beimage[besize++] = pc;
+                        count = 0;
+                    }
+                    else
+                    {
+                        for ( j = 0; j < count; ++j )
+                            beimage[besize++] = pc;
+                        count = 0;
+                    }
+                }
+            }
+            pc = c;
+            ++count;
+            if ( count == 256 )
+            {
+                beimage[besize++] = 128;
+                beimage[besize++] = count - 1;
+                beimage[besize++] = c;
+                count = 0;
+            }
+        }
+        if ( count > 0 )
+        {
+            if ( count == 1 && c == 128 )
+            {
+                beimage[besize++] = 128;
+                beimage[besize++] = 0;
+            }
+            if ( count > 2 || c == 128 )
+            {
+                beimage[besize++] = 128;
+                beimage[besize++] = count - 1;
+                beimage[besize++] = c;
+            }
+            else
+            {
+                for ( j = 0; j < count; ++j )
+                    beimage[besize++] = c;
+            }
+        }
+        h.ras_length = besize;
+        break;
+
+    default:
+        pm_error( "unknown rasterfile type" );
+    }
+
+    if ( colormap == NULL )
+    {
+        h.ras_maptype = RMT_NONE;
+        h.ras_maplength = 0;
+    }
+    else
+    {
+        h.ras_maptype = colormap->type;
+        switch ( colormap->type )
+        {
+        case RMT_EQUAL_RGB:
+            h.ras_maplength = colormap->length * 3;
+            break;
+
+        case RMT_RAW:
+            h.ras_maplength = colormap->length;
+            break;
+
+        default:
+            pm_error( "unknown colormap type" );
+        }
+    }
+
+    if ( pm_writebiglong( out, h.ras_magic ) == -1 )
+        return PIX_ERR;
+    if ( pm_writebiglong( out, h.ras_width ) == -1 )
+        return PIX_ERR;
+    if ( pm_writebiglong( out, h.ras_height ) == -1 )
+        return PIX_ERR;
+    if ( pm_writebiglong( out, h.ras_depth ) == -1 )
+        return PIX_ERR;
+    if ( pm_writebiglong( out, h.ras_length ) == -1 )
+        return PIX_ERR;
+    if ( pm_writebiglong( out, h.ras_type ) == -1 )
+        return PIX_ERR;
+    if ( pm_writebiglong( out, h.ras_maptype ) == -1 )
+        return PIX_ERR;
+    if ( pm_writebiglong( out, h.ras_maplength ) == -1 )
+        return PIX_ERR;
+
+    if ( colormap != NULL )
+    {
+        switch ( colormap->type )
+        {
+        case RMT_EQUAL_RGB:
+            if ( fwrite( colormap->map[0], 1, colormap->length, out ) !=
+                 colormap->length )
+                return PIX_ERR;
+            if ( fwrite( colormap->map[1], 1, colormap->length, out ) !=
+                 colormap->length )
+                return PIX_ERR;
+            if ( fwrite( colormap->map[2], 1, colormap->length, out ) !=
+                 colormap->length )
+                return PIX_ERR;
+            break;
+
+        case RMT_RAW:
+            if ( fwrite( colormap->map[0], 1, colormap->length, out ) !=
+                 colormap->length )
+                return PIX_ERR;
+            break;
+        }
+    }
+
+    switch ( type )
+    {
+    case RT_STANDARD:
+    case RT_FORMAT_RGB:
+        if ( fwrite( p->pr_data->md_image, 1, h.ras_length, out ) !=
+             h.ras_length )
+            return PIX_ERR;
+        break;
+
+    case RT_BYTE_ENCODED:
+        if ( fwrite( beimage, 1, besize, out ) != besize )
+        {
+            free( beimage );
+            return PIX_ERR;
+        }
+        free( beimage );
+        break;
+    }
+
+    return 0;
+}
+
+int
+pr_load_header( in, hP )
+    FILE* in;
+    struct rasterfile* hP;
+{
+    if ( pm_readbiglong( in, &(hP->ras_magic) ) == -1 )
+        return PIX_ERR;
+    if ( hP->ras_magic != RAS_MAGIC )
+        return PIX_ERR;
+    if ( pm_readbiglong( in, &(hP->ras_width) ) == -1 )
+        return PIX_ERR;
+    if ( pm_readbiglong( in, &(hP->ras_height) ) == -1 )
+        return PIX_ERR;
+    if ( pm_readbiglong( in, &(hP->ras_depth) ) == -1 )
+        return PIX_ERR;
+    if ( pm_readbiglong( in, &(hP->ras_length) ) == -1 )
+        return PIX_ERR;
+    if ( pm_readbiglong( in, &(hP->ras_type) ) == -1 )
+        return PIX_ERR;
+    if ( pm_readbiglong( in, &(hP->ras_maptype) ) == -1 )
+        return PIX_ERR;
+    if ( pm_readbiglong( in, &(hP->ras_maplength) ) == -1 )
+        return PIX_ERR;
+    return 0;
+}
+
+int
+pr_load_colormap( in, hP, colormap )
+    FILE* in;
+    struct rasterfile* hP;
+    colormap_t* colormap;
+{
+    if ( colormap == NULL || hP->ras_maptype == RMT_NONE )
+    {
+        int i;
+
+        for ( i = 0; i < hP->ras_maplength; ++i )
+            if ( getc( in ) == EOF )
+                return PIX_ERR;
+    }
+    else
+    {
+        colormap->type = hP->ras_maptype;
+        switch ( hP->ras_maptype )
+        {
+        case RMT_EQUAL_RGB:
+            colormap->length = hP->ras_maplength / 3;
+            MALLOCARRAY( colormap->map[0], colormap->length );
+            if ( colormap->map[0] == NULL )
+                return PIX_ERR;
+            MALLOCARRAY( colormap->map[1], colormap->length );
+            if ( colormap->map[1] == NULL )
+            {
+                free( colormap->map[0] );
+                return PIX_ERR;
+            }
+            MALLOCARRAY( colormap->map[2], colormap->length );
+            if ( colormap->map[2] == NULL )
+            {
+                free( colormap->map[0] );
+                free( colormap->map[1] );
+                return PIX_ERR;
+            }
+            if ( fread( colormap->map[0], 1, colormap->length, in ) != 
+                 colormap->length ||
+                 fread( colormap->map[1], 1, colormap->length, in ) != 
+                 colormap->length ||
+                 fread( colormap->map[2], 1, colormap->length, in ) != 
+                 colormap->length )
+            {
+                free( colormap->map[0] );
+                free( colormap->map[1] );
+                free( colormap->map[2] );
+                return PIX_ERR;
+            }
+            break;
+
+        case RMT_RAW: {
+            size_t bytesRead;
+
+            colormap->length = hP->ras_maplength;
+            MALLOCARRAY( colormap->map[0], colormap->length );
+            if ( colormap->map[0] == NULL )
+                return PIX_ERR;
+            colormap->map[2] = colormap->map[1] = colormap->map[0];
+            bytesRead = fread( colormap->map[0], 1, hP->ras_maplength, in );
+            if ( bytesRead != hP->ras_maplength )
+            {
+                free( colormap->map[0] );
+                return PIX_ERR;
+            }
+        }
+            break;
+
+        default:
+            pm_error( "unknown colormap type" );
+        }
+    }
+    return 0;
+}
+
+struct pixrect*
+pr_load_image( in, hP, colormap )
+    FILE* in;
+    struct rasterfile* hP;
+    colormap_t* colormap;
+{
+    struct pixrect* p;
+    unsigned char* beimage;
+    register unsigned char* bep;
+    register unsigned char* bp;
+    register unsigned char c;
+    int i;
+    register int j, count;
+
+    p = mem_create( hP->ras_width, hP->ras_height, hP->ras_depth );
+    if ( p == NULL )
+        return NULL;
+
+    switch ( hP->ras_type )
+    {
+    case RT_OLD:
+        pm_error( "This program does not know the Old rasterfile type" );
+
+    case RT_FORMAT_TIFF:
+        pm_error( "This program does not know the TIFF rasterfile type" );
+
+    case RT_FORMAT_IFF:
+        pm_error( "This program does not know the IFF rasterfile type" );
+
+    case RT_EXPERIMENTAL:
+        pm_error( "This program does not know the Experimental "
+                  "rasterfile type" );
+
+    case RT_STANDARD:
+    case RT_FORMAT_RGB:
+        /* Ignore hP->ras_length. */
+        i = p->pr_size.y * p->pr_data->md_linebytes;
+        if ( fread( p->pr_data->md_image, 1, i, in ) != i )
+        {
+            mem_free( p );
+            return NULL;
+        }
+        break;
+
+    case RT_BYTE_ENCODED:
+        MALLOCARRAY( beimage, hP->ras_length );
+        if ( beimage == NULL )
+        {
+            mem_free( p );
+            return NULL;
+        }
+        if ( fread( beimage, 1, hP->ras_length, in ) != hP->ras_length )
+        {
+            mem_free( p );
+            free( beimage );
+            return NULL;
+        }
+        bep = beimage;
+        bp = p->pr_data->md_image;
+        for ( i = 0; i < hP->ras_length; )
+        {
+            c = *bep++;
+            if ( c == 128 )
+            {
+                count = ( *bep++ ) + 1;
+                if ( count == 1 )
+                {
+                    *bp++ = 128;
+                    i += 2;
+                }
+                else
+                {
+                    c = *bep++;
+                    for ( j = 0; j < count; ++j )
+                        *bp++ = c;
+                    i += 3;
+                }
+            }
+            else
+            {
+                *bp++ = c;
+                ++i;
+            }
+        }
+        free( beimage );
+        break;
+
+    default:
+        pm_error( "unknown rasterfile type" );
+    }
+
+    return p;
+}
diff --git a/converter/other/rast.h b/converter/other/rast.h
new file mode 100644
index 00000000..1854e495
--- /dev/null
+++ b/converter/other/rast.h
@@ -0,0 +1,111 @@
+/* rast.h - header file for Sun raster files
+**
+** The format of a Sun raster file is as follows.  First, a struct
+** rasterfile.  Note the 32-bit magic number at the beginning; this
+** identifies the file type and lets you figure out whether you need
+** to do little-endian / big-endian byte-swapping or not.  (The PBMPLUS
+** implementation does not do byte-swapping; instead, it reads all
+** multi-byte values a byte at a time.)
+**
+** After the struct is an optional colormap.  If ras_maptype is RMT_NONE,
+** no map is present; if it's RMT_EQUAL_RGB then the map consists of
+** three unsigned-char arrays ras_maplength long, one each for r g and b.
+** I don't know what RMT_RAW means.  Black and white bitmaps are stored
+** as ras_maptype == RMT_NONE and ras_depth == 1, with the bits stored
+** eight to a byte MSB first.
+**
+** Finally comes the image data.  If ras_type is RT_OLD or RT_STANDARD,
+** the data is just plain old uncompressed bytes, padded out to a multiple
+** of 16 bits in each row.  If ras_type is RT_BYTE_ENCODED, a run-length
+** compression scheme is used: an escape-byte of 128 indicates a run;
+** the next byte is a count, and the one after that is the byte to be
+** replicated.  The one exception to this is if the count is 1; then
+** there is no third byte in the packet, it means to put a single 128
+** in the data stream.
+*/
+
+#ifndef RAST_H_INCLUDED
+#define RAST_H_INCLUDED
+
+#define PIX_ERR		-1
+
+struct rasterfile {
+    long ras_magic;
+#define	RAS_MAGIC	0x59a66a95
+    long ras_width;
+    long ras_height;
+    long ras_depth;
+    long ras_length;
+    long ras_type;
+#define RT_OLD		0	/* Raw pixrect image in 68000 byte order */
+#define RT_STANDARD	1	/* Raw pixrect image in 68000 byte order */
+#define RT_BYTE_ENCODED	2	/* Run-length compression of bytes */
+#define RT_FORMAT_RGB	3	/* XRGB or RGB instead of XBGR or BGR */
+#define RT_FORMAT_TIFF	4	/* tiff <-> standard rasterfile */
+#define RT_FORMAT_IFF	5	/* iff (TAAC format) <-> standard rasterfile */
+#define RT_EXPERIMENTAL 0xffff	/* Reserved for testing */
+    long ras_maptype;
+#define RMT_NONE	0
+#define RMT_EQUAL_RGB	1
+#define RMT_RAW		2
+    long ras_maplength;
+    };
+
+struct pixrectops {
+    int	(*pro_rop)();
+    int	(*pro_stencil)();
+    int	(*pro_batchrop)();
+    int	(*pro_nop)();
+    int	(*pro_destroy)();
+    int	(*pro_get)();
+    int	(*pro_put)();
+    int	(*pro_vector)();
+    struct pixrect* (*pro_region)();
+    int	(*pro_putcolormap)();
+    int	(*pro_getcolormap)();
+    int	(*pro_putattributes)();
+    int	(*pro_getattributes)();
+    };
+
+struct pr_size {
+    int x, y;
+    };
+struct pr_pos {
+    int x, y;
+    };
+
+struct pixrect {
+    struct pixrectops* pr_ops;
+    struct pr_size pr_size;
+    int pr_depth;
+    struct mpr_data* pr_data;	/* work-alike only handles memory pixrects */
+    };
+
+struct mpr_data {
+    int md_linebytes;
+    unsigned char* md_image;	/* note, byte not short -- avoid pr_flip() */
+    struct pr_pos md_offset;
+    short md_primary;
+    short md_flags;
+    };
+
+typedef struct {
+    int type;
+    int length;
+    unsigned char* map[3];
+    } colormap_t;
+
+/* And the routine definitions. */
+
+struct pixrect* mem_create ARGS(( int w, int h, int depth ));
+void mem_free ARGS(( struct pixrect* p ));
+
+int pr_dump ARGS(( struct pixrect* p, FILE* out, colormap_t* colormap, int type, int copy_flag ));
+
+int pr_load_header ARGS(( FILE* in, struct rasterfile* hP ));
+
+int pr_load_colormap ARGS(( FILE* in, struct rasterfile* hP, colormap_t* colormap ));
+
+struct pixrect* pr_load_image ARGS(( FILE* in, struct rasterfile* hP, colormap_t* colormap ));
+
+#endif
diff --git a/converter/other/rasttopnm.c b/converter/other/rasttopnm.c
new file mode 100644
index 00000000..aa55850b
--- /dev/null
+++ b/converter/other/rasttopnm.c
@@ -0,0 +1,263 @@
+/* rasttopnm.c - read a Sun rasterfile and produce a portable anymap
+**
+** 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.
+*/
+
+#include "pnm.h"
+#include "rast.h"
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    struct rasterfile header;
+    colormap_t pr_colormap;
+    int grayscale;
+    struct pixrect* pr;
+    xel* xelrow;
+    register xel* xP;
+    int argn, rows, cols, format, depth, i, row, mask;
+    register int col;
+    xelval maxval;
+    xel zero, one;
+    int linesize;
+    unsigned char* data;
+    unsigned char* byteP;
+
+    pnm_init( &argc, argv );
+
+    argn = 1;
+
+    if ( argn != argc )
+	{
+	ifp = pm_openr( argv[argn] );
+	++argn;
+	}
+    else
+	ifp = stdin;
+
+    if ( argn != argc )
+	pm_usage( "[rastfile]" );
+
+    /* Read in the rasterfile.  First the header. */
+    if ( pr_load_header( ifp, &header ) != 0 )
+	pm_error( "unable to read in rasterfile header" );
+
+    cols = header.ras_width;
+    rows = header.ras_height;
+    depth = header.ras_depth;
+
+    if ( cols <= 0 )
+	pm_error( "invalid cols: %d", cols );
+    if ( rows <= 0 )
+	pm_error( "invalid rows: %d", rows );
+
+    /* If there is a color map, read it. */
+    grayscale = 1;
+    if ( header.ras_maplength != 0 )
+	{
+	if ( pr_load_colormap( ifp, &header, &pr_colormap ) != 0 )
+	    pm_error( "unable to skip colormap data" );
+	for ( i = 0; i < header.ras_maplength / 3; ++i )
+	    if ( pr_colormap.map[0][i] != pr_colormap.map[1][i] ||
+		 pr_colormap.map[1][i] != pr_colormap.map[2][i] )
+		{
+		grayscale = 0;
+		break;
+		}
+	}
+
+    /* Check the depth and color map. */
+    switch ( depth )
+	{
+	case 1:
+	if ( header.ras_maptype == RMT_NONE && header.ras_maplength == 0 )
+	    {
+	    maxval = 1;
+	    format = PBM_TYPE;
+	    PNM_ASSIGN1( zero, maxval );
+	    PNM_ASSIGN1( one, 0 );
+	    }
+	else if ( header.ras_maptype == RMT_EQUAL_RGB &&
+		  header.ras_maplength == 6 )
+	    {
+	    if ( grayscale )
+		{
+		maxval = 255;
+		format = PGM_TYPE;
+		PNM_ASSIGN1( zero, pr_colormap.map[0][0] );
+		PNM_ASSIGN1( one, pr_colormap.map[0][1] );
+		}
+	    else
+		{
+		maxval = 255;
+		format = PPM_TYPE;
+		PPM_ASSIGN(
+		    zero, pr_colormap.map[0][0], pr_colormap.map[1][0],
+		    pr_colormap.map[2][0] );
+		PPM_ASSIGN(
+		    one, pr_colormap.map[0][1], pr_colormap.map[1][1],
+		    pr_colormap.map[2][1] );
+		}
+	    }
+	else
+	    pm_error(
+            "this depth-1 rasterfile has a non-standard colormap - "
+            "type %ld length %ld",
+            header.ras_maptype, header.ras_maplength );
+	break;
+
+	case 8:
+	if ( grayscale )
+	    {
+	    maxval = 255;
+	    format = PGM_TYPE;
+	    }
+	else if ( header.ras_maptype == RMT_EQUAL_RGB )
+	    {
+	    maxval = 255;
+	    format = PPM_TYPE;
+	    }
+	else
+	    pm_error(
+            "this depth-8 rasterfile has a non-standard colormap - "
+            "type %ld length %ld",
+            header.ras_maptype, header.ras_maplength );
+	break;
+
+	case 24:
+	case 32:
+	if ( header.ras_maptype == RMT_NONE && header.ras_maplength == 0 )
+	    ;
+	else if ( header.ras_maptype == RMT_RAW || header.ras_maplength == 768 )
+	    ;
+	else
+	    pm_error(
+            "this depth-%d rasterfile has a non-standard colormap - "
+            "type %ld length %ld",
+            depth, header.ras_maptype, header.ras_maplength );
+	maxval = 255;
+	format = PPM_TYPE;
+	break;
+
+	default:
+	pm_error(
+	    "invalid depth: %d.  Can only handle depth 1, 8, 24, or 32.",
+	    depth );
+	}
+
+    /* Now load the data.  The pixrect returned is a memory pixrect. */
+    if ( ( pr = pr_load_image( ifp, &header, NULL ) ) == NULL )
+	pm_error(
+	    "unable to read in the image from the rasterfile" );
+
+    linesize = ( (struct mpr_data*) pr->pr_data )->md_linebytes;
+    data = ( (struct mpr_data*) pr->pr_data )->md_image;
+
+    pm_close( ifp );
+
+    /* Now write out the anymap. */
+    pnm_writepnminit( stdout, cols, rows, maxval, format, 0 );
+    xelrow = pnm_allocrow( cols );
+    switch ( PNM_FORMAT_TYPE(format) )
+        {
+        case PBM_TYPE:
+        pm_message( "writing PBM file" );
+        break;
+
+        case PGM_TYPE:
+        pm_message( "writing PGM file" );
+        break;
+
+        case PPM_TYPE:
+        pm_message( "writing PPM file" );
+        break;
+
+        default:
+        pm_error( "shouldn't happen" );
+        }
+
+    for ( row = 0; row < rows; ++row )
+	{
+	byteP = data;
+	switch ( depth )
+	    {
+	    case 1:
+	    mask = 0x80;
+	    for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
+		{
+		if ( mask == 0 )
+		    {
+		    ++byteP;
+		    mask = 0x80;
+		    }
+		*xP = ( *byteP & mask ) ? one : zero;
+		mask = mask >> 1;
+		}
+	    break;
+
+	    case 8:
+	    for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
+		{
+		if ( header.ras_maplength == 0 )
+		    PNM_ASSIGN1( *xP, *byteP );
+		else if ( grayscale )
+		    PNM_ASSIGN1( *xP, pr_colormap.map[0][*byteP] );
+		else
+		    PPM_ASSIGN(
+			*xP, pr_colormap.map[0][*byteP],
+			pr_colormap.map[1][*byteP],
+			pr_colormap.map[2][*byteP] );
+		++byteP;
+		}
+	    break;
+
+	    case 24:
+	    case 32:
+	    for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
+		{
+		register xelval r, g, b;
+
+		if ( depth == 32 )
+		    ++byteP;
+		if ( header.ras_type == RT_FORMAT_RGB )
+		    {
+		    r = *byteP++;
+		    g = *byteP++;
+		    b = *byteP++;
+		    }
+		else
+		    {
+		    b = *byteP++;
+		    g = *byteP++;
+		    r = *byteP++;
+		    }
+		if ( header.ras_maplength == 0 )
+		    PPM_ASSIGN( *xP, r, g, b );
+		else
+		    PPM_ASSIGN(
+			*xP, pr_colormap.map[0][r], pr_colormap.map[1][g],
+			pr_colormap.map[2][b] );
+		}
+	    break;
+
+	    default:
+	    pm_error( "can't happen" );
+	    }
+	data += linesize;
+	pnm_writepnmrow( stdout, xelrow, cols, maxval, format, 0 );
+	}
+
+    pm_close( stdout );
+
+    exit( 0 );
+    }
diff --git a/converter/other/rla.h b/converter/other/rla.h
new file mode 100644
index 00000000..875cbfc6
--- /dev/null
+++ b/converter/other/rla.h
@@ -0,0 +1,45 @@
+typedef struct
+{
+    short left;
+    short right;
+    short bottom;
+    short top;
+} window_s;
+
+typedef struct
+{
+    window_s	window;
+    window_s	active_window;
+    short	    frame;
+    short       storage_type;
+    short       num_chan;
+    short       num_matte;
+    short       num_aux;
+    unsigned short       revision;
+    char        gamma[16];
+    char        red_pri[24];
+    char        green_pri[24];
+    char        blue_pri[24];
+    char        white_pri[24];
+    long        job_num;
+    char        name[128];
+    char        desc[128];
+    char        program[64];
+    char        machine[32];
+    char        user[32];
+    char        date[20];
+    char        aspect[24];
+    char        aspect_ratio[8];
+    char        chan[32];
+    short       field;
+    char        time[12];
+    char        filter[32];
+    short       chan_bits;
+    short       matte_type;
+    short       matte_bits;
+    short       aux_type;
+    short       aux_bits;
+    char        aux[32];
+    char        space[36];
+    long        next;
+} rlahdr;
diff --git a/converter/other/rlatopam.c b/converter/other/rlatopam.c
new file mode 100644
index 00000000..ae1326ca
--- /dev/null
+++ b/converter/other/rlatopam.c
@@ -0,0 +1,433 @@
+/** rlatopam.c - read Alias/Wavefront RLA or RPF file
+ **
+ ** Copyright (C) 2005 Matte World Digital
+ **
+ ** Author: Simon Walton
+ **
+ ** 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 <errno.h>
+
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "pam.h"
+#include "rla.h"
+
+static int * offsets;
+static bool is_float;
+static bool has_matte;
+
+static unsigned int depth;
+static unsigned int width;
+static unsigned int height;
+static unsigned int chanBits;
+static short storageType;
+static struct pam outpam;
+static unsigned int numChan;
+
+
+static void
+parseCommandLine(int           const argc,
+                 char **       const argv,
+                 const char ** const inputFileNameP) {
+
+    if (argc-1 < 1)
+        *inputFileNameP = "-";
+    else {
+        *inputFileNameP = argv[1];
+
+        if (argc-1 > 1)
+            pm_error("There is at most one argument - input file name.  "
+                     "You specified %u", argc-1);
+    }
+}
+
+
+
+static bool littleEndian;
+
+
+static void
+determineEndianness(void) {
+
+    union {
+        unsigned char bytes[2];
+        unsigned short number;
+    } u;
+
+    u.number = 1;
+
+    littleEndian = (u.bytes[0] == 1);
+}
+
+
+
+static unsigned short
+byteswap(unsigned short const input) {
+
+    return (input << 8) | (input >> 8);
+}
+
+
+
+static void
+read_header(FILE *   const ifP,
+            rlahdr * const hdrP) {
+
+    rlahdr hdr;
+    size_t bytesRead;
+    
+    fseek (ifP, 0, SEEK_SET);
+    
+    /* Here we have a hack.  The bytes in the file are almost in the
+       same format as the compiler stores 'hdr' in memory.  The only
+       difference is that the compiler may store the integer values
+       in the obscene little-endian format.  So we just read the whole
+       header into 'hdr' as if it were the right format, and then
+       correct the integer values by swapping their bytes.
+
+       The _right_ way to do this is to read the file one field at a time,
+       using pm_readbigshort() where appropriate.
+    */
+
+    bytesRead = fread(&hdr, sizeof hdr, 1, ifP);
+    if (bytesRead != 1)
+        pm_error("Unexpected EOF on input file.");
+
+    if (littleEndian) {
+        hdr.window.left          = byteswap(hdr.window.left);
+        hdr.window.right         = byteswap(hdr.window.right);
+        hdr.window.bottom        = byteswap(hdr.window.bottom);
+        hdr.window.top           = byteswap(hdr.window.top);
+        hdr.active_window.left   = byteswap(hdr.active_window.left);
+        hdr.active_window.right  = byteswap(hdr.active_window.right);
+        hdr.active_window.bottom = byteswap(hdr.active_window.bottom);
+        hdr.active_window.top    = byteswap(hdr.active_window.top);
+        hdr.storage_type         = byteswap(hdr.storage_type);
+        hdr.num_chan             = byteswap(hdr.num_chan);
+        hdr.num_matte            = byteswap(hdr.num_matte);
+        hdr.revision             = byteswap(hdr.revision);
+        hdr.chan_bits            = byteswap(hdr.chan_bits);
+        hdr.matte_type           = byteswap(hdr.matte_type);
+        hdr.matte_bits           = byteswap(hdr.matte_bits);
+    }
+
+    if (hdr.revision != 0xfffe && hdr.revision != 0xfffd)
+        pm_error("Invalid file header.  \"revision\" field should contain "
+                 "0xfffe or 0xfffd, but contains 0x%04x", hdr.revision);
+
+    *hdrP = hdr;
+}
+
+
+
+static void
+decodeFP(unsigned char * const in,
+         unsigned char * const out,
+         int             const width,
+         int             const stride) {
+
+    unsigned int x;
+    unsigned char * inputCursor;
+    unsigned char * outputCursor;
+
+    inputCursor = &in[0];
+
+    for (x = 0; x < width; ++x) {
+        union {char bytes [4]; float fv;} fi;
+        unsigned short val;
+
+        if (littleEndian) {
+            fi.bytes [3] = *inputCursor++;
+            fi.bytes [2] = *inputCursor++;
+            fi.bytes [1] = *inputCursor++;
+            fi.bytes [0] = *inputCursor++;
+        } else {
+            fi.bytes [0] = *inputCursor++;
+            fi.bytes [1] = *inputCursor++;
+            fi.bytes [2] = *inputCursor++;
+            fi.bytes [3] = *inputCursor++;
+        }
+
+        val = fi.fv > 1 ? 65535 : (fi.fv < 0 ? 0 :
+                                   (unsigned short) (65535 * fi.fv + .5));
+        outputCursor[0] = val >> 8; outputCursor[1] = val & 0xff;
+        outputCursor += stride;
+    }
+}
+
+
+
+static unsigned char *
+decode(unsigned char * const input,
+       unsigned char * const output,
+       int             const xFile,
+       int             const xImage,
+       int             const stride) {
+
+    int x;
+    unsigned int bytes;
+    unsigned int useX;
+    unsigned char * inputCursor;
+    unsigned char * outputCursor;
+
+    inputCursor = &input[0];
+    outputCursor = &output[0];
+    x = xFile;
+    bytes = 0;
+    useX = 0;
+    
+    while (x > 0) {
+        int count;
+
+        count = *(signed char *)inputCursor++;
+        ++bytes;
+
+        if (count >= 0) {
+            /* Repeat pixel value (count + 1) times. */
+            while (count-- >= 0) {
+                if (useX < xImage) {
+                    *outputCursor = *inputCursor;
+                    outputCursor += stride;
+                }
+                --x;
+                ++useX;
+            }
+            ++inputCursor;
+            ++bytes;
+        } else {
+            /* Copy (-count) unencoded values. */
+            for (count = -count; count > 0; --count) {
+                if (useX < xImage) {
+                    *outputCursor = *inputCursor;
+                    outputCursor += stride;
+                }
+                ++inputCursor;
+                ++bytes;
+                --x;
+                ++useX;
+            }
+        }
+    }
+    return inputCursor;
+}
+
+
+
+static void
+decode_row(FILE *          const ifP,
+           int             const row,
+           unsigned char * const rb) {
+
+    static unsigned char * read_buffer = NULL;
+    unsigned int chan;
+    int rc;
+
+    if (!read_buffer) {
+        MALLOCARRAY(read_buffer, width * 4);
+        if (read_buffer == 0)
+            pm_error("Unable to get memory for read_buffer");
+    }
+
+    rc = fseek (ifP, offsets [height - row - 1], SEEK_SET);
+    if (rc != 0)
+        pm_error("fseek() failed with errno %d (%s)",
+                 errno, strerror(errno));
+
+    for (chan = 0; chan < outpam.depth; ++chan) {
+        unsigned short length;
+        size_t bytesRead;
+        
+        pm_readbigshortu(ifP, &length);
+        if (length > width * 4)
+            pm_error("Line too long - row %u, channel %u", row, chan);
+
+        bytesRead = fread(read_buffer, 1, length, ifP);
+        if (bytesRead != length)
+            pm_error("EOF encountered unexpectedly");
+
+        if (is_float)
+            decodeFP(read_buffer, rb + chan * 2, width, outpam.depth * 2);
+        else if (depth > 8) {
+            /* Hi byte */
+            unsigned char * const newpos =
+                decode(read_buffer, rb + chan * 2, width, width,
+                       outpam.depth * 2);
+            /* Lo byte */
+            decode(newpos, rb + chan * 2 + 1, width, width,
+                   outpam.depth * 2);
+        } else
+            decode(read_buffer, rb + chan, width, width, outpam.depth); 
+    }
+}
+
+
+
+static void
+getHeaderInfo(FILE *         const ifP,
+              unsigned int * const widthP,
+              unsigned int * const heightP,
+              unsigned int * const depthP,
+              bool *         const hasMatteP,
+              unsigned int * const chanBitsP,
+              short *        const storageType) {
+    
+    rlahdr hdr;
+    int width, height;
+
+    read_header(ifP, &hdr);
+
+    height = hdr.active_window.top - hdr.active_window.bottom + 1;
+    width  = hdr.active_window.right - hdr.active_window.left + 1;
+    if (width <=0)
+        pm_error("Invalid input image.  It says its width isn't positive");
+    if (height <=0)
+        pm_error("Invalid input image.  It says its height isn't positive");
+
+    *widthP  = width;
+    *heightP = height;
+
+    if (hdr.num_chan != 1 && hdr.num_chan != 3)
+        pm_error ("Input image has bad number of channels: %d.  "
+                  "Should be 1 or 3.",
+                  hdr.num_chan);
+
+    *depthP = hdr.chan_bits <= 8 ? 8 : 16;
+
+    *hasMatteP = (hdr.num_matte > 0);
+    *chanBitsP = hdr.chan_bits;
+}
+
+
+
+static void
+readOffsetArray(FILE *       const ifP,
+                int **       const offsetsP,
+                unsigned int const height) {
+
+    int * offsets;
+    unsigned int row;
+
+    MALLOCARRAY(offsets, height);
+    if (offsets == NULL)
+        pm_error("Unable to allocate memory for the offsets array");
+
+    for (row = 0; row < height; ++row) {
+        long l;
+        pm_readbiglong(ifP, &l);
+        offsets[row] = l;
+    }
+    *offsetsP = offsets;
+}
+
+
+
+static void
+destroyOffsetArray(int * const offsets) {
+
+    free(offsets);
+}
+
+
+
+static void
+readAndWriteRaster(FILE *             const ifP,
+                   const struct pam * const outpamP) {
+
+    unsigned char * rowBuffer;
+    tuple * tuplerow;
+    unsigned int row;
+
+    /* Hold one row of all image planes */
+    rowBuffer = calloc(1, width * outpamP->depth * 4);
+    if (rowBuffer == NULL)
+        pm_error("Unable to allocate memor for row buffer.");
+
+    tuplerow = pnm_allocpamrow(outpamP);
+
+    for (row = 0; row < height; ++row) {
+        unsigned int col;
+        unsigned char * rbP;
+
+        decode_row(ifP, row, rowBuffer);
+        for (col = 0, rbP = rowBuffer; col < width; ++col) {
+            unsigned int chan;
+            for (chan = 0; chan < outpamP->depth; ++chan) {
+                if (depth > 8) {
+                    tuplerow[col][chan] = 256 * rbP[0] + rbP[1];
+                    rbP += 2;
+                } else {
+                    tuplerow[col][chan] = *rbP;
+                    rbP += 1;
+                }
+            }
+        }
+        pnm_writepamrow(outpamP, tuplerow);
+    }
+    pnm_freepamrow(tuplerow);
+    free(rowBuffer);
+}
+
+
+
+int
+main(int    argc,
+     char * argv[]) {
+
+    const char * inputFileName;
+    FILE * ifP;
+
+    pnm_init(&argc, argv);
+
+    determineEndianness();
+
+    parseCommandLine(argc, argv, &inputFileName);
+
+    ifP = pm_openr_seekable(inputFileName);
+
+    getHeaderInfo(ifP, &width, &height, &depth, &has_matte,
+                  &chanBits, &storageType);
+
+    outpam.size   = outpam.len = sizeof (struct pam);
+    outpam.file   = stdout;
+    outpam.format = PAM_FORMAT;
+    outpam.height = height;
+    outpam.width  = width;
+    outpam.depth  = numChan + (has_matte ? 1 : 0);
+    outpam.maxval = (1 << (chanBits > 16 ? 
+                           (9 + (chanBits - 1) % 8)
+                                /* Take top 2 of 3 or 4 bytes */
+                           : chanBits)) - 1;
+
+    /* Most apps seem to assume 32 bit integer is really floating point */
+    if (chanBits == 32 || storageType == 4) {
+        is_float = TRUE;
+        outpam.maxval = 65535;
+        depth = 16;
+    }
+
+    outpam.bytes_per_sample = depth / 8;
+    strcpy(outpam.tuple_type, (numChan == 3 ? "RGB" : "GRAYSCALE"));
+    if (has_matte)
+        strcat(outpam.tuple_type, "A");
+
+    readOffsetArray(ifP, &offsets, height);
+
+    pnm_writepaminit(&outpam);
+
+    readAndWriteRaster(ifP, &outpam);
+
+    destroyOffsetArray(offsets);
+    
+    pm_close(ifP);
+
+    return 0; 
+}
diff --git a/converter/other/rletopnm.c b/converter/other/rletopnm.c
new file mode 100644
index 00000000..aaa86388
--- /dev/null
+++ b/converter/other/rletopnm.c
@@ -0,0 +1,497 @@
+/*
+ * This is derived from the file of the same name dated June 5, 1995,
+ * copied from the Army High Performance Computing Research Center's
+ * media-tools.tar.gz package, received from 
+ * http://www.arc.umn.edu/gvl-software/media-tools.tar.gz on 2000.04.13.
+ *
+ * This software is copyrighted as noted below.  It may be freely copied,
+ * modified, and redistributed, provided that the copyright notice is
+ * preserved on all copies.
+ *
+ * There is no warranty or other guarantee of fitness for this software,
+ * it is provided solely "as is".  Bug reports or fixes may be sent
+ * to the author, who may or may not act on them as he desires.
+ *
+ * You may not include this software in a program or other software product
+ * without supplying the source, or without informing the end-user that the
+ * source is available for no extra charge.
+ *
+ * If you modify this software, you should include a notice giving the
+ * name of the person performing the modification, the date of modification,
+ * and the reason for such modification.
+ */
+/*
+ * rletopnm - A conversion program to convert from Utah's "rle" image format
+ *            to pbmplus ppm or pgm image formats.
+ *
+ * Author:      Wes Barris (wes@msc.edu)
+ *              AHPCRC
+ *              Minnesota Supercomputer Center, Inc.
+ * Date:        March 30, 1994
+ * Copyright (c) Minnesota Supercomputer Center 1994
+ * 
+ * 2000.04.13 adapted for Netpbm by Bryan Henderson.  Quieted compiler 
+ *            warnings.  Added --alpha option.  Accept input on stdin
+ *
+ */
+
+#define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+/*-----------------------------------------------------------------------------
+ * System includes.
+ */
+#include <string.h>
+#include <stdio.h>
+#define NO_DECLARE_MALLOC
+#include <rle.h>
+
+#include "pnm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+
+#define HMSG if (headerDump) pm_message
+#define GRAYSCALE   001 /* 8 bits, no colormap */
+#define PSEUDOCOLOR 010 /* 8 bits, colormap */
+#define TRUECOLOR   011 /* 24 bits, colormap */
+#define DIRECTCOLOR 100 /* 24 bits, no colormap */
+#define RLE_MAXVAL 255
+/*
+ * Utah type declarations.
+ */
+rle_hdr     hdr;
+rle_map     *colormap;
+
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    char *input_filename;
+    unsigned int headerdump;
+    unsigned int verbose;
+    char *alpha_filename;
+    bool alpha_stdout;
+};
+
+
+
+/*
+ * Other declarations.
+ */
+int     visual, maplen;
+int     width, height;
+
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                   struct cmdlineInfo * const 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'ed */
+    optStruct3 opt;
+    unsigned int option_def_index;
+
+    unsigned int alphaoutSpec;
+
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3('h', "headerdump", OPT_FLAG,   
+            NULL,                      &cmdlineP->headerdump,     0);
+    OPTENT3('v', "verbose",    OPT_FLAG,   
+            NULL,                      &cmdlineP->verbose,        0);
+    OPTENT3(0,   "alphaout",   OPT_STRING, 
+            &cmdlineP->alpha_filename, &alphaoutSpec,             0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = TRUE;  /* We have 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 all of *cmdlineP. */
+
+    if (!alphaoutSpec)
+        cmdlineP->alpha_filename = NULL;
+
+    if (argc - 1 == 0)
+        cmdlineP->input_filename = NULL;  /* he wants stdin */
+    else if (argc - 1 == 1) {
+        if (STREQ(argv[1], "-"))
+            cmdlineP->input_filename = NULL;  /* he wants stdin */
+        else 
+            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;
+}
+
+
+
+static void
+reportRleGetSetupError(int const rleGetSetupRc) {
+    
+    switch (rleGetSetupRc) {
+    case -1:
+        pm_error("According to the URT library, the input is not "
+                 "an RLE file.  rle_get_setup() failed.");
+        break;
+    case -2:
+        pm_error("Unable to get memory for the color map.  "
+                 "rle_get_setup() failed.");
+        break;
+    case -3:
+        pm_error("Input file is empty.  rle_get_setup() failed.");
+        break;
+    case -4:
+        pm_error("End of file in the middle of where the RLE header should "
+                 "be.  rle_get_setup() failed.");
+        break;
+    default:
+        pm_error("rle_get_setup() failed for an unknown reason");
+    }
+}
+
+
+/*-----------------------------------------------------------------------------
+ *                                                         Read the rle header.
+ */
+static void 
+read_rle_header(FILE * const ifp,
+                bool   const headerDump) {
+    int rc;
+    int  i;
+    hdr.rle_file = ifp;
+    rc = rle_get_setup(&hdr);
+    if (rc != 0)
+        reportRleGetSetupError(rc);
+
+    width = hdr.xmax - hdr.xmin + 1;
+    height = hdr.ymax - hdr.ymin + 1;
+    HMSG("Image size: %dx%d", width, height);
+    if (hdr.ncolors == 1 && hdr.ncmap == 3) {
+        visual = PSEUDOCOLOR;
+        colormap = hdr.cmap;
+        maplen = (1 << hdr.cmaplen);
+        HMSG("Mapped color image with a map of length %d.",
+                maplen);
+    }
+    else if (hdr.ncolors == 3 && hdr.ncmap == 0) {
+        visual = DIRECTCOLOR;
+        HMSG("24 bit color image, no colormap.");
+    }
+    else if (hdr.ncolors == 3 && hdr.ncmap == 3) {
+        visual = TRUECOLOR;
+        colormap = hdr.cmap;
+        maplen = (1 << hdr.cmaplen);
+        HMSG(
+                "24 bit color image with color map of length %d" ,maplen);
+    }
+    else if (hdr.ncolors == 1 && hdr.ncmap == 0) {
+        visual = GRAYSCALE;
+        HMSG("Grayscale image.");
+    }
+    else {
+        fprintf(stderr,
+                "ncolors = %d, ncmap = %d, I don't know how to handle this!",
+                hdr.ncolors, hdr.ncmap);
+        exit(-1);
+    }
+    if (hdr.alpha == 0) {
+        HMSG("No alpha channel.");
+    } else if (hdr.alpha == 1) {
+        HMSG("Alpha channel exists!");
+    } else {
+        fprintf(stderr, "alpha = %d, I don't know how to handle this!\n",
+                hdr.alpha);
+        exit(-1);
+    }
+    switch (hdr.background) {
+    case 0:
+        HMSG("Use all pixels, ignore background color.");
+        break;
+    case 1:
+        HMSG("Use only non-background pixels, ignore background color.");
+        break;
+    case 2:
+        HMSG("Use only non-background pixels, "
+             "clear to background color (default).");
+        break;
+    default:
+        HMSG("Unknown background flag!");
+        break;
+    }
+    if (hdr.background == 2)
+        for (i = 0; i < hdr.ncolors; i++)
+            HMSG(" %d", hdr.bg_color[i]);
+    if (hdr.ncolors == 1 && hdr.ncmap == 3) {
+        HMSG(" (%d %d %d)",
+                hdr.cmap[hdr.bg_color[0]]>>8,
+                hdr.cmap[hdr.bg_color[0]+256]>>8,
+                hdr.cmap[hdr.bg_color[0]+512]>>8);
+    }
+    else if (hdr.ncolors == 3 && hdr.ncmap == 3) {
+        HMSG(" (%d %d %d)",
+                hdr.cmap[hdr.bg_color[0]]>>8,
+                hdr.cmap[hdr.bg_color[1]+256]>>8,
+                hdr.cmap[hdr.bg_color[2]+512]>>8);
+    }
+    if (hdr.comments)
+        for (i = 0; hdr.comments[i] != NULL; i++)
+            HMSG("%s", hdr.comments[i]);
+}
+/*-----------------------------------------------------------------------------
+ *                                                    Write the ppm image data.
+ */
+static void 
+write_ppm_data(FILE * const imageout_file,
+               FILE * const alpha_file) {
+
+    rle_pixel ***scanlines, **scanline;
+    pixval r, g, b;
+    pixel *pixelrow;
+    gray *alpharow;
+   
+    int scan, x, y;
+    /*
+     *  Allocate some stuff.
+     */
+    pixelrow = ppm_allocrow(width);
+    alpharow = pgm_allocrow(width);
+
+    MALLOCARRAY(scanlines, height);
+    RLE_CHECK_ALLOC( hdr.cmd, scanlines, "scanline pointers" );
+
+    for ( scan = 0; scan < height; scan++ )
+        RLE_CHECK_ALLOC( hdr.cmd, (rle_row_alloc(&hdr, &scanlines[scan]) >= 0),
+                         "pixel memory" );
+    /*
+     * Loop through those scan lines.
+     */
+    for (scan = 0; scan < height; scan++)
+        y = rle_getrow(&hdr, scanlines[height - scan - 1]);
+    for (scan = 0; scan < height; scan++) {
+        scanline = scanlines[scan];
+        switch (visual) {
+        case GRAYSCALE:    /* 8 bits without colormap */
+            for (x = 0; x < width; x++) {
+                r = scanline[0][x];
+                g = scanline[0][x];
+                b = scanline[0][x];
+                PPM_ASSIGN(pixelrow[x], r, g, b);
+                if (hdr.alpha)
+                    alpharow[x] = scanline[-1][x];
+                else 
+                    alpharow[x] = 0;
+            }
+            break;
+        case TRUECOLOR:    /* 24 bits with colormap */
+            for (x = 0; x < width; x++) {
+                r = colormap[scanline[0][x]]>>8;
+                g = colormap[scanline[1][x]+256]>>8;
+                b = colormap[scanline[2][x]+512]>>8;
+                PPM_ASSIGN(pixelrow[x], r, g, b);
+                if (hdr.alpha) 
+                    alpharow[x] = colormap[scanline[-1][x]];
+                else
+                    alpharow[x] = 0;
+            }
+            break;
+        case DIRECTCOLOR:  /* 24 bits without colormap */
+            for (x = 0; x < width; x++) {
+                r = scanline[0][x];
+                g = scanline[1][x];
+                b = scanline[2][x];
+                PPM_ASSIGN(pixelrow[x], r, g, b);
+                if (hdr.alpha)
+                    alpharow[x] = scanline[-1][x];
+                else
+                    alpharow[x] = 0;
+            }
+            break;
+        case PSEUDOCOLOR:  /* 8 bits with colormap */
+            for (x = 0; x < width; x++) {
+                r = colormap[scanline[0][x]]>>8;
+                g = colormap[scanline[0][x]+256]>>8;
+                b = colormap[scanline[0][x]+512]>>8;
+                PPM_ASSIGN(pixelrow[x], r, g, b);
+                if (hdr.alpha) 
+                    alpharow[x] = colormap[scanline[-1][x]];
+                else
+                    alpharow[x] = 0;
+            }
+            break;
+        default:
+            break;
+        }
+        /*
+         * Write the scan line.
+         */
+        if (imageout_file ) 
+            ppm_writeppmrow(imageout_file, pixelrow, width, RLE_MAXVAL, 0);
+        if (alpha_file)
+            pgm_writepgmrow(alpha_file, alpharow, width, RLE_MAXVAL, 0);
+
+    } /* end of for scan = 0 to height */
+
+    /* Free scanline memory. */
+    for ( scan = 0; scan < height; scan++ )
+        rle_row_free( &hdr, scanlines[scan] );
+    free( scanlines );
+    ppm_freerow(pixelrow);
+    pgm_freerow(alpharow);
+}
+
+
+
+static void 
+write_pgm_data(FILE * const imageout_file,
+               FILE * const alpha_file) {
+/*----------------------------------------------------------------------------
+   Write the PGM image data
+-----------------------------------------------------------------------------*/
+    rle_pixel ***scanlines, **scanline;
+    gray * pixelrow;
+    gray * alpharow;
+    int scan;
+    int y;
+    /*
+     *  Allocate some stuff.
+     */
+    pixelrow = pgm_allocrow(width);
+    alpharow = pgm_allocrow(width);
+
+    MALLOCARRAY(scanlines, height);
+    RLE_CHECK_ALLOC( hdr.cmd, scanlines, "scanline pointers" );
+
+    for (scan = 0; scan < height; ++scan)
+        RLE_CHECK_ALLOC(hdr.cmd, (rle_row_alloc(&hdr, &scanlines[scan]) >= 0),
+                        "pixel memory" );
+    /*
+     * Loop through those scan lines.
+     */
+    for (scan = 0; scan < height; ++scan)
+        y = rle_getrow(&hdr, scanlines[height - scan - 1]);
+
+    for (scan = 0; scan < height; ++scan) {
+        int x;
+        scanline = scanlines[scan];
+        for (x = 0; x < width; ++x) {
+            pixelrow[x] = scanline[0][x];
+            if (hdr.alpha) 
+                alpharow[x] = scanline[1][x];
+            else
+                alpharow[x] = 0;
+        }
+        if (imageout_file) 
+            pgm_writepgmrow(imageout_file, pixelrow, width, RLE_MAXVAL, 0);
+        if (alpha_file)
+            pgm_writepgmrow(alpha_file, alpharow, width, RLE_MAXVAL, 0);
+
+        }   /* end of for scan = 0 to height */
+
+    /* Free scanline memory. */
+    for (scan = 0; scan < height; ++scan)
+        rle_row_free(&hdr, scanlines[scan]);
+    free(scanlines);
+    pgm_freerow(pixelrow);
+    pgm_freerow(alpharow);
+}
+
+
+
+/*-----------------------------------------------------------------------------
+ *                               Convert a Utah rle file to a pbmplus pnm file.
+ */
+int
+main(int argc, char ** argv) {
+
+    struct cmdlineInfo cmdline;
+    FILE        *ifp;
+    FILE *imageout_file, *alpha_file;
+    char *fname = NULL;
+
+    pnm_init( &argc, argv );
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    if ( cmdline.input_filename != NULL ) 
+        ifp = pm_openr( cmdline.input_filename );
+    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;
+
+
+    /*
+     * Open the file.
+     */
+    /* Initialize header. */
+    hdr = *rle_hdr_init( (rle_hdr *)NULL );
+    rle_names( &hdr, cmd_name( argv ), fname, 0 );
+
+    /*
+     * Read the rle file header.
+     */
+    read_rle_header(ifp, cmdline.headerdump || cmdline.verbose);
+    if (cmdline.headerdump)
+        exit(0);
+
+    /* 
+     * Write the alpha file header
+     */
+    if (alpha_file)
+        pgm_writepgminit(alpha_file, width, height, RLE_MAXVAL, 0);
+
+    /*
+     * Write the pnm file header.
+     */
+    switch (visual) {
+    case GRAYSCALE:   /* 8 bits without colormap -> pgm */
+        if (cmdline.verbose)
+            pm_message("Writing pgm file.");
+        if (imageout_file)
+            pgm_writepgminit(imageout_file, width, height, RLE_MAXVAL, 0);
+        write_pgm_data(imageout_file, alpha_file);
+        break;
+    default:      /* anything else -> ppm */
+        if (cmdline.verbose)
+            pm_message("Writing ppm file.");
+        if (imageout_file)
+            ppm_writeppminit(imageout_file, width, height, RLE_MAXVAL, 0);
+        write_ppm_data(imageout_file, alpha_file);
+        break;
+    }
+   
+    pm_close(ifp);
+    if (imageout_file) 
+        pm_close( imageout_file );
+    if (alpha_file)
+        pm_close( alpha_file );
+
+    return 0;
+}
diff --git a/converter/other/sgi.h b/converter/other/sgi.h
new file mode 100644
index 00000000..3700d356
--- /dev/null
+++ b/converter/other/sgi.h
@@ -0,0 +1,33 @@
+#ifndef SGI_H_INCLUDED
+#define SGI_H_INCLUDED
+
+/* sgi.h - definitions for SGI format */
+
+typedef struct {
+    short           magic;
+    char            storage;
+    char            bpc;            /* pixel size: 1 = bytes, 2 = shorts */
+    unsigned short  dimension;      /* 1 = single row, 2 = B/W, 3 = RGB */
+    unsigned short  xsize,          /* width in pixels */
+                    ysize,          /* height in pixels */
+                    zsize;          /* # of channels; B/W=1, RGB=3, RGBA=4 */
+    long            pixmin, pixmax; /* min/max pixel values */
+    char            dummy1[4];
+    char            name[80];
+    long            colormap;
+    char            dummy2[404];
+} Header;
+#define HeaderSize  512
+
+#define SGI_MAGIC           (short)474
+
+#define STORAGE_VERBATIM    0
+#define STORAGE_RLE         1
+
+#define CMAP_NORMAL         0
+#define CMAP_DITHERED       1   /* not supported */
+#define CMAP_SCREEN         2   /* not supported */
+#define CMAP_COLORMAP       3   /* not supported */
+
+#endif
+
diff --git a/converter/other/sgitopnm.c b/converter/other/sgitopnm.c
new file mode 100644
index 00000000..0d26bb9c
--- /dev/null
+++ b/converter/other/sgitopnm.c
@@ -0,0 +1,463 @@
+/* sgitopnm.c - read an SGI image and and produce a portable anymap
+**
+** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
+**
+** Based on the SGI image description v0.9 by Paul Haeberli (paul@sgi.comp)
+** Available via ftp from sgi.com:graphics/SGIIMAGESPEC
+**
+** 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.
+**
+** 29Jan94: first version
+** 08Feb94: minor bugfix
+** 29Jul00: added -channel option (smar@reptiles.org)
+*/
+#include "pnm.h"
+#include "sgi.h"
+#include "mallocvar.h"
+#ifndef VMS
+#include <unistd.h>
+#endif
+
+/*#define DEBUG*/
+
+#ifndef SEEK_SET
+#define SEEK_SET    0
+#endif
+
+
+/* entry in RLE offset table */
+typedef struct {
+    long start;     /* offset in file */
+    long length;    /* length of compressed scanline */
+} TabEntry;
+
+typedef short       ScanElem;
+typedef ScanElem *  ScanLine;
+
+/* prototypes */
+static unsigned char get_byte ARGS(( FILE* f ));
+static long get_big_long ARGS((FILE *f));
+static short get_big_short ARGS((FILE *f));
+static short get_byte_as_short ARGS((FILE *f));
+static void readerr ARGS((FILE *f));
+static const char *
+compression_name(char compr);
+static void       read_bytes ARGS((FILE *ifp, int n, char *buf));
+static Header *   read_header ARGS((FILE *ifp, int channel));
+static TabEntry * read_table ARGS((FILE *ifp, int tablen));
+static ScanLine * read_channels ARGS((FILE *ifp, Header *head, TabEntry *table, short (*func) ARGS((FILE *)), int ochan ));
+static void       image_to_pnm ARGS((Header *head, ScanLine *image, xelval maxval, int channel));
+static void       rle_decompress ARGS((ScanElem *src, int srclen, ScanElem *dest, int destlen));
+
+#define WORSTCOMPR(x)   (2*(x) + 2)
+
+
+static short verbose = 0;
+
+
+int
+main(argc, argv)
+    int argc;
+    char *argv[];
+{
+    FILE *ifp;
+    int argn;
+    const char * const usage = "[-verbose] [-channel n] [sgifile]";
+    TabEntry *table = NULL;
+    ScanLine *image;
+    Header *head;
+    pixval maxval;
+    int channel = -1;
+
+    pnm_init(&argc, argv);
+
+    argn = 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], "-noverbose", 4) )
+            verbose = 0;
+        else
+        if( pm_keymatch(argv[argn], "-channel", 2) ) {
+            char *s;
+
+            ++argn;
+            if( argn >= argc )
+                pm_usage(usage);
+
+            s = argv[argn];
+            channel = strtol(argv[argn], &s, 10);
+            if( s == argv[argn] || channel < 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);
+
+    head = read_header(ifp, channel);
+    maxval = head->pixmax - head->pixmin;
+    if( maxval > PNM_OVERALLMAXVAL )
+        pm_error("Maximum sample value in input image (%d) is too large.  "
+                 "This program's limit is %d.",
+                 maxval, PNM_OVERALLMAXVAL);
+    if (channel >= head->zsize)
+        pm_error("channel out of range - only %d channels in image",
+            head->zsize);
+    if( head->storage != STORAGE_VERBATIM )
+        table = read_table(ifp, head->ysize * head->zsize);
+    if( head->bpc == 1 )
+        image = read_channels(ifp, head, table, get_byte_as_short, channel);
+    else
+        image = read_channels(ifp, head, table, get_big_short, channel);
+
+    image_to_pnm(head, image, (xelval)maxval, channel);
+    pm_close(ifp);
+
+    exit(0);
+}
+
+
+static Header *
+read_header(ifp, channel)
+    FILE *ifp;
+    int channel;
+{
+    Header *head;
+
+    MALLOCVAR_NOFAIL(head);
+
+    head->magic     = get_big_short(ifp);
+    head->storage   = get_byte(ifp);
+    head->bpc       = get_byte(ifp);
+    head->dimension = get_big_short(ifp);
+    head->xsize     = get_big_short(ifp);
+    head->ysize     = get_big_short(ifp);
+    head->zsize     = get_big_short(ifp);
+    head->pixmin    = get_big_long(ifp);
+    head->pixmax    = get_big_long(ifp);
+    read_bytes(ifp, 4, head->dummy1);
+    read_bytes(ifp, 80, head->name);
+    head->colormap  = get_big_long(ifp);
+    read_bytes(ifp, 404, head->dummy2);
+
+    if( head->magic != SGI_MAGIC )
+        pm_error("bad magic number - not an SGI image");
+    if( head->storage != STORAGE_VERBATIM && head->storage != STORAGE_RLE )
+        pm_error("unknown compression type");
+    if( head->bpc < 1 || head->bpc > 2 )
+        pm_error("illegal precision value %d (only 1-2 allowed)", head->bpc );
+    if( head->colormap != CMAP_NORMAL )
+        pm_error("non-normal pixel data of a form we don't recognize");
+
+    /* adjust ysize/zsize to dimension, just to be sure */
+    switch( head->dimension ) {
+        case 1:
+            head->ysize = 1;
+            break;
+        case 2:
+            head->zsize = 1;
+            break;
+        case 3:
+            switch( head->zsize ) {
+                case 1:
+                    head->dimension = 2;
+                    break;
+                case 2:
+                    pm_error("don\'t know how to interpret 2-channel image");
+                    break;
+                case 3:
+                    break;
+                default:
+                    if (channel < 0)
+                        pm_message("%d-channel image, using only first 3 channels", head->zsize);
+                    break;
+            }
+            break;
+        default:
+            pm_error("illegal dimension value %d (only 1-3 allowed)", head->dimension);
+    }
+
+    if( verbose ) {
+        pm_message("raster size %dx%d, %d channels", head->xsize, head->ysize, head->zsize);
+        pm_message("compression: %d = %s", head->storage, compression_name(head->storage));
+        head->name[79] = '\0';  /* just to be safe */
+        pm_message("Image name: \"%s\"", head->name);
+#ifdef DEBUG
+        pm_message("bpc: %d    dimension: %d    zsize: %d", head->bpc, head->dimension, head->zsize);
+        pm_message("pixmin: %ld    pixmax: %ld    colormap: %ld", head->pixmin, head->pixmax, head->colormap);
+#endif
+    }
+
+    return head;
+}
+
+
+static TabEntry *
+read_table(ifp, tablen)
+    FILE *ifp;
+    int tablen;
+{
+    TabEntry *table;
+    int i;
+
+    MALLOCARRAY_NOFAIL(table, tablen);
+
+#ifdef DEBUG
+    pm_message("reading offset table");
+#endif
+
+    for( i = 0; i < tablen; i++ )
+        table[i].start = get_big_long(ifp);
+    for( i = 0; i < tablen; i++ )
+        table[i].length = get_big_long(ifp);
+
+    return table;
+}
+
+
+
+static ScanLine *
+read_channels(ifp, head, table, func, ochan)
+    FILE *ifp;
+    Header *head;
+    TabEntry *table;
+    short (*func) ARGS((FILE *));
+    int ochan;
+{
+    ScanLine *image;
+    ScanElem *temp;
+    int channel, maxchannel, row, sgi_index, i;
+    long offset, length;
+
+#ifdef DEBUG
+    pm_message("reading channels");
+#endif
+
+    if (ochan < 0) {
+        maxchannel = (head->zsize < 3) ? head->zsize : 3;
+        MALLOCARRAY_NOFAIL(image, head->ysize * maxchannel);
+    } else {
+        maxchannel = ochan + 1;
+        MALLOCARRAY_NOFAIL(image, head->ysize);
+    }
+    if ( table ) 
+        MALLOCARRAY_NOFAIL(temp, WORSTCOMPR(head->xsize));
+
+    for( channel = 0; channel < maxchannel;  channel++ ) {
+#ifdef DEBUG
+        pm_message("    channel %d", channel);
+#endif
+        for( row = 0; row < head->ysize; row++ ) {
+            int iindex;
+
+            sgi_index = channel * head->ysize + row;
+            iindex = (ochan < 0) ? sgi_index : row;
+            if (ochan < 0 || ochan == channel)
+                MALLOCARRAY_NOFAIL(image[iindex], head->xsize);
+
+            if( table ) {
+                if (channel < ochan)
+                    continue;
+
+                offset = table[sgi_index].start;
+                length = table[sgi_index].length;
+                if( head->bpc == 2 )
+                    length /= 2;   
+                    /* doc says length is in bytes, we are reading words */
+                if( fseek(ifp, offset, SEEK_SET) != 0 )
+                    pm_error("seek error for offset %ld", offset);
+
+                for( i = 0; i < length; i++ )
+                    temp[i] = (*func)(ifp);
+                rle_decompress(temp, length, image[iindex], head->xsize);
+            }
+            else {
+                for( i = 0; i < head->xsize; i++ )
+                    image[iindex][i] = (*func)(ifp);
+            }
+        }
+    }
+
+    if( table ) free(temp);
+    return image;
+}
+
+
+static void
+image_to_pnm(Header *head, ScanLine *image, xelval maxval, int channel)
+{
+    int col, row, format;
+    xel *pnmrow = pnm_allocrow(head->xsize);
+    int sub = head->pixmin;
+
+    if( head->zsize == 1 || channel >= 0) {
+        pm_message("writing PGM image");
+        format = PGM_TYPE;
+    }
+    else {
+        pm_message("writing PPM image");
+        format = PPM_TYPE;
+    }
+
+    pnm_writepnminit(stdout, head->xsize, head->ysize, (xelval)maxval, format, 0);
+    for( row = head->ysize-1; row >= 0; row-- ) {
+        for( col = 0; col < head->xsize; col++ ) {
+            if( format == PGM_TYPE )
+                PNM_ASSIGN1(pnmrow[col], image[row][col] - sub);
+            else {
+                pixval r, g, b;
+                r = image[row][col] - sub;
+                g = image[head->ysize + row][col] - sub;
+                b = image[2* head->ysize + row][col] - sub;
+                PPM_ASSIGN(pnmrow[col], r, g, b);
+            }
+        }
+        pnm_writepnmrow(stdout, pnmrow, head->xsize, (xelval)maxval, format, 0);
+    }
+    pnm_freerow(pnmrow);
+}
+
+
+static void
+rle_decompress(src, srcleft, dest, destleft)
+    ScanElem *src;
+    int srcleft;
+    ScanElem *dest;
+    int destleft;
+{
+    int count;
+    unsigned char el;
+
+    while( srcleft ) {
+        el = (unsigned char)(*src++ & 0xff);
+        --srcleft;
+        count = (int)(el & 0x7f);
+
+        if( count == 0 )
+            return;
+        if( destleft < count )
+            pm_error("RLE error: too much input data (space left %d, need %d)", destleft, count);
+        destleft -= count;
+        if( el & 0x80 ) {
+            if( srcleft < count )
+                pm_error("RLE error: not enough data for literal run (data left %d, need %d)", srcleft, count);
+            srcleft -= count;
+            while( count-- )
+                *dest++ = *src++;
+        }
+        else {
+            if( srcleft == 0 )
+                pm_error("RLE error: not enough data for replicate run");
+            while( count-- )
+                *dest++ = *src;
+            ++src;
+            --srcleft;
+        }
+    }
+    pm_error("RLE error: no terminating 0-byte");
+}
+
+
+/* basic I/O functions, taken from ilbmtoppm.c */
+
+static short
+get_big_short(ifp)
+    FILE *ifp;
+{
+    short s;
+
+    if( pm_readbigshort(ifp, &s) == -1 )
+        readerr(ifp);
+
+    return s;
+}
+
+static long
+get_big_long(ifp)
+    FILE *ifp;
+{
+    long l;
+
+    if( pm_readbiglong(ifp, &l) == -1 )
+        readerr(ifp);
+
+    return l;
+}
+
+static unsigned char
+get_byte(ifp)
+    FILE* ifp;
+{
+    int i;
+
+    i = getc(ifp);
+    if( i == EOF )
+        readerr(ifp);
+
+    return (unsigned char) i;
+}
+
+
+static void
+readerr(f)
+    FILE *f;
+{
+    if( ferror(f) )
+        pm_error("read error");
+    else
+        pm_error("premature EOF");
+}
+
+
+static void
+read_bytes(ifp, n, buf)
+    FILE *ifp;
+    int n;
+    char *buf;
+{
+    int r;
+
+    r = fread((void *)buf, 1, n, ifp);
+    if( r != n )
+        readerr(ifp);
+}
+
+
+static short
+get_byte_as_short(ifp)
+    FILE *ifp;
+{
+    return (short)get_byte(ifp);
+}
+
+
+static const char *
+compression_name(char compr)
+{
+    switch( compr ) {
+        case STORAGE_VERBATIM:
+            return "none";
+        case STORAGE_RLE:
+            return "RLE";
+        default:
+            return "unknown";
+    }
+}
+
diff --git a/converter/other/sirtopnm.c b/converter/other/sirtopnm.c
new file mode 100644
index 00000000..fafcc913
--- /dev/null
+++ b/converter/other/sirtopnm.c
@@ -0,0 +1,95 @@
+/* sirtopnm.c - read a Solitaire Image Recorder file and write a portable anymap
+**
+** Copyright (C) 1991 by Marvin Landis.
+**
+** 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 "pnm.h"
+
+int main( argc, argv )
+int argc;
+char* argv[];
+{
+    FILE *ifp;
+    xel *xelrow, *xP;
+    unsigned char *sirarray;
+    int rows, cols, row, format, picsize, planesize;
+    register int col, i;
+    short info;
+
+    pnm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[sirfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    pm_readlittleshort( ifp, &info );
+    if ( info != 0x3a4f)
+	pm_error( "Input file is not a Solitaire file" );
+    pm_readlittleshort( ifp, &info );
+    pm_readlittleshort( ifp, &info );
+    if ( info == 17 )
+    {
+	format = PGM_TYPE;
+    }
+    else if ( info == 11 )
+    {
+	format = PPM_TYPE;
+    }
+    else
+	pm_error( "Input is not MGI TYPE 11 or MGI TYPE 17" );
+    pm_readlittleshort( ifp, &info );
+    cols = (int) ( info );
+    pm_readlittleshort( ifp, &info );
+    rows = (int) ( info );
+    for ( i = 1; i < 1531; i++ )
+	pm_readlittleshort( ifp, &info );
+
+    pnm_writepnminit( stdout, cols, rows, 255, format, 0 );
+    xelrow = pnm_allocrow( cols );
+    switch ( PNM_FORMAT_TYPE(format) )
+    {
+	case PGM_TYPE:
+            pm_message( "Writing a PGM file" );
+	    for ( row = 0; row < rows; ++row )
+	    {
+	        for ( col = 0, xP = xelrow; col < cols; col++, xP++ )
+	        	PNM_ASSIGN1( *xP, fgetc( ifp ) );
+	        pnm_writepnmrow( stdout, xelrow, cols, 255, format, 0 );
+	    }
+	    break;
+	case PPM_TYPE:
+	    picsize = cols * rows * 3;
+	    planesize = cols * rows;
+            if ( !( sirarray = (unsigned char*) malloc( picsize ) ) ) 
+	        pm_error( "Not enough memory to load SIR file" );
+	    if ( fread( sirarray, 1, picsize, ifp ) != picsize )
+	        pm_error( "Error reading SIR file" );
+            pm_message( "Writing a PPM file" );
+            for ( row = 0; row < rows; row++ )
+	    {
+	        for ( col = 0, xP = xelrow; col < cols; col++, xP++ )
+        	    PPM_ASSIGN( *xP, sirarray[row*cols+col],
+				 sirarray[planesize + (row*cols+col)],
+				 sirarray[2*planesize + (row*cols+col)] );
+                pnm_writepnmrow( stdout, xelrow, cols, 255, format, 0 );
+	    }
+	    break;
+	default:
+	    pm_error( "Shouldn't happen" );
+    }
+
+    pm_close( ifp );
+
+    exit( 0 );
+}
diff --git a/converter/other/svgtopam.c b/converter/other/svgtopam.c
new file mode 100644
index 00000000..268515ad
--- /dev/null
+++ b/converter/other/svgtopam.c
@@ -0,0 +1,940 @@
+/*============================================================================
+                               svgtopam
+==============================================================================
+  This is not useful today.  It is merely a stub from which someone who
+  cares about SVG can build a full converter.
+
+  The framework is all there; it should be just a matter of coding to
+  add each of the SVG features to it.
+
+  Today, the program works fine on an image that consists solely of
+  <path> elements, which use only the "M", "L", and "z" commands.
+
+  By Bryan Henderson, San Jose, California.  May 2006
+
+  Contributed to the public domain.
+============================================================================*/
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <libxml/xmlreader.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
+#include "ppm.h"
+#include "ppmdraw.h"
+
+
+static bool traceDraw;
+
+struct cmdlineInfo {
+    const char * inputFileName;
+    unsigned int trace;
+};
+
+
+
+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;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "trace",     OPT_FLAG,   NULL,                  
+            &cmdlineP->trace,       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->inputFileName = "-";
+    else {
+        cmdlineP->inputFileName = argv[1];
+        
+        if (argc-1 > 1)
+            pm_error("Too many arguments (%u).  The only non-option argument "
+                     "is the input file name.", argc-1);
+    }
+}
+
+
+
+/*============================================================================
+   Wrappers for libxml2 routines.
+
+   The difference is that these use conventional C data types and have
+   shorter names.
+=============================================================================*/
+
+static const char *
+getAttribute(xmlTextReaderPtr const xmlReaderP,
+             const char *     const attributeName) {
+
+    return (const char *)
+        xmlTextReaderGetAttribute(xmlReaderP, (const xmlChar *)attributeName);
+}
+
+
+
+static const char *
+currentNodeName(xmlTextReaderPtr const xmlReaderP) {
+
+    return (const char *)xmlTextReaderConstName(xmlReaderP);
+}
+
+
+
+/*===========================================================================*/
+
+#define OUTPUT_MAXVAL 255
+
+typedef struct {
+    unsigned int width;
+    unsigned int height;
+    pixel ** pixels;
+    pixval maxval;
+} canvas;
+
+typedef struct {
+    pixel fillColor;
+} style;
+
+
+
+typedef struct {
+    const char * pathText;
+        /* This is e.g. "M0 0 L1 1 L9 8 Z" */
+    style        style;
+        /* This is the style as given by a 'style' attribute of <path> */
+    unsigned int pathTextLength;
+        /* This is the length in characters of 'pathText'.  It's redundant
+           with 'pathText' and exists for convenience.
+        */
+} path;
+
+static void
+createPath(const char * const pathText,
+           style        const style,
+           path **      const pathPP) {
+/*----------------------------------------------------------------------------
+   Create a path as described by a <path> element whose "style" attribute
+   indicates style 'style' and whose "d" attribute indicates path data
+   'pathText'.
+-----------------------------------------------------------------------------*/
+    bool error;
+    path * pathP;
+    
+    MALLOCVAR(pathP);
+    if (pathP == NULL)
+        error = TRUE;
+    else {
+        pathP->style = style;
+
+        pathP->pathText = strdup(pathText);
+        if (pathP->pathText == NULL)
+            error = TRUE;
+        else {
+            pathP->pathTextLength = strlen(pathP->pathText);
+
+            error = FALSE;
+        }
+        if (error)
+            free(pathP);
+    }
+    if (error )
+        *pathPP = NULL;
+    else
+        *pathPP = pathP;
+}
+
+
+
+static void
+destroyPath(path * const pathP) {
+    
+    assert(pathP->pathTextLength == strlen(pathP->pathText));
+
+    strfree(pathP->pathText);
+
+    free(pathP);
+}
+
+
+
+typedef struct {
+    uint x;
+    uint y;
+} point;
+
+static point
+makePoint(uint const x,
+          uint const y) {
+
+    point p;
+    
+    p.x = x;
+    p.y = y;
+    
+    return p;
+}
+
+
+
+typedef enum {
+    PATH_MOVETO,
+    PATH_LINETO,
+    PATH_CLOSEPATH,
+    PATH_CUBIC
+} pathCommandVerb;
+
+typedef struct {
+    point dest;
+} pathMovetoArgs;
+
+typedef struct {
+    /* Draw a line segment from current point to 'dest' */
+    point dest;
+} pathLinetoArgs;
+
+typedef struct {
+    /* Draw a cubic spline from current point to 'dest' with control points
+       'ctl1' at the beginning of the curve and 'ctl2' at the end.
+
+       I.e. it's a section of a cubic curve which passes through the
+       current point and 'dest' and whose slope at the current point
+       is that of the line through the current point and 'ctl1' and
+       whose slope at 'dest' is that of the line through 'dest' and
+       'ctl2';
+
+       A cubic curve is a plot of a polynomial equation of degree 3
+       (or less, for our purposes).
+    */
+    point dest;
+    point ctl1;
+    point ctl2;
+} pathCubicArgs;
+
+typedef struct {
+    pathCommandVerb verb;
+    union {
+        pathMovetoArgs moveto;
+        pathLinetoArgs lineto;
+        pathCubicArgs  cubic;
+    } args;
+} pathCommand;
+
+
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   This is an object for reading through a path from beginning to end.
+-----------------------------------------------------------------------------*/
+    path *       pathP;
+    unsigned int cursor;
+} pathReader;
+
+static void
+createPathReader(path *        const pathP,
+                 pathReader ** const pathReaderPP) {
+
+    pathReader * pathReaderP;
+
+    MALLOCVAR_NOFAIL(pathReaderP);
+
+    pathReaderP->pathP = pathP;
+    pathReaderP->cursor = 0;
+
+    *pathReaderPP = pathReaderP;
+}
+
+static void
+destroyPathReader(pathReader * const pathReaderP) {
+    free(pathReaderP);
+}
+
+
+
+static void
+skipWhiteSpace(pathReader * const pathReaderP) {
+/*----------------------------------------------------------------------------
+   Move the cursor over any white space where it now points.
+-----------------------------------------------------------------------------*/
+    const path * const pathP = pathReaderP->pathP;
+
+    while (isspace(pathP->pathText[pathReaderP->cursor]) &&
+           pathReaderP->cursor < pathP->pathTextLength)
+        ++pathReaderP->cursor;
+}
+
+
+
+static void
+getNumber(pathReader * const pathReaderP,
+          uint *       const numberP) {
+
+    const path * const pathP          = pathReaderP->pathP;
+    const char * const pathText       = pathP->pathText;
+    size_t       const pathTextLength = pathP->pathTextLength;
+
+    assert(!isspace(pathText[pathReaderP->cursor]));
+
+    if (pathReaderP->cursor >= pathTextLength)
+        pm_error("Path description ends where a number was expected.");
+    else {
+        uint number;
+
+        number = 0;  /* initial value */
+
+        while (pathReaderP->cursor < pathTextLength &&
+               isdigit(pathText[pathReaderP->cursor])) {
+            number = 10 * number + (pathText[pathReaderP->cursor] - '0');
+            ++pathReaderP->cursor;
+        }
+        *numberP = number;
+    }
+}
+
+
+
+static void
+getNextCommand(pathReader *  const pathReaderP,
+               pathCommand * const pathCommandP,
+               bool *        const endOfPathP) {
+
+    const path * const pathP          = pathReaderP->pathP;
+    const char * const pathText       = pathP->pathText;
+    size_t       const pathTextLength = pathP->pathTextLength;
+
+    skipWhiteSpace(pathReaderP);
+
+    if (pathReaderP->cursor >= pathTextLength)
+        *endOfPathP = true;
+    else {
+        switch (pathText[pathReaderP->cursor++]) {
+        case 'M':
+            pathCommandP->verb = PATH_MOVETO;
+            skipWhiteSpace(pathReaderP);
+            getNumber(pathReaderP, &pathCommandP->args.moveto.dest.x);
+            skipWhiteSpace(pathReaderP);
+            getNumber(pathReaderP, &pathCommandP->args.moveto.dest.y);
+            break;
+        case 'L':
+            pathCommandP->verb = PATH_LINETO;
+            skipWhiteSpace(pathReaderP);
+            getNumber(pathReaderP, &pathCommandP->args.lineto.dest.x);
+            skipWhiteSpace(pathReaderP);
+            getNumber(pathReaderP, &pathCommandP->args.lineto.dest.y);
+            break;
+        case 'C':
+            pathCommandP->verb = PATH_CUBIC;
+            skipWhiteSpace(pathReaderP);
+            getNumber(pathReaderP, &pathCommandP->args.cubic.ctl1.x);
+            skipWhiteSpace(pathReaderP);
+            getNumber(pathReaderP, &pathCommandP->args.cubic.ctl1.y);
+            skipWhiteSpace(pathReaderP);
+            getNumber(pathReaderP, &pathCommandP->args.cubic.ctl2.x);
+            skipWhiteSpace(pathReaderP);
+            getNumber(pathReaderP, &pathCommandP->args.cubic.ctl2.y);
+            skipWhiteSpace(pathReaderP);
+            getNumber(pathReaderP, &pathCommandP->args.cubic.dest.x);
+            skipWhiteSpace(pathReaderP);
+            getNumber(pathReaderP, &pathCommandP->args.cubic.dest.y);
+            break;
+        case 'z':
+            pathCommandP->verb = PATH_CLOSEPATH;
+            break;
+        default:
+            pm_error("Unrecognized command in <path>: '%c'",
+                     pathText[pathReaderP->cursor++]);
+        }
+    }
+}
+
+
+static void
+outlineObject(path *           const pathP,
+              struct fillobj * const fillObjP) {
+/*----------------------------------------------------------------------------
+  Create a fill object, which contains and outline of the object and
+  can be used with ppmd_fill() to fill the figure.  The outline is as
+  described by *pathP.
+-----------------------------------------------------------------------------*/
+    pathReader * pathReaderP;
+    bool endOfPath;
+    point currentPos;
+    point subpathStart;
+        /* Point at which the current subpath starts */
+
+    endOfPath = false;
+    subpathStart = makePoint(0,0);
+    currentPos = subpathStart;
+
+    createPathReader(pathP, &pathReaderP);
+
+    while (!endOfPath) {
+        pathCommand pathCommand;
+        getNextCommand(pathReaderP, &pathCommand, &endOfPath);
+        if (!endOfPath) {
+            switch (pathCommand.verb) {
+            case PATH_MOVETO:
+                if (traceDraw)
+                    pm_message("Moving to (%u, %u)",
+                               pathCommand.args.moveto.dest.x,
+                               pathCommand.args.moveto.dest.y);
+                subpathStart = pathCommand.args.moveto.dest;
+                currentPos = subpathStart;
+                break;
+            case PATH_LINETO: {
+                point const dest = pathCommand.args.lineto.dest;
+                if (traceDraw)
+                    pm_message("Lining to (%u, %u)", dest.x, dest.y);
+                ppmd_line(NULL, 0, 0, 0,
+                          currentPos.x, currentPos.y, dest.x, dest.y,
+                          ppmd_fill_drawproc, fillObjP);
+                currentPos = dest;
+            } break;
+            case PATH_CLOSEPATH:
+                if (traceDraw)
+                    pm_message("Closing.");
+                ppmd_line(NULL, 0, 0, 0,
+                          currentPos.x, currentPos.y,
+                          subpathStart.x, subpathStart.y,
+                          ppmd_fill_drawproc, fillObjP);
+                currentPos = subpathStart;
+                break;
+            case PATH_CUBIC: {
+                point const dest = pathCommand.args.cubic.dest;
+                point const ctl1 = pathCommand.args.cubic.ctl1;
+                point const ctl2 = pathCommand.args.cubic.ctl2;
+                if (traceDraw)
+                    pm_message("Doing cubic spline to (%u, %u)",
+                               dest.x, dest.y);
+                /* We need to write ppmd_spline4() */
+                ppmd_spline4(NULL, 0, 0, 0,
+                             currentPos.x, currentPos.y,
+                             ctl1.x, ctl1.y,
+                             ctl2.x, ctl2.y,
+                             dest.x, dest.y,
+                             ppmd_fill_drawproc, fillObjP);
+                currentPos = dest;
+            } break;
+            }
+        }
+    }
+    destroyPathReader(pathReaderP);
+}
+
+
+
+static void
+drawPath(canvas * const canvasP,
+         path *   const pathP) {
+/*----------------------------------------------------------------------------
+   Draw the path 'pathP' on the canvas 'canvasP'.
+-----------------------------------------------------------------------------*/
+    struct fillobj * fillObjP;
+
+    if (traceDraw)
+        pm_message("Drawing path '%s' with fill color (%u, %u, %u)",
+                   pathP->pathText,
+                   pathP->style.fillColor.r,
+                   pathP->style.fillColor.g,
+                   pathP->style.fillColor.b);
+
+    fillObjP = ppmd_fill_create();
+
+    outlineObject(pathP, fillObjP);
+
+    ppmd_fill(canvasP->pixels, canvasP->width, canvasP->height,
+              canvasP->maxval,
+              fillObjP, 
+              PPMD_NULLDRAWPROC, &pathP->style.fillColor);
+
+    ppmd_fill_destroy(fillObjP);
+}
+
+
+
+static style
+interpretStyle(const char * const styleAttr) {
+
+    style style;
+
+    char * buffer;
+
+    char * p;
+
+    buffer = strdup(styleAttr);
+    if (buffer == NULL)
+        pm_error("Could not get memory for a buffer to parse style attribute");
+
+    p = &buffer[0];
+
+    while (p) {
+        const char * const token = strsepN(&p, ";");
+        const char * strippedToken;
+        const char * p;
+        char * buffer;
+
+        for (p = &token[0]; isspace(*p); ++p);
+        
+        strippedToken = p;
+
+        buffer = strdup(strippedToken);
+
+        if (strlen(strippedToken) > 0) {
+            char * const colonPos = strchr(buffer, ':');
+
+            if (colonPos == NULL)
+                pm_error("There is no colon in the attribute specification "
+                         "'%s' in the 'style' attribute of a <path> "
+                         "element.", strippedToken);
+            else {
+                const char * const value = colonPos + 1;
+                const char * const name  = &buffer[0];
+                
+                *colonPos = '\0';
+
+                if (streq(name, "fill")) {
+                    style.fillColor = ppm_parsecolor(value, OUTPUT_MAXVAL);
+                } else if (streq(name, "stroke")) {
+                    if (!streq(value, "none"))
+                        pm_error("Value of 'stroke' attribute in the 'style' "
+                                 "attribute of a <path> element is '%s'.  We "
+                                 "understand only 'none'", value);
+                } else
+                    pm_error("Unrecognized attribute '%s' "
+                             "in the 'style' attribute "
+                             "of a <path> element", name);
+            }
+        }
+        free(buffer);
+    }
+
+    free(buffer);
+
+    return style;
+}
+
+
+static void
+getPathAttributes(xmlTextReaderPtr const xmlReaderP,
+                  style *          const styleP,
+                  const char **    const pathP) {
+
+    const char * const style = getAttribute(xmlReaderP, "style");
+    const char * const d     = getAttribute(xmlReaderP, "d");
+
+    *styleP = interpretStyle(style);
+    *pathP = d;
+}
+
+
+
+static void
+processSubPathNode(xmlTextReaderPtr const xmlReaderP,
+                   bool *           const endOfPathP) {
+
+    xmlReaderTypes const nodeType  = xmlTextReaderNodeType(xmlReaderP);
+
+    *endOfPathP = FALSE;  /* initial assumption */
+
+    switch (nodeType) {
+    case XML_READER_TYPE_ELEMENT:
+        pm_error("<path> contains a <%s> element.  <path> should have "
+                 "no contents", currentNodeName(xmlReaderP));
+        break;
+    case XML_READER_TYPE_END_ELEMENT: {
+        const char * nodeName = currentNodeName(xmlReaderP);
+        if (streq(nodeName, "path"))
+            *endOfPathP = TRUE;
+        else
+            pm_error("</%s> found where </path> expected", nodeName);
+        } break;
+    default:
+        /* Just ignore whatever this is.  Contents of <path> are
+           meaningless; all the information is in the attributes 
+        */
+        break;
+    }
+}
+
+
+
+static void
+processPathElement(xmlTextReaderPtr const xmlReaderP,
+                   canvas *         const canvasP) {
+
+    style style;
+    const char * pathData;
+    path * pathP;
+    bool endOfPath;
+
+    assert(xmlTextReaderNodeType(xmlReaderP) == XML_READER_TYPE_ELEMENT);
+    assert(streq(currentNodeName(xmlReaderP), "path"));
+
+    getPathAttributes(xmlReaderP, &style, &pathData);
+
+    createPath(pathData, style, &pathP);
+
+    if (pathP) {
+        drawPath(canvasP, pathP);
+        destroyPath(pathP);
+    }
+
+    endOfPath = xmlTextReaderIsEmptyElement(xmlReaderP);
+
+    while (!endOfPath) {
+        int rc;
+
+        rc = xmlTextReaderRead(xmlReaderP);
+        
+        switch (rc) {
+        case 1:
+            processSubPathNode(xmlReaderP, &endOfPath);
+            break;
+        case 0:
+            pm_error("Input file ends in the middle of a <path>");
+            break;
+        default:
+            pm_error("xmlTextReaderRead() failed, rc=%d", rc);
+        }
+    }
+}
+
+
+
+static void
+stringToUint(const char *   const string,
+             unsigned int * const uintP,
+             const char **  const errorP) {
+
+    /* TODO: move this to nstring.c */
+
+    if (strlen(string) == 0)
+        asprintfN(errorP, "Value is a null string");
+    else {
+        char * tailptr;
+
+        *uintP = strtoul(string, &tailptr, 10);
+
+        if (*tailptr != '\0')
+            asprintfN(errorP, "Non-numeric crap in string: '%s'", tailptr);
+        else
+            *errorP = NULL;
+    }
+}
+
+
+
+static void
+getSvgAttributes(xmlTextReaderPtr const xmlReaderP,
+                 unsigned int *   const colsP,
+                 unsigned int *   const rowsP) {
+
+    const char * const width  = getAttribute(xmlReaderP, "width");
+    const char * const height = getAttribute(xmlReaderP, "height");
+
+    const char * error;
+
+    stringToUint(width, colsP, &error);
+    if (error) {
+        pm_error("'width' attribute of <svg> has invalid value.  %s", error);
+        strfree(error);
+    }
+    stringToUint(height, rowsP, &error);
+    if (error) {
+        pm_error("'height' attribute of <svg> has invalid value.  %s", error);
+        strfree(error);
+    }
+}
+
+
+
+static void
+processSubSvgElement(xmlTextReaderPtr const xmlReaderP,
+                     canvas *         const canvasP) {
+
+    const char * const nodeName = currentNodeName(xmlReaderP);
+
+    assert(xmlTextReaderNodeType(xmlReaderP) == XML_READER_TYPE_ELEMENT);
+    
+    if (streq(nodeName, "path"))
+        processPathElement(xmlReaderP, canvasP);
+    else
+        pm_error("This image contains a <%s> element.  This program "
+                 "understands only <path>!", nodeName);
+}
+
+
+
+static void
+processSubSvgNode(xmlTextReaderPtr const xmlReaderP,
+                  canvas *         const canvasP,
+                  bool *           const endOfSvgP) {
+
+    xmlReaderTypes const nodeType = xmlTextReaderNodeType(xmlReaderP);
+
+    *endOfSvgP = FALSE;  /* initial assumption */
+
+    switch (nodeType) {
+    case XML_READER_TYPE_ELEMENT:
+        processSubSvgElement(xmlReaderP, canvasP);
+        break;
+    case XML_READER_TYPE_END_ELEMENT: {
+        const char * const nodeName = currentNodeName(xmlReaderP);
+        if (streq(nodeName, "svg"))
+            *endOfSvgP = TRUE;
+        else
+            pm_error("</%s> found where </svg> expected", nodeName);
+    } break;
+    default:
+        /* Just ignore whatever this is */
+        break;
+    }
+}
+
+
+
+static void
+createCanvas(unsigned int const width,
+             unsigned int const height,
+             pixval       const maxval,
+             canvas **    const canvasPP) {
+
+    canvas * canvasP;
+
+    MALLOCVAR_NOFAIL(canvasP);
+
+    canvasP->width  = width;
+    canvasP->height = height;
+    canvasP->pixels = ppm_allocarray(width, height);
+    canvasP->maxval = maxval;
+
+    *canvasPP = canvasP;
+}
+
+
+
+static void
+destroyCanvas(canvas * const canvasP) {
+
+    ppm_freearray(canvasP->pixels, canvasP->height);
+
+    free(canvasP);
+}
+
+
+
+static void
+writePam(FILE *   const ofP,
+         canvas * const canvasP) {
+
+    unsigned int row;
+    struct pam pam;
+    tuple * tuplerow;
+
+    pam.size             = sizeof(pam);
+    pam.len              = PAM_STRUCT_SIZE(tuple_type);
+    pam.file             = ofP;
+    pam.format           = PAM_FORMAT;
+    pam.plainformat      = 0;
+    pam.width            = canvasP->width;
+    pam.height           = canvasP->height;
+    pam.depth            = 3;
+    pam.maxval           = OUTPUT_MAXVAL;
+    strcpy(pam.tuple_type, PAM_PPM_TUPLETYPE);
+    
+    pnm_writepaminit(&pam);
+
+    tuplerow = pnm_allocpamrow(&pam);
+
+    for (row = 0; row < (unsigned)pam.height; ++row) {
+        pixel * const pixelrow = canvasP->pixels[row];
+        unsigned int col;
+
+        for (col = 0; col < (unsigned)pam.width; ++col) {
+            pixel const thisPixel = pixelrow[col];
+            assert(pam.depth >= 3);
+
+            tuplerow[col][PAM_RED_PLANE] = PPM_GETR(thisPixel);
+            tuplerow[col][PAM_GRN_PLANE] = PPM_GETG(thisPixel);
+            tuplerow[col][PAM_BLU_PLANE] = PPM_GETB(thisPixel);
+        }
+        pnm_writepamrow(&pam, tuplerow);
+    }
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+static void
+processSvgElement(xmlTextReaderPtr const xmlReaderP,
+                  FILE *           const ofP) {
+
+    unsigned int width, height;
+    bool endOfSvg;
+    canvas * canvasP;
+
+    assert(xmlTextReaderNodeType(xmlReaderP) == XML_READER_TYPE_ELEMENT);
+    assert(streq(currentNodeName(xmlReaderP), "svg"));
+
+    getSvgAttributes(xmlReaderP, &width, &height);
+
+    createCanvas(width, height, 255, &canvasP);
+
+    endOfSvg = xmlTextReaderIsEmptyElement(xmlReaderP);
+
+    while (!endOfSvg) {
+        int rc;
+
+        rc = xmlTextReaderRead(xmlReaderP);
+        
+        switch (rc) {
+        case 1:
+            processSubSvgNode(xmlReaderP, canvasP, &endOfSvg);
+            break;
+        case 0:
+            pm_error("Input file ends in the middle of a <svg>");
+            break;
+        default:
+            pm_error("xmlTextReaderRead() failed, rc=%d", rc);
+        }
+    }
+    writePam(ofP, canvasP);
+
+    destroyCanvas(canvasP);
+}
+
+
+
+static void
+processTopLevelElement(xmlTextReaderPtr const xmlReaderP,
+                       FILE *           const ofP) {
+
+    const char * const nodeName = currentNodeName(xmlReaderP);
+
+    assert(xmlTextReaderNodeType(xmlReaderP) == XML_READER_TYPE_ELEMENT);
+    assert(xmlTextReaderDepth(xmlReaderP) == 0);
+
+    if (!streq(nodeName, "svg"))
+        pm_error("Not an SVG image.  This XML document consists of "
+                 "a <%s> element, whereas an SVG image is an <svg> "
+                 "element.", nodeName);
+    else
+        processSvgElement(xmlReaderP, ofP);
+}
+
+
+
+static void
+processTopLevelNode(xmlTextReaderPtr const xmlReaderP,
+                    FILE *           const ofP) {
+
+    unsigned int   const depth    = xmlTextReaderDepth(xmlReaderP);
+    xmlReaderTypes const nodeType = xmlTextReaderNodeType(xmlReaderP);
+
+    assert(depth == 0);
+
+    switch (nodeType) {
+    case XML_READER_TYPE_ELEMENT:
+        processTopLevelElement(xmlReaderP, ofP);
+        break;
+    default:
+        /* Just ignore whatever this is */
+        break;
+    }
+}
+
+
+
+static void
+processDocument(xmlTextReaderPtr const xmlReaderP,
+                FILE *           const ofP) {
+
+    bool eof;
+
+    eof = false;
+
+    while (!eof) {
+        int rc;
+
+        rc = xmlTextReaderRead(xmlReaderP);
+        
+        switch (rc) {
+        case 1:
+            processTopLevelNode(xmlReaderP, ofP);
+            break;
+        case 0:
+            eof = true;
+            break;
+        default:
+            pm_error("xmlTextReaderRead() failed, rc=%d", rc);
+        }
+    }
+}
+
+
+
+int
+main(int argc, char **argv) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    xmlTextReaderPtr xmlReaderP;
+
+    pnm_init(&argc, argv);
+
+    xmlInitParser();
+
+    LIBXML_TEST_VERSION;
+
+    parseCommandLine(argc, argv, &cmdline);
+    
+    traceDraw = cmdline.trace;
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    xmlReaderP = xmlReaderForFd(fileno(ifP), "SVG_IMAGE", NULL, 0);
+
+    if (xmlReaderP) {
+        processDocument(xmlReaderP, stdout);
+
+        /* xmlTextReaderIsValid() does not appear to work.  It always says
+           the document is invalid
+        */
+
+        xmlFreeTextReader(xmlReaderP);
+    } else
+        pm_error("Failed to create xmlReader");
+
+    xmlCleanupParser();
+
+    return 0;
+}
diff --git a/converter/other/tiff.c b/converter/other/tiff.c
new file mode 100644
index 00000000..a498571b
--- /dev/null
+++ b/converter/other/tiff.c
@@ -0,0 +1,468 @@
+/*============================================================================
+                                 tiff.c
+==============================================================================
+
+  Stuff specific to the TIFF format -- essentially and extension to libtiff.
+
+============================================================================*/
+
+#include <string.h>
+
+#ifdef VMS
+#ifdef SYSV
+#undef SYSV
+#endif
+#include <tiffioP.h>
+#endif
+#include <tiffio.h>
+
+#include "pm_c_util.h"
+#include "nstring.h"
+#include "mallocvar.h"
+#include "pm.h"
+#include "pm_tiff.h"
+
+
+
+static uint32
+number(const char * const value,
+       tagvalmap    const tagvallist[]) {
+    
+    char * ep;
+    long num;
+
+    if (strlen(value) == 0)
+        pm_error("null string where numeric tag value or enumerated tag "
+                 "value name expected");
+    
+    num = strtol(value, &ep, 10);
+    if (*ep != '\0') {
+        /* It's not a numeric string, so it must be an enumerated value name */
+        unsigned int i;
+        for (i = 0; tagvallist[i].name; ++i) {
+            if (STRCASEEQ(value, tagvallist[i].name))
+                return tagvallist[i].value;
+        }
+        pm_error("'%s' is neither a number nor a valid value name", value);
+        return 0; /* avoid compiler warning */
+    } else
+        return num;
+}
+
+
+
+static double
+dnumber(const char * const value) {
+
+    char * ep;
+    double num;
+    
+    num = strtod(value, &ep);
+    if (ep == value || *ep != '\0')
+        pm_error("Bad floating point number %s", value);
+
+    return num;
+}
+
+
+
+#if 0
+static void
+putByte(TIFF *          const tifP,
+        unsigned int    const tagnum,
+        const char *    const value,
+        tagvalmap const tagvallist[]) {
+
+    uint16 const num = number(value, tagvallist);
+
+    TIFFSetField(tifP, tagnum, num);
+}
+#endif
+
+
+
+static void
+putAscii(TIFF *       const tifP,
+         unsigned int const tagnum,
+         const char * const value,
+         tagvalmap    const tagvallist[]) {
+
+    TIFFSetField(tifP, tagnum, value);
+}
+
+
+
+static void
+putShort(TIFF *       const tifP,
+         unsigned     const tagnum,
+         const char * const value,
+         tagvalmap    const tagvallist[]) {
+
+    uint16 const num = number(value, tagvallist);
+
+    TIFFSetField(tifP, tagnum, num);
+}
+
+
+
+static void
+putLong(TIFF *       const tifP,
+        unsigned int const tagnum,
+        const char * const value,
+        tagvalmap    const tagvallist[]) {
+
+    uint32 const num = number(value, tagvallist);
+
+    TIFFSetField(tifP, tagnum, num);
+}
+
+
+
+static void
+putRational(TIFF *       const tifP,
+            unsigned     const tagnum,
+            const char * const value,
+            tagvalmap    const tagvallist[]) {
+
+    float const num = dnumber(value);
+
+    TIFFSetField(tifP, tagnum, num);
+}
+
+
+
+static void
+putCountBytes(TIFF *       const tifP,
+              unsigned     const tagnum,
+              const char * const value,
+              tagvalmap    const tagvallist[]) {
+
+    uint32 const len = strlen(value)/2;
+
+    char * data;
+    uint32 i;
+    bool   error;
+
+    MALLOCARRAY_NOFAIL(data, len);
+
+    for (i = 0, error = FALSE; !error && i < len; ++i) {
+        unsigned int val;
+        int    nb;
+        int    rc;
+        rc = sscanf(&value[i*2], "%2x%n", &val, &nb);
+        if (rc != 1 || nb != 2)
+            error = TRUE;
+        else
+            data[i] = val;
+    }
+    if (!error)
+        TIFFSetField(tifP, tagnum, len, (void *)data);
+
+    free(data);
+}
+
+
+
+#define TV(p,a) { #a, p##a, }
+
+static tagvalmap const 
+tvm_compression[] = {
+    TV(COMPRESSION_,NONE),
+    TV(COMPRESSION_,CCITTRLE),
+    TV(COMPRESSION_,CCITTFAX3),
+    TV(COMPRESSION_,CCITTFAX4),
+    TV(COMPRESSION_,LZW),
+    TV(COMPRESSION_,OJPEG),
+    TV(COMPRESSION_,JPEG),
+    TV(COMPRESSION_,NEXT),
+    TV(COMPRESSION_,CCITTRLEW),
+    TV(COMPRESSION_,PACKBITS),
+    TV(COMPRESSION_,THUNDERSCAN),
+    TV(COMPRESSION_,IT8CTPAD),
+    TV(COMPRESSION_,IT8LW),
+    TV(COMPRESSION_,IT8MP),
+    TV(COMPRESSION_,IT8BL),
+    TV(COMPRESSION_,PIXARFILM),
+    TV(COMPRESSION_,PIXARLOG),
+    TV(COMPRESSION_,DEFLATE),
+    TV(COMPRESSION_,ADOBE_DEFLATE),
+    TV(COMPRESSION_,DCS),
+    TV(COMPRESSION_,JBIG),
+    TV(COMPRESSION_,SGILOG),
+    TV(COMPRESSION_,SGILOG24),
+    { NULL, 0, },
+};
+
+static tagvalmap const
+tvm_faxmode[] = {
+    TV(FAXMODE_,CLASSIC),
+    TV(FAXMODE_,NORTC),
+    TV(FAXMODE_,NOEOL),
+    TV(FAXMODE_,BYTEALIGN),
+    TV(FAXMODE_,WORDALIGN),
+    TV(FAXMODE_,CLASSF),
+    { NULL, 0, },
+};
+
+static tagvalmap const
+tvm_fillorder[] = {
+    TV(FILLORDER_,MSB2LSB),
+    TV(FILLORDER_,LSB2MSB),
+    { NULL, 0, },
+};
+
+static tagvalmap
+const tvm_group3options[] = {
+    TV(GROUP3OPT_,2DENCODING),
+    TV(GROUP3OPT_,UNCOMPRESSED),
+    TV(GROUP3OPT_,FILLBITS),
+    { NULL, 0, },
+};
+
+static tagvalmap
+const tvm_group4options[] = {
+    TV(GROUP4OPT_,UNCOMPRESSED),
+    { NULL, 0, },
+};
+
+static tagvalmap
+const tvm_inkset[] = {
+    TV(INKSET_,CMYK),
+    { NULL, 0, },
+};
+
+static tagvalmap
+const tvm_orientation[] = {
+    TV(ORIENTATION_,TOPLEFT),
+    TV(ORIENTATION_,TOPRIGHT),
+    TV(ORIENTATION_,BOTRIGHT),
+    TV(ORIENTATION_,BOTLEFT),
+    TV(ORIENTATION_,LEFTTOP),
+    TV(ORIENTATION_,RIGHTTOP),
+    TV(ORIENTATION_,RIGHTBOT),
+    TV(ORIENTATION_,LEFTBOT),
+    { NULL, 0, },
+};
+
+static tagvalmap const
+tvm_photometric[] = {
+    TV(PHOTOMETRIC_,MINISWHITE),
+    TV(PHOTOMETRIC_,MINISBLACK),
+    TV(PHOTOMETRIC_,RGB),
+    TV(PHOTOMETRIC_,PALETTE),
+    TV(PHOTOMETRIC_,MASK),
+    TV(PHOTOMETRIC_,SEPARATED),
+    TV(PHOTOMETRIC_,YCBCR),
+    TV(PHOTOMETRIC_,CIELAB),
+    TV(PHOTOMETRIC_,LOGL),
+    TV(PHOTOMETRIC_,LOGLUV),
+    { NULL, 0, },
+};
+
+static tagvalmap const
+tvm_planarconfig[] = {
+    TV(PLANARCONFIG_,CONTIG),
+    TV(PLANARCONFIG_,SEPARATE),
+    { NULL, 0, },
+};
+
+static tagvalmap const
+tvm_resolutionunit[] = {
+    TV(RESUNIT_,NONE),
+    TV(RESUNIT_,INCH),
+    TV(RESUNIT_,CENTIMETER),
+    { NULL, 0, },
+};
+
+static tagvalmap const
+tvm_sampleformat[] = {
+    TV(SAMPLEFORMAT_,UINT),
+    TV(SAMPLEFORMAT_,INT),
+    TV(SAMPLEFORMAT_,IEEEFP),
+    TV(SAMPLEFORMAT_,VOID),
+    { NULL, 0, },
+};
+
+static tagvalmap const
+tvm_subfiletype[] = {
+    TV(FILETYPE_,REDUCEDIMAGE),
+    TV(FILETYPE_,PAGE),
+    TV(FILETYPE_,MASK),
+    { NULL, 0, },
+};
+
+static tagvalmap const
+tvm_threshholding[] =
+{
+    TV(THRESHHOLD_,BILEVEL),
+    TV(THRESHHOLD_,HALFTONE),
+    TV(THRESHHOLD_,ERRORDIFFUSE),
+    { NULL, 0, },
+};
+#undef TV
+
+/*
+ * For full functionality, some tags have an implied count
+ * (eg. colormap, dotrange). See TIFFGetField(3).
+ * Also, some are arrays.
+ */
+#define DECL(a,p,c) { #a, TIFFTAG_##a, p, c, }
+
+static tagDefinition const
+tagDefinitions[] = {
+    DECL(ARTIST,                  putAscii,    NULL),
+    DECL(BADFAXLINES,             putLong,     NULL),
+    DECL(BITSPERSAMPLE,           putShort,    NULL),
+    DECL(CELLLENGTH,              NULL,        NULL),
+    DECL(CELLWIDTH,               NULL,        NULL),
+    DECL(CLEANFAXDATA,            putShort,    NULL),
+    DECL(COLORMAP,                NULL,        NULL),
+    DECL(COLORRESPONSEUNIT,       NULL,        NULL),
+    DECL(COMPRESSION,             putShort,    tvm_compression),
+    DECL(CONSECUTIVEBADFAXLINES,  putLong,     NULL),
+    DECL(COPYRIGHT,               putAscii,    NULL),
+    DECL(DATATYPE,                putShort,    NULL),
+    DECL(DATETIME,                putAscii,    NULL),
+    DECL(DCSBALANCEARRAY,         NULL,        NULL),
+    DECL(DCSCALIBRATIONFD,        NULL,        NULL),
+    DECL(DCSCLIPRECTANGLE,        NULL,        NULL),
+    DECL(DCSCORRECTMATRIX,        NULL,        NULL),
+    DECL(DCSGAMMA,                NULL,        NULL),
+    DECL(DCSHUESHIFTVALUES,       NULL,        NULL),
+    DECL(DCSIMAGERTYPE,           NULL,        NULL),
+    DECL(DCSINTERPMODE,           NULL,        NULL),
+    DECL(DCSTOESHOULDERPTS,       NULL,        NULL),
+    DECL(DOCUMENTNAME,            putAscii,    NULL),
+    DECL(DOTRANGE,                NULL,        NULL),
+    DECL(EXTRASAMPLES,            NULL,        NULL),
+    DECL(FAXFILLFUNC,             NULL,        NULL),
+    DECL(FAXMODE,                 putLong,     tvm_faxmode),
+    DECL(FAXRECVPARAMS,           NULL,        NULL),
+    DECL(FAXRECVTIME,             NULL,        NULL),
+    DECL(FAXSUBADDRESS,           NULL,        NULL),
+    DECL(FEDEX_EDR,               NULL,        NULL),
+    DECL(FILLORDER,               putShort,    tvm_fillorder),
+    DECL(FRAMECOUNT,              NULL,        NULL),
+    DECL(FREEBYTECOUNTS,          NULL,        NULL),
+    DECL(FREEOFFSETS,             NULL,        NULL),
+    DECL(GRAYRESPONSECURVE,       NULL,        NULL),
+    DECL(GRAYRESPONSEUNIT,        NULL,        NULL),
+    DECL(GROUP3OPTIONS,           putLong,     tvm_group3options),
+    DECL(GROUP4OPTIONS,           putLong,     tvm_group4options),
+    DECL(HALFTONEHINTS,           NULL,        NULL),
+    DECL(HOSTCOMPUTER,            putAscii,    NULL),
+    DECL(ICCPROFILE,              putCountBytes, NULL),
+    DECL(IMAGEDEPTH,              putLong,     NULL),
+    DECL(IMAGEDESCRIPTION,        putAscii,    NULL),
+    DECL(IMAGELENGTH,             putLong,     NULL),
+    DECL(IMAGEWIDTH,              putLong,     NULL),
+    DECL(INKNAMES,                putAscii,    NULL),
+    DECL(INKSET,                  putShort,    tvm_inkset),
+    DECL(IT8BITSPEREXTENDEDRUNLENGTH, NULL,    NULL),
+    DECL(IT8BITSPERRUNLENGTH,     NULL,        NULL),
+    DECL(IT8BKGCOLORINDICATOR,    NULL,        NULL),
+    DECL(IT8BKGCOLORVALUE,        NULL,        NULL),
+    DECL(IT8COLORCHARACTERIZATION, NULL,       NULL),
+    DECL(IT8COLORSEQUENCE,        NULL,        NULL),
+    DECL(IT8COLORTABLE,           NULL,        NULL),
+    DECL(IT8HEADER,               NULL,        NULL),
+    DECL(IT8IMAGECOLORINDICATOR,  NULL,        NULL),
+    DECL(IT8IMAGECOLORVALUE,      NULL,        NULL),
+    DECL(IT8PIXELINTENSITYRANGE,  NULL,        NULL),
+    DECL(IT8RASTERPADDING,        NULL,        NULL),
+    DECL(IT8SITE,                 NULL,        NULL),
+    DECL(IT8TRANSPARENCYINDICATOR, NULL,       NULL),
+    DECL(JBIGOPTIONS,             NULL,        NULL),
+    DECL(JPEGACTABLES,            NULL,        NULL),
+    DECL(JPEGCOLORMODE,           NULL,        NULL),
+    DECL(JPEGDCTABLES,            NULL,        NULL),
+    DECL(JPEGIFBYTECOUNT,         NULL,        NULL),
+    DECL(JPEGIFOFFSET,            NULL,        NULL),
+    DECL(JPEGLOSSLESSPREDICTORS,  NULL,        NULL),
+    DECL(JPEGPOINTTRANSFORM,      NULL,        NULL),
+    DECL(JPEGPROC,                NULL,        NULL),
+    DECL(JPEGQTABLES,             NULL,        NULL),
+    DECL(JPEGQUALITY,             NULL,        NULL),
+    DECL(JPEGRESTARTINTERVAL,     NULL,        NULL),
+    DECL(JPEGTABLES,              NULL,        NULL),
+    DECL(JPEGTABLESMODE,          NULL,        NULL),
+    DECL(MAKE,                    putAscii,    NULL),
+    DECL(MATTEING,                putShort,    NULL),
+    DECL(MAXSAMPLEVALUE,          putShort,    NULL),
+    DECL(MINSAMPLEVALUE,          putShort,    NULL),
+    DECL(MODEL,                   putAscii,    NULL),
+    DECL(NUMBEROFINKS,            NULL,        NULL),
+    DECL(ORIENTATION,             putShort,    tvm_orientation),
+    DECL(OSUBFILETYPE,            NULL,        NULL),
+    DECL(PAGENAME,                putAscii,    NULL),
+    DECL(PAGENUMBER,              NULL,        NULL),
+    DECL(PHOTOMETRIC,             putShort,    tvm_photometric),
+    DECL(PHOTOSHOP,               NULL,        NULL),
+    DECL(PIXAR_FOVCOT,            NULL,        NULL),
+    DECL(PIXAR_IMAGEFULLLENGTH,   NULL,        NULL),
+    DECL(PIXAR_IMAGEFULLWIDTH,    NULL,        NULL),
+    DECL(PIXAR_MATRIX_WORLDTOCAMERA, NULL,     NULL),
+    DECL(PIXAR_MATRIX_WORLDTOSCREEN, NULL,     NULL),
+    DECL(PIXAR_TEXTUREFORMAT,     NULL,        NULL),
+    DECL(PIXAR_WRAPMODES,         NULL,        NULL),
+    DECL(PIXARLOGDATAFMT,         NULL,        NULL),
+    DECL(PIXARLOGQUALITY,         NULL,        NULL),
+    DECL(PLANARCONFIG,            putShort,    tvm_planarconfig),
+    DECL(PREDICTOR,               putShort,    NULL),
+    DECL(PRIMARYCHROMATICITIES,   NULL,        NULL),
+    DECL(REFERENCEBLACKWHITE,     NULL,        NULL),
+    DECL(REFPTS,                  NULL,        NULL),
+    DECL(REGIONAFFINE,            NULL,        NULL),
+    DECL(REGIONTACKPOINT,         NULL,        NULL),
+    DECL(REGIONWARPCORNERS,       NULL,        NULL),
+    DECL(RESOLUTIONUNIT,          putShort,    tvm_resolutionunit),
+    DECL(RICHTIFFIPTC,            NULL,        NULL),
+    DECL(ROWSPERSTRIP,            putLong,     NULL),
+    DECL(SAMPLEFORMAT,            putShort,    tvm_sampleformat),
+    DECL(SAMPLESPERPIXEL,         putShort,    NULL),
+    DECL(SGILOGDATAFMT,           NULL,        NULL),
+    DECL(SMAXSAMPLEVALUE,         NULL,        NULL),
+    DECL(SMINSAMPLEVALUE,         NULL,        NULL),
+    DECL(SOFTWARE,                putAscii,    NULL),
+    DECL(STONITS,                 NULL,        NULL),
+    DECL(STRIPBYTECOUNTS,         NULL,        NULL),
+    DECL(STRIPOFFSETS,            NULL,        NULL),
+    DECL(SUBFILETYPE,             putLong,     tvm_subfiletype),
+    DECL(SUBIFD,                  NULL,        NULL),
+    DECL(TARGETPRINTER,           putAscii,    NULL),
+    DECL(THRESHHOLDING,           putShort,    tvm_threshholding),
+    DECL(TILEBYTECOUNTS,          NULL,        NULL),
+    DECL(TILEDEPTH,               putLong,     NULL),
+    DECL(TILELENGTH,              putLong,     NULL),
+    DECL(TILEOFFSETS,             NULL,        NULL),
+    DECL(TILEWIDTH,               putLong,     NULL),
+    DECL(TRANSFERFUNCTION,        NULL,        NULL),
+    DECL(WHITEPOINT,              NULL,        NULL),
+    DECL(WRITERSERIALNUMBER,      NULL,        NULL),
+    DECL(XPOSITION,               putRational, NULL),
+    DECL(XRESOLUTION,             putRational, NULL),
+    DECL(YCBCRCOEFFICIENTS,       NULL,        NULL),
+    DECL(YCBCRPOSITIONING,        NULL,        NULL),
+    DECL(YCBCRSUBSAMPLING,        NULL,        NULL),
+    DECL(YPOSITION,               putRational, NULL),
+    DECL(YRESOLUTION,             putRational, NULL),
+    DECL(ZIPQUALITY,              NULL,        NULL),
+};
+#undef DECL
+
+
+
+const tagDefinition *
+tagDefFind(const char * const name) {
+
+    unsigned int i;
+
+    for (i = 0;
+         i < ARRAY_SIZE(tagDefinitions) && tagDefinitions[i].name;
+         ++i) {
+        if (STRCASEEQ(tagDefinitions[i].name, name))
+            return &tagDefinitions[i];
+    }
+
+    return NULL;
+}
diff --git a/converter/other/tifftopnm.c b/converter/other/tifftopnm.c
new file mode 100644
index 00000000..5969a49a
--- /dev/null
+++ b/converter/other/tifftopnm.c
@@ -0,0 +1,1062 @@
+/*
+** tifftopnm.c - converts a Tagged Image File to a portable anymap
+**
+** Derived by Jef Poskanzer from tif2ras.c, which is:
+**
+** Copyright (c) 1990 by Sun Microsystems, Inc.
+**
+** Author: Patrick J. Naughton
+** naughton@wind.sun.com
+**
+** 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.
+*/
+
+/* Design note:
+
+   We have two different ways of converting from Tiff, as provided by the
+   Tiff library:  
+
+   1) decode the entire image into memory at once, using
+      TIFFRGBAImageGet(), then convert to PNM and output row by row.
+   
+   2) read, convert, and output one row at a time using TIFFReadScanline().
+
+   (1) is preferable because the Tiff library does more of the work, which
+   means it understands more of the Tiff format possibilities now and in 
+   the future.  Also, some compressed TIFF formats don't allow you to 
+   extract an individual row.
+
+   (2) uses far less memory, and because our code does more of the work,
+   it's possible that it can be more flexible or at least give better
+   diagnostic information if there's something wrong with the TIFF.
+
+   In Netpbm, we stress function over performance, so by default we
+   try (1) first, and if we can't get enough memory for the decoded
+   image or TIFFRGBAImageGet() fails, we fall back to (2).  But we
+   give the user the -byrow option to order (2) only.
+*/
+
+#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 "pnm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+
+#ifdef VMS
+#ifdef SYSV
+#undef SYSV
+#endif
+#include <tiffioP.h>
+#endif
+/* See warning about tiffio.h in pamtotiff.c */
+#include <tiffio.h>
+
+/* The following are in current tiff.h, but so that we can compile against
+   older tiff libraries, we define them here.
+*/
+
+#ifndef PHOTOMETRIC_LOGL
+#define PHOTOMETRIC_LOGL 32844
+#endif
+#ifndef PHOTOMETRIC_LOGLUV
+#define PHOTOMETRIC_LOGLUV 32845
+#endif
+
+#define MAXCOLORS 1024
+#ifndef PHOTOMETRIC_DEPTH
+#define PHOTOMETRIC_DEPTH 32768
+#endif
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    char * inputFilename;
+    unsigned int headerdump;
+    char * alphaFilename;
+    bool alphaStdout;
+    unsigned int respectfillorder;   /* -respectfillorder option */
+    unsigned int byrow;
+    unsigned int verbose;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const 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!
+-----------------------------------------------------------------------------*/
+    optStruct3 opt;
+    optEntry *option_def;
+    unsigned int option_def_index;
+    unsigned int alphaSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;
+    opt.allowNegNum = FALSE;
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "verbose", 
+            OPT_FLAG,   NULL, &cmdlineP->verbose,              0);
+    OPTENT3(0, "respectfillorder", 
+            OPT_FLAG,   NULL, &cmdlineP->respectfillorder,     0);
+    OPTENT3(0,   "byrow",   
+            OPT_FLAG,   NULL, &cmdlineP->byrow,                0);
+    OPTENT3('h', "headerdump", 
+            OPT_FLAG,   NULL, &cmdlineP->headerdump,           0);
+    OPTENT3(0,   "alphaout",   
+            OPT_STRING, &cmdlineP->alphaFilename, &alphaSpec,  0);
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+
+    if (argc - 1 == 0)
+        cmdlineP->inputFilename = strdup("-");  /* he wants stdin */
+    else if (argc - 1 == 1)
+        cmdlineP->inputFilename = strdup(argv[1]);
+    else 
+        pm_error("Too many arguments.  The only argument accepted "
+                 "is the input file name");
+
+    if (alphaSpec) {
+        if (STREQ(cmdlineP->alphaFilename, "-"))
+            cmdlineP->alphaStdout = TRUE;
+        else
+            cmdlineP->alphaStdout = FALSE;
+    } else {
+        cmdlineP->alphaFilename = NULL;
+        cmdlineP->alphaStdout = FALSE;
+    }
+}
+
+
+
+static void 
+read_directory(TIFF * const tif,
+               unsigned short * const bps_p,
+               unsigned short * const spp_p,
+               unsigned short * const photomet_p,
+               unsigned short * const planarconfig_p,
+               unsigned short * const fillorder_p,
+               unsigned int *   const cols_p,
+               unsigned int *   const rows_p,
+               bool             const headerdump) {
+/*----------------------------------------------------------------------------
+   Read various values of TIFF tags from the TIFF directory, and
+   default them if not in there and make guesses where values are
+   invalid.  Exit program with error message if required tags aren't
+   there or values are inconsistent or beyond our capabilities.  if
+   'headerdump' is true, issue informational messages about what we
+   find.
+
+   The TIFF library is capable of returning invalid values (if the
+   input file contains invalid values).  We generally return those
+   invalid values to our caller.
+-----------------------------------------------------------------------------*/
+    int rc;
+    unsigned short tiff_bps;
+    unsigned short tiff_spp;
+
+    if (headerdump)
+        TIFFPrintDirectory(tif, stderr, TIFFPRINT_NONE);
+
+    rc = TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &tiff_bps);
+    *bps_p = (rc == 0) ? 1 : tiff_bps;
+
+    if (*bps_p < 1 || (*bps_p > 8 && *bps_p != 16 && *bps_p != 32))
+        pm_error("This program can process Tiff images with only "
+                 "1-8 or 16 bits per sample.  The input Tiff image "
+                 "has %d bits per sample.", *bps_p);
+
+    rc = TIFFGetFieldDefaulted(tif, TIFFTAG_FILLORDER, fillorder_p);
+    rc = TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &tiff_spp);
+    *spp_p = (rc == 0) ? 1: tiff_spp;
+
+    rc = TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, photomet_p);
+    if (rc == 0)
+        pm_error("PHOTOMETRIC tag is not in Tiff file.  "
+                 "TIFFGetField() of it failed.\n"
+                 "This means the input is not valid Tiff.");
+
+    if (*spp_p > 1) {
+        rc = TIFFGetField(tif, TIFFTAG_PLANARCONFIG, planarconfig_p);
+        if (rc == 0)
+            pm_error("PLANARCONFIG tag is not in Tiff file, though it "
+                     "has more than one sample per pixel.  "
+                     "TIFFGetField() of it failed.  This means the input "
+                     "is not valid Tiff.");
+    } else {
+        *planarconfig_p = PLANARCONFIG_CONTIG;
+    }
+
+    switch(*planarconfig_p) {
+    case PLANARCONFIG_CONTIG:
+        break;
+    case PLANARCONFIG_SEPARATE:
+        if (*photomet_p != PHOTOMETRIC_RGB && 
+            *photomet_p != PHOTOMETRIC_SEPARATED)
+            pm_error("This program can handle separate planes only "
+                     "with RGB (PHOTOMETRIC tag = %d) or SEPARATED "
+                     "(PHOTOMETRIC tag = %d) data.  The input Tiff file " 
+                     "has PHOTOMETRIC tag = %d.",
+                     PHOTOMETRIC_RGB, PHOTOMETRIC_SEPARATED, *photomet_p);
+        break;
+    default:
+        pm_error("Unrecognized PLANARCONFIG tag value in Tiff input: %d.\n",
+                 *planarconfig_p);
+    }
+
+    rc = TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, cols_p);
+    if (rc == 0)
+        pm_error("Input Tiff file is invalid.  It has no IMAGEWIDTH tag.");
+    rc = TIFFGetField( tif, TIFFTAG_IMAGELENGTH, rows_p );
+    if (rc == 0)
+        pm_error("Input Tiff file is invalid.  It has no IMAGELENGTH tag.");
+
+    if (headerdump) {
+        pm_message( "%dx%dx%d image", *cols_p, *rows_p, *bps_p * *spp_p );
+        pm_message( "%d bits/sample, %d samples/pixel", *bps_p, *spp_p );
+    }
+}
+
+
+
+static void
+readscanline(TIFF *         const tif, 
+             unsigned char  scanbuf[], 
+             int            const row, 
+             int            const plane,
+             unsigned int   const cols, 
+             unsigned short const bps,
+             unsigned short const spp,
+             unsigned short const fillorder,
+             unsigned int * const samplebuf) {
+/*----------------------------------------------------------------------------
+   Read one scanline out of the Tiff input and store it into samplebuf[].
+   Unlike the scanline returned by the Tiff library function, samplebuf[]
+   is composed of one sample per array element, which makes it easier for
+   our caller to process.
+
+   scanbuf[] is a scratch array for our use, which is big enough to hold
+   a Tiff scanline.
+-----------------------------------------------------------------------------*/
+    int rc;
+    const unsigned int bpsmask = (1 << bps) - 1;
+      /* A mask for taking the lowest 'bps' bits of a number */
+
+    /* The TIFFReadScanline man page doesn't tell the format of its
+       'buf' return value, but it is exactly the same format as the 'buf'
+       input to TIFFWriteScanline.  The man page for that doesn't say 
+       anything either, but the source code for Pnmtotiff contains a
+       specification.
+    */
+
+    rc = TIFFReadScanline(tif, scanbuf, row, plane);
+    if (rc < 0)
+        pm_error( "Unable to read row %d, plane %d of input Tiff image.  "
+                  "TIFFReadScanline() failed.",
+                  row, plane);
+    else if (bps == 8) {
+        int sample;
+        for (sample = 0; sample < cols*spp; sample++) 
+            samplebuf[sample] = scanbuf[sample];
+    } else if (bps < 8) {
+        /* Note that in this format, samples do not span bytes.  Rather,
+           each byte may have don't-care bits in the right-end positions.
+           At least that's how I infer the format from reading pnmtotiff.c
+           -Bryan 00.11.18
+           */
+        int sample;
+        int bitsleft;
+        unsigned char * inP;
+
+        for (sample = 0, bitsleft=8, inP=scanbuf; 
+             sample < cols*spp; 
+             sample++) {
+            if (bitsleft == 0) {
+                ++inP; 
+                bitsleft = 8;
+            } 
+            switch (fillorder) {
+            case FILLORDER_MSB2LSB:
+                samplebuf[sample] = (*inP >> (bitsleft-bps)) & bpsmask; 
+                break;
+            case FILLORDER_LSB2MSB:
+                samplebuf[sample] = (*inP >> (8-bitsleft)) & bpsmask;
+                break;
+            default:
+                pm_error("Internal error: invalid value for fillorder: %u", 
+                         fillorder);
+            }
+            bitsleft -= bps; 
+            if (bitsleft < bps)
+                /* Don't count dregs at end of byte */
+                bitsleft = 0;
+       }
+    } else if (bps == 16) {
+        /* Before Netpbm 9.17, this program assumed that scanbuf[]
+           contained an array of bytes as read from the Tiff file.  In
+           fact, in this bps == 16 case, it's an array of "shorts",
+           each stored in whatever format this platform uses (which is
+           none of our concern).  The pre-9.17 code also presumed that
+           the TIFF "FILLORDER" tag determined the order in which the
+           bytes of each sample appear in a TIFF file, which is
+           contrary to the TIFF spec.  
+        */
+        uint16 * const scanbuf16 = (uint16 *) scanbuf;
+        unsigned int sample;
+
+        for (sample = 0; sample < cols*spp; ++sample)
+            samplebuf[sample] = scanbuf16[sample];
+    } else if (bps == 32) {
+        uint32 * const scanbuf32 = (uint32 *) scanbuf;
+        unsigned int sample;
+        
+        for (sample = 0; sample < cols*spp; ++sample)
+            samplebuf[sample] = scanbuf32[sample];
+    } else 
+        pm_error("Internal error: invalid bits per sample passed to "
+                 "readscanline()");
+}
+
+
+
+static void
+pick_cmyk_pixel(const unsigned int samplebuf[], const int sample_cursor,
+                xelval * const r_p, xelval * const b_p, xelval * const g_p) {
+
+    /* Note that the TIFF spec does not say which of the 4 samples is
+       which, but common sense says they're in order C,M,Y,K.  Before
+       Netpbm 10.21 (March 2004), we assumed C,Y,M,K for some reason.
+       But combined with a compensating error in the CMYK->RGB
+       calculation, it had the same effect as C,M,Y,K.
+    */
+    unsigned int const c = samplebuf[sample_cursor+0];
+    unsigned int const m = samplebuf[sample_cursor+1];
+    unsigned int const y = samplebuf[sample_cursor+2];
+    unsigned int const k = samplebuf[sample_cursor+3];
+
+    /* The CMYK->RGB formula used by TIFFRGBAImageGet() in the TIFF 
+       library is the following, (with some apparent confusion with
+       the names of the yellow and magenta pigments being reversed).
+
+       R = (1-K)*(1-C)     (with C,Y,M,K normalized to 0..1)
+       G = (1-K)*(1-M)
+       B = (1-K)*(1-Y)
+
+       We used that too before Netpbm 10.21 (March 2004).
+
+       Now we use the inverse of what Pnmtotiffcmyk has always used, which
+       makes sense as follows:  A microliter of black ink is simply a 
+       substitute for a microliter each of cyan, magenta, and yellow ink.
+       Yellow ink removes blue light from what the white paper reflects.  
+    */
+
+    *r_p = 255 - MIN(255, c + k);
+    *g_p = 255 - MIN(255, m + k);
+    *b_p = 255 - MIN(255, y + k);
+}
+
+
+
+static void
+computeFillorder(unsigned short   const fillorderTag, 
+                 unsigned short * const fillorderP, 
+                 bool             const respectfillorder) {
+
+    if (respectfillorder) {
+        if (fillorderTag != FILLORDER_MSB2LSB && 
+            fillorderTag != FILLORDER_LSB2MSB)
+            pm_error("Invalid value in Tiff input for the FILLORDER tag: %u.  "
+                     "Valid values are %u and %u.  Try omitting the "
+                     "-respectfillorder option.", 
+                     fillorderTag, FILLORDER_MSB2LSB, FILLORDER_LSB2MSB);
+        else
+            *fillorderP = fillorderTag;
+    } else {
+        *fillorderP = FILLORDER_MSB2LSB;
+        if (fillorderTag != *fillorderP)
+            pm_message("Warning: overriding FILLORDER tag in the tiff input "
+                       "and assuming msb-to-lsb.  Consider the "
+                       "-respectfillorder option.");
+    }
+}
+
+
+
+static void
+analyzeImageType(TIFF *             const tif, 
+                 unsigned short     const bps, 
+                 unsigned short     const spp, 
+                 unsigned short     const photomet,
+                 xelval *           const maxvalP, 
+                 int *              const formatP, 
+                 xel                      colormap[],
+                 bool               const headerdump,
+                 struct cmdlineInfo const cmdline) {
+
+    bool grayscale; 
+
+    if (bps == 1 && spp == 1) {
+        if (cmdline.headerdump)
+            pm_message("bilevel");
+        grayscale = TRUE;
+        *maxvalP = 1;
+    } else {
+        /* How come we don't deal with the photometric for the monochrome 
+           case (make sure it's one we know)?  -Bryan 00.03.04
+        */
+        switch (photomet) {
+        case PHOTOMETRIC_MINISBLACK:
+        case PHOTOMETRIC_MINISWHITE:
+            if (spp != 1)
+                pm_error("This grayscale image has %d samples per pixel.  "
+                         "We understand only 1.", spp);
+            grayscale = TRUE;
+            *maxvalP = pm_bitstomaxval(MIN(bps,16));
+            if (headerdump)
+                pm_message("grayscale image, (min=%s) output maxval %u ", 
+                           photomet == PHOTOMETRIC_MINISBLACK ? 
+                           "black" : "white",
+                           *maxvalP
+                           );
+            break;
+            
+        case PHOTOMETRIC_PALETTE: {
+            int i;
+            int numcolors;
+            unsigned short* redcolormap;
+            unsigned short* greencolormap;
+            unsigned short* bluecolormap;
+
+            if (headerdump)
+                pm_message("colormapped");
+
+            if (spp != 1)
+                pm_error("This paletted image has %d samples per pixel.  "
+                         "We understand only 1.", spp);
+
+            if (!TIFFGetField(tif, TIFFTAG_COLORMAP, 
+                              &redcolormap, &greencolormap, &bluecolormap))
+                pm_error("error getting colormaps");
+
+            numcolors = 1 << bps;
+            if (numcolors > MAXCOLORS)
+                pm_error("too many colors");
+            *maxvalP = PNM_MAXMAXVAL;
+            grayscale = FALSE;
+            for (i = 0; i < numcolors; ++i) {
+                xelval r, g, b;
+                r = (long) redcolormap[i] * PNM_MAXMAXVAL / 65535L;
+                g = (long) greencolormap[i] * PNM_MAXMAXVAL / 65535L;
+                b = (long) bluecolormap[i] * PNM_MAXMAXVAL / 65535L;
+                PPM_ASSIGN(colormap[i], r, g, b);
+            }
+        }
+        break;
+
+        case PHOTOMETRIC_SEPARATED: {
+            unsigned short inkset;
+
+            if (headerdump)
+                pm_message("color separation");
+            if (TIFFGetField(tif, TIFFTAG_INKNAMES, &inkset) == 1
+                && inkset != INKSET_CMYK)
+            if (inkset != INKSET_CMYK) 
+                pm_error("This color separation file uses an inkset (%d) "
+                         "we can't handle.  We handle only CMYK.", inkset);
+            if (spp != 4) 
+                pm_error("This CMYK color separation file is %d samples per "
+                         "pixel.  "
+                         "We need 4 samples, though: C, M, Y, and K.  ",
+                         spp);
+            grayscale = FALSE;
+            *maxvalP = (1 << bps) - 1;
+        }
+        break;
+            
+        case PHOTOMETRIC_RGB:
+            if (headerdump)
+                pm_message("RGB truecolor");
+            grayscale = FALSE;
+
+            if (spp != 3 && spp != 4)
+                pm_error("This RGB image has %d samples per pixel.  "
+                         "We understand only 3 or 4.", spp);
+
+            *maxvalP = (1 << bps) - 1;
+            break;
+
+        case PHOTOMETRIC_MASK:
+            pm_error("don't know how to handle PHOTOMETRIC_MASK");
+
+        case PHOTOMETRIC_DEPTH:
+            pm_error("don't know how to handle PHOTOMETRIC_DEPTH");
+
+        case PHOTOMETRIC_YCBCR:
+            pm_error("don't know how to handle PHOTOMETRIC_YCBCR");
+
+        case PHOTOMETRIC_CIELAB:
+            pm_error("don't know how to handle PHOTOMETRIC_CIELAB");
+
+        case PHOTOMETRIC_LOGL:
+            pm_error("don't know how to handle PHOTOMETRIC_LOGL");
+
+        case PHOTOMETRIC_LOGLUV:
+            pm_error("don't know how to handle PHOTOMETRIC_LOGLUV");
+            
+        default:
+            pm_error("unknown photometric: %d", photomet);
+        }
+    }
+    if (*maxvalP > PNM_OVERALLMAXVAL)
+        pm_error("bits/sample (%d) in the input image is too large.",
+                 bps);
+    if (grayscale) {
+        if (*maxvalP == 1) {
+            *formatP = PBM_TYPE;
+            pm_message("writing PBM file");
+        } else {
+            *formatP = PGM_TYPE;
+            pm_message("writing PGM file");
+        }
+    } else {
+        *formatP = PPM_TYPE;
+        pm_message("writing PPM file");
+    }
+}
+
+
+
+static void
+convertRow(unsigned int   const samplebuf[], 
+           xel                  xelrow[], 
+           gray                 alpharow[], 
+           int            const cols, 
+           xelval         const maxval, 
+           unsigned short const photomet, 
+           unsigned short const spp,
+           xel            const colormap[]) {
+/*----------------------------------------------------------------------------
+   Assuming samplebuf[] is an array of raster values as returned by the Tiff
+   library, convert it to a libnetpbm row in xelrow[] and alpharow[].
+-----------------------------------------------------------------------------*/
+    switch (photomet) {
+    case PHOTOMETRIC_MINISBLACK: {
+        int col;
+        for (col = 0; col < cols; ++col) {
+            PNM_ASSIGN1(xelrow[col], samplebuf[col]);
+            alpharow[col] = 0;
+        }
+    }
+    break;
+    
+    case PHOTOMETRIC_MINISWHITE: {
+        int col;
+        for (col = 0; col < cols; ++col) {
+            PNM_ASSIGN1(xelrow[col], maxval - samplebuf[col]);
+            alpharow[col] = 0;
+        }
+    }
+    break;
+
+    case PHOTOMETRIC_PALETTE: {
+        int col;
+        for ( col = 0; col < cols; ++col ) {
+            /* We know the following array index is in bounds because
+               we filled samplebuf with samples of 'bps' bits each and
+               we verified that the largest number that fits in 'bps'
+               bits is less than MAXCOLORS, the dimension of the array.
+            */
+            xelrow[col] = colormap[samplebuf[col]];
+            alpharow[col] = 0;
+        }
+    }
+    break;
+
+    case PHOTOMETRIC_SEPARATED: {
+        int col, sample;
+        for (col = 0, sample = 0; col < cols; ++col, sample+=spp) {
+            xelval r, g, b;
+            pick_cmyk_pixel(samplebuf, sample, &r, &b, &g);
+            
+            PPM_ASSIGN(xelrow[col], r, g, b);
+            alpharow[col] = 0;
+        }
+    }
+    break;
+
+    case PHOTOMETRIC_RGB: {
+        int col, sample;
+        for (col = 0, sample = 0; col < cols; ++col, sample+=spp) {
+            PPM_ASSIGN(xelrow[col], samplebuf[sample+0],
+                       samplebuf[sample+1], samplebuf[sample+2]);
+            if (spp >= 4)
+                alpharow[col] = samplebuf[sample+3];
+            else
+                alpharow[col] = 0;
+        }
+        break;
+    }       
+    default:
+        pm_error("internal error:  unknown photometric in the picking "
+                 "routine: %d", photomet);
+    }
+}
+
+
+
+static void
+scale32to16(unsigned int       samplebuf[],
+            unsigned int const cols,
+            unsigned int const spp) {
+/*----------------------------------------------------------------------------
+  Convert every sample in samplebuf[] to something that can be expressed
+  in 16 bits, assuming it takes 32 bits now.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    for (i = 0; i < cols * spp; ++i)
+        samplebuf[i] >>= 16; 
+}
+
+
+
+static void
+convertMultiPlaneRow(TIFF *         const tif,
+                     xel                   xelrow[],
+                     gray                  alpharow[],
+                     int             const cols,
+                     xelval          const maxval,
+                     int             const row,
+                     unsigned short  const photomet,
+                     unsigned short  const bps,
+                     unsigned short  const spp,
+                     unsigned short  const fillorder,
+                     unsigned char * const scanbuf,
+                     unsigned int *  const samplebuf) {
+
+    /* The input is in separate planes, so we need to read one
+       scanline for the reds, another for the greens, then another
+       for the blues.
+    */
+
+    int col;
+
+    if (photomet != PHOTOMETRIC_RGB)
+        pm_error("This is a multiple-plane file, but is not an RGB "
+                 "file.  This program does not know how to handle that.");
+    else {
+        /* First, clear the buffer so we can add red, green,
+           and blue one at a time.  
+                */
+        for (col = 0; col < cols; ++col) 
+            PPM_ASSIGN(xelrow[col], 0, 0, 0);
+
+        /* Read the reds */
+        readscanline(tif, scanbuf, row, 0, cols, bps, spp, fillorder, 
+                     samplebuf);
+        if (bps == 32)
+            scale32to16(samplebuf, cols, spp);
+        for (col = 0; col < cols; ++col) 
+            PPM_PUTR(xelrow[col], samplebuf[col]);
+                
+        /* Next the greens */
+        readscanline(tif, scanbuf, row, 1, cols, bps, spp, fillorder,
+                     samplebuf);
+        if (bps == 32)
+            scale32to16(samplebuf, cols, spp);
+        for (col = 0; col < cols; ++col) 
+            PPM_PUTG( xelrow[col], samplebuf[col] );
+            
+        /* And finally the blues */
+        readscanline(tif, scanbuf, row, 2, cols, bps, spp, fillorder,
+                     samplebuf);
+        if (bps == 32)
+            scale32to16(samplebuf, cols, spp);
+        for (col = 0; col < cols; ++col) 
+            PPM_PUTB(xelrow[col], samplebuf[col]);
+
+        /* Could there be an alpha plane?  (We assume no.  But if so,
+           here is where to read it) 
+        */
+        for (col = 0; col < cols; ++col) 
+            alpharow[col] = 0;
+    }
+}
+
+
+
+static void
+convertRasterByRows(FILE *         const imageoutFile, 
+                    FILE *         const alphaFile,
+                    unsigned int   const cols, 
+                    unsigned int   const rows,
+                    xelval         const maxval,
+                    int            const format, 
+                    TIFF *         const tif,
+                    unsigned short const photomet, 
+                    unsigned short const planarconfig,
+                    unsigned short const bps,
+                    unsigned short const spp,
+                    unsigned short const fillorder,
+                    xel                  colormap[]) {
+/*----------------------------------------------------------------------------
+   With the TIFF header all processed (and relevant information from it in 
+   our arguments), write out the TIFF raster to the file images *imageoutFile
+   and *alphaFile.
+
+   Do this one row at a time, employing the TIFF library's
+   TIFFReadScanline.
+-----------------------------------------------------------------------------*/
+    unsigned char * scanbuf;
+        /* Buffer for a raster line in the format returned by TIFF library's
+           TIFFReadScanline
+        */
+    unsigned int * samplebuf;
+        /* Same info as 'scanbuf' above, but with each raster column (sample)
+           represented as single array element, so it's easy to work with.
+        */
+    xel* xelrow;
+        /* The ppm-format row of the image row we are presently converting */
+    gray* alpharow;
+        /* The pgm-format row representing the alpha values for the image 
+           row we are presently converting.
+        */
+
+    int row;
+
+    scanbuf = (unsigned char *) malloc(TIFFScanlineSize(tif));
+    if (scanbuf == NULL)
+        pm_error("can't allocate memory for scanline buffer");
+
+    MALLOCARRAY(samplebuf, cols * spp);
+    if (samplebuf == NULL)
+        pm_error ("can't allocate memory for row buffer");
+
+    xelrow = pnm_allocrow(cols);
+    alpharow = pgm_allocrow(cols);
+
+    for ( row = 0; row < rows; ++row ) {
+        /* Read one row of samples into samplebuf[] */
+
+        if (planarconfig == PLANARCONFIG_CONTIG) {
+            readscanline(tif, scanbuf, row, 0, cols, bps, spp, fillorder, 
+                         samplebuf);
+            if (bps == 32)
+                scale32to16(samplebuf, cols, spp);
+            convertRow(samplebuf, xelrow, alpharow, cols, maxval, 
+                       photomet, spp, colormap);
+        } else 
+            convertMultiPlaneRow(tif, xelrow, alpharow, cols, maxval, row,
+                                 photomet, bps, spp, fillorder,
+                                 scanbuf, samplebuf);
+            
+        if (imageoutFile != NULL) 
+            pnm_writepnmrow( imageoutFile, 
+                             xelrow, cols, (xelval) maxval, format, 0 );
+        if (alphaFile != NULL) 
+            pgm_writepgmrow( alphaFile, alpharow, cols, (gray) maxval, 0);
+    }
+    pgm_freerow(alpharow);
+    pnm_freerow(xelrow);
+
+    free(samplebuf);
+    free(scanbuf);
+}    
+
+
+
+
+static void 
+convertTiffRaster(uint32 *        const raster, 
+                  unsigned int    const cols,
+                  unsigned int    const rows,
+                  xelval          const maxval,
+                  int             const format,
+                  FILE *          const imageoutFile,
+                  FILE *          const alphaFile) {
+/*----------------------------------------------------------------------------
+   Convert the raster 'raster' from the format generated by the TIFF library
+   to PPM (plus PGM alpha mask where applicable) and output it to
+   the files *imageoutFile and *alphaFile in format 'format' with maxval
+   'maxval'.  The raster is 'cols' wide by 'rows' high.
+-----------------------------------------------------------------------------*/
+    xel* xelrow;
+        /* The ppm-format row of the image row we are
+           presently converting 
+        */
+    gray* alpharow;
+        /* The pgm-format row representing the alpha values
+           for the image row we are presently converting.  
+        */
+    int row;
+
+    xelrow = pnm_allocrow(cols);
+    alpharow = pgm_allocrow(cols);
+
+    for (row = 0; row < rows; ++row) {
+        uint32* rp;  
+            /* Address of pixel in 'raster' we are presently converting */
+        int col;
+
+        /* Start at beginning of row: */
+        rp = raster + (rows - row - 1) * cols;
+    
+        for (col = 0; col < cols; ++col) {
+            uint32 const tiffPixel = *rp++;
+                    
+            PPM_ASSIGN(xelrow[col], 
+                       TIFFGetR(tiffPixel) * maxval / 255, 
+                       TIFFGetG(tiffPixel) * maxval / 255, 
+                       TIFFGetB(tiffPixel) * maxval / 255);
+            alpharow[col] = TIFFGetA(tiffPixel) * maxval / 255 ;
+        }
+        
+        if (imageoutFile != NULL) 
+            pnm_writepnmrow(imageoutFile, xelrow, cols, maxval, format, 0);
+        if (alphaFile != NULL) 
+            pgm_writepgmrow(alphaFile, alpharow, cols, maxval, 0);
+    }
+    
+    pgm_freerow(alpharow);
+    pnm_freerow(xelrow);
+}    
+
+
+
+enum convertDisp {CONV_DONE, CONV_OOM, CONV_UNABLE, CONV_FAILED, 
+                  CONV_NOTATTEMPTED};
+
+static void
+convertRasterInMemory(FILE *         const imageoutFile, 
+                      FILE *         const alphaFile,
+                      unsigned int   const cols, 
+                      unsigned int   const rows,
+                      xelval         const maxval,
+                      int            const format, 
+                      TIFF *         const tif,
+                      unsigned short const photomet, 
+                      unsigned short const planarconfig,
+                      unsigned short const bps,
+                      unsigned short const spp,
+                      unsigned short const fillorder,
+                      xel                  colormap[],
+                      enum convertDisp * const statusP) {
+/*----------------------------------------------------------------------------
+   With the TIFF header all processed (and relevant information from it in 
+   our arguments), write out the TIFF raster to the file images *imageoutFile
+   and *alphaFile.
+
+   Do this by reading the entire TIFF image into memory at once and formatting
+   it with the TIFF library's TIFFRGBAImageGet().
+
+   Return *statusP == CONV_OOM iff we are unable to proceed because we cannot
+   get memory to store the entire raster.  This means Caller may still be able
+   to do the conversion using a row-by-row strategy.  Like typical Netpbm
+   programs, we simply abort the program if we are unable to allocate
+   memory for other things.
+-----------------------------------------------------------------------------*/
+    if (rows == 0 || cols == 0) 
+        *statusP = CONV_DONE;
+    else {
+        char emsg[1024] ;
+        int ok;
+        ok = TIFFRGBAImageOK(tif, emsg);
+        if (!ok) {
+            pm_message(emsg);
+            *statusP = CONV_UNABLE;
+        } else {
+            uint32* raster ;
+
+            /* Note that TIFFRGBAImageGet() converts any bits per sample
+               to 8.  Maxval of the raster it returns is always 255.
+            */
+            MALLOCARRAY(raster, cols * rows);
+            if (raster == NULL) {
+                pm_message("Unable to allocate space for a raster of %u "
+                           "pixels.", cols * rows);
+                *statusP = CONV_OOM;
+            } else {
+                int const stopOnErrorFalse = FALSE;
+                TIFFRGBAImage img ;
+                int ok;
+                
+                ok = TIFFRGBAImageBegin(&img, tif, stopOnErrorFalse, emsg) ;
+                if (!ok) {
+                    pm_message(emsg);
+                    *statusP = CONV_FAILED;
+                } else {
+                    int ok;
+                    ok = TIFFRGBAImageGet(&img, raster, cols, rows);
+                    TIFFRGBAImageEnd(&img) ;
+                    if (!ok) {
+                        pm_message(emsg);
+                        *statusP = CONV_FAILED;
+                    } else {
+                        *statusP = CONV_DONE;
+                        convertTiffRaster(raster, cols, rows, maxval, format, 
+                                          imageoutFile, alphaFile);
+                    }
+                } 
+                free(raster);
+            }
+        }
+    }
+}
+
+
+
+static void
+convertImage(TIFF *             const tifP,
+             FILE *             const alphaFile, 
+             FILE *             const imageoutFile,
+             struct cmdlineInfo const cmdline) {
+
+    unsigned int cols, rows;
+    int format;
+    xelval maxval;
+    unsigned short bps, spp;
+
+    xel colormap[MAXCOLORS];
+    unsigned short photomet, planarconfig, fillorderTag;
+    unsigned short fillorder;
+
+    read_directory(tifP, &bps, &spp, &photomet, &planarconfig, &fillorderTag,
+                   &cols, &rows, 
+                   cmdline.headerdump);
+
+
+    computeFillorder(fillorderTag, &fillorder, cmdline.respectfillorder);
+
+    analyzeImageType(tifP, bps, spp, photomet, 
+                     &maxval, &format, colormap, cmdline.headerdump, cmdline);
+
+    if (imageoutFile != NULL) 
+        pnm_writepnminit( imageoutFile, 
+                          cols, rows, (xelval) maxval, format, 0 );
+    if (alphaFile != NULL) 
+        pgm_writepgminit( alphaFile, cols, rows, (gray) maxval, 0 );
+
+    {
+        enum convertDisp status;
+        if (cmdline.byrow)
+            status = CONV_NOTATTEMPTED;
+        else {
+            convertRasterInMemory(
+                imageoutFile, alphaFile, cols, rows, maxval, format, 
+                tifP, photomet, planarconfig, bps, spp, fillorder,
+                colormap, &status);
+        }
+        if (status == CONV_DONE) {
+            if (bps > 8)
+                pm_message("actual resolution has been reduced to 24 bits "
+                           "per pixel in the conversion.  You can get the "
+                           "full %u bits that are in the TIFF with the "
+                           "-byrow option.", bps);
+        } else {
+            if (status != CONV_NOTATTEMPTED)
+                pm_message("In-memory conversion failed; "
+                           "using more primitive row-by-row conversion.");
+            
+            convertRasterByRows(
+                imageoutFile, alphaFile, cols, rows, maxval, format, 
+                tifP, photomet, planarconfig, bps, spp, fillorder, colormap);
+        }
+    }
+}
+
+
+
+static void
+convertIt(TIFF *             const tifP,
+          FILE *             const alphaFile, 
+          FILE *             const imageoutFile,
+          struct cmdlineInfo const cmdline) {
+
+    unsigned int imageSeq;
+    bool eof;
+
+    imageSeq = 0;
+    eof = FALSE;
+
+    while (!eof) {
+        bool success;
+
+        if (cmdline.verbose)
+            pm_message("Converting Image %u", imageSeq);
+        convertImage(tifP, alphaFile, imageoutFile, cmdline);
+        success = TIFFReadDirectory(tifP);
+        eof = !success;
+        ++imageSeq;
+    }
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    struct cmdlineInfo cmdline;
+    TIFF * tif;
+    FILE * alphaFile;
+    FILE * imageoutFile;
+
+    pnm_init( &argc, argv );
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    if (!STREQ(cmdline.inputFilename, "-")) {
+        tif = TIFFOpen(cmdline.inputFilename, "r");
+        if (tif == NULL)
+            pm_error("error opening TIFF file %s", cmdline.inputFilename);
+    } else {
+        tif = TIFFFdOpen(0, "Standard Input", "r");
+        if (tif == NULL)
+            pm_error("error opening standard input as TIFF file");
+    }
+
+    if (cmdline.alphaStdout)
+        alphaFile = stdout;
+    else if (cmdline.alphaFilename == NULL) 
+        alphaFile = NULL;
+    else
+        alphaFile = pm_openw(cmdline.alphaFilename);
+
+    if (cmdline.alphaStdout) 
+        imageoutFile = NULL;
+    else
+        imageoutFile = stdout;
+
+    convertIt(tif, alphaFile, imageoutFile, cmdline);
+
+    if (imageoutFile != NULL) 
+        pm_close( imageoutFile );
+    if (alphaFile != NULL)
+        pm_close( alphaFile );
+
+    strfree(cmdline.inputFilename);
+
+    /* If the program failed, it previously aborted with nonzero completion
+       code, via various function calls.
+    */
+    return 0;
+}
diff --git a/converter/other/x10wd.h b/converter/other/x10wd.h
new file mode 100644
index 00000000..3c4fb8ca
--- /dev/null
+++ b/converter/other/x10wd.h
@@ -0,0 +1,32 @@
+/* x10wd.h - the following defs are taken from various X10 header files
+*/
+
+#ifndef X10WD_H_INCLUDED
+#define X10WD_H_INCLUDED
+
+#define XYFormat 0
+#define ZFormat 1
+
+#define X10WD_FILE_VERSION 6
+typedef struct {
+    int header_size;		/* Size of the entire file header (bytes). */
+    int file_version;		/* X10WD_FILE_VERSION */
+    int display_type;		/* Display type. */
+    int display_planes;		/* Number of display planes. */
+    int pixmap_format;		/* Pixmap format. */
+    int pixmap_width;		/* Pixmap width. */
+    int pixmap_height;		/* Pixmap height. */
+    short window_width;		/* Window width. */
+    short window_height;	/* Window height. */
+    short window_x;		/* Window upper left X coordinate. */
+    short window_y;		/* Window upper left Y coordinate. */
+    short window_bdrwidth;	/* Window border width. */
+    short window_ncolors;	/* number of Color entries in this window */
+    } X10WDFileHeader;
+
+typedef struct {
+    int pixel;
+    unsigned short red, green, blue;
+    } X10Color;
+
+#endif
diff --git a/converter/other/x11wd.h b/converter/other/x11wd.h
new file mode 100644
index 00000000..711248f5
--- /dev/null
+++ b/converter/other/x11wd.h
@@ -0,0 +1,82 @@
+/* x11wd.h - the following defs are taken from various X.V11R2 header files
+*/
+
+#ifndef X11WD_H_INCLUDED
+#define X11WD_H_INCLUDED
+
+enum byteorder {LSBFirst=0, MSBFirst=1};
+/* They used to be defined wrongly as macros:  2000.05.08 -Bryan
+
+#define LSBFirst    0
+#define MSBFirst    1
+*/
+
+#define XYBitmap    0
+#define XYPixmap    1
+#define ZPixmap     2
+
+enum visualclass {StaticGray=0,GrayScale=1,StaticColor=2,PseudoColor=3,
+                  TrueColor=4, DirectColor=5};
+
+/* They used to be defined wrongly as macros:  2000.05.08 -Bryan
+#define StaticGray  0
+#define GrayScale   1
+#define StaticColor 2
+#define PseudoColor 3
+#define TrueColor   4
+#define DirectColor 5
+*/
+
+typedef uint32n xwdval;
+#define XWDVAL_MAX ((xwdval)(-1))
+#define X11WD_FILE_VERSION 7
+typedef struct {
+    xwdval header_size;     /* Size of the entire file header (bytes). */
+    xwdval file_version;    /* X11WD_FILE_VERSION */
+    xwdval pixmap_format;   /* Pixmap format */
+    xwdval pixmap_depth;    /* Pixmap depth */
+    xwdval pixmap_width;    /* Pixmap width */
+    xwdval pixmap_height;   /* Pixmap height */
+    xwdval xoffset;     /* Bitmap x offset */
+    xwdval byte_order;      /* MSBFirst, LSBFirst */
+    xwdval bitmap_unit;     /* Bitmap unit */
+    xwdval bitmap_bit_order;    /* MSBFirst, LSBFirst */
+    xwdval bitmap_pad;      /* Bitmap scanline pad */
+    xwdval bits_per_pixel;  /* Bits per pixel */
+    xwdval bytes_per_line;  /* Bytes per scanline */
+    xwdval visual_class;    /* Class of colormap */
+    xwdval red_mask;        /* Z red mask */
+    xwdval green_mask;      /* Z green mask */
+    xwdval blue_mask;       /* Z blue mask */
+    xwdval bits_per_rgb;    /* Log base 2 of distinct color values */
+    xwdval colormap_entries;
+        /* I have no idea what this is.  An old comment says "number of
+           entries in colormap," but readers seem to use 'ncolors' for that
+           instead.  That's how Pnmtoxwd sets ncolors, and is how Xwdtopnm
+           interprets it.  Xwdtopnm doesn't even look at 'colormap_entries'.
+
+           This could be an old mistake; maybe colormap_entries was 
+           originally the number of entries in the colormap, and ncolors
+           was the number of distinct colors in the image (which might be
+           less than colormap_entries or, for direct color, could be much
+           larger).
+        */
+    xwdval ncolors;
+        /* Number of entries in the color map (for direct color, it's the
+           number of entries in each of them).  See 'colormap_entries'. 
+        */
+    xwdval window_width;    /* Window width */
+    xwdval window_height;   /* Window height */
+    int32n window_x;        /* Window upper left X coordinate */
+    int32n window_y;        /* Window upper left Y coordinate */
+    xwdval window_bdrwidth; /* Window border width */
+    } X11WDFileHeader;
+
+typedef struct {
+    uint32n num;
+    unsigned short red, green, blue;
+    char flags;         /* do_red, do_green, do_blue */
+    char pad;
+    } X11XColor;
+
+#endif
diff --git a/converter/other/xwdtopnm.c b/converter/other/xwdtopnm.c
new file mode 100644
index 00000000..28c38cfc
--- /dev/null
+++ b/converter/other/xwdtopnm.c
@@ -0,0 +1,1280 @@
+/* xwdtopnm.c - read an X11 or X10 window dump file and write a PNM image.
+**
+** 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.
+*/
+
+/* IMPLEMENTATION NOTES:
+
+   The file X11/XWDFile.h from the X Window System is an authority for the
+   format of an XWD file.  Netpbm uses its own declaration, though.
+*/
+
+
+#define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "pm_c_util.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "pnm.h"
+#include "x10wd.h"
+#include "x11wd.h"
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    char *input_filename;
+    unsigned int verbose;
+    unsigned int debug;
+    unsigned int headerdump;
+};
+
+
+static bool debug;
+static bool verbose;
+
+#ifdef DEBUG_PIXEL
+static unsigned int pixel_count = 0;
+#endif
+
+/* Byte-swapping junk. */
+
+static int
+zero_bits(const unsigned long mask) {
+/*----------------------------------------------------------------------------
+   Return the number of consective zero bits at the least significant end
+   of the binary representation of 'mask'.  E.g. if mask == 0x00fff800,
+   we would return 11.
+-----------------------------------------------------------------------------*/
+    int i;
+    unsigned long shifted_mask;
+
+    for (i=0, shifted_mask = mask; 
+         i < sizeof(mask)*8 && (shifted_mask & 0x00000001) == 0;
+         i++, shifted_mask >>= 1 );
+    return(i);
+}
+
+
+
+static int
+one_bits(const unsigned long input) {
+/*----------------------------------------------------------------------------
+   Return the number of one bits in the binary representation of 'input'.
+-----------------------------------------------------------------------------*/
+    int one_bits;
+    unsigned long mask;
+
+    one_bits = 0;   /* initial value */
+    for (mask = 0x00000001; mask != 0x00000000; mask <<= 1)
+        if (input & mask) one_bits++;
+
+    return(one_bits);
+}
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   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;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+
+    OPTENT3(0,   "verbose",    OPT_FLAG,   NULL, &cmdlineP->verbose,       0);
+    OPTENT3(0,   "debug",      OPT_FLAG,   NULL, &cmdlineP->debug,         0);
+    OPTENT3(0,   "headerdump", OPT_FLAG,   NULL, &cmdlineP->headerdump,    0);
+
+    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->input_filename = NULL;  /* he wants stdin */
+    else if (argc - 1 == 1) {
+        if (STREQ(argv[1], "-"))
+            cmdlineP->input_filename = NULL;  /* he wants stdin */
+        else 
+            cmdlineP->input_filename = strdup(argv[1]);
+    } else 
+        pm_error("Too many arguments.  The only argument accepted\n"
+                 "is the input file specification");
+}
+
+
+
+static void
+processX10Header(X10WDFileHeader *  const h10P, 
+                 FILE *             const file,
+                 int *              const colsP, 
+                 int *              const rowsP, 
+                 int *              const padrightP, 
+                 xelval *           const maxvalP, 
+                 enum visualclass * const visualclassP, 
+                 int *              const formatP, 
+                 xel **             const colorsP, 
+                 int *              const bits_per_pixelP, 
+                 int *              const bits_per_itemP, 
+                 unsigned long *    const red_maskP, 
+                 unsigned long *    const green_maskP, 
+                 unsigned long *    const blue_maskP,
+                 enum byteorder *   const byte_orderP,
+                 enum byteorder *   const bit_orderP) {
+
+    int i;
+    X10Color* x10colors;
+    bool grayscale;
+    bool byte_swap;
+
+    *maxvalP = 65535;   /* Initial assumption */
+
+    if ( h10P->file_version != X10WD_FILE_VERSION ) {
+        byte_swap = TRUE;
+        h10P->header_size     = pm_bs_long(h10P->header_size);
+        h10P->file_version    = pm_bs_long(h10P->file_version);
+        h10P->display_type    = pm_bs_long(h10P->display_type);
+        h10P->display_planes  = pm_bs_long(h10P->display_planes);
+        h10P->pixmap_format   = pm_bs_long(h10P->pixmap_format);
+        h10P->pixmap_width    = pm_bs_long(h10P->pixmap_width);
+        h10P->pixmap_height   = pm_bs_long(h10P->pixmap_height);
+        h10P->window_width    = pm_bs_short(h10P->window_width);
+        h10P->window_height   = pm_bs_short(h10P->window_height);
+        h10P->window_x        = pm_bs_short(h10P->window_x);
+        h10P->window_y        = pm_bs_short(h10P->window_y);
+        h10P->window_bdrwidth = pm_bs_short(h10P->window_bdrwidth);
+        h10P->window_ncolors  = pm_bs_short(h10P->window_ncolors);
+    } else
+        byte_swap = FALSE;
+
+    for ( i = 0; i < h10P->header_size - sizeof(*h10P); ++i )
+        if ( getc( file ) == EOF )
+            pm_error( "couldn't read rest of X10 XWD file header" );
+
+    /* Check whether we can handle this dump. */
+    if ( h10P->window_ncolors > 256 )
+        pm_error( "can't handle X10 window_ncolors > %d", 256 );
+    if ( h10P->pixmap_format != ZFormat && h10P->display_planes != 1 )
+        pm_error(
+            "can't handle X10 pixmap_format %d with planes != 1",
+            h10P->pixmap_format );
+
+    grayscale = TRUE;  /* initial assumption */
+    if ( h10P->window_ncolors != 0 ) {
+        /* Read X10 colormap. */
+        MALLOCARRAY( x10colors, h10P->window_ncolors );
+        if ( x10colors == NULL )
+            pm_error( "out of memory" );
+        for ( i = 0; i < h10P->window_ncolors; ++i ) {
+            if ( fread( &x10colors[i], sizeof(X10Color), 1, file ) != 1 )
+                pm_error( "couldn't read X10 XWD colormap" );
+            if ( byte_swap ) {
+                x10colors[i].red   = pm_bs_short(x10colors[i].red);
+                x10colors[i].green = pm_bs_short(x10colors[i].green);
+                x10colors[i].blue  = pm_bs_short(x10colors[i].blue);
+            }
+            if ( x10colors[i].red != x10colors[i].green ||
+                 x10colors[i].green != x10colors[i].blue )
+                grayscale = FALSE;
+        }
+    }
+
+    if ( h10P->display_planes == 1 ) {
+        *formatP = PBM_TYPE;
+        *visualclassP = StaticGray;
+        *maxvalP = 1;
+        *colorsP = pnm_allocrow( 2 );
+        PNM_ASSIGN1( (*colorsP)[0], 0 );
+        PNM_ASSIGN1( (*colorsP)[1], *maxvalP );
+        *padrightP =
+            ( ( h10P->pixmap_width + 15 ) / 16 ) * 16 - h10P->pixmap_width;
+        *bits_per_itemP = 16;
+        *bits_per_pixelP = 1;
+    } else if ( h10P->window_ncolors == 0 ) { 
+        /* Must be grayscale. */
+        *formatP = PGM_TYPE;
+        *visualclassP = StaticGray;
+        *maxvalP = ( 1 << h10P->display_planes ) - 1;
+        *colorsP = pnm_allocrow( *maxvalP + 1 );
+        for ( i = 0; i <= *maxvalP; ++i )
+            PNM_ASSIGN1( (*colorsP)[i], i );
+        *padrightP =
+            ( ( h10P->pixmap_width + 15 ) / 16 ) * 16 - h10P->pixmap_width;
+        *bits_per_itemP = 16;
+        *bits_per_pixelP = 1;
+    } else {
+        *colorsP = pnm_allocrow( h10P->window_ncolors );
+        *visualclassP = PseudoColor;
+        if ( grayscale ) {
+            *formatP = PGM_TYPE;
+            for ( i = 0; i < h10P->window_ncolors; ++i )
+                PNM_ASSIGN1( (*colorsP)[i], x10colors[i].red );
+        } else {
+            *formatP = PPM_TYPE;
+            for ( i = 0; i < h10P->window_ncolors; ++i )
+                PPM_ASSIGN(
+                    (*colorsP)[i], x10colors[i].red, x10colors[i].green,
+                    x10colors[i].blue);
+        }
+
+        *padrightP = h10P->pixmap_width & 1;
+        *bits_per_itemP = 8;
+        *bits_per_pixelP = 8;
+    }
+    *colsP = h10P->pixmap_width;
+    *rowsP = h10P->pixmap_height;
+    *byte_orderP = MSBFirst;
+    *bit_orderP = LSBFirst;
+}
+
+
+
+static void
+fixH11ByteOrder(X11WDFileHeader *  const h11P,
+                X11WDFileHeader ** const h11FixedPP) {
+
+    X11WDFileHeader * h11FixedP;
+
+    MALLOCVAR_NOFAIL(h11FixedP);
+
+    if (h11P->file_version == X11WD_FILE_VERSION) {
+        memcpy(h11FixedP, h11P, sizeof(*h11FixedP));
+    } else {
+        h11FixedP->header_size      = pm_bs_long(h11P->header_size);
+        h11FixedP->file_version     = pm_bs_long(h11P->file_version);
+        h11FixedP->pixmap_format    = pm_bs_long(h11P->pixmap_format);
+        h11FixedP->pixmap_depth     = pm_bs_long(h11P->pixmap_depth);
+        h11FixedP->pixmap_width     = pm_bs_long(h11P->pixmap_width);
+        h11FixedP->pixmap_height    = pm_bs_long(h11P->pixmap_height);
+        h11FixedP->xoffset          = pm_bs_long(h11P->xoffset);
+        h11FixedP->byte_order       = pm_bs_long(h11P->byte_order);
+        h11FixedP->bitmap_unit      = pm_bs_long(h11P->bitmap_unit);
+        h11FixedP->bitmap_bit_order = pm_bs_long(h11P->bitmap_bit_order);
+        h11FixedP->bitmap_pad       = pm_bs_long(h11P->bitmap_pad);
+        h11FixedP->bits_per_pixel   = pm_bs_long(h11P->bits_per_pixel);
+        h11FixedP->bytes_per_line   = pm_bs_long(h11P->bytes_per_line);
+        h11FixedP->visual_class     = pm_bs_long(h11P->visual_class);
+        h11FixedP->red_mask         = pm_bs_long(h11P->red_mask);
+        h11FixedP->green_mask       = pm_bs_long(h11P->green_mask);
+        h11FixedP->blue_mask        = pm_bs_long(h11P->blue_mask);
+        h11FixedP->bits_per_rgb     = pm_bs_long(h11P->bits_per_rgb);
+        h11FixedP->colormap_entries = pm_bs_long(h11P->colormap_entries);
+        h11FixedP->ncolors          = pm_bs_long(h11P->ncolors);
+        h11FixedP->window_width     = pm_bs_long(h11P->window_width);
+        h11FixedP->window_height    = pm_bs_long(h11P->window_height);
+        h11FixedP->window_x         = pm_bs_long(h11P->window_x);
+        h11FixedP->window_y         = pm_bs_long(h11P->window_y);
+        h11FixedP->window_bdrwidth  = pm_bs_long(h11P->window_bdrwidth);
+    }
+    *h11FixedPP = h11FixedP;
+}
+
+
+
+static void
+readX11Colormap(FILE *      const file,
+                int         const ncolors, 
+                bool        const byteSwap,
+                X11XColor** const x11colorsP) {
+                
+    X11XColor * x11colors;
+    int rc;
+
+    /* Read X11 colormap. */
+    MALLOCARRAY(x11colors, ncolors);
+    if (x11colors == NULL)
+        pm_error("out of memory");
+    rc = fread(x11colors, sizeof(x11colors[0]), ncolors, file);
+    if (rc != ncolors)
+        pm_error("couldn't read X11 XWD colormap");
+    if (byteSwap) {
+        unsigned int i;
+        for (i = 0; i < ncolors; ++i) {
+            x11colors[i].red   = pm_bs_short(x11colors[i].red);
+            x11colors[i].green = pm_bs_short(x11colors[i].green);
+            x11colors[i].blue  = pm_bs_short(x11colors[i].blue);
+        }
+    }
+    if (debug) {
+        unsigned int i;
+        for (i = 0; i < ncolors && i < 8; ++i)
+            pm_message("Color %d r/g/b = %d/%d/%d", i, 
+                       x11colors[i].red, x11colors[i].green, 
+                       x11colors[i].blue);
+    }
+    *x11colorsP = x11colors;
+}
+
+
+
+static bool
+colormapAllGray(const X11XColor* const x11colors,
+                unsigned int     const ncolors) {
+
+    unsigned int i;
+    bool grayscale;
+
+    grayscale = TRUE;  /* initial assumption */
+    for (i = 0; i < ncolors; ++i) 
+        if (x11colors[i].red != x11colors[i].green ||
+            x11colors[i].green != x11colors[i].blue )
+            grayscale = FALSE;
+
+    return grayscale;
+}
+
+
+
+static void
+dumpX11Header(X11WDFileHeader * const h11P) {
+
+    const char * formatDesc;
+    const char * byteOrderDesc;
+    const char * bitOrderDesc;
+    const char * visualClassDesc;
+
+    X11WDFileHeader * h11FixedP;
+
+    fixH11ByteOrder(h11P, &h11FixedP);
+ 
+    switch(h11FixedP->pixmap_format) {
+    case XYBitmap: formatDesc = "XY bit map"; break;
+    case XYPixmap: formatDesc = "XY pix map"; break;
+    case ZPixmap:  formatDesc = "Z pix map";  break;
+    default:       formatDesc = "???";
+    }
+
+    switch(h11FixedP->byte_order) {
+    case LSBFirst: byteOrderDesc = "LSB first"; break;
+    case MSBFirst: byteOrderDesc = "MSB first"; break;
+    default:       byteOrderDesc = "???";
+    }
+
+    switch (h11FixedP->bitmap_bit_order) {
+    case LSBFirst: bitOrderDesc = "LSB first"; break;
+    case MSBFirst: bitOrderDesc = "MSB first"; break;
+    default:       bitOrderDesc = "???";
+    }
+
+    switch (h11FixedP->visual_class) {
+    case StaticGray:  visualClassDesc = "StaticGray";  break;
+    case GrayScale:   visualClassDesc = "GrayScale";   break;
+    case StaticColor: visualClassDesc = "StaticColor"; break;
+    case PseudoColor: visualClassDesc = "PseudoColor"; break;
+    case TrueColor:   visualClassDesc = "TrueColor"; break;
+    case DirectColor: visualClassDesc = "DirectColor"; break;
+    default:          visualClassDesc = "???";
+    }
+
+    pm_message("File version: %u", h11FixedP->file_version);
+    pm_message("Format: %s (%u)", formatDesc, h11FixedP->pixmap_format);
+    pm_message("Width:  %u", h11FixedP->pixmap_width);
+    pm_message("Height: %u", h11FixedP->pixmap_height);
+    pm_message("Depth: %u bits", h11FixedP->pixmap_depth);
+    pm_message("X offset: %u", h11FixedP->xoffset);
+    pm_message("byte order: %s (%u)", byteOrderDesc, h11FixedP->byte_order);
+    pm_message("bitmap unit: %u", h11FixedP->bitmap_unit);
+    pm_message("bit order: %s (%u)", 
+               bitOrderDesc, h11FixedP->bitmap_bit_order);
+    pm_message("bitmap pad: %u", h11FixedP->bitmap_pad);
+    pm_message("bits per pixel: %u", h11FixedP->bits_per_pixel);
+    pm_message("bytes per line: %u", h11FixedP->bytes_per_line);
+    pm_message("visual class: %s (%u)", 
+               visualClassDesc, h11FixedP->visual_class);
+    pm_message("red mask:   %08x", h11FixedP->red_mask);
+    pm_message("green mask: %08x", h11FixedP->green_mask);
+    pm_message("blue mask:  %08x", h11FixedP->blue_mask);
+    pm_message("bits per rgb: %u", h11FixedP->bits_per_rgb);
+    pm_message("number of colormap entries: %u", h11FixedP->colormap_entries);
+    pm_message("number of colors in colormap: %u", h11FixedP->ncolors);
+    pm_message("window width:  %u", h11FixedP->window_width);
+    pm_message("window height: %u", h11FixedP->window_height);
+    pm_message("window upper left X coordinate: %u", h11FixedP->window_x);
+    pm_message("window upper left Y coordinate: %u", h11FixedP->window_y);
+    pm_message("window border width: %u", h11FixedP->window_bdrwidth);
+
+    free(h11FixedP);
+}
+
+
+
+static void
+processX11Header(X11WDFileHeader *  const h11P, 
+                 FILE *             const file,
+                 int *              const colsP, 
+                 int *              const rowsP, 
+                 int *              const padrightP, 
+                 xelval *           const maxvalP, 
+                 enum visualclass * const visualclassP, 
+                 int *              const formatP, 
+                 xel **             const colorsP, 
+                 int *              const bits_per_pixelP, 
+                 int *              const bits_per_itemP, 
+                 unsigned long *    const red_maskP, 
+                 unsigned long *    const green_maskP, 
+                 unsigned long *    const blue_maskP,
+                 enum byteorder *   const byte_orderP,
+                 enum byteorder *   const bit_orderP) {
+
+    int i;
+    X11XColor* x11colors;
+    bool grayscale;
+    bool const byte_swap = (h11P->file_version != X11WD_FILE_VERSION);
+    X11WDFileHeader * h11FixedP;
+ 
+    fixH11ByteOrder(h11P, &h11FixedP);
+
+    if (byte_swap && verbose)
+        pm_message("Header is different endianness from this machine.");
+    
+    for (i = 0; i < h11FixedP->header_size - sizeof(*h11FixedP); ++i)
+        if (getc(file) == EOF)
+            pm_error("couldn't read rest of X11 XWD file header");
+
+    /* Check whether we can handle this dump. */
+    if ( h11FixedP->pixmap_depth > 24 )
+        pm_error( "can't handle X11 pixmap_depth > 24" );
+    if ( h11FixedP->bits_per_rgb > 24 )
+        pm_error( "can't handle X11 bits_per_rgb > 24" );
+    if ( h11FixedP->pixmap_format != ZPixmap && h11FixedP->pixmap_depth != 1 )
+        pm_error(
+            "can't handle X11 pixmap_format %d with depth != 1",
+            h11FixedP->pixmap_format );
+    if ( h11FixedP->bitmap_unit != 8 && h11FixedP->bitmap_unit != 16 &&
+         h11FixedP->bitmap_unit != 32 )
+        pm_error(
+            "X11 bitmap_unit (%d) is non-standard - can't handle",
+            h11FixedP->bitmap_unit );
+    /* The following check was added in 10.19 (November 2003) */
+    if ( h11FixedP->bitmap_pad != 8 && h11FixedP->bitmap_pad != 16 &&
+         h11FixedP->bitmap_pad != 32 )
+        pm_error(
+            "X11 bitmap_pad (%d) is non-standard - can't handle",
+            h11FixedP->bitmap_unit );
+
+    if ( h11FixedP->ncolors > 0 ) {
+        readX11Colormap( file, h11FixedP->ncolors, byte_swap, &x11colors );
+        grayscale = colormapAllGray( x11colors, h11FixedP->ncolors );
+    } else
+        grayscale = TRUE;
+
+    *visualclassP = (enum visualclass) h11FixedP->visual_class;
+    if ( *visualclassP == DirectColor ) {
+        unsigned int i;
+        *formatP = PPM_TYPE;
+        *maxvalP = 65535;
+        /*
+          DirectColor is like PseudoColor except that there are essentially
+          3 colormaps (shade maps) -- one for each color component.  Each pixel
+          is composed of 3 separate indices.
+        */
+
+        *colorsP = pnm_allocrow( h11FixedP->ncolors );
+        for ( i = 0; i < h11FixedP->ncolors; ++i )
+            PPM_ASSIGN(
+                (*colorsP)[i], x11colors[i].red, x11colors[i].green,
+                x11colors[i].blue);
+    } else if ( *visualclassP == TrueColor ) {
+        *formatP = PPM_TYPE;
+
+        *maxvalP = pm_lcm(pm_bitstomaxval(one_bits(h11FixedP->red_mask)),
+                          pm_bitstomaxval(one_bits(h11FixedP->green_mask)),
+                          pm_bitstomaxval(one_bits(h11FixedP->blue_mask)),
+                          PPM_OVERALLMAXVAL
+            );
+    }
+    else if ( *visualclassP == StaticGray && h11FixedP->bits_per_pixel == 1 ) {
+        *formatP = PBM_TYPE;
+        *maxvalP = 1;
+        *colorsP = pnm_allocrow( 2 );
+        PNM_ASSIGN1( (*colorsP)[0], *maxvalP );
+        PNM_ASSIGN1( (*colorsP)[1], 0 );
+    } else if ( *visualclassP == StaticGray ) {
+        unsigned int i;
+        *formatP = PGM_TYPE;
+        *maxvalP = ( 1 << h11FixedP->bits_per_pixel ) - 1;
+        *colorsP = pnm_allocrow( *maxvalP + 1 );
+        for ( i = 0; i <= *maxvalP; ++i )
+            PNM_ASSIGN1( (*colorsP)[i], i );
+    } else {
+        *colorsP = pnm_allocrow( h11FixedP->ncolors );
+        if ( grayscale ) {
+            unsigned int i;
+            *formatP = PGM_TYPE;
+            for ( i = 0; i < h11FixedP->ncolors; ++i )
+                PNM_ASSIGN1( (*colorsP)[i], x11colors[i].red );
+        } else {
+            unsigned int i;
+            *formatP = PPM_TYPE;
+            for ( i = 0; i < h11FixedP->ncolors; ++i )
+                PPM_ASSIGN(
+                    (*colorsP)[i], x11colors[i].red, x11colors[i].green,
+                    x11colors[i].blue);
+        }
+        *maxvalP = 65535;
+    }
+
+    *colsP = h11FixedP->pixmap_width;
+    *rowsP = h11FixedP->pixmap_height;
+    *padrightP =
+        h11FixedP->bytes_per_line * 8 / h11FixedP->bits_per_pixel -
+        h11FixedP->pixmap_width;
+    /* According to X11/XWDFile.h, the item size is 'bitmap_pad' for some
+       images and 'bitmap_unit' for others.  This is strange, so there may
+       be some subtlety of their definitions that we're missing.
+
+       See comments in getpix() about what an item is.
+
+       Ben Kelley in January 2002 had a 32 bits-per-pixel xwd file
+       from a truecolor 32 bit window on a Hummingbird Exceed X server
+       on Win32, and it had bitmap_unit = 8 and bitmap_pad = 32.
+
+       But Björn Eriksson in October 2003 had an xwd file from a 24
+       bit-per-pixel direct color window that had bitmap_unit = 32 and
+       bitmap_pad = 8.  This was made by Xwd in Red Hat Xfree86 4.3.0-2.
+
+       Before Netpbm 9.23 (January 2002), we used bitmap_unit as the
+       item size always.  Then, until 10.19 (November 2003), we used
+       bitmap_pad when pixmap_depth > 1 and pixmap_format == ZPixmap.
+       We still don't see any logic in these fields at all, but we
+       figure whichever one is greater (assuming both are meaningful)
+       has to be the item size.  
+    */
+    *bits_per_itemP = MAX(h11FixedP->bitmap_pad, h11FixedP->bitmap_unit);
+
+    *bits_per_pixelP = h11FixedP->bits_per_pixel;
+
+    *byte_orderP = (enum byteorder) h11FixedP->byte_order;
+    *bit_orderP = (enum byteorder) h11FixedP->bitmap_bit_order;
+    *red_maskP = h11FixedP->red_mask;
+    *green_maskP = h11FixedP->green_mask;
+    *blue_maskP = h11FixedP->blue_mask;
+
+    free(h11FixedP);
+} 
+
+
+
+static void
+getinit(FILE *             const ifP, 
+        int *              const colsP, 
+        int *              const rowsP, 
+        int *              const padrightP, 
+        xelval *           const maxvalP, 
+        enum visualclass * const visualclassP, 
+        int *              const formatP, 
+        xel **             const colorsP,
+        int *              const bits_per_pixelP, 
+        int *              const bits_per_itemP, 
+        unsigned long *    const red_maskP, 
+        unsigned long *    const green_maskP,
+        unsigned long *    const blue_maskP,
+        enum byteorder *   const byte_orderP,
+        enum byteorder *   const bit_orderP,
+        bool               const headerDump) {
+/*----------------------------------------------------------------------------
+   Read the header from the XWD image in input stream 'ifP'.  Leave
+   the stream positioned to the beginning of the raster.
+
+   Return various fields from the header.
+
+   Return as *padrightP the number of additional pixels of padding are
+   at the end of each line of input.  This says the input stream
+   contains *colsP pixels of image data plus *padrightP pixels of
+   padding.
+-----------------------------------------------------------------------------*/
+    /* Assume X11 headers are larger than X10 ones. */
+    unsigned char header[sizeof(X11WDFileHeader)];
+    X10WDFileHeader* h10P;
+    X11WDFileHeader* h11P;
+    int rc;
+
+    h10P = (X10WDFileHeader*) header;
+    h11P = (X11WDFileHeader*) header;
+    if ( sizeof(*h10P) > sizeof(*h11P) )
+        pm_error("ARGH!  On this machine, X10 headers are larger than "
+                 "X11 headers!\n    You will have to re-write xwdtopnm." );
+    
+    /* We read an X10 header's worth of data from the file, then look
+       at it to see if it looks like an X10 header.  If so we process
+       the X10 header.  If not, but it looks like the beginning of an
+       X11 header, we read more bytes so we have an X11 header's worth
+       of data, then process the X11 header.  Otherwise, we raise an
+       error.  
+    */
+
+    rc = fread(&header[0], sizeof(*h10P), 1, ifP);
+    if (rc != 1)
+        pm_error( "couldn't read XWD file header" );
+
+    if (h10P->file_version == X10WD_FILE_VERSION ||
+        pm_bs_long(h10P->file_version) == X10WD_FILE_VERSION) {
+        
+        if (verbose)
+            pm_message("Input is X10");
+        processX10Header(h10P, ifP, colsP, rowsP, padrightP, maxvalP, 
+                         visualclassP, formatP, 
+                         colorsP, bits_per_pixelP, bits_per_itemP, 
+                         red_maskP, green_maskP, blue_maskP, 
+                         byte_orderP, bit_orderP);
+    } else if (h11P->file_version == X11WD_FILE_VERSION ||
+               pm_bs_long(h11P->file_version) == X11WD_FILE_VERSION) {
+        
+        int rc;
+
+        if (verbose)
+            pm_message("Input is X11");
+
+        /* Read the balance of the X11 header */
+        rc = fread(&header[sizeof(*h10P)], 
+                   sizeof(*h11P) - sizeof(*h10P), 1, ifP);
+        if (rc != 1)
+            pm_error("couldn't read end of X11 XWD file header");
+
+        if (headerDump)
+            dumpX11Header(h11P);
+
+        processX11Header(h11P, ifP, colsP, rowsP, padrightP, maxvalP, 
+                         visualclassP, formatP, 
+                         colorsP, bits_per_pixelP, bits_per_itemP, 
+                         red_maskP, green_maskP, blue_maskP, 
+                         byte_orderP, bit_orderP);
+    } else
+        pm_error("unknown XWD file version: %u", h11P->file_version);
+}
+
+
+
+#ifdef DEBUG_PIXEL
+#define DEBUG_PIXEL_1 \
+   if (pixel_count < 4) \
+       pm_message("getting pixel %d", pixel_count);
+
+#define DEBUG_PIXEL_2 \
+    if (pixel_count < 4) \
+        pm_message("item: %.8lx", row_controlP->item.l); 
+
+
+#define DEBUG_PIXEL_3 \
+    if (pixel_count < 4) \
+        pm_message("  bits_taken: %lx(%d), carryover_bits: %lx(%d), " \
+                     "pixel: %lx", \
+                     bits_taken, bits_to_take, row_controlP->carryover_bits, \
+                     row_controlP->bits_carried_over, pixel);
+
+#define DEBUG_PIXEL_4 \
+    if (pixel_count < 4) { \
+        pm_message("  row_control.bits_carried_over = %d" \
+                   "  carryover_bits= %.8lx", \
+                   row_controlP->bits_carried_over, \
+                   row_controlP->carryover_bits); \
+        pm_message("  row_control.bits_used = %d", \
+                   row_controlP->bits_used); \
+        pm_message("  row_control.bits_left = %d", \
+                   row_controlP->bits_left); \
+                   } \
+    \
+    pixel_count++;
+#else
+#define DEBUG_PIXEL_1 do {} while(0)
+#define DEBUG_PIXEL_2 do {} while(0)
+#define DEBUG_PIXEL_3 do {} while(0)
+#define DEBUG_PIXEL_4 do {} while(0)
+#endif
+
+
+
+/*----------------------------------------------------------------------------
+   The pixel reader.
+
+   The pixel reader is an object that reads an XWD raster and gives you
+   one pixel at a time from it.
+
+   It consists of a structure of type 'pixelReader' and the
+   getpix() and pixelReaderInit() subroutines.
+-----------------------------------------------------------------------------*/
+
+typedef struct {
+    /* This structure contains the state of the getpix() reader as it
+       reads across a row in the input image.
+       */
+    FILE * fileP;
+    unsigned long itemBuffer;
+        /* The item buffer.  This contains what's left of the item
+           most recently read from the image file -- an item goes from the
+           XWD raster into here and then bits disappear from it as they
+           become part of pixels returned by the object.
+
+           'nBitsLeft' tells how many bits are in the buffer now.  It's
+           zero when nothing has ever been read from the file.  Only
+           the least signficant 'nBitsLeft' bits are meaningful.
+
+           The numeric value of the member is the number whose pure
+           binary representation is the bit string in the buffer.
+
+           That bit string starts out as the contents of one item of
+           the XWD raster, with the "byte order" value from the XWD
+           header applied.  E.g. Assume bits per item is 16 and the
+           and the "byte order" value is "MSB First".  Assume the
+           raster contains 0x01 at Offset 109 and 0x02 at Offset 110.
+           The value of this member just after that item is read is
+           two hundred fifty-eight.  If we are running on a
+           little-endian machine, it appears in memory as 0x02 at
+           Address A and 0x01 at Address A+1.
+
+           Then we pull bits from either the beginning or end of the
+           buffer according to the "bit order" value from the XWD
+           header.  E.g. in the example above, assume "bit order" is
+           lsb first.  and bits per pixel is 4.  After reading the
+           first pixel (0010b) from the buffer, 'itemBuffer' is
+           sixteen (0x010) and 'nBitsLeft' is 12.
+        */
+    unsigned int nBitsLeft;
+      /* This is the number of bits in the current item that have not yet
+         been returned as part of a pixel.
+      */
+    int bitsPerPixel;
+    int bitsPerItem;
+    enum byteorder byteOrder;
+    enum byteorder bitOrder;
+} pixelReader;
+
+
+
+static void
+pixelReaderInit(pixelReader *  const pixelReaderP,
+                FILE *         const fileP,
+                int            const bitsPerPixel,
+                int            const bitsPerItem, 
+                enum byteorder const byteOrder,
+                enum byteorder const bitOrder) {
+    
+    pixelReaderP->fileP           = fileP;
+    pixelReaderP->bitsPerPixel    = bitsPerPixel;
+    pixelReaderP->bitsPerItem     = bitsPerItem;
+    pixelReaderP->byteOrder       = byteOrder;
+    pixelReaderP->bitOrder        = bitOrder;
+
+    pixelReaderP->nBitsLeft       = 0;
+}    
+
+
+
+static void
+readItem(pixelReader * const rdrP) {
+/*----------------------------------------------------------------------------
+   Read one item from the XWD raster associated with pixel reader *rdrP.
+
+   Put the item into the item buffer of the pixel reader object *rdrP.
+-----------------------------------------------------------------------------*/
+    assert(rdrP->nBitsLeft == 0);
+
+    switch (rdrP->bitsPerItem) {
+    case 8: {
+        unsigned char const item8 = getc(rdrP->fileP);
+        rdrP->itemBuffer = item8;
+        rdrP->nBitsLeft = 8;
+    }
+        break;
+
+    case 16: {
+        short item16;
+
+        switch (rdrP->byteOrder) {
+        case MSBFirst:
+            pm_readbigshort(rdrP->fileP, &item16);
+            break;
+        case LSBFirst:
+            pm_readlittleshort(rdrP->fileP, &item16);
+            break;
+        }
+        rdrP->itemBuffer = (unsigned short)item16;
+        rdrP->nBitsLeft = 16;
+    }
+        break;
+        
+    case 32: {
+        long item32;
+        
+        switch (rdrP->byteOrder) {
+        case MSBFirst:
+            pm_readbiglong(rdrP->fileP, &item32);
+            break;
+        case LSBFirst:
+            pm_readlittlelong(rdrP->fileP, &item32);
+            break;
+        }
+        rdrP->itemBuffer = item32;
+        rdrP->nBitsLeft = 32;
+    }
+        break;
+    default:
+        pm_error("INTERNAL ERROR: impossible bits_per_item");
+    }
+}
+
+
+
+static unsigned long const lsbmask[] = {
+/*----------------------------------------------------------------------------
+   lsbmask[i] is the mask you use to select the i least signficant bits
+   of a bit string.
+-----------------------------------------------------------------------------*/
+    0x00000000,
+    0x00000001, 0x00000003, 0x00000007, 0x0000000f, 
+    0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
+    0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff,
+    0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
+    0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff,
+    0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff,
+    0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff,
+    0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff
+};
+
+
+static unsigned long
+getpix(pixelReader * const rdrP) {
+/*----------------------------------------------------------------------------
+   Get a pixel from the input image.
+
+   A pixel is a bit string.  It may be either an rgb triplet or an index
+   into the colormap (or even an rgb triplet of indices into the colormaps!).
+   We don't care -- it's just a bit string.
+
+   We return an integer.  It's the integer that the pixel represents as
+   pure binary cipher, with the first bit the most significant bit.
+   
+   The basic unit of storage in the input file is an "item."  An item
+   can be 1, 2, or 4 bytes, and 'bits_per_item' tells us which.  Each
+   item can have its bytes stored in forward or reverse order, and
+   'byte_order' tells us which.
+
+   Each item can contain one or more pixels, and may contain
+   fractional pixels.  'bits_per_pixel' tells us how many bits each
+   pixel has, and 'bits_per_pixel' is always less than or equal to
+   'bits_per_item', but not necessarily a factor of it.  Within an item,
+   after taking care of the endianness of its storage format, the pixels
+   may be arranged from left to right or right to left.  'bit_order' tells
+   us which.
+
+   But it's not that simple.  Sometimes dummy pixels are added to the
+   right edge of the image in order to make an integral number of
+   items in each row of the raster.  getpix() doesn't know anything
+   about that, though -- it gets the dummy pixels the same as any
+   other pixel.  (This program is written as if it is always whole
+   pixels that get added for padding, but I wonder.  When pixels are 3
+   or 4 bytes and items are 4 bytes, sub-pixel padding would make
+   sense.  But then, maybe in formats with 3 or 4 bytes pixels,
+   there's never padding.  That seems to be the case so far...).  The
+   XWD header has a field that tells how many bytes there are per XWD
+   raster line, so that's the final word on how much padding there is.
+   
+   The most difficult part of getting the pixel is the ones that span
+   items.  We detect when an item has part, but not all, of the next
+   pixel (after the one we return) in it and store the fragment as
+   "carryover" bits for use in the next call to this function.
+
+   All this state information (carryover bits, etc.) is kept in 
+   *row_controlP.
+
+-----------------------------------------------------------------------------*/
+    unsigned long pixel;
+        /* Accumulator for the value we ultimately return.  We shift in
+           bits from the right end.  The number of bits presently in the
+           accumulator is rdrP->bitsPerPixel - bitsStillNeeded .
+        */
+    
+    unsigned int nBitsStillNeeded;
+        /* How many bits we still need to add to 'pixel',
+           as we build it up to the
+           full amount we have to return.  The bits are right justified in
+           it -- additional bits will shift in from the right.
+        */
+
+    assert(rdrP->bitsPerPixel <= 32);
+
+    pixel = 0;
+    nBitsStillNeeded = rdrP->bitsPerPixel;
+
+    while (nBitsStillNeeded > 0) {
+        if (rdrP->nBitsLeft == 0)
+            /* Buffer's empty.  Have to go back to the well for
+               rdrP->bitsPerItem more bits.
+            */
+            readItem(rdrP);
+
+        {
+            unsigned int const nBitsToTake =
+                MIN(rdrP->nBitsLeft, nBitsStillNeeded);
+            unsigned long const bitsToTakeMask = lsbmask[nBitsToTake];
+                /* E.g. if nbitsToTake is 4, this is 0x0000000F */
+
+            unsigned long bitsToTake;
+                /* The actual bits we take, in the 'nBitsToTake' low bits */
+
+            assert(nBitsToTake <= 32);
+            
+            if (rdrP->bitOrder == MSBFirst) {
+                unsigned int const nBitsToLeave =
+                    rdrP->nBitsLeft - nBitsToTake;
+                bitsToTake =
+                    (rdrP->itemBuffer >> nBitsToLeave) & bitsToTakeMask;
+            } else {
+                bitsToTake = rdrP->itemBuffer & bitsToTakeMask;
+                rdrP->itemBuffer >>= nBitsToTake;
+            }                
+            /* Shift the bits into the right end of the accumulator */
+            pixel <<= nBitsToTake;
+            pixel |= bitsToTake;
+
+            rdrP->nBitsLeft -= nBitsToTake;
+            nBitsStillNeeded -= nBitsToTake;
+        }
+    }
+    return pixel;
+}
+
+
+
+static void
+reportInfo(int              const cols, 
+           int              const rows, 
+           int              const padright, 
+           xelval           const maxval, 
+           enum visualclass const visualclass,
+           int              const format, 
+           int              const bits_per_pixel,
+           int              const bits_per_item, 
+           int              const red_mask, 
+           int              const green_mask, 
+           int              const blue_mask,
+           enum byteorder   const byte_order, 
+           enum byteorder   const bit_order) {
+    
+    const char *visualclass_name;
+    const char *byte_order_name;
+    const char *bit_order_name;
+    switch (visualclass) {
+    case StaticGray:  visualclass_name="StaticGray";  break;
+    case GrayScale:   visualclass_name="Grayscale";   break;
+    case StaticColor: visualclass_name="StaticColor"; break;
+    case PseudoColor: visualclass_name="PseudoColor"; break;
+    case TrueColor:   visualclass_name="TrueColor";   break;
+    case DirectColor: visualclass_name="DirectColor"; break;
+    default:          visualclass_name="(invalid)";    break;
+    }
+    switch (byte_order) {
+    case MSBFirst: byte_order_name = "MSBFirst";  break;
+    case LSBFirst: byte_order_name = "LSBFirst";  break;
+    default:       byte_order_name = "(invalid)"; break;
+    }
+    switch (bit_order) {
+    case MSBFirst: bit_order_name = "MSBFirst";  break;
+    case LSBFirst: bit_order_name = "LSBFirst";  break;
+    default:       bit_order_name = "(invalid)"; break;
+    }
+    pm_message("%d rows of %d columns with maxval %d",
+               rows, cols, maxval);
+    pm_message("padright=%d.  visualclass = %s.  format=%d (%c%c)",
+               padright, visualclass_name, 
+               format, format/256, format%256);
+    pm_message("bits_per_pixel=%d; bits_per_item=%d",
+               bits_per_pixel, bits_per_item);
+    pm_message("byte_order=%s; bit_order=%s",
+               byte_order_name, bit_order_name);
+    pm_message("red_mask=0x%.8x; green_mask=0x%.8x; blue_mask=0x%.8x",
+               red_mask, green_mask, blue_mask);
+}
+
+
+
+static void 
+convertRowSimpleIndex(pixelReader *  const pixelReaderP,
+                      int            const cols,
+                      const xel *    const colors,
+                      xel *          const xelrow) {
+    
+    unsigned int col;
+    for (col = 0; col < cols; ++col)
+        xelrow[col] = colors[getpix(pixelReaderP)];
+}
+
+
+
+static void
+convertRowDirect(pixelReader *  const pixelReaderP,
+                 int            const cols,
+                 const xel *    const colors,
+                 unsigned long  const red_mask,
+                 unsigned long  const grn_mask,
+                 unsigned long  const blu_mask,
+                 xel *          const xelrow) {
+        
+    unsigned int col;
+
+    for (col = 0; col < cols; ++col) {
+        unsigned long pixel;
+            /* This is a triplet of indices into the color map, packed
+               into this bit string according to red_mask, etc.
+            */
+        unsigned int red_index, grn_index, blu_index;
+            /* These are indices into the color map, unpacked from 'pixel'.
+             */
+            
+        pixel = getpix(pixelReaderP);
+
+        red_index = (pixel & red_mask) >> zero_bits(red_mask);
+        grn_index = (pixel & grn_mask) >> zero_bits(grn_mask); 
+        blu_index = (pixel & blu_mask) >> zero_bits(blu_mask);
+
+        PPM_ASSIGN(xelrow[col],
+                   PPM_GETR(colors[red_index]),
+                   PPM_GETG(colors[grn_index]),
+                   PPM_GETB(colors[blu_index])
+            );
+    }
+}
+
+
+
+static void
+convertRowTrueColor(pixelReader *  const pixelReaderP,
+                    int                  const cols,
+                    pixval               const maxval,
+                    const xel *          const colors,
+                    unsigned long        const red_mask,
+                    unsigned long        const grn_mask,
+                    unsigned long        const blu_mask,
+                    xel *                const xelrow) {
+
+    unsigned int col;
+    unsigned int red_shift, grn_shift, blu_shift;
+    unsigned int red_maxval, grn_maxval, blu_maxval;
+
+    red_shift = zero_bits(red_mask);
+    grn_shift = zero_bits(grn_mask);
+    blu_shift = zero_bits(blu_mask);
+
+    red_maxval = red_mask >> red_shift;
+    grn_maxval = grn_mask >> grn_shift;
+    blu_maxval = blu_mask >> blu_shift;
+
+    for (col = 0; col < cols; ++col) {
+        unsigned long pixel;
+
+        pixel = getpix(pixelReaderP);
+
+        /* The parsing of 'pixel' used to be done with hardcoded layout
+           parameters.  See comments at end of this file.
+        */
+        PPM_ASSIGN(xelrow[col],
+                   ((pixel & red_mask) >> red_shift) * maxval / red_maxval,
+                   ((pixel & grn_mask) >> grn_shift) * maxval / grn_maxval,
+                   ((pixel & blu_mask) >> blu_shift) * maxval / blu_maxval
+            );
+
+    }
+}
+
+
+
+static void
+convertRow(pixelReader *    const pixelReaderP,
+           FILE *           const ofP,
+           int              const padright, 
+           int              const cols, 
+           xelval           const maxval,
+           int              const format, 
+           unsigned long    const red_mask, 
+           unsigned long    const green_mask, 
+           unsigned long    const blue_mask, 
+           const xel*       const colors, 
+           enum visualclass const visualclass) {
+/*----------------------------------------------------------------------------
+   Read a row from the XWD pixel input stream 'pixelReaderP' and write
+   it to the PNM output stream 'ofP'.
+
+   The row is 'cols' pixels.
+
+   After reading the 'cols' pixels, we read and discard an additional
+   'padright' pixels from the input stream, so as to read the entire
+   input line.
+-----------------------------------------------------------------------------*/
+    xel* xelrow;
+    xelrow = pnm_allocrow(cols);
+
+    switch (visualclass) {
+    case StaticGray:
+    case GrayScale:
+    case StaticColor:
+    case PseudoColor:
+        convertRowSimpleIndex(pixelReaderP, cols, colors, xelrow);
+        break;
+    case DirectColor: 
+        convertRowDirect(pixelReaderP, cols, colors,
+                         red_mask, green_mask, blue_mask,
+                         xelrow);
+        
+        break;
+    case TrueColor: 
+        convertRowTrueColor(pixelReaderP, cols, maxval, colors,
+                            red_mask, green_mask, blue_mask,
+                            xelrow);
+        break;
+            
+    default:
+        pm_error("unknown visual class");
+    }
+    {
+        unsigned int col;
+        for (col = 0; col < padright; ++col)
+            getpix(pixelReaderP);
+    }
+    pnm_writepnmrow(ofP, xelrow, cols, maxval, format, 0);
+    pnm_freerow(xelrow);
+}
+
+
+
+static void
+reportOutputType(int const format) {
+
+    switch (PNM_FORMAT_TYPE(format)) {
+    case PBM_TYPE:
+        pm_message("writing PBM file");
+    break;
+
+    case PGM_TYPE:
+        pm_message("writing PGM file");
+    break;
+    
+    case PPM_TYPE:
+        pm_message("writing PPM file");
+    break;
+    
+    default:
+        pm_error("shouldn't happen");
+    }
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    int rows, cols, format, padright;
+    unsigned int row;
+    int bits_per_pixel;
+    int bits_per_item;
+    unsigned long red_mask, green_mask, blue_mask;
+    xelval maxval;
+    enum visualclass visualclass;
+    enum byteorder byte_order, bit_order;
+    xel *colors;  /* the color map */
+    pixelReader pixelReader;
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    debug = cmdline.debug;
+    verbose = cmdline.verbose;
+
+    if (cmdline.input_filename != NULL) 
+        ifP = pm_openr(cmdline.input_filename);
+    else
+        ifP = stdin;
+
+    getinit(ifP, &cols, &rows, &padright, &maxval, &visualclass, &format, 
+            &colors, &bits_per_pixel, &bits_per_item, 
+            &red_mask, &green_mask, &blue_mask, &byte_order, &bit_order,
+            cmdline.headerdump);
+    
+    if (verbose) 
+        reportInfo(cols, rows, padright, maxval, visualclass,
+                   format, bits_per_pixel, bits_per_item,
+                   red_mask, green_mask, blue_mask, 
+                   byte_order, bit_order);
+
+    pixelReaderInit(&pixelReader, ifP, bits_per_pixel, bits_per_item,
+                    byte_order, bit_order);
+
+    pnm_writepnminit(stdout, cols, rows, maxval, format, 0);
+
+    reportOutputType(format);
+
+    for (row = 0; row < rows; ++row) {
+        convertRow(&pixelReader, stdout,
+                   padright, cols, maxval, format,
+                   red_mask, green_mask, blue_mask, colors, visualclass);
+    }
+    
+    pm_close(ifP);
+    pm_close(stdout);
+    
+    return 0;
+}
+
+
+/*
+   This used to be the way we parsed a direct/true color pixel.  I'm 
+   keeping it here in case we find out some application needs it this way.
+
+   There doesn't seem to be any reason to do this hard-coded stuff when
+   the header contains 32 bit masks that tell exactly how to extract the
+   3 colors in all cases.
+
+   We know for a fact that 16 bit TrueColor output from XFree86's xwd
+   doesn't match these hard-coded shift amounts, so we have replaced
+   this whole switch thing.  -Bryan 00.03.01
+
+   switch (bits_per_pixel) {
+                
+   case 16:
+       PPM_ASSIGN( *xP,
+                   ( ( ul & red_mask )   >> 0    ),
+                   ( ( ul & green_mask ) >> 5  ),
+                   ( ( ul & blue_mask )  >> 11) );
+       break;
+
+   case 24:
+   case 32:
+       PPM_ASSIGN( *xP, ( ( ul & 0xff0000 ) >> 16 ),
+                   ( ( ul & 0xff00 ) >> 8 ),
+                   ( ul & 0xff ) );
+       break;
+
+   default:
+       pm_error( "True/Direct is valid only with 16, 24, and 32 bits" );
+   }
+*/
diff --git a/converter/other/zeisstopnm.c b/converter/other/zeisstopnm.c
new file mode 100644
index 00000000..e94d9b44
--- /dev/null
+++ b/converter/other/zeisstopnm.c
@@ -0,0 +1,187 @@
+/* zeisstopnm.c - convert a Zeiss confocal image into a portable anymap
+**
+** Copyright (C) 1993 by Oliver Trepte, oliver@fysik4.kth.se
+**
+** Derived from the pbmplus 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.
+**
+**
+** This conversion utility is based on a mail from Keith Bartels to
+** the confocal mail group (confocal@ubvm.cc.buffalo.edu) in June 1993.
+**
+** I'm including here a description of the Zeiss confocal image format. I
+** obtained this over the phone on 4-10-1991 form a Zeiss Engineer, and from
+** what I hear, it is probably not correct for the new scopes.
+** Zeiss puts its header information and the end of the file, so I call it
+** a tailer.  This is nice because most conversions programs that work with raw
+** image files will read the image simply ignore the tailer.  The file contains:
+**
+** The image data: NxN  1 byte pixels (where N is the number of pixels in a
+** scan line) in a standard raw rastering order.
+**
+** The tailer contains:
+** 256 bytes: the blue Look-Up-Table (LUT)
+** 256 bytes: the red LUT
+** 256 bytes: the green LUT
+** 8 bytes: empty
+** 2 bytes: no. of columns in the image. hi-byte, low-byte
+** 2 bytes: no. of rows in the image.
+** 2 bytes: x-position of upper left pixel  (0 for 512x512 images)
+** 2 bytes: y-position of upper left pixel  (0 for 512x512 images)
+** 16 bytes: empty
+** 32 bytes: test from upper right corner of image, ASCII.
+** 128 bytes: text from the bottom two rows of the screen, ASCII.
+** 64 bytes: reserved
+** -----------
+** 1024 bytes TOTAL
+**
+** So, image files contain NxN + 1024 bytes.
+**
+** Keith Bartels
+** keith@VISION.EE.UTEXAS.EDU
+**
+*/
+
+#include "pnm.h"
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    int argn, row, i;
+    register int col;
+    int rows=0, cols=0;
+    int format = 0;
+    xel* xelrow;
+    register xel* xP;
+    char* buf = NULL;
+    unsigned char *lutr, *lutg, *lutb;
+    long nread = 0;
+    unsigned char* byteP;
+    const char* const usage = "[-pgm|-ppm] [Zeissfile]";
+
+
+    pnm_init( &argc, argv );
+
+    argn = 1;
+
+    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
+	{
+	if ( pm_keymatch( argv[argn], "-pgm", 3 ) )
+	    {
+	    if ( argn >= argc )
+		pm_usage( usage );
+	    format = PGM_TYPE;
+	    }
+	else if ( pm_keymatch( argv[argn], "-ppm", 3 ) )
+	    {
+	    if ( argn >= argc )
+		pm_usage( usage );
+	    format = PPM_TYPE;
+	    }
+	else
+	    pm_usage( usage );
+	++argn;
+	}
+
+    if ( argn < argc )
+	{
+	ifp = pm_openr( argv[argn] );
+	++argn;
+	}
+    else
+	ifp = stdin;
+
+    if ( argn != argc )
+	pm_usage( usage );
+
+    /* Read the image to a buffer */
+
+    buf = pm_read_unknown_size( ifp, &nread );
+
+    /* Check the format of the file */
+
+    if (nread <=1024)
+	pm_error( "Input file not in Zeiss format (too small)" );
+
+    lutg = (unsigned char *)buf+(nread-1024+512);
+    lutr = (unsigned char *)buf+(nread-1024+256);
+    lutb = (unsigned char *)buf+(nread-1024);
+
+    cols = ((unsigned char) buf[nread-1024+768+8]) +
+	(((unsigned char) buf[nread-1024+768+9]) << 8);
+    rows = ((unsigned char) buf[nread-1024+768+10]) +
+	(((unsigned char) buf[nread-1024+768+11]) << 8);
+
+    if ( cols <= 0 )
+	pm_error( "invalid cols: %d", cols );
+    if ( rows <= 0 )
+	pm_error( "invalid rows: %d", rows );
+
+    if (cols*rows != nread-1024)
+	pm_error( "Hmm, %d rows, %d cols, %ld total image size",
+		 rows, cols, nread-1024);
+
+    /* Choose pgm or ppm */
+    /* If the LUTs all contain 0,1,2,3,4..255, it is a pgm file */
+
+    for (i=0; i<256 && format==0; i++)
+	if (lutr[i] != i || lutg[i] != i || lutb[i] != i)
+	    format = PPM_TYPE;
+
+    if (format == 0)
+	format = PGM_TYPE;
+
+    pnm_writepnminit( stdout, cols, rows, 255, format, 0 );
+    xelrow = pnm_allocrow( cols );
+    byteP = (unsigned char *) buf;
+
+    switch ( PNM_FORMAT_TYPE(format) )
+        {
+        case PGM_TYPE:
+        pm_message( "writing PGM file, %d rows %d columns", rows, cols );
+        break;
+
+        case PPM_TYPE:
+        pm_message( "writing PPM file, %d rows %d columns", rows, cols );
+        break;
+
+        default:
+        pm_error( "shouldn't happen" );
+        }
+
+    for ( row = 0; row < rows; ++row )
+    {
+	switch ( PNM_FORMAT_TYPE(format) )
+	{
+	case PGM_TYPE:
+	    for ( col = 0, xP = xelrow; col < cols; ++col, ++xP, ++byteP )
+		PNM_ASSIGN1( *xP, *byteP );
+	    break;
+
+	case PPM_TYPE:
+	    for ( col = 0, xP = xelrow; col < cols; ++col, ++xP, ++byteP )
+		PPM_ASSIGN( *xP, lutr[*byteP], lutg[*byteP], lutb[*byteP] );
+	    break;
+
+        default:
+	    pm_error( "shouldn't happen" );
+        }
+
+	pnm_writepnmrow( stdout, xelrow, cols, 255, format, 0 );
+    }
+
+    free( buf );
+    pm_close( stdout );
+
+    exit( 0 );
+}
diff --git a/converter/pbm/Makefile b/converter/pbm/Makefile
new file mode 100644
index 00000000..747f2a4d
--- /dev/null
+++ b/converter/pbm/Makefile
@@ -0,0 +1,78 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/pbm
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+PORTBINARIES =	atktopbm brushtopbm cmuwmtopbm ddbugtopbm g3topbm escp2topbm \
+		icontopbm macptopbm mdatopbm mgrtopbm mrftopbm \
+		pbmto10x pbmto4425 pbmtoascii pbmtoatk \
+		pbmtobbnbg pbmtocmuwm pbmtodjvurle \
+		pbmtoepsi pbmtoepson pbmtoescp2 \
+		pbmtog3 pbmtogem pbmtogo pbmtoibm23xx pbmtoicon pbmtolj \
+		pbmtoln03 pbmtolps \
+		pbmtomacp pbmtomatrixorbital pbmtomda pbmtomgr pbmtomrf \
+		pbmtonokia \
+		pbmtopi3 pbmtoplot pbmtopsg3 pbmtoptx pbmtowbmp \
+		pbmtox10bm pbmtoxbm pbmtoybm pbmtozinc \
+		pi3topbm pktopbm \
+		wbmptopbm xbmtopbm ybmtopbm	
+
+ifneq ($(LEX)x,x)
+  PORTBINARIES += thinkjettopbm
+endif
+
+#pbmpage uses sqrt(), which is sometimes in libc, not libm.  Is it ever
+#in libm?
+MATHBINARIES =	pbmtopk
+BINARIES =	$(PORTBINARIES) $(MATHBINARIES)
+SCRIPTS =
+
+OBJECTS = $(BINARIES:%=%.o)
+
+MERGEBINARIES = $(BINARIES)
+MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2)
+
+SUBDIRS=pbmtoppa
+
+.PHONY: all
+all: $(BINARIES) $(SUBDIRS:%=%/all)
+
+include $(SRCDIR)/Makefile.common
+
+ifneq ($(LEX)x,x)
+thinkjettopbm.c1:%.c1:%.l
+	$(LEX) -t $< >$@
+endif
+
+thinkjettopbm.c:%.c:%.c1 $(SRCDIR)/lib/util/lexheader
+# Different versions of Lex produce subtly different output, from the
+# same .l source file.  The .c1 file contains the raw output from Lex.
+# We now massage it so it will compile.  We must add some definitions
+# at the top (the lexheader file).  We must remove any yylex and
+# yywrap prototype, as our .l file already contains one.  The Lex
+# version "Software Generation Utilities (SGU) Solaris-ELF (4.0)"
+# puts declarations for yylex and yywrap, as external symbols,
+# into its output, causing a duplicate declaration error at compile time.
+#
+# Schwarb Manfred reports that it compiles OK, but with warnings, on
+# Solaris.  Solaris Lex has a -e option that eliminates the lex
+# warnings, but causes compiler warnings.  AIX and Flex don't have a
+# -e option.  -Bryan 2001.05.16.
+#
+# But Peter Weisz reported on 2002.12.11 that on Solaris, compile
+# failed due to a duplicate declaration of yylex and yywrap with Netpbm
+# 10.12, which does not remove those declarations as the current version
+# does.
+	cat $(SRCDIR)/lib/util/lexheader $< | \
+	  grep -v "^[[:space:]]*int yylex(void);" | \
+	  grep -v "^[[:space:]]*int yywrap(void);" \
+	  >$@
+
+clean: localclean
+.PHONY: localclean
+localclean:
+	-rm -f thinkjettopbm.c
diff --git a/converter/pbm/atktopbm.c b/converter/pbm/atktopbm.c
new file mode 100644
index 00000000..c4a81808
--- /dev/null
+++ b/converter/pbm/atktopbm.c
@@ -0,0 +1,362 @@
+/* atktopbm.c - convert Andrew Toolkit raster object to portable bitmap
+**
+** Copyright (C) 1991 by Bill Janssen
+**
+** 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 <string.h>
+#include <sys/types.h>
+
+#include "nstring.h"
+#include "pbm.h"
+#include "mallocvar.h"
+
+
+/* readatkraster
+**
+** Routine for reading rasters in .raster form.  (BE2 rasters version 2.)
+*/
+
+/* codes for data stream */
+#define WHITEZERO   'f'
+#define WHITETWENTY 'z'
+#define BLACKZERO   'F'
+#define BLACKTWENTY 'Z'
+#define OTHERZERO   0x1F
+
+#define WHITEBYTE   0x00
+#define BLACKBYTE   0xFF
+
+/* error codes (copied from $ANDREW/atk/basics/common/dataobj.ch) */
+/* return values from Read */
+#define dataobject_NOREADERROR  0
+#define dataobject_PREMATUREEOF 1
+#define dataobject_NOTBE2DATASTREAM 2 /* backward compatibility */
+#define dataobject_NOTATKDATASTREAM 2 /* preferred version */
+#define dataobject_MISSINGENDDATAMARKER 3
+#define dataobject_OBJECTCREATIONFAILED 4
+#define dataobject_BADFORMAT 5
+
+/* ReadRow(file, row, length) 
+** Reads from 'file' the encoding of bytes to fill in 'row'.  Row will be
+** truncated or padded (with WHITE) to exactly 'length' bytes.
+**
+** Returns the code that terminated the row.  This may be
+**      '|'     correct end of line
+**      '\0'    if the length was satisfied (before a terminator)
+**      EOF     if the file ended
+**      '\'  '{'    other recognized ends. 
+** The '|' is the expected end and pads the row with WHITE.
+** The '\' and '{' are error conditions and may indicate the
+** beginning of some other portion of the data stream.
+** If the terminator is '\' or '{', it is left at the front of the input.
+** '|' is gobbled up.
+*/
+
+/* macros to generate case entries for switch statement */
+#define case1(v) case v
+#define case4(v) case v: case (v)+1: case (v)+2: case(v)+3
+#define case6(v) case4(v): case ((v)+4): case ((v)+5)
+#define case8(v) case4(v): case4((v)+4)
+
+static long
+ReadRow(FILE * const file, unsigned char * const row, long const length) {
+/*----------------------------------------------------------------------------
+  'file' is where to get them from.
+  'row' is where to put bytes.
+  'length' is how many bytes in row must be filled.
+-----------------------------------------------------------------------------*/
+    /* Each input character is processed by the central loop.  There are 
+    ** some input codes which require two or three characters for
+    ** completion; these are handled by advancing the state machine.
+    ** Errors are not processed; instead the state machine is reset
+    ** to the Ready state whenever a character unacceptable to the
+    ** current state is read.
+    */
+    enum stateCode {
+        Ready,      /* any input code is allowed */
+        HexDigitPending,    /* have seen the first of a hex digit pair */
+        RepeatPending,  /* repeat code has been seen:
+                   must be followed by two hex digits */
+        RepeatAndDigit};    /* have seen repeat code and its first
+                   following digit */
+    enum stateCode InputState;  /* current state */
+    register int c;     /* the current input character */
+    register long repeatcount = 0;  /* current repeat value */
+    register long hexval;   /* current hex value */
+    long pendinghex = 0;    /* the first of a pair of hex characters */
+    int lengthRemaining;
+    unsigned char * cursor;
+    
+    /* We cannot exit when length becomes zero because we need to check 
+    ** to see if a row ending character follows.  Thus length is checked
+    ** only when we get a data generating byte.  If length then is
+    ** zero, we ungetc the byte.
+    */
+
+    lengthRemaining = length;
+    cursor = row;
+
+    InputState = Ready;
+    while ((c=getc(file)) != EOF) switch (c) {
+
+    case8(0x0):
+    case8(0x8):
+    case8(0x10):
+    case8(0x18):
+    case1(' '):
+        /* control characters and space are legal and ignored */
+        break;
+    case1(0x40):    /* '@' */
+    case1(0x5B):    /* '[' */
+    case4(0x5D):    /*  ']'  '^'  '_'  '`' */
+    case4(0x7D):    /* '}'  '~'  DEL  0x80 */
+    default:        /* all above 0x80 */
+        /* error code:  Ignored at present.  Reset InputState. */
+        InputState = Ready;
+        break;
+
+    case1(0x7B):    /* '{' */
+    case1(0x5C):    /* '\\' */
+        /* illegal end of line:  exit anyway */
+        ungetc(c, file);    /* retain terminator in stream */
+        /* DROP THROUGH */
+    case1(0x7C):    /* '|' */
+        /* legal end of row: may have to pad  */
+        while (lengthRemaining-- > 0)
+            *cursor++ = WHITEBYTE;
+        return c;
+    
+    case1(0x21):
+    case6(0x22):
+    case8(0x28):
+        /* punctuation characters: repeat byte given by two
+        ** succeeding hex chars
+        */
+        if (lengthRemaining <= 0) {
+            ungetc(c, file);
+            return('\0');
+        }
+        repeatcount = c - OTHERZERO;
+        InputState = RepeatPending;
+        break;
+
+    case8(0x30):
+    case8(0x38):
+        /* digit (or following punctuation)  -  hex digit */
+        hexval = c - 0x30;
+        goto hexdigit;
+    case6(0x41):
+        /* A ... F    -  hex digit */
+        hexval = c - (0x41 - 0xA);
+        goto hexdigit;
+    case6(0x61):
+        /* a ... f  - hex digit */
+        hexval = c - (0x61 - 0xA);
+        goto hexdigit;
+
+    case8(0x67):
+    case8(0x6F):
+    case4(0x77):
+        /* g ... z   -   multiple WHITE bytes */
+        if (lengthRemaining <= 0) {
+            ungetc(c, file);
+            return('\0');
+        }
+        repeatcount = c - WHITEZERO;
+        hexval = WHITEBYTE;
+        goto store;
+    case8(0x47):
+    case8(0x4F):
+    case4(0x57):
+        /* G ... Z   -   multiple BLACK bytes */
+        if (lengthRemaining <= 0) {
+            ungetc(c, file);
+            return('\0');
+        }
+        repeatcount = c - BLACKZERO;
+        hexval = BLACKBYTE;
+        goto store;
+
+hexdigit:
+        /* process a hex digit.  Use InputState to determine
+            what to do with it. */
+        if (lengthRemaining <= 0) {
+            ungetc(c, file);
+            return('\0');
+        }
+        switch(InputState) {
+        case Ready:
+            InputState = HexDigitPending;
+            pendinghex = hexval << 4;
+            break;
+        case HexDigitPending:
+            hexval |= pendinghex;
+            repeatcount = 1;
+            goto store;
+        case RepeatPending:
+            InputState = RepeatAndDigit;
+            pendinghex = hexval << 4;
+            break;
+        case RepeatAndDigit:
+            hexval |= pendinghex;
+            goto store;
+        }
+        break;
+
+store:
+        /* generate byte(s) into the output row 
+            Use repeatcount, depending on state.  */
+        if (lengthRemaining < repeatcount) 
+            /* reduce repeat count if it would exceed
+                available space */
+            repeatcount = lengthRemaining;
+        lengthRemaining -= repeatcount;  /* do this before repeatcount-- */
+        while (repeatcount-- > 0)
+                *cursor++ = hexval;
+        InputState = Ready;
+        break;
+
+    } /* end of while( - )switch( - ) */
+    return EOF;
+}
+
+
+
+#undef case1
+#undef case4
+#undef case6
+#undef case8
+
+
+
+static void
+ReadATKRaster(FILE * const file, 
+              int * const rwidth, 
+              int * const rheight, 
+              unsigned char ** const destaddrP) {
+
+    int row, rowlen;  /* count rows;  byte length of row */
+    int version;
+    char keyword[6];
+    int discardid;
+    int objectid;     /* id read for the incoming pixel image */
+    long tc;            /* temp */
+    int width, height;      /* dimensions of image */
+
+    if (fscanf(file, "\\begindata{raster,%d", &discardid) != 1
+                || getc(file) != '}' || getc(file) != '\n')
+      pm_error ("input file not Andrew raster object");
+
+    fscanf(file, " %d ", &version);
+    if (version < 2) 
+      pm_error ("version too old to parse");
+
+    {
+        unsigned int options;
+        long xscale, yscale;
+        long xoffset, yoffset, subwidth, subheight;
+        /* ignore all these features: */
+        fscanf(file, " %u %ld %ld %ld %ld %ld %ld",  
+               &options, &xscale, &yscale, &xoffset, 
+               &yoffset, &subwidth, &subheight);
+    }
+    /* scan to end of line in case this is actually something beyond V2 */
+    while (((tc=getc(file)) != '\n') && (tc != '\\') && (tc != EOF)) {}
+
+    /* read the keyword */
+    fscanf(file, " %5s", keyword);
+    if (!STREQ(keyword, "bits"))
+      pm_error ("keyword is not 'bits'!");
+
+    fscanf(file, " %d %d %d ", &objectid, &width, &height);
+
+    if (width < 1 || height < 1 || width > 1000000 || height > 1000000) 
+      pm_error ("bad width or height");
+
+    *rwidth = width;
+    *rheight = height;
+    rowlen = (width + 7) / 8;
+    MALLOCARRAY(*destaddrP, height * rowlen);
+    if (destaddrP == NULL)
+        pm_error("Unable to allocate %u bytes for the input image.",
+                 height * rowlen);
+    for (row = 0;   row < height;   row++)
+      {
+        long c;
+
+        c = ReadRow(file, *destaddrP + (row * rowlen), rowlen);
+        if (c != '|')
+          {
+        if (c == EOF)
+          pm_error ("premature EOF");
+        else
+          pm_error ("bad format");
+        break;
+          }
+      }
+    while (! feof(file) && getc(file) != '\\') {};  /* scan for \enddata */
+    if (fscanf(file, "enddata{raster,%d", &discardid) != 1
+        || getc(file) != '}' || getc(file) != '\n')
+      pm_error ("missing end-of-object marker");
+}
+
+
+
+int
+main(int argc, char **argv) {
+
+    FILE *ifp;
+    register bit *bitrow, *bP;
+    int rows, cols, row, col, charcount;
+    unsigned char *data, mask;
+
+
+    pbm_init ( &argc, argv );
+
+    if ( argc > 2 )
+        pm_usage( "[raster obj]" );
+    
+    if ( argc == 2 )
+        ifp = pm_openr( argv[1] );
+    else
+        ifp = stdin;
+
+    ReadATKRaster( ifp, &cols, &rows, &data );
+
+    pm_close( ifp );
+
+    pbm_writepbminit( stdout, cols, rows, 0 );
+    bitrow = pbm_allocrow( cols );
+
+    for ( row = 0; row < rows; ++row )
+    {
+        charcount = 0;
+        mask = 0x80;
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+        {
+            if ( charcount >= 8 )
+            {
+                ++data;
+                charcount = 0;
+                mask = 0x80;
+            }
+            *bP = ( *data & mask ) ? PBM_BLACK : PBM_WHITE;
+            ++charcount;
+            mask >>= 1;
+        }
+        ++data;
+        pbm_writepbmrow( stdout, bitrow, cols, 0 );
+    }
+
+    pm_close( stdout );
+    exit( 0 );
+}
+
diff --git a/converter/pbm/brushtopbm.c b/converter/pbm/brushtopbm.c
new file mode 100644
index 00000000..0cffaa4d
--- /dev/null
+++ b/converter/pbm/brushtopbm.c
@@ -0,0 +1,107 @@
+/* brushtopbm.c - read a doodle brush file and write a portable bitmap
+**
+** Copyright (C) 1988 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 "pbm.h"
+
+static void getinit ARGS(( FILE* file, int* colsP, int* rowsP ));
+static bit getbit ARGS(( FILE* file ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, padright, row, col;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[brushfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    getinit( ifp, &cols, &rows );
+
+    pbm_writepbminit( stdout, cols, rows, 0 );
+    bitrow = pbm_allocrow( cols );
+
+    /* Compute padding to round cols up to the next multiple of 16. */
+    padright = ( ( cols + 15 ) / 16 ) * 16 - cols;
+
+    for ( row = 0; row < rows; ++row )
+	{
+	/* Get data. */
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+	    *bP = getbit( ifp );
+	/* Discard line padding. */
+        for ( col = 0; col < padright; ++col )
+	    (void) getbit( ifp );
+	/* Write row. */
+	pbm_writepbmrow( stdout, bitrow, cols, 0 );
+	}
+
+    pm_close( ifp );
+    pm_close( stdout );
+    
+    exit( 0 );
+    }
+
+
+static int item, bitsperitem, bitshift;
+
+static void
+getinit( file, colsP, rowsP )
+    FILE* file;
+    int* colsP;
+    int* rowsP;
+    {
+    int i;
+
+    if ( getc( file ) != 1 )
+	pm_error( "bad magic number 1" );
+    if ( getc( file ) != 0 )
+	pm_error( "bad magic number 2" );
+    *colsP = getc( file ) << 8;
+    *colsP += getc( file );
+    *rowsP = getc( file ) << 8;
+    *rowsP += getc( file );
+    bitsperitem = 8;
+
+    /* Junk rest of header. */
+    for ( i = 0; i < 10; ++i )  /* 10 is just a guess at the header size */
+	(void) getc( file );
+    }
+
+static bit
+getbit( file )
+    FILE* file;
+    {
+    bit b;
+
+    if ( bitsperitem == 8 )
+	{
+	item = getc( file );
+	bitsperitem = 0;
+	bitshift = 7;
+	}
+    ++bitsperitem;
+    b = ( ( item >> bitshift) & 1 ) ? PBM_WHITE : PBM_BLACK;
+    --bitshift;
+    return b;
+    }
diff --git a/converter/pbm/cmuwm.h b/converter/pbm/cmuwm.h
new file mode 100644
index 00000000..e667f25e
--- /dev/null
+++ b/converter/pbm/cmuwm.h
@@ -0,0 +1,17 @@
+/* cmuwm.h - definitions for the CMU window manager format
+*/
+
+#ifndef CMUWM_H_INCLUDED
+#define CMUWM_H_INCLUDED
+
+struct cmuwm_header
+    {
+    long magic;
+    long width;
+    long height;
+    short depth;
+    };
+
+#define CMUWM_MAGIC 0xf10040bbL
+
+#endif
diff --git a/converter/pbm/cmuwmtopbm.c b/converter/pbm/cmuwmtopbm.c
new file mode 100644
index 00000000..dce1d449
--- /dev/null
+++ b/converter/pbm/cmuwmtopbm.c
@@ -0,0 +1,114 @@
+/* cmuwmtopbm.c - read a CMU window manager bitmap and produce a portable bitmap
+**
+** 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 "pbm.h"
+#include "cmuwm.h"
+
+static void getinit ARGS(( FILE* file, int* colsP, int* rowsP, short* depthP, int* padrightP ));
+static bit getbit ARGS(( FILE* file ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, padright, row, col;
+    short depth;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[cmuwmfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    getinit( ifp, &cols, &rows, &depth, &padright );
+    if ( depth != 1 )
+	pm_error(
+	    "CMU window manager file has depth of %d, must be 1",
+	    (int) depth );
+
+    pbm_writepbminit( stdout, cols, rows, 0 );
+    bitrow = pbm_allocrow( cols );
+
+    for ( row = 0; row < rows; ++row )
+	{
+	/* Get data. */
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+	    *bP = getbit( ifp );
+	/* Discard line padding */
+        for ( col = 0; col < padright; ++col )
+	    (void) getbit( ifp );
+	pbm_writepbmrow( stdout, bitrow, cols, 0 );
+	}
+
+    pm_close( ifp );
+    pm_close( stdout );
+
+    exit( 0 );
+    }
+
+static int item, bitsperitem, bitshift;
+
+static void
+getinit( file, colsP, rowsP, depthP, padrightP )
+    FILE* file;
+    int* colsP;
+    int* rowsP;
+    short* depthP;
+    int* padrightP;
+    {
+    long l;
+
+    if ( pm_readbiglong( file, &l ) == -1 )
+	pm_error( "EOF / read error" );
+    if ( l != CMUWM_MAGIC )
+	pm_error( "bad magic number in CMU window manager file" );
+    if ( pm_readbiglong( file, &l ) == -1 )
+	pm_error( "EOF / read error" );
+    *colsP = (int) l;
+    if ( pm_readbiglong( file, &l ) == -1 )
+	pm_error( "EOF / read error" );
+    *rowsP = (int) l;
+    if ( pm_readbigshort( file, depthP ) == -1 )
+	pm_error( "EOF / read error" );
+    *padrightP = ( ( *colsP + 7 ) / 8 ) * 8 - *colsP;
+
+    bitsperitem = 0;
+    }
+
+static bit
+getbit( file )
+    FILE* file;
+    {
+    bit b;
+
+    if ( bitsperitem == 0 )
+	{
+	item = getc( file );
+	if ( item == EOF )
+	    pm_error( "EOF / read error" );
+	bitsperitem = 8;
+	bitshift = 7;
+	}
+    b = ( ( item >> bitshift) & 1 ) ? PBM_WHITE : PBM_BLACK;
+    --bitsperitem;
+    --bitshift;
+    return b;
+    }
diff --git a/converter/pbm/ddbugtopbm.c b/converter/pbm/ddbugtopbm.c
new file mode 100644
index 00000000..8b0a6d0e
--- /dev/null
+++ b/converter/pbm/ddbugtopbm.c
@@ -0,0 +1,173 @@
+/* ddbugtopbm - convert Diddle/DiddleBug sketch DB to PBM files.
+ * Copyright (c) 2002 Russell Marks. See COPYING for licence details.
+ *
+ * The decompression code (uncompress_sketch() is directly from
+ * DiddleBug itself, which like ddbugtopbm is distributed under the
+ * terms of the GNU GPL. As such, the following copyright applies to
+ * uncompress_sketch():
+ *
+ * Copyright (c) 1999,2000 Mitch Blevins <mblevin@debian.org>.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  
+ *
+ * Adapted to Netpbm by Bryan Henderson 2003.08.09.  Bryan got his copy
+ * from ftp:ibiblio.com/pub/linux/apps/graphics/convert, dated 2002.08.21.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "pm_c_util.h"
+#include "pbm.h"
+#include "mallocvar.h"
+
+/* this is basically UncompressSketch() from DiddleBug 2.50's diddlebug.c */
+static void 
+uncompress_sketch(unsigned char * const cPtr,
+                  unsigned char * const uPtr,
+                  int             const size) {
+    int i, j;
+    unsigned char num_bytes;
+
+    j = 0;
+    for (i=0; i<size; ++i) {
+        if (cPtr[i]&0x80) {
+            num_bytes = cPtr[i]&0x3f;  /* ~(0x40|0x80) */
+            ++num_bytes;
+            if (cPtr[i]&0x40) {
+                /* Mixed */
+                memmove(uPtr+j, cPtr+i+1, num_bytes);
+                i += num_bytes;
+            } else {
+                /* Black */
+                memset(uPtr+j, 0xff, num_bytes);
+            }
+        } else {
+            /* White */
+            num_bytes = cPtr[i]&0x7f; /* ~0x80 */
+            ++num_bytes;
+            memset(uPtr+j, 0x00, num_bytes);
+        }
+        j += num_bytes;
+    }
+}
+
+
+
+static const char *
+make_noname(void) {
+    static char name[128];
+    static int num=0;
+    FILE *out;
+
+    out = NULL;
+
+    do {
+        num++;
+        if (out != NULL) 
+            fclose(out);
+        sprintf(name, "sketch-%04d.pbm", num);
+    } while (num<10000 && (out = fopen(name, "rb")) != NULL);
+
+    if (num>=10000)
+        pm_error("too many unnamed sketches!");
+
+    return(name);
+}
+
+
+
+int 
+main(int argc, char ** argv) {
+    FILE * const in=stdin;
+
+    static unsigned char buf[64*1024];
+    int *recoffsets;
+    int f;
+    int numrecs;
+    bool is_diddle;
+
+    pbm_init(&argc, argv);
+
+    if (argc-1 > 0)
+        pm_error("Program takes no arguments.");
+
+    /* main DB header */
+    fread(buf, 1, 64, in);
+
+    fread(buf, 1, 14, in);
+    if (memcmp(buf, "DIDL", 4) != 0 && memcmp(buf, "DIDB", 4) !=0 )
+        pm_error("not a Diddle or DiddleBug DB.");
+    is_diddle = (memcmp(buf, "DIDL", 4) == 0);
+    numrecs = buf[12]*256+buf[13];
+
+    MALLOCARRAY_NOFAIL(recoffsets, numrecs);
+
+    for (f = 0; f < numrecs; ++f) {
+        fread(buf, 1, 8, in);
+        recoffsets[f] = ((buf[0]<<24)|(buf[1]<<16)|(buf[2]<<8)|buf[3]);
+    }
+
+    for (f = 0; f < numrecs; ++f) {
+        static unsigned char obuf[64*1024];
+        char * nameptr;
+        char * ptr;
+        const char * outfilename;
+        FILE *out;
+
+        fseek(in, recoffsets[f], SEEK_SET);
+        fread(buf, 1, 16*1024, in);  /* XXX crappy! */
+
+        if (is_diddle) {
+            /* Diddle */
+            memcpy(obuf, buf, 160*160/8);
+            nameptr=(char*)(buf+(160*160/8));
+        } else {
+            /* DiddleBug */
+            int const sketchlen = buf[8]*256+buf[9];
+            uncompress_sketch(buf+26+80+1, obuf, sketchlen-80-1);
+            nameptr = (char*)(buf+26+sketchlen);
+        }
+
+        for (ptr = nameptr; *ptr; ++ptr) {
+            if (!isalnum(*ptr) && strchr("()-_+=[]:;,.<>?",*ptr) == NULL)
+                *ptr='_';
+            if (isupper(*ptr)) 
+                *ptr = tolower(*ptr);
+        }
+
+        if (*nameptr==0)
+            outfilename = make_noname();
+        else {
+            strcat(nameptr, ".pbm");
+            outfilename = nameptr;
+        }
+
+
+        pm_message("extracting sketch %2d as `%s'", f, outfilename);
+        if((out=fopen(outfilename,"wb"))==NULL)
+            pm_message("WARNING: couldn't open file '%s'.  Carrying on...", 
+                       outfilename);
+        else {
+            pbm_writepbminit(out, 160, 160, FALSE);
+            fwrite(obuf,1,160*160/8,out);
+            fclose(out);
+        }
+    }
+    return 0;
+}
diff --git a/converter/pbm/escp2topbm.c b/converter/pbm/escp2topbm.c
new file mode 100644
index 00000000..049ed23c
--- /dev/null
+++ b/converter/pbm/escp2topbm.c
@@ -0,0 +1,143 @@
+/* escp2topbm.c - read an Epson ESC/P2 printer file and
+**                 create a pbm file from the raster data,
+**                 ignoring all other data.
+**                 Can be regarded as a simple raster printer emulator
+**                 with a RLE run length decoder.
+**                 This program was made primarily for the test of pbmtoescp2
+**
+** Copyright (C) 2003 by Ulrich Walcher (u.walcher@gmx.de)
+**                       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 <string.h>
+
+#include "pbm.h"
+#include "mallocvar.h"
+
+/* RLE decoder */
+static unsigned int 
+dec_epson_rle(unsigned        const int k, 
+              unsigned        const char * in, 
+              unsigned char * const out) {
+
+    unsigned int i;
+    unsigned int pos;
+    unsigned int dpos;
+
+    pos = 0;  /* initial value */
+    dpos = 0; /* initial value */
+
+    while (dpos < k) {
+        if (in[pos] < 128) {
+            for (i = 0; i < in[pos] + 1; ++i)
+                out[dpos+i] = in[pos + i + 1];     
+            /* copy through */
+            pos += i + 1;
+        } else {
+            for (i = 0; i < 257 - in[pos]; ++i)
+                out[dpos + i] = in[pos + 1];  
+            /* inflate this run */
+            pos += 2;
+        }
+        dpos += i;
+    }
+    return pos;        /* return number of treated input bytes */
+}
+
+
+
+int
+main(int    argc,
+     char * argv[]) {
+
+    unsigned int const size = 4096; /* arbitrary value */
+
+    FILE *ifP;
+    unsigned int i, len, pos, opos, width, height;
+    unsigned char *input, *output;
+    const char * fileName;
+
+    pbm_init(&argc, argv);
+
+    MALLOCARRAY(input, size);
+    MALLOCARRAY(output, size);
+    
+    if (input == NULL || output == NULL)
+        pm_error("Cannot allocate memory");
+
+    if (argc-1 > 1)
+        pm_error("Too many arguments (%u).  Only argument is filename.",
+                 argc-1);
+
+    if (argc == 2)
+        fileName = argv[1];
+    else
+        fileName = "-";
+
+    ifP = pm_openr(fileName);
+
+    /* read the whole file */
+    len = 0;  /* initial value */
+    for (i = 0; !feof(ifP); ++i) {
+        size_t bytesRead;
+        REALLOCARRAY(input, (i+1) * size);
+        if (input == NULL)
+            pm_error("Cannot allocate memory");
+        bytesRead = fread(input + i * size, 1, size, ifP);
+        len += bytesRead;
+    }
+
+    /* filter out raster data */
+    height = 0;  /* initial value */
+    pos = 0;     /* initial value */
+    opos = 0;    /* initial value */
+
+    while (pos < len) {
+        /* only ESC sequences are regarded  */
+        if (input[pos] == '\x1b' && input[pos+1] == '.') {
+            unsigned int const k =
+                input[pos+5] * ((input[pos+7] * 256 + input[pos+6] + 7) / 8);
+            height += input[pos+5];
+            width = input[pos+7] * 256 + input[pos+6];
+            REALLOCARRAY(output, opos + k);
+            if (output == NULL)
+                pm_error("Cannot allocate memory");
+
+            switch (input[pos+2]) {
+            case 0:
+                /* copy the data block */
+                memcpy(output + opos, input + pos + 8, k);        
+                pos += k + 8;
+                opos += k;
+                break;
+            case 1: {
+                /* inflate the data block */
+                unsigned int l;
+                l = dec_epson_rle(k,input+pos+8,output+opos);  
+                pos += l + 8;
+                opos += k;
+            }
+                break;
+            default:
+                pm_error("unknown compression mode");
+                break;
+            }
+        }
+        else
+            ++pos;      /* skip bytes outside the ESCX sequence */
+    }
+
+    pbm_writepbminit(stdout, width, height, 0);
+    fwrite(output, opos, 1, stdout);
+    free(input); free(output);
+    fclose(stdout); fclose(ifP);
+
+    return 0;
+}
diff --git a/converter/pbm/g3.h b/converter/pbm/g3.h
new file mode 100644
index 00000000..e982f2da
--- /dev/null
+++ b/converter/pbm/g3.h
@@ -0,0 +1,146 @@
+/* G3 fax format declarations */
+
+#ifndef G3_H_INCLUDED
+#define G3_H_INCLUDED
+
+/* G3 is nearly universal as the format for fax transmissions in the
+   US.  Its full name is CCITT Group 3 (G3).  It is specified in
+   Recommendations T.4 and T.30 and in EIA Standards EIA-465 and
+   EIA-466.  It dates to 1993.
+
+   G3 faxes are 204 dots per inch (dpi) horizontally and 98 dpi (196
+   dpi optionally, in fine-detail mode) vertically.  Since G3 neither
+   assumes error free transmission nor retransmits when errors occur,
+   the encoding scheme used is differential only over small segments
+   never exceeding 2 lines at standard resolution or 4 lines for
+   fine-detail. (The incremental G3 encoding scheme is called
+   two-dimensional and the number of lines so encoded is specified by
+   a parameter called k.)
+
+   G3 specifies much more than the format of the bit stream, which is
+   the subject of this header file.  It also specifies layers
+   underneath the bit stream.
+
+   There is also the newer G4.  
+*/
+
+typedef struct g3TableEntry {
+    short int code;
+    short int length;
+} g3TableEntry;
+
+static struct g3TableEntry ttable[] = {
+/*    TERMWHITE           TERMBLACK   */
+    { 0x35, 8 },    { 0x37, 10 },       /* white 0 , black 0 */
+    { 0x07, 6 },    { 0x02,  3 },
+    { 0x07, 4 },    { 0x03,  2 },
+    { 0x08, 4 },    { 0x02,  2 },
+    { 0x0b, 4 },    { 0x03,  3 },
+    { 0x0c, 4 },    { 0x03,  4 },
+    { 0x0e, 4 },    { 0x02,  4 },
+    { 0x0f, 4 },    { 0x03,  5 },
+    { 0x13, 5 },    { 0x05,  6 },
+    { 0x14, 5 },    { 0x04,  6 },
+    { 0x07, 5 },    { 0x04,  7 },
+    { 0x08, 5 },    { 0x05,  7 },
+    { 0x08, 6 },    { 0x07,  7 },
+    { 0x03, 6 },    { 0x04,  8 },
+    { 0x34, 6 },    { 0x07,  8 },
+    { 0x35, 6 },    { 0x18,  9 },
+    { 0x2a, 6 },    { 0x17, 10 },
+    { 0x2b, 6 },    { 0x18, 10 },
+    { 0x27, 7 },    { 0x08, 10 },
+    { 0x0c, 7 },    { 0x67, 11 },
+    { 0x08, 7 },    { 0x68, 11 },
+    { 0x17, 7 },    { 0x6c, 11 },
+    { 0x03, 7 },    { 0x37, 11 },
+    { 0x04, 7 },    { 0x28, 11 },
+    { 0x28, 7 },    { 0x17, 11 },
+    { 0x2b, 7 },    { 0x18, 11 },
+    { 0x13, 7 },    { 0xca, 12 },
+    { 0x24, 7 },    { 0xcb, 12 },
+    { 0x18, 7 },    { 0xcc, 12 },
+    { 0x02, 8 },    { 0xcd, 12 },
+    { 0x03, 8 },    { 0x68, 12 },
+    { 0x1a, 8 },    { 0x69, 12 },
+    { 0x1b, 8 },    { 0x6a, 12 },
+    { 0x12, 8 },    { 0x6b, 12 },
+    { 0x13, 8 },    { 0xd2, 12 },
+    { 0x14, 8 },    { 0xd3, 12 },
+    { 0x15, 8 },    { 0xd4, 12 },
+    { 0x16, 8 },    { 0xd5, 12 },
+    { 0x17, 8 },    { 0xd6, 12 },
+    { 0x28, 8 },    { 0xd7, 12 },
+    { 0x29, 8 },    { 0x6c, 12 },
+    { 0x2a, 8 },    { 0x6d, 12 },
+    { 0x2b, 8 },    { 0xda, 12 },
+    { 0x2c, 8 },    { 0xdb, 12 },
+    { 0x2d, 8 },    { 0x54, 12 },
+    { 0x04, 8 },    { 0x55, 12 },
+    { 0x05, 8 },    { 0x56, 12 },
+    { 0x0a, 8 },    { 0x57, 12 },
+    { 0x0b, 8 },    { 0x64, 12 },
+    { 0x52, 8 },    { 0x65, 12 },
+    { 0x53, 8 },    { 0x52, 12 },
+    { 0x54, 8 },    { 0x53, 12 },
+    { 0x55, 8 },    { 0x24, 12 },
+    { 0x24, 8 },    { 0x37, 12 },
+    { 0x25, 8 },    { 0x38, 12 },
+    { 0x58, 8 },    { 0x27, 12 },
+    { 0x59, 8 },    { 0x28, 12 },
+    { 0x5a, 8 },    { 0x58, 12 },
+    { 0x5b, 8 },    { 0x59, 12 },
+    { 0x4a, 8 },    { 0x2b, 12 },
+    { 0x4b, 8 },    { 0x2c, 12 },
+    { 0x32, 8 },    { 0x5a, 12 },
+    { 0x33, 8 },    { 0x66, 12 },
+    { 0x34, 8 },    { 0x67, 12 },       /* white 63 , black 63 */
+
+/* mtable */    
+/*    MKUPWHITE           MKUPBLACK   */
+    { 0x00, 0 },    { 0x00,  0 },   /* dummy to simplify pointer math */
+    { 0x1b, 5 },    { 0x0f, 10 },   /* white 64 , black 64 */
+    { 0x12, 5 },    { 0xc8, 12 },
+    { 0x17, 6 },    { 0xc9, 12 },
+    { 0x37, 7 },    { 0x5b, 12 },
+    { 0x36, 8 },    { 0x33, 12 },
+    { 0x37, 8 },    { 0x34, 12 },
+    { 0x64, 8 },    { 0x35, 12 },
+    { 0x65, 8 },    { 0x6c, 13 },
+    { 0x68, 8 },    { 0x6d, 13 },
+    { 0x67, 8 },    { 0x4a, 13 },
+    { 0xcc, 9 },    { 0x4b, 13 },
+    { 0xcd, 9 },    { 0x4c, 13 },
+    { 0xd2, 9 },    { 0x4d, 13 },
+    { 0xd3, 9 },    { 0x72, 13 },
+    { 0xd4, 9 },    { 0x73, 13 },
+    { 0xd5, 9 },    { 0x74, 13 },
+    { 0xd6, 9 },    { 0x75, 13 },
+    { 0xd7, 9 },    { 0x76, 13 },
+    { 0xd8, 9 },    { 0x77, 13 },
+    { 0xd9, 9 },    { 0x52, 13 },
+    { 0xda, 9 },    { 0x53, 13 },
+    { 0xdb, 9 },    { 0x54, 13 },
+    { 0x98, 9 },    { 0x55, 13 },
+    { 0x99, 9 },    { 0x5a, 13 },
+    { 0x9a, 9 },    { 0x5b, 13 },
+    { 0x18, 6 },    { 0x64, 13 },
+    { 0x9b, 9 },    { 0x65, 13 },
+    { 0x08, 11 },   { 0x08, 11 },        /* extable len = 1792 */
+    { 0x0c, 11 },   { 0x0c, 11 },
+    { 0x0d, 11 },   { 0x0d, 11 },
+    { 0x12, 12 },   { 0x12, 12 },
+    { 0x13, 12 },   { 0x13, 12 },
+    { 0x14, 12 },   { 0x14, 12 },
+    { 0x15, 12 },   { 0x15, 12 },
+    { 0x16, 12 },   { 0x16, 12 },
+    { 0x17, 12 },   { 0x17, 12 },
+    { 0x1c, 12 },   { 0x1c, 12 },
+    { 0x1d, 12 },   { 0x1d, 12 },
+    { 0x1e, 12 },   { 0x1e, 12 },
+    { 0x1f, 12 },   { 0x1f, 12 },
+};
+
+#define mtable ((ttable)+64*2)
+
+#endif
diff --git a/converter/pbm/g3topbm.c b/converter/pbm/g3topbm.c
new file mode 100644
index 00000000..1eefee96
--- /dev/null
+++ b/converter/pbm/g3topbm.c
@@ -0,0 +1,739 @@
+/*===========================================================================
+                            g3topbm
+=============================================================================
+
+  This program reads a Group 3 FAX file and produces a PBM image.
+
+  Bryan Henderson wrote this on August 5, 2004 and contributed it to 
+  the public domain.
+
+  This program is designed to be a drop-in replacement for the program
+  of the same name that was distributed with Pbmplus and Netpbm since
+  1989, written by Paul Haeberli <paul@manray.sgi.com>.
+
+  Bryan used ideas on processing G3 data from Haeberli's code, but did
+  not use any of the code.
+
+  Others have modified the program since Bryan's initial work, each
+  contributing their work to the public domain.
+===========================================================================*/
+
+#include "pbm.h"
+#include "shhopt.h"
+#include "nstring.h"
+#include "mallocvar.h"
+#include "g3.h"
+#include "bitreverse.h"
+
+#define MAXCOLS 10800
+#define MAXROWS 14400   /* this allows up to two pages of image */
+
+#define WHASHA 3510
+#define WHASHB 1178
+
+#define BHASHA 293
+#define BHASHB 2695
+
+#define HASHSIZE 1021
+
+static g3TableEntry * whash[HASHSIZE];
+static g3TableEntry * bhash[HASHSIZE];
+
+
+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 reversebits;
+    unsigned int kludge;
+    unsigned int stretch;
+    unsigned int stop_error;
+    unsigned int expectedLineSize;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** const 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;  /* malloc'ed */
+        /* Instructions to OptParseOptions3 on how to parse our options.  */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int widthSpec, paper_sizeSpec;
+    const char * paperSize;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "reversebits",      OPT_FLAG,  NULL, &cmdlineP->reversebits,
+            0);
+    OPTENT3(0, "kludge",           OPT_FLAG,  NULL, &cmdlineP->kludge,
+            0);
+    OPTENT3(0, "stretch",          OPT_FLAG,  NULL, &cmdlineP->stretch, 
+            0);
+    OPTENT3(0, "stop_error",       OPT_FLAG,  NULL, &cmdlineP->stop_error, 
+            0);
+    OPTENT3(0, "width",            OPT_UINT,  &cmdlineP->expectedLineSize,
+            &widthSpec,                0);
+    OPTENT3(0, "paper_size",       OPT_STRING, &paperSize,
+            &paper_sizeSpec,           0);
+    
+    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 (widthSpec && paper_sizeSpec)
+        pm_error("You can't specify both -width and -paper_size");
+
+    if (widthSpec) {
+        if (cmdlineP->expectedLineSize < 1)
+            pm_error("-width must be at least 1");
+    } else if (paper_sizeSpec) {
+        if (STRCASEEQ(paperSize, "A6"))
+            cmdlineP->expectedLineSize = 864;
+        else if (STRCASEEQ(paperSize, "A5"))
+            cmdlineP->expectedLineSize = 1216;
+        else if (STRCASEEQ(paperSize, "A4"))
+            cmdlineP->expectedLineSize = 1728;
+        else if (STRCASEEQ(paperSize, "B4"))
+            cmdlineP->expectedLineSize = 2048;
+        else if (STRCASEEQ(paperSize, "A3"))
+            cmdlineP->expectedLineSize = 2432;
+        else
+            pm_error("Unrecognized value for -paper_size '%s'.  "
+                     "We recognize only A3, A4, A5, A6, and B4.",
+                     paperSize);
+    } else
+        cmdlineP->expectedLineSize = 0;
+
+    if (argc-1 == 0) 
+        cmdlineP->inputFilespec = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
+    else
+        cmdlineP->inputFilespec = argv[1];
+}
+
+
+
+struct bitStream {
+
+    FILE * fileP;
+    bool reversebits;
+    int shdata;
+        /* 8-bit buffer for rawgetbit(). */
+    unsigned int shbit;
+        /* single bit mask for the bit of 'shdata' that is next in the stream.
+           zero when 'shdata' is empty.
+        */
+    unsigned int zeroBitCount;
+        /* Number of consecutive zero bits the stream has seen.  Note that
+           because an EOL mark ends in a one bit, this starts over for each
+           line.
+        */
+};
+
+
+
+static void
+readBit(struct bitStream * const bitStreamP,
+        unsigned int *     const bitP,
+        const char **      const errorP) {
+/*----------------------------------------------------------------------------
+   Return the next raw bit from the G3 input stream.
+
+   Do not call this outside of the bit stream object; Caller is responsible
+   for maintaining object state.
+-----------------------------------------------------------------------------*/
+    *errorP = NULL;  /* initial assumption */
+
+    if ((bitStreamP->shbit & 0xff) == 0) {
+        bitStreamP->shdata = getc(bitStreamP->fileP);
+        if (bitStreamP->shdata == EOF)
+            asprintfN(errorP, "EOF or error reading file");
+        else {
+            bitStreamP->shbit = 0x80;
+            if ( bitStreamP->reversebits )
+                bitStreamP->shdata = bitreverse[ bitStreamP->shdata ];
+            }
+    }
+
+    if (bitStreamP->shdata & bitStreamP->shbit)
+        *bitP = 1;
+    else
+        *bitP = 0;
+
+    bitStreamP->shbit >>= 1;
+}
+
+
+
+static void
+readBitAndDetectEol(struct bitStream * const bitStreamP,
+                    unsigned int *     const bitP,
+                    bool *             const eolP,
+                    const char **      const errorP) {
+/*----------------------------------------------------------------------------
+   Same as readBit(), but iff the bit read is the final bit of an EOL
+   mark, return *eolP == TRUE.
+-----------------------------------------------------------------------------*/
+    readBit(bitStreamP, bitP, errorP);
+    if (!*errorP) {
+        bool eol;
+
+        eol = FALSE;  /* initial assumption */
+        if (*bitP == 0)
+            ++bitStreamP->zeroBitCount;
+        else {
+            if (bitStreamP->zeroBitCount >= 11)
+                eol = TRUE;
+            bitStreamP->zeroBitCount = 0;
+        }
+        *eolP = eol;
+    }
+}
+
+
+static void
+initBitStream(struct bitStream * const bitStreamP,
+              FILE *             const fileP,
+              bool               const reversebits) {
+    
+    bitStreamP->fileP        = fileP;
+    bitStreamP->reversebits  = reversebits;
+    bitStreamP->shbit        = 0x00;
+    bitStreamP->zeroBitCount = 0;
+}
+
+
+
+static void
+skipToNextLine(struct bitStream * const bitStreamP) {
+
+    bool eol;
+    const char * error;
+
+    eol = FALSE;
+    error = NULL;
+    
+    while (!eol && !error) {
+        unsigned int bit;
+        
+        readBitAndDetectEol(bitStreamP, &bit, &eol, &error);
+    }
+}
+
+
+
+static void
+addtohash(g3TableEntry *     hash[], 
+          g3TableEntry       table[], 
+          unsigned int const n, 
+          int          const a, 
+          int          const b) {
+    
+    unsigned int i;
+
+    for (i = 0; i < n; ++i) {
+        g3TableEntry * const teP = &table[i*2];
+        unsigned int const pos =
+            ((teP->length + a) * (teP->code + b)) % HASHSIZE;
+        if (hash[pos])
+            pm_error("internal error: addtohash fatal hash collision");
+        hash[pos] = teP;
+    }
+}
+
+
+
+static g3TableEntry*
+hashfind(g3TableEntry *       hash[], 
+         int          const length, 
+         int          const code, 
+         int          const a, 
+         int          const b) {
+
+    unsigned int pos;
+    g3TableEntry * te;
+
+    pos = ((length + a) * (code + b)) % HASHSIZE;
+    te = hash[pos];
+    return ((te && te->length == length && te->code == code) ? te : 0);
+}
+
+
+
+static void
+buildHashes(g3TableEntry * (*whashP)[HASHSIZE],
+            g3TableEntry * (*bhashP)[HASHSIZE]) {
+
+    unsigned int i;
+
+    for (i = 0; i < HASHSIZE; ++i)
+        (*whashP)[i] = (*bhashP)[i] = NULL;
+
+    addtohash(*whashP, &ttable[0], 64, WHASHA, WHASHB);
+    addtohash(*whashP, &mtable[2], 40, WHASHA, WHASHB);
+
+    addtohash(*bhashP, &ttable[1], 64, BHASHA, BHASHB);
+    addtohash(*bhashP, &mtable[3], 40, BHASHA, BHASHB);
+
+}
+
+
+
+static void
+makeRowWhite(bit *        const bitrow,
+             unsigned int const cols) {
+
+    unsigned int col;
+    for (col = 0; col < MAXCOLS; ++col)
+        bitrow[col] = PBM_WHITE;
+}
+
+
+
+static g3TableEntry *
+g3code(unsigned int const curcode,
+       unsigned int const curlen,
+       bit          const color) {
+
+    g3TableEntry * retval;
+
+    switch (color) {
+    case PBM_WHITE:
+        if (curlen < 4)
+            retval = NULL;
+        else
+            retval = hashfind(whash, curlen, curcode, WHASHA, WHASHB);
+        break;
+    case PBM_BLACK:
+        if (curlen < 2)
+            retval = NULL;
+        else
+            retval = hashfind(bhash, curlen, curcode, BHASHA, BHASHB);
+        break;
+    default:
+        pm_error("INTERNAL ERROR: color is not black or white");
+    }
+    return retval;
+}
+
+
+
+enum g3tableId {TERMWHITE, TERMBLACK, MKUPWHITE, MKUPBLACK};
+
+
+
+static void
+processG3Code(g3TableEntry * const teP,
+              bit *          const bitrow,
+              unsigned int * const colP,
+              bit *          const colorP,
+              unsigned int * const countP) {
+              
+    enum g3tableId const teId =
+        (teP > mtable ? 2 : 0) + (teP - ttable) % 2;
+
+    unsigned int teCount;
+    
+    switch(teId) {
+    case TERMWHITE: teCount = (teP - ttable    ) / 2;      break;
+    case TERMBLACK: teCount = (teP - ttable - 1) / 2;      break;
+    case MKUPWHITE: teCount = (teP - mtable    ) / 2 * 64; break;
+    case MKUPBLACK: teCount = (teP - mtable - 1) / 2 * 64; break;
+    }
+
+    switch (teId) {
+    case TERMWHITE:
+    case TERMBLACK: {
+        unsigned int runLengthSoFar;
+        unsigned int col;
+        
+        col = *colP;
+        runLengthSoFar = MIN(*countP + teCount, MAXCOLS - col);
+
+        if (runLengthSoFar > 0) {
+            if (*colorP == PBM_WHITE) {
+                /* Row was initialized to white, so we just skip */
+                col += runLengthSoFar;
+            } else {
+                unsigned int i;
+                for (i = 0; i < runLengthSoFar; ++i)
+                    bitrow[col++] = PBM_BLACK;
+            }
+        }
+        *colorP = !*colorP;
+        *countP = 0;
+        *colP   = col;
+    } break;
+    case MKUPWHITE:
+    case MKUPBLACK:
+        *countP += teCount;
+        break;
+    default:
+        pm_error("Can't happen");
+    }
+}
+
+
+
+static void
+formatBadCodeException(const char ** const exceptionP,
+                       unsigned int  const col,
+                       unsigned int  const curlen,
+                       unsigned int  const curcode) {
+
+    asprintfN(exceptionP,
+        "bad code word at Column %u.  "
+        "No prefix of the %u bits 0x%x matches any recognized "
+        "code word and no code words longer than 12 bits are "
+        "defined.  ",
+        col, curlen, curcode);
+}
+
+
+
+static void
+readFaxRow(struct bitStream * const bitStreamP,
+           bit *              const bitrow,
+           unsigned int *     const lineLengthP,
+           const char **      const exceptionP,
+           const char **      const errorP) {
+/*----------------------------------------------------------------------------
+  Read one line of G3 fax from the bit stream *bitStreamP into 
+  bitrow[].  Return the length of the line, in pixels, as *lineLengthP.
+
+  If there's a problem with the line, return as much of it as we can,
+  advance the input stream past the next EOL mark, and put a text
+  description of the problem in newly malloc'ed storage at
+  *exceptionP.  If there's no problem, return *exceptionP = NULL.
+
+  We guarantee that we make progress through the input stream.
+
+  Iff there is an error, return a text description of it in newly
+  malloc'ed storage at *errorP and all other specified behavior 
+  (including return values) is unspecified.
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+    unsigned int curlen;  
+        /* Number of bits we've read so far for the code we're currently 
+           reading
+        */
+    unsigned int curcode; 
+        /* What we've assembled so far of the code we're currently reading */
+    unsigned int count;
+        /* Number of consecutive pixels of the same color */
+    bit currentColor;
+        /* The color of the current run of pixels */
+    g3TableEntry * te;
+        /* Address of structure that describes the current G3 code */
+    bool done;
+
+    makeRowWhite(bitrow, MAXCOLS);  /* initialize row */
+
+    col = 0;
+    curlen = 0;
+    curcode = 0;
+    currentColor = PBM_WHITE;
+    count = 0;
+    *exceptionP = NULL;
+    *errorP = NULL;
+    done = FALSE;
+
+    while (!done) {
+        if (col >= MAXCOLS) {
+            asprintfN(exceptionP, "Line is too long for this program to "
+                      "handle -- longer than %u columns", MAXCOLS);
+            done = TRUE;
+        } else {
+            unsigned int bit;
+            bool eol;
+            const char * error;
+
+            readBitAndDetectEol(bitStreamP, &bit, &eol, &error);
+            if (error) {
+                if (col > 0)
+                    /* We got at least some of the row, so it's only an
+                       exception, not a fatal error.
+                    */
+                    *exceptionP = error;
+                else
+                    *errorP = error;
+                done = TRUE;
+            } else if (eol)
+                done = TRUE;
+            else {
+                curcode = (curcode << 1) | bit;
+                curlen++;
+            
+                if (curlen > 13) {
+                    formatBadCodeException(exceptionP, col, curlen, curcode);
+                    done = TRUE;
+                } else if (curcode != 0) {
+                    te = g3code(curcode, curlen, currentColor);
+                    
+                    if (te) {
+                        processG3Code(te, bitrow, &col, &currentColor, &count);
+                        
+                        curcode = 0;
+                        curlen = 0;
+                    }
+                }
+            }
+        }
+    }
+    if (*exceptionP)
+        skipToNextLine(bitStreamP);
+
+    *lineLengthP = col;
+}
+
+
+
+static void
+freeBits(bit **       const bits,
+         unsigned int const rows,
+         bool         const stretched) {
+
+    unsigned int row;
+
+    for (row = 0; row < rows; ++row) {
+        if (stretched && row % 2 == 1) {
+            /* This is just a pointer to the previous row; don't want to
+               free it twice.
+            */
+        } else 
+            pbm_freerow(bits[row]);
+    }
+    free(bits);
+}
+
+
+
+static void
+handleRowException(const char * const exception,
+                   const char * const error,
+                   unsigned int const row,
+                   bool         const tolerateErrors) {
+
+
+    if (exception) {
+        if (tolerateErrors)
+            pm_message("Problem reading Row %u.  Skipping rest of row.  %s",
+                       row, exception);
+        else
+            pm_error("Problem reading Row %u.  Aborting.  %s", row, exception);
+        strfree(exception);
+    }
+
+    if (error) {
+        if (tolerateErrors)
+            pm_message("Unable to read Row %u.  Skipping rest of image.  %s",
+                       row, error);
+        else
+            pm_error("Unable to read Row %u.  Aborting.  %s", row, error);
+        strfree(error);
+    }
+}
+
+
+
+typedef struct {
+    unsigned int expectedLineSize;
+        /* The size that lines are supposed to be.  Zero means we're happy
+           with any size.
+        */
+    unsigned int maxLineSize;
+        /* The maximum line size we have seen so far, or zero if we have
+           not seen any lines yet.
+        */
+    bool warned;
+        /* We have warned the user that he has a line length problem */
+    bool tolerateErrors;
+        /* Try to continue when we detect a line size error, as opposed to
+           aborting the program.
+        */
+} lineSizeAnalyzer;
+
+
+
+static void
+initializeLineSizeAnalyzer(lineSizeAnalyzer * const analyzerP,
+                           unsigned int       const expectedLineSize,
+                           bool               const tolerateErrors) {
+
+    analyzerP->expectedLineSize = expectedLineSize;
+    analyzerP->tolerateErrors   = tolerateErrors;
+
+    analyzerP->maxLineSize = 0;
+    analyzerP->warned      = FALSE;
+}
+
+
+
+static void
+analyzeLineSize(lineSizeAnalyzer * const analyzerP,
+                unsigned int       const thisLineSize) {
+
+    const char * error;
+
+    if (analyzerP->expectedLineSize &&
+        thisLineSize != analyzerP->expectedLineSize)
+        asprintfN(&error, "Image contains a line of %u pixels.  "
+                  "You specified lines should be %u pixels.",
+                  thisLineSize, analyzerP->expectedLineSize);
+    else {
+        if (analyzerP->maxLineSize && thisLineSize != analyzerP->maxLineSize)
+            asprintfN(&error, "There are at least two different "
+                      "line lengths in this image, "
+                      "%u pixels and %u pixels.  "
+                      "This is a violation of the G3 standard.  ",
+                      thisLineSize, analyzerP->maxLineSize);
+        else
+            error = NULL;
+    }
+
+    if (error) {
+        if (analyzerP->tolerateErrors) {
+            if (!analyzerP->warned) {
+                pm_message("Warning: %s.", error);
+                analyzerP->warned = TRUE;
+            }
+        } else
+            pm_error("%s", error);
+
+        strfree(error);
+    }
+    analyzerP->maxLineSize = MAX(thisLineSize, analyzerP->maxLineSize);
+}
+
+
+
+/* An empty line means EOF.  An ancient comment in the code said there
+   is supposed to 6 EOL marks in a row to indicate EOF, but the code
+   checked for 3 and considered 2 in row just to mean a zero length
+   line.  Starting in Netpbm 10.24 (August 2004), we assume there is
+   no valid reason to have an empty line and recognize EOF as any
+   empty line.  Alternatively, we could read off and ignore two empty
+   lines without a 3rd.  
+*/
+
+static void
+readFax(struct bitStream * const bitStreamP,
+        bool               const stretch,
+        unsigned int       const expectedLineSize,
+        bool               const tolerateErrors,
+        bit ***            const bitsP,
+        unsigned int *     const colsP,
+        unsigned int *     const rowsP) {
+
+    lineSizeAnalyzer lineSizeAnalyzer;
+    bit ** bits;
+    const char * error;
+    bool eof;
+    unsigned int row;
+    
+    MALLOCARRAY_NOFAIL(bits, MAXROWS);
+
+    initializeLineSizeAnalyzer(&lineSizeAnalyzer,
+                               expectedLineSize, tolerateErrors);
+
+    eof = FALSE;
+    error = NULL;
+    row = 0;
+
+    while (!eof && !error) {
+        unsigned int lineSize;
+
+        if (row >= MAXROWS)
+            asprintfN(&error, "Image is too tall.  This program can "
+                      "handle at most %u rows", MAXROWS);
+        else {
+            const char * exception;
+
+            bits[row] = pbm_allocrow(MAXCOLS);
+            readFaxRow(bitStreamP, bits[row], &lineSize, &exception, &error);
+
+            handleRowException(exception, error, row, tolerateErrors);
+
+            if (!error) {
+                if (lineSize == 0) {
+                    /* EOF.  See explanation above */
+                    eof = TRUE;
+                } else {
+                    analyzeLineSize(&lineSizeAnalyzer, lineSize);
+                    
+                    if (stretch) {
+                        ++row;
+                        if (row >= MAXROWS)
+                            asprintfN(&error, "Image is too tall.  This "
+                                      "program can handle at most %u rows "
+                                      "after stretching", MAXROWS);
+                        else
+                            bits[row] = bits[row-1];
+                    }
+                    ++row;
+                }
+            }
+        }
+    }
+    *rowsP  = row;
+    *colsP  = lineSizeAnalyzer.maxLineSize;
+    *bitsP  = bits;
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    struct bitStream bitStream;
+    unsigned int rows, cols;
+    bit ** bits;
+
+    pbm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    initBitStream(&bitStream, ifP, cmdline.reversebits);
+
+    if (cmdline.kludge) {
+        /* Skip extra lines to get in sync. */
+        skipToNextLine(&bitStream);
+        skipToNextLine(&bitStream);
+        skipToNextLine(&bitStream);
+    }
+    skipToNextLine(&bitStream);
+
+    buildHashes(&whash, &bhash);
+
+    readFax(&bitStream, cmdline.stretch, cmdline.expectedLineSize,
+            !cmdline.stop_error, 
+            &bits, &cols, &rows);
+
+    pm_close(ifP);
+
+    pbm_writepbm(stdout, bits, cols, rows, 0);
+    pm_close(stdout);
+
+    freeBits(bits, rows, cmdline.stretch);
+
+    return 0;
+}
diff --git a/converter/pbm/icontopbm.c b/converter/pbm/icontopbm.c
new file mode 100644
index 00000000..d6dba8ae
--- /dev/null
+++ b/converter/pbm/icontopbm.c
@@ -0,0 +1,159 @@
+/* icontopbm.c - read a Sun icon file and produce a portable bitmap
+**
+** Copyright (C) 1988 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 "nstring.h"
+#include "pbm.h"
+
+/* size in bytes of a bitmap */
+#define BitmapSize(width, height) (((((width) + 15) >> 3) &~ 1) * (height))
+
+static void
+ReadIconFile(FILE *                const file, 
+             int *                 const widthP, 
+             int *                 const heightP, 
+             short unsigned int ** const dataP) {
+
+    char variable[80+1];
+    int ch;
+    int status, value, i, data_length, gotsome;
+
+    gotsome = 0;
+    *widthP = *heightP = -1;
+    for ( ; ; )
+    {
+        while ( ( ch = getc( file ) ) == ',' || ch == '\n' || ch == '\t' ||
+                ch == ' ' )
+            ;
+        for ( i = 0;
+              ch != '=' && ch != ',' && ch != '\n' && ch != '\t' && 
+                  ch != ' ' && (i < (sizeof(variable) - 1));
+              i++ )
+        {
+            variable[i] = ch;
+            if ((ch = getc( file )) == EOF)
+                pm_error( "invalid input file -- premature EOF" );
+        }
+        variable[i] = '\0';
+
+        if ( STREQ( variable, "*/" )&& gotsome )
+            break;
+
+        if ( fscanf( file, "%d", &value ) != 1 )
+            continue;
+
+        if ( STREQ( variable, "Width" ) )
+        {
+            *widthP = value;
+            gotsome = 1;
+        }
+        else if ( STREQ( variable, "Height" ) )
+        {
+            *heightP = value;
+            gotsome = 1;
+        }
+        else if ( STREQ( variable, "Depth" )  )
+        {
+            if ( value != 1 )
+                pm_error( "invalid depth" );
+            gotsome = 1;
+        }
+        else if ( STREQ( variable, "Format_version" ) )
+        {
+            if ( value != 1 )
+                pm_error( "invalid Format_version" );
+            gotsome = 1;
+        }
+        else if ( STREQ( variable, "Valid_bits_per_item" ) )
+        {
+            if ( value != 16 )
+                pm_error( "invalid Valid_bits_per_item" );
+            gotsome = 1;
+        }
+    }
+
+    if ( *widthP <= 0 )
+        pm_error( "invalid width (must be positive): %d", *widthP );
+    if ( *heightP <= 0 )
+        pm_error( "invalid height (must be positive): %d", *heightP );
+
+    data_length = BitmapSize( *widthP, *heightP );
+    *dataP = (short unsigned int *) malloc( data_length );
+    if ( *dataP == NULL )
+        pm_error( "out of memory" );
+    data_length /= sizeof( short );
+    
+    for ( i = 0 ; i < data_length; i++ )
+    {
+        if ( i == 0 )
+            status = fscanf( file, " 0x%4hx", *dataP );
+        else
+            status = fscanf( file, ", 0x%4hx", *dataP + i );
+        if ( status != 1 )
+            pm_error( "error 4 scanning bits item" );
+    }
+}
+
+
+
+int
+main(int  argc, char ** argv) {
+
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, row, col, shortcount, mask;
+    short unsigned int * data;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+        pm_usage( "[iconfile]" );
+
+    if ( argc == 2 )
+        ifp = pm_openr( argv[1] );
+    else
+        ifp = stdin;
+
+    ReadIconFile( ifp, &cols, &rows, &data );
+
+    pm_close( ifp );
+
+    pbm_writepbminit( stdout, cols, rows, 0 );
+    bitrow = pbm_allocrow( cols );
+
+    for ( row = 0; row < rows; row++ )
+    {
+        shortcount = 0;
+        mask = 0x8000;
+        for ( col = 0, bP = bitrow; col < cols; col++, bP++ )
+        {
+            if ( shortcount >= 16 )
+            {
+                data++;
+                shortcount = 0;
+                mask = 0x8000;
+            }
+            *bP = ( *data & mask ) ? PBM_BLACK : PBM_WHITE;
+            shortcount++;
+            mask = mask >> 1;
+        }
+        data++;
+        pbm_writepbmrow( stdout, bitrow, cols, 0 );
+    }
+
+    pm_close( stdout );
+    exit( 0 );
+}
+
diff --git a/converter/pbm/macp.h b/converter/pbm/macp.h
new file mode 100644
index 00000000..26a720a2
--- /dev/null
+++ b/converter/pbm/macp.h
@@ -0,0 +1,12 @@
+/* macp.h - header file for MacPaint files
+*/
+
+#ifndef MACP_H_INCLUDED
+#define MACP_H_INCLUDED
+
+#define	HEADER_LENGTH	512
+#define	MAX_LINES	720
+#define	BYTES_WIDE	72
+#define MAX_COLS	576	/* = BYTES_WIDE * 8 */
+
+#endif
diff --git a/converter/pbm/macptopbm.c b/converter/pbm/macptopbm.c
new file mode 100644
index 00000000..f4a341d3
--- /dev/null
+++ b/converter/pbm/macptopbm.c
@@ -0,0 +1,140 @@
+/* macptopbm.c - read a MacPaint file and produce a portable bitmap
+**
+** Copyright (C) 1988 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 "pbm.h"
+#include "macp.h"
+
+static void ReadMacPaintFile ARGS(( FILE* file, int extraskip, int* scanLineP, unsigned char Pic[MAX_LINES][BYTES_WIDE] ));
+
+static unsigned char Pic[MAX_LINES][BYTES_WIDE];
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    int argn, extraskip, scanLine, rows, cols, row, bcol, i;
+    const char* usage = "[-extraskip N] [macpfile]";
+
+
+    pbm_init( &argc, argv );
+
+    argn = 1;
+    extraskip = 0;
+
+    /* Check for flags. */
+    if ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
+	{
+	if ( pm_keymatch( argv[argn], "-extraskip", 2 ) )
+	    {
+	    argn++;
+	    if ( argn == argc || sscanf( argv[argn], "%d", &extraskip ) != 1 )
+		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 );
+
+    ReadMacPaintFile( ifp, extraskip, &scanLine, Pic );
+
+    pm_close( ifp );
+
+    cols = BYTES_WIDE * 8;
+    rows = scanLine;
+    pbm_writepbminit( stdout, cols, rows, 0 );
+    bitrow = pbm_allocrow( cols );
+
+    for ( row = 0; row < rows; row++ )
+	{
+	for ( bcol = 0; bcol < BYTES_WIDE; bcol++ )
+	    for ( i = 0; i < 8; i++ )
+		bitrow[bcol * 8 + i] =
+		    ( (Pic[row][bcol] >> (7 - i)) & 1 ) ? PBM_BLACK : PBM_WHITE;
+	pbm_writepbmrow( stdout, bitrow, cols, 0 );
+	}
+
+    pm_close( stdout );
+    exit( 0 );
+    }
+
+/*
+** Some of the following routine is:
+**
+**                Copyright 1987 by Patrick J. Naughton
+**                         All Rights Reserved
+** 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.
+*/
+
+static void
+ReadMacPaintFile( file, extraskip, scanLineP, Pic )
+    FILE* file;
+    int extraskip;
+    int* scanLineP;
+    unsigned char Pic[MAX_LINES][BYTES_WIDE];
+    {
+    unsigned int i, j, k;
+    unsigned char ch;
+
+    /* Skip over the header. */
+    for ( i = 0; i < extraskip; i++ )
+	getc( file );
+    for ( i = 0; i < HEADER_LENGTH; i++ )
+	getc( file );
+
+    *scanLineP = 0;
+    k = 0;
+
+    while ( *scanLineP < MAX_LINES )
+	{
+	ch = (unsigned char) getc( file );	/* Count byte */
+	i = (unsigned int) ch;
+	if ( ch < 0x80 )
+	    {	/* Unpack next (I+1) chars as is */
+	    for ( j = 0; j <= i; j++ )
+		if ( *scanLineP < MAX_LINES )
+		    {
+		    Pic[*scanLineP][k++] = (unsigned char) getc( file );
+		    if ( ! (k %= BYTES_WIDE) )
+			*scanLineP += 1;
+		    }
+	    }
+	else
+	    {	/* Repeat next char (2's comp I) times */
+	    ch = getc( file );
+	    for ( j = 0; j <= 256 - i; j++ )
+		if ( *scanLineP < MAX_LINES )
+		    {
+		    Pic[*scanLineP][k++] = (unsigned char) ch;
+		    if ( ! (k %= BYTES_WIDE) )
+			*scanLineP += 1;
+		    }
+	    }
+	}
+    }
diff --git a/converter/pbm/mdaspec.txt b/converter/pbm/mdaspec.txt
new file mode 100644
index 00000000..a93c0af7
--- /dev/null
+++ b/converter/pbm/mdaspec.txt
@@ -0,0 +1,239 @@
+
+         MicroDesign 3 page (.MDP) and area (.MDA) file specifications
+                                       
+   This publication and all information contained herein is Copyright ©
+   Creative Technology 1992. This information carries no guarantees of
+   accuracy or liabilities on the part of either Creative Technology or
+   myself.
+   
+   A hard copy of this document (with far better artwork) is available
+   from Creative Technology, 10 Park Street, Uttoxeter, Staffs ST14 7AG,
+   creative@net-shopper.co.uk.
+   
+   [Note: throughout this file hexadecimal numbers are represented as
+   #nn; 'one pixel' refers to one PCW screen pixel, ie. a MicroDesign
+   half-pixel; 'word' format indicates a 16-bit word stored with the LSBs
+   first]
+   
+The MDA (MicroDesign Area) file formats
+
+   There are two different MDA file formats. The earlier MicroDesign2
+   format uses a simple compression technique to reduce continuous areas
+   of white and black, while the more recent MicroDesign3 format uses a
+   more sophisticated technique which generally results in smaller disc
+   files.
+   
+   MicroDesign2, ProSCAN and Tweak (versions 1 and 2) can only load and
+   save the earlier format, but either format may be loaded or saved in
+   MicroDesign3. In MD3, the filetype is detected automatically on load,
+   but the user must choose whether to save in 'AREA2' or 'AREA3' format.
+   
+   The format is identified in byte 21 of the initial file 'stamp' record
+   - for a MicroDesign2 area this byte is "0" (#30), whereas for a
+   MicroDesign3 area it is "3" (#33).
+   
+   When loaded into memory and uncompressed an Area file can occupy up
+   the 720k of data, but its size on disc is indeterminate due to the
+   compression used. Because of the compression it is not possible to
+   perform 'random-access' reads or writes to the disc file - it must be
+   read sequentially in order correctly to decompress the data.
+   
+   The older MicroDesign2 Area file format is as follows:
+   
+Bytes 0..127: file 'stamp':
+    0 -   3 (#00 - #03)  .MDA            File Type  (4 bytes)
+    4 -  17 (#04 - #11)  MicroDesignPCW  Program Identifier (14 bytes)
+   18 -  22 (#12 - #16)  v1.00           File Version  (5 bytes)
+   23 -  24 (#17 - #18)  CR,LF           ie. 13,10 decimal (2 bytes)
+   25 -  31 (#19 - #1F)  xxxxxxx         User Serial No (ASCII) (7 bytes)
+   32 -  33 (#20 - #21)  CR,LF           ie. 13,10 decimal (2 bytes)
+   34 - 127 (#22 - #7F)                  fill with zeroes (#00) (94 bytes)
+
+Bytes 128..: file proper:
+  128 - 129 (#80 - #81)  Height in Lines (multiple of 4) (word)
+  130 - 131 (#82 - #83)  Width in Bytes (Pixels * 8) (word)
+  132 -     (#84 -    )  Bit-Image Data as follows...
+
+   Bytes read from left to right in lines, top line first.
+   
+   Each byte is standard 1-bit-per-pixel layout where MSB = LH pixel, LSB
+   = RH pixel, 1 = white ('on'), and 0 = black ('off'). Each #00 (all
+   black) or #FF (all white) byte is followed by a 'count' byte (ie. #00
+   #03 means 3 whole bytes width of solid black; #FF #A0 means 160 bytes
+   width of solid white). A value #00 for the count byte means 256. This
+   'count' can overrun into the next (several) lines.
+   
+   For example: (. represents black, # white)
+   
+    ....#### ##..##.. ####.... ........ ..###### ######## ########
+       0F       CC       F0      00,01     3F          FF,02
+
+    ####.... ........ ........ ........ ........ ........ ........
+       F0                            00,06
+
+   Because of this compression the file length is indeterminate, but
+   there must be HEIGHT * WIDTH bytes of actual image data by the time it
+   has been uncompressed.
+   
+   The newer MicroDesign3 Area file format is as follows:
+   
+Bytes 0..127: file 'stamp':
+    0 -   3 (#00 - #03)  .MDA            File Type  (4 bytes)
+    4 -  17 (#04 - #11)  MicroDesignPCW  Program Identifier (14 bytes)
+   18 -  22 (#12 - #16)  v1.30           File Version  (5 bytes)
+   23 -  24 (#17 - #18)  CR,LF           ie. 13,10 decimal (2 bytes)
+   25 -  31 (#19 - #1F)  xxxxxxx         User Serial No (ASCII) (7 bytes)
+   32 -  33 (#20 - #21)  CR,LF           ie. 13,10 decimal (2 bytes)
+   34 - 127 (#22 - #7F)                  fill with zeroes (#00) (94 bytes)
+
+Bytes 128..: file proper:
+  128 - 129 (#80 - #81)  Height in Lines (multiple of 4) (word)
+  130 - 131 (#82 - #83)  Width in Bytes (Pixels * 8) (word)
+  132 -     (#84 -    )  Bit-Image Data as follows...
+
+   Bytes read from left to right in lines, top line first.
+   
+   Each byte is standard 1-bit-per-pixel layout where MSB = LH pixel, LSB
+   = RH pixel, 1 = white ('on'), and 0 = black ('off').
+   
+   Each line of data is compressed according to one of three 'line
+   types'. The first byte of data for each line is the line type.
+   
+Line type byte:
+  #00: Line is ALL-SAME-BYTE type
+  #01: Line is DATA type
+  #02: Line is DIFFERENCE DATA type
+
+   The actual data for each line follows this type byte:
+   
+   ALL-SAME-BYTE type:
+          One more byte follows the initial #00 type byte - this is the
+          actual bit-image data with which to fill the whole line width.
+          
+          eg. Whole line of white = #00 #FF, whole line of black = #00
+          #00
+          
+   DATA type:
+          Following the initial #01 type byte, the data content of the
+          line is compressed as follows:
+          
+          Data is encoded in 'blocks', which are of *either* repeating
+          data bytes *or* non-repeating bytes. Each block starts with a
+          control byte, which determines whether the data which follows
+          it is *either* just one data byte to be repeated *or* a
+          sequence of dissimilar bytes.
+          
+          If the control byte N is negative (-1 to -127 in two's
+          complement - #FF for -1, #81 for -127), *one* data byte follows
+          which is to be repeated -N times. This means that there are to
+          be a total of (-N+1) occurrences of this data byte.
+          
+          If the control byte N is positive (0 to 127; #00 to #7F), it is
+          followed by (N+1) bytes of dissimilar data to load directly
+          into the line.
+          
+          For instance: 01 (line type), then:
+          
+....#### ##..##.. ####.... ........ ######## ######## ########
+          03,0F,CC,F0,00                       FE,FF
+
+#####... #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#. #.#..... ........
+  00,F8                 FD,AA                     01,A0,00
+
+          Note: the POSITIVE control bytes are 1 LESS than the number of
+          dissimilar bytes following them; NEGATIVE ones are (MINUS) 1
+          LESS than the number of occurrences of the byte following them.
+          
+   DIFFERENCE DATA type:
+          Following the initial #02 type byte, the difference between the
+          data content of this line and the content of the previous line
+          is stored: ie. this line is XORed with the previous line to
+          produce a 'difference' line, which is then compressed using the
+          same method as for a DATA type line.
+          
+          For instance, the following line stored as a 'difference' line
+          from the line above would be 02 (line type), then: (the second
+          pixel row below shows the results of XORing the first two
+          lines)
+          
+....#### ##..##.. ##..##.. ......## ######## ######## ########
+........ ........ ..####.. ......## ........ ........ ........
+      FF,00           01,3C,03                 FE,00
+
+######.. #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#. ........
+.....#.. ........ ........ ........ ........ ....#.#. ........
+ 00,04                  FD,00                     01,0A,00
+
+          Note: this DIFFERENCE encoding is used if it will result in
+          less bytes of data being stored in the file. The line type of
+          the previous line is irrelevant.
+          
+   Because of these various types of compression the file length on the
+   disc is indeterminate, but there must be HEIGHT * WIDTH bytes of
+   actual image data by the time it has been uncompressed.
+   
+The MDP (MicroDesign Page) file formats
+
+   MicroDesign3's Page (.MDP) files are identical to its Area (.MDA)
+   files except for the following differences:
+   
+Bytes 0..127: file 'stamp':
+    0 -   3 (#00 - #03)  .MDP            File Type  (4 bytes)
+   34       (#22)        nn: dpi         00 = 240dpi
+                                         01 = 360dpi
+                                         02 = 300dpi
+   35       (#23)        nn: format      00 = A5 portrait
+                                         01 = A5 landscape
+                                         02 = A4 portrait
+                                         03 = A4 landscape
+                                         04 = A5 portrait (hi-res)
+                                         05 = A5 landscape (hi-res)
+   36       (#24)        nn: page ram required in 16k blocks
+
+   In all other respects a Page (.MDP) file is identical to an Area
+   (.MDA) file (MD3 type).
+   
+The CUT image format
+
+   (Note: this information is in no way 'official' and results from my
+   own dabblings; it is separate from the above information on the MDA
+   format and is included here for convenience. No warranty expressed or
+   implied)
+   
+   CUT files have a format as follows:
+   
+    0 -   1 (#00 - #01)  Height code h1 (word) (see below)
+    2 -   3 (#02 - #03)  Width code w1 (word) (see below)
+    4 -     (#04 -    )  Bitmap data, row-by-row
+
+   The height in pixels h1 can be calculated from the height code h using
+   h = (h1+3)/2; the width in pixels is w = w1+2; the number of whole
+   bytes per row in the file is wb = INT((w+8)/8). Bitmap data in the
+   file is stored row-by-row, with a whole number of bytes per row; the
+   LSBs of the last byte that extend beyond the right edge of the image
+   should be discarded. As usual in bitmap data the MSB is towards the
+   left and the LSB towards the right.
+   
+   Note: the formula above for wb implies that if the image is a whole
+   multiple of 8 pixels wide, none of the bits from the last byte in the
+   row will be used. Strange, but there you go.
+   
+The GRF image format
+
+   (Same disclaimer applies; this is all from experiment)
+   
+   GRF files are substantially similar to CUTs (although slightly more
+   logical) and have a format as follows:
+   
+    0 -   1 (#00 - #01)  Width in pixels (word)
+    2 -   3 (#02 - #03)  Height in pixels (word)
+    4 -     (#04 -    )  Bitmap data, row-by-row
+
+   The number of bytes per row is simply the width in pixels divided by
+   8, rounded up; no catches here. As with CUTs unused rightmost bits
+   should be discarded.
+     _________________________________________________________________
+                                      
+   
+    Go to CP/M page or main page
+    Last updated 16 April 1997; Jacob Nevins
diff --git a/converter/pbm/mdatopbm.c b/converter/pbm/mdatopbm.c
new file mode 100644
index 00000000..d8e06572
--- /dev/null
+++ b/converter/pbm/mdatopbm.c
@@ -0,0 +1,271 @@
+
+/***************************************************************************
+
+    MDATOPBM: Convert Microdesign area to portable bitmap
+    Copyright (C) 1999 John Elliott <jce@seasip.demon.co.uk>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    See the file mdaspec.txt for a specification of the MDA format.
+******************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include "pbm.h"
+#include "mallocvar.h"
+
+/* Simple MDA -> portable bitmap converter */
+
+typedef unsigned char mdbyte;   /* Must be exactly one byte */
+
+static FILE *infile;            /* Input file */
+static mdbyte header[128];      /* MDA file header */
+static bit **data;          /* PBM image */
+static mdbyte *mdrow;           /* MDA row after decompression (MD3 only) */
+static int bInvert = 0;     /* Invert image? */
+static int bScale  = 0;     /* Scale image? */
+static int bAscii  = 0;     /* Ouput ASCII PBM? */
+static int nInRows, nInCols;        /* Height, width of input (rows x bytes) */
+static int nOutCols, nOutRows;      /* Height, width of output (rows x bytes) */
+
+static mdbyte 
+getbyte(void) {
+    /* Read a byte from the input stream, with error trapping */
+    int b;
+
+    b = fgetc(infile);
+
+    if (b == EOF) pm_error("Unexpected end of MDA file\n");
+    
+    return (mdbyte)b;
+}
+
+
+
+static void 
+render_byte(int *col, int *xp, int *yp, int b) {
+
+/* Convert a byte to 8 cells in the destination bitmap 
+ *
+ * *col = source column
+ * *xp  = destination column
+ * *yp  = destination row
+ *  b   = byte to draw
+ *
+ * Will update *col, *xp and *yp to point to the next bit of the row.
+ */
+
+    int mask = 0x80;
+    int n;
+    int y3 = *yp;
+
+    if (bScale) y3 *= 2;
+
+    if (y3 >= nOutRows) return;
+
+    for (n = 0; n < 8; ++n) {
+        if (bInvert) data[y3][*xp] = (b & mask) ? PBM_BLACK : PBM_WHITE;
+        else         data[y3][*xp] = (b & mask) ? PBM_WHITE : PBM_BLACK;
+        mask = mask >> 1;
+        if (bScale) data[y3+1][*xp] = data[y3][*xp];
+        ++(*xp);
+    }
+    ++(*col);       /* Next byte */
+    if ((*col) >= nInCols) {
+        /* Onto next line? */
+        *col = 0;
+        *xp = 0;
+        ++(*yp);
+    }
+}
+
+
+static void 
+md2_trans(void) {
+    /* Convert a MicroDesign 2 area to PBM */
+    /* MD2 has RLE encoding that may go over */
+
+    int x1, y1, col;    /* multiple lines. */
+    mdbyte b;
+    int c;
+
+    x1 = y1 = col = 0;
+
+    while (y1 < nInRows) {
+        b = getbyte();
+    
+        if (b == 0 || b == 0xFF) {
+            /* RLE sequence */
+            c = getbyte();
+            if (c == 0) c = 256;
+            while (c > 0) { 
+                render_byte(&col, &x1, &y1, b); 
+                --c; 
+            }
+        }
+        else 
+            render_byte(&col, &x1, &y1, b);    /* Not RLE */
+    }
+}
+
+
+
+static void 
+md3_trans(void) {
+    /* Convert MD3 file. MD3 are encoded as rows, and 
+       there are three types. 
+    */
+    int x1, y1, col;
+    mdbyte b;
+    int c, d, n;
+
+    for (y1 = 0; y1 < nInRows; ++y1) {
+        b = getbyte();   /* Row type */
+        switch(b)  {
+        case 0: /* All the same byte */
+            c = getbyte();
+            for (n = 0; n < nInCols; n++) 
+                mdrow[n] = c;
+            break;
+            
+        case 1:      /* Encoded data */
+        case 2: col = 0; /* Encoded as XOR with previous row */
+            while (col < nInCols) {
+                c = getbyte();
+                if (c >= 129) {
+                    /* RLE sequence */
+                    c = 257 - c;
+                    d = getbyte();
+                    for (n = 0; n < c; ++n) {
+                        if (b == 1) 
+                            mdrow[col++] = d;
+                        else 
+                            mdrow[col++] ^= d;
+                    }   
+                } else {
+                    /* not RLE sequence */
+                        ++c;
+                        for (n = 0; n < c; ++n) {
+                            d = getbyte();
+                            if (b == 1) 
+                                mdrow[col++] = d;
+                            else
+                                mdrow[col++] ^= d;
+                        }
+                } 
+            }
+        }
+        /* Row loaded. Convert it. */
+        x1 = 0; col = 0; 
+        for (n = 0; n < nInCols; ++n) {
+            d  = y1;
+            render_byte(&col, &x1, &d, mdrow[n]);
+        }
+    }
+}
+
+
+
+static void 
+usage(char *s) {        
+    printf("mdatopbm v1.00, Copyright (C) 1999 "
+           "John Elliott <jce@seasip.demon.co.uk>\n"
+           "This program is redistributable under the terms of "
+           "the GNU General Public\n"
+           "License, version 2 or later.\n\n"
+           "Usage: %s [ -a ] [ -d ] [ -i ] [ -- ] [ infile ]\n\n"
+           "-a: Output an ASCII pbm file\n"
+           "-d: Double height (to compensate for the PCW aspect ratio)\n"
+           "-i: Invert colors\n"
+           "--: No more options (use if filename begins with a dash)\n",
+           s);
+
+    exit(0);
+}
+
+
+
+int 
+main(int argc, char **argv) {
+    int n, optstop = 0;
+    char *fname = NULL;
+
+    pbm_init(&argc, argv);
+
+    /* Parse options */
+
+    for (n = 1; n < argc; ++n) {
+        if (argv[n][0] == '-' && !optstop) {   
+            if (argv[n][1] == 'a' || argv[n][1] == 'A') bAscii = 1;
+            if (argv[n][1] == 'd' || argv[n][1] == 'D') bScale = 1;
+            if (argv[n][1] == 'i' || argv[n][1] == 'I') bInvert = 1;
+            if (argv[n][1] == 'h' || argv[n][1] == 'H') usage(argv[0]);
+            if (argv[n][1] == '-' && argv[n][2] == 0 && !fname) {
+                /* "--" */
+                optstop = 1;
+            }
+            if (argv[n][1] == '-' && (argv[n][2] == 'h' || argv[n][2] == 'H'))
+                usage(argv[0]);
+        }
+        else if (argv[n][0] && !fname) {
+            /* Filename */
+            fname = argv[n];
+        }
+    }
+
+    if (fname) 
+        infile = pm_openr(fname);
+    else
+        infile = stdin;
+
+    /* Read MDA file header */
+
+    if (fread(header, 1, 128, infile) < 128)
+        pm_error("Not a .MDA file\n");
+
+    if (strncmp((char*) header, ".MDA", 4) && 
+        strncmp((char*) header, ".MDP", 4))
+        pm_error("Not a .MDA file\n");
+
+    {
+        short yy;
+        pm_readlittleshort(infile, &yy); nInRows = yy;
+        pm_readlittleshort(infile, &yy); nInCols = yy;
+    }
+    
+    nOutCols = 8 * nInCols;
+    nOutRows = nInRows;
+    if (bScale) 
+        nOutRows *= 2;
+
+    data = pbm_allocarray(nOutCols, nOutRows);
+    
+    MALLOCARRAY_NOFAIL(mdrow, nInCols);
+
+    if (header[21] == '0') 
+        md2_trans();
+    else
+        md3_trans();
+
+    pbm_writepbm(stdout, data, nInCols*8, nOutRows, bAscii);
+
+    if (infile != stdin) 
+        pm_close(infile);
+    fflush(stdout);
+    pbm_freearray(data, nOutRows);
+    free(mdrow);
+
+    return 0;
+}
diff --git a/converter/pbm/mgr.h b/converter/pbm/mgr.h
new file mode 100644
index 00000000..eeb39d4f
--- /dev/null
+++ b/converter/pbm/mgr.h
@@ -0,0 +1,25 @@
+/* mgr.h - the following defs are taken from the MGR header file lib/dump.h
+*/
+
+#ifndef MGR_H_INCLUDED
+#define MGR_H_INCLUDED
+
+struct old_b_header {
+   char magic[2];
+   char h_wide;
+   char l_wide;
+   char h_high;
+   char l_high;
+   };
+
+struct b_header {
+   char magic[2];
+   char h_wide;
+   char l_wide;
+   char h_high;
+   char l_high;
+   char depth;
+   char _reserved;
+   };
+
+#endif
diff --git a/converter/pbm/mgrtopbm.c b/converter/pbm/mgrtopbm.c
new file mode 100644
index 00000000..cea4be48
--- /dev/null
+++ b/converter/pbm/mgrtopbm.c
@@ -0,0 +1,145 @@
+/* mgrtopbm.c - read a MGR bitmap and produce a portable bitmap
+**
+** 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 <errno.h>
+
+#include "pbm.h"
+#include "mgr.h"
+
+
+static unsigned char item;
+static int bitsperitem, bitshift;
+
+static void
+getinit(FILE * const file, 
+        int *  const colsP, 
+        int *  const rowsP, 
+        int *  const depthP, 
+        int *  const padrightP,
+        int *  const bitsperitemP) {
+
+    struct b_header head;
+    int pad;
+
+    if (fread(&head, sizeof(struct old_b_header), 1, file ) != 1)
+        pm_error("Unable to read 1st byte of file.  "
+                 "fread() returns errno %d (%s)",
+                 errno, strerror(errno));
+    if (head.magic[0] == 'y' && head.magic[1] == 'z') { 
+        /* new style bitmap */
+        if (fread(&head.depth, 
+                  sizeof(head) - sizeof(struct old_b_header), 1, file) 
+             != 1 )
+            pm_error("Unable to read header after 1st byte.  "
+                     "fread() returns errno %d (%s)",
+                     errno, strerror(errno));
+        *depthP = (int) head.depth - ' ';
+        pad = 8;
+    } else if (head.magic[0] == 'x' && head.magic[1] == 'z') { 
+        /* old style bitmap with 32-bit padding */
+        *depthP = 1;
+        pad = 32;
+    } else if (head.magic[0] == 'z' && head.magic[1] == 'z') { 
+        /* old style bitmap with 16-bit padding */
+        *depthP = 1;
+        pad = 16;
+    } else if (head.magic[0] == 'z' && head.magic[1] == 'y') {
+        /* old style 8-bit pixmap with 16-bit padding */
+        *depthP = 8;
+        pad = 16;
+    } else {
+        pm_error("bad magic chars in MGR file: '%c%c'",
+                 head.magic[0], head.magic[1] );
+        pad = -1;  /* should never reach here */
+    }
+
+    if (head.h_wide < ' ' || head.l_wide < ' ')
+        pm_error("Invalid width field in MGR header");
+    if (head.h_high < ' ' || head.l_high < ' ')
+        pm_error("Invalid width field in MGR header");
+    
+    *colsP = (((int)head.h_wide - ' ') << 6) + ((int)head.l_wide - ' ');
+    *rowsP = (((int)head.h_high - ' ') << 6) + ((int) head.l_high - ' ');
+    *padrightP = ( ( *colsP + pad - 1 ) / pad ) * pad - *colsP;
+    
+    *bitsperitemP = 8;
+}
+
+
+
+static bit
+getbit( file )
+    FILE* file;
+    {
+    bit b;
+
+    if ( bitsperitem == 8 )
+	{
+	item = getc( file );
+	bitsperitem = 0;
+	bitshift = 7;
+	}
+    bitsperitem++;
+    b = ( ( item >> bitshift) & 1 ) ? PBM_BLACK : PBM_WHITE;
+    bitshift--;
+    return b;
+    }
+
+
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, depth, padright, row, col;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[mgrfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    getinit( ifp, &cols, &rows, &depth, &padright, &bitsperitem );
+    if ( depth != 1 )
+	pm_error( "MGR file has depth of %d, must be 1", depth );
+
+    pbm_writepbminit( stdout, cols, rows, 0 );
+    bitrow = pbm_allocrow( cols );
+
+    for ( row = 0; row < rows; row++ )
+	{
+	/* Get data, bit-reversed within each byte. */
+        for ( col = 0, bP = bitrow; col < cols; col++, bP++ )
+	    *bP = getbit( ifp );
+	/* Discard line padding */
+        for ( col = 0; col < padright; col ++ )
+	    (void) getbit( ifp );
+	pbm_writepbmrow( stdout, bitrow, cols, 0 );
+	}
+
+    pm_close( ifp );
+    pm_close( stdout );
+
+    exit( 0 );
+    }
+
+
diff --git a/converter/pbm/mrftopbm.c b/converter/pbm/mrftopbm.c
new file mode 100644
index 00000000..696fe839
--- /dev/null
+++ b/converter/pbm/mrftopbm.c
@@ -0,0 +1,210 @@
+/* mrftopbm - convert mrf to pbm
+ * public domain by RJM
+ *
+ * Adapted to Netpbm by Bryan Henderson 2003.08.09.  Bryan got his copy from
+ * ftp://ibiblio.org/pub/linux/apps/convert, dated 1997.08.19.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "pm_c_util.h"
+#include "nstring.h"
+#include "pbm.h"
+
+
+static int bitbox;
+static int bitsleft;
+
+
+static void 
+bit_init(void) {
+    bitbox=0; 
+    bitsleft=0;
+}
+
+
+
+static int 
+bit_input(FILE * const in) {
+    if (bitsleft == 0)   {
+        bitbox = fgetc(in);
+        bitsleft = 8;
+    }
+    --bitsleft;
+    return((bitbox&(1<<bitsleft))?1:0);
+}
+
+
+
+static void 
+doSquare(FILE *          const in,
+          unsigned char * const image,
+          int             const ox,
+          int             const oy,
+          int             const w,
+          int             const size) {
+
+    if (size == 1 || bit_input(in)) { 
+        /* It's all black or all white.  Next bit says which. */
+
+        unsigned int const c = bit_input(in);
+
+        unsigned int y;
+
+        for (y = 0; y < size; ++y) {
+            unsigned int x;
+            for (x = 0; x < size; ++x)
+                image[(oy+y)*w+ox+x] = c;
+        }
+    } else {
+        /* not all one color, so recurse. */
+        doSquare(in, image, ox,      oy,     w, size >> 1);
+        doSquare(in, image, ox+size, oy,     w, size >> 1);
+        doSquare(in, image, ox,      oy+size,w, size >> 1);
+        doSquare(in, image, ox+size, oy+size,w, size >> 1);
+    }
+}
+
+
+
+static void
+writeOutput(FILE *                const ofP,
+            int                   const cols,
+            int                   const rows,
+            const unsigned char * const image) {
+            
+    /* w64 is units-of-64-bits width, h64 same for height */
+    unsigned int const w64 = (cols+63)/64;
+
+    bit * bitrow;
+    unsigned int row;
+
+    pbm_writepbminit(ofP, cols, rows, FALSE);
+
+    bitrow = pbm_allocrow(cols);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+     
+        for (col = 0; col < cols; ++col)
+            bitrow[col] = 
+                (image[row * (w64*64) + col] == 1) ? PBM_WHITE : PBM_BLACK;
+
+        pbm_writepbmrow(ofP, bitrow, cols, FALSE);
+    }
+    pbm_freerow(bitrow);
+}
+
+
+
+static void
+readMrfImage(FILE *           const ifP,
+             bool             const expandAll,
+             unsigned char ** const imageP,
+             unsigned int *   const colsP,
+             unsigned int *   const rowsP) {
+
+    static unsigned char buf[128];
+    unsigned int rows;
+    unsigned int cols;
+    unsigned int w64, h64;
+
+    unsigned char * image;
+
+    fread(buf, 1, 13, ifP);
+
+    if (memcmp(buf, "MRF1", 4) != 0)
+        pm_error("Input is not an mrf image.  "
+                 "We know this because it does not start with 'MRF1'.");
+
+    if (buf[12] != 0)
+        pm_error("can't handle file subtype %u", buf[12]);
+
+    cols = (buf[4] << 24) | (buf[5] << 16) | (buf[06] << 8) | buf[07] << 0;
+    rows = (buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11] << 0;
+
+    /* w64 is units-of-64-bits width, h64 same for height */
+    w64 = (cols+63)/64;
+    h64 = (rows+63)/64;
+    if (expandAll) {
+        *colsP = w64*64;
+        *rowsP = h64*64;
+    } else {
+        *colsP = cols;
+        *rowsP = rows;
+    }
+
+    if (UINT_MAX/w64/64/h64/64 == 0)
+        pm_error("Ridiculously large, unprocessable image: %u cols x %u rows",
+                 cols, rows);
+
+    image = calloc(w64*h64*64*64,1);
+    if (image == NULL)
+        pm_error("Unable to get memory for raster");
+                 
+    /* now recursively input squares. */
+
+    bit_init();
+
+    {
+        unsigned int row;
+        for (row = 0; row < h64; ++row) {
+            unsigned int col;
+            for (col = 0; col < w64; ++col)
+                doSquare(ifP, image, col*64, row*64, w64*64, 64);
+        }
+    }
+    *imageP = image;
+}
+
+
+
+int 
+main(int argc, char *argv[]) {
+
+    FILE *ifP;
+    FILE *ofP;
+    unsigned char *image;
+    bool expandAll;
+    unsigned int cols, rows;
+
+    pbm_init(&argc, argv);
+
+    expandAll = FALSE;  /* initial assumption */
+
+    if (argc-1 >= 1 && STREQ(argv[1], "-a")) {
+        expandAll = TRUE;
+        argc--,argv++;
+    }
+
+    if (argc-1 > 1)
+        pm_error("Too many arguments: %d.  Only argument is input file",
+                 argc-1);
+
+    if (argc-1 == 1) 
+        ifP = pm_openr(argv[1]);
+    else
+        ifP = stdin;
+
+    ofP = stdout;
+
+    readMrfImage(ifP, expandAll, &image, &cols, &rows);
+    
+    pm_close(ifP);
+    
+    writeOutput(ofP, cols, rows, image);
+
+    free(image);
+
+    return 0;
+}
+
+
+
+
+
+
diff --git a/converter/pbm/pbmto10x.c b/converter/pbm/pbmto10x.c
new file mode 100644
index 00000000..f8a38b5d
--- /dev/null
+++ b/converter/pbm/pbmto10x.c
@@ -0,0 +1,169 @@
+/* pbmto10x.c - read a portable bitmap and produce a Gemini 10X printer file
+**
+** Copyright (C) 1990, 1994 by Ken Yap
+**
+** 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 to shorten stripes and eliminate blank stripes. Dec 1994.
+*/
+
+#include "pbm.h"
+#include "mallocvar.h"
+
+#define LOW_RES_ROWS    8       /* printed per pass */
+#define HIGH_RES_ROWS   16      /* printed per pass */
+
+static int  highres = 0;
+static FILE *ifp;
+static int  rows, cols, format;
+
+
+
+static void
+outstripe(char * const stripe, 
+          char * const sP, 
+          int    const reschar) {
+
+    int ncols;
+
+    char * p;
+
+    p = sP;  /* initial value */
+
+    /* scan backwards, removing empty columns */
+    while (p != stripe) 
+        if (*--p != 0) {
+            ++p;
+            break;
+        }
+    ncols = p - stripe;
+    if (ncols > 0) {
+        printf("\033%c%c%c", reschar, ncols % 256, ncols / 256);
+        fwrite(stripe, sizeof(char), ncols, stdout);
+    }
+    putchar('\n');          /* flush buffer */
+}
+
+
+
+static void
+res_60x72(void) {
+    int i, item, npins, row, col;
+    bit *bitrows[LOW_RES_ROWS], *bP[LOW_RES_ROWS];
+    char *stripe, *sP;
+
+    MALLOCARRAY(stripe, cols);
+    if (stripe == NULL)
+        pm_error("Unable to allocate %u bytes for a stripe buffer.",
+                 cols * sizeof(stripe[0]));
+    for (i = 0; i < LOW_RES_ROWS; ++i)
+        bitrows[i] = pbm_allocrow(cols);
+    printf("\033A\010");        /* '\n' = 8/72 */
+    for (row = 0, sP = stripe; row < rows; row += LOW_RES_ROWS, sP = stripe) {
+        if (row + LOW_RES_ROWS <= rows)
+            npins = LOW_RES_ROWS;
+        else
+            npins = rows - row;
+        for (i = 0; i < npins; ++i)
+            pbm_readpbmrow(ifp, bP[i] = bitrows[i], cols, format);
+        for (col = 0; col < cols; ++col) {
+            item = 0;
+            for (i = 0; i < npins; ++i)
+                if (*(bP[i]++) == PBM_BLACK)
+                    item |= 1 << (7 - i);
+            *sP++ = item;
+        }
+        outstripe(stripe, sP, 'K');
+    }
+    printf("\033@");
+    free(stripe);
+}
+
+
+
+static void
+res_120x144(void) {
+    int i, pin, item, npins, row, col;
+    bit *bitrows[HIGH_RES_ROWS], *bP[HIGH_RES_ROWS];
+    char *stripe, *sP;
+
+    MALLOCARRAY(stripe, cols);
+    if (stripe == NULL)
+        pm_error("Unable to allocate %u bytes for a stripe buffer.",
+                 cols * sizeof(stripe[0]));
+    for (i = 0; i < HIGH_RES_ROWS; ++i)
+        bitrows[i] = pbm_allocrow(cols);
+    printf("\0333\001");            /* \n = 1/144" */
+    for (row = 0, sP = stripe; row < rows; row += HIGH_RES_ROWS, sP = stripe) {
+        if (row + HIGH_RES_ROWS <= rows)
+            npins = HIGH_RES_ROWS;
+        else
+            npins = rows - row;
+        for (i = 0; i < npins; ++i)
+            pbm_readpbmrow(ifp, bP[i] = bitrows[i], cols, format);
+        for (col = 0; col < cols; ++col) {
+            item = 0;
+            /* even rows */
+            for (pin = i = 0; i < npins; i += 2, ++pin)
+                if (*(bP[i]++) == PBM_BLACK)
+                    item |= 1 << (7 - pin);
+            *sP++ = item;
+        }
+        outstripe(stripe, sP, 'L');
+        sP = stripe;
+        for (col = 0; col < cols; ++col) {
+            item = 0;
+            /* odd rows */
+            for (i = 1, pin = 0; i < npins; i += 2, ++pin)
+                if (*(bP[i]++) == PBM_BLACK)
+                    item |= 1 << (7 - pin);
+            *sP++ = item;
+        }
+        outstripe(stripe, sP, 'L');
+        printf("\033J\016");        /* 14/144 down, \n did 1/144 */
+    }
+    printf("\033@");
+    free(stripe);
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    const char * fname;
+
+    pbm_init( &argc, argv );
+
+    if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'h') {
+        highres = 1;
+        --argc;
+        ++argv;
+    }
+    if (argc-1 > 1)
+        pm_error("Too many arguments.  Only argument is file name");
+    else if (argc-1 == 1)
+        fname = argv[1];
+    else
+        fname = "-";
+    
+    ifp = pm_openr(fname);
+
+    pbm_readpbminit(ifp, &cols, &rows, &format);
+
+    if (highres)
+        res_120x144();
+    else
+        res_60x72();
+
+    pm_close(ifp);
+    exit(0);
+}
+
+
+
diff --git a/converter/pbm/pbmto4425.c b/converter/pbm/pbmto4425.c
new file mode 100644
index 00000000..605b12d5
--- /dev/null
+++ b/converter/pbm/pbmto4425.c
@@ -0,0 +1,179 @@
+#include <string.h>
+
+#include "nstring.h"
+#include "pbm.h"
+
+static char bit_table[2][3] = {
+{1, 4, 0x10},
+{2, 8, 0x40}
+};
+
+static int vmap_width;
+static int vmap_height;
+
+static int xres;
+static int yres;
+
+static char *vmap;
+
+
+static void
+init_map()
+{
+    int x, y;
+
+
+    for(x = 0; x < vmap_width; ++x)
+    {
+        for(y = 0; y < vmap_height; ++y)
+        {
+            vmap[y*(vmap_width) + x] = 0x20;
+        }
+    }
+}
+
+
+
+static void
+set_vmap(x, y)
+    int x, y;
+{
+    int ix, iy;
+
+    ix = x/2;
+    iy = y/3;
+
+    vmap[iy*(vmap_width) + ix] |= bit_table[x % 2][y % 3];
+}
+
+
+
+static void
+fill_map(pbmfp)
+    FILE *pbmfp;
+{
+    bit **pbm_image;
+    int cols;
+    int rows;
+    int x;
+    int y;
+
+    pbm_image = pbm_readpbm(pbmfp, &cols, &rows);
+    for(y = 0; y < rows && y < yres; ++y)
+    {
+        for(x = 0; x < cols && x < xres; ++x)
+        {
+            if(pbm_image[y][x] == PBM_WHITE)
+            {
+                set_vmap(x, y);
+            }
+        }
+    }
+}
+
+
+static void
+print_map()
+{
+    int x, y;
+    int last_byte;
+
+#ifdef BUFFERED
+    char *iobuf;
+    iobuf = (char *)malloc(BUFSIZ);
+    if(iobuf == NULL)
+    {
+        pm_message( "Can't allocate space for I/O buffer.  "
+                    "Using unbuffered I/O...\n" );
+        setbuf(stdout, NULL);
+    }
+    else
+    {
+        setbuf(stdout, iobuf);
+    }
+#endif
+
+    fputs("\033[H\033[J", stdout);	/* clear screen */
+    fputs("\033[?3h", stdout);	/* 132 column mode */
+    fputs("\033)}\016", stdout);	/* mosaic mode */
+
+    for(y = 0; y < vmap_height; ++y)
+    {
+        for(last_byte = vmap_width - 1;
+            last_byte >= 0
+                && vmap[y * vmap_width + last_byte] == 0x20;
+            --last_byte)
+            ;
+
+        for(x = 0; x <= last_byte; ++x)
+        {
+            fputc(vmap[y*(vmap_width) + x], stdout);
+        }
+        fputc('\n', stdout);
+    }
+
+    fputs("\033(B\017", stdout);
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+    int argn;
+    const char *pbmfile;
+    FILE *pbmfp;
+    const char *usage="[pbmfile]";
+
+    pbm_init( &argc, argv );
+    for(argn = 1;
+        argn < argc && argv[argn][0] == '-' && strlen(argv[argn]) > 1;
+        ++argn)
+    {
+        pm_usage(usage);
+    }
+
+    if(argn >= argc)
+    {
+        pbmfile = "-";
+    }
+    else if(argc - argn != 1)
+    {
+        pm_usage(usage);
+    }
+    else
+    {
+        pbmfile = argv[argn];
+    }
+
+    if(STREQ(pbmfile, "-"))
+    {
+        pbmfp = stdin;
+    }
+    else
+    {
+        pbmfp = pm_openr( argv[argn] );
+    }
+
+    vmap_width = 132;
+    vmap_height = 23;
+
+    xres = vmap_width * 2;
+    yres = vmap_height * 3;
+
+    vmap = malloc(vmap_width * vmap_height * sizeof(char));
+    if(vmap == NULL)
+	{
+        pm_error( "Cannot allocate memory" );
+	}
+
+    init_map();
+    fill_map(pbmfp);
+    print_map();
+    /* If the program failed, it previously aborted with nonzero completion
+       code, via various function calls.
+    */
+    return 0; 
+}
+
+
+
diff --git a/converter/pbm/pbmtoascii.c b/converter/pbm/pbmtoascii.c
new file mode 100644
index 00000000..0472f809
--- /dev/null
+++ b/converter/pbm/pbmtoascii.c
@@ -0,0 +1,165 @@
+/* pbmtoascii.c - read a portable bitmap and produce ASCII graphics
+**
+** Copyright (C) 1988, 1992 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 "pbm.h"
+
+#define SQQ '\''
+#define BSQ '\\'
+
+/* Bit-map for 1x2 mode:
+**     1
+**     2
+*/
+static char carr1x2[4] = {
+/*   0    1    2    3   */
+    ' ', '"', 'o', 'M' };
+
+/* Bit-map for 2x4 mode (hex):
+**     1  2
+**     4  8
+**     10 20
+**     40 80
+** The idea here is first to preserve geometry, then to show density.
+*/
+#define D08 'M'
+#define D07 'H'
+#define D06 '&'
+#define D05 '$'
+#define D04 '?'
+static char carr2x4[256] = {
+/*0  1    2   3    4   5    6   7    8   9    A   B    C   D    E   F  */
+' ',SQQ, '`','"', '-',SQQ, SQQ,SQQ, '-','`', '`','`', '-','^', '^','"',/*00-0F*/
+'.',':', ':',':', '|','|', '/',D04, '/','>', '/','>', '~','+', '/','*',/*10-1F*/
+'.',':', ':',':', BSQ,BSQ, '<','<', '|',BSQ, '|',D04, '~',BSQ, '+','*',/*20-2F*/
+'-',':', ':',':', '~',D04, '<','<', '~','>', D04,'>', '=','b', 'd','#',/*30-3F*/
+'.',':', ':',':', ':','!', '/',D04, ':',':', '/',D04, ':',D04, D04,'P',/*40-4F*/
+',','i', '/',D04, '|','|', '|','T', '/',D04, '/','7', 'r','}', '/','P',/*50-5F*/
+',',':', ';',D04, '>',D04, 'S','S', '/',')', '|','7', '>',D05, D05,D06,/*60-6F*/
+'v',D04, D04,D05, '+','}', D05,'F', '/',D05, '/',D06, 'p','D', D06,D07,/*70-7F*/
+'.',':', ':',':', ':',BSQ, ':',D04, ':',BSQ, '!',D04, ':',D04, D04,D05,/*80-8F*/
+BSQ,BSQ, ':',D04, BSQ,'|', '(',D05, '<','%', D04,'Z', '<',D05, D05,D06,/*90-9F*/
+',',BSQ, 'i',D04, BSQ,BSQ, D04,BSQ, '|','|', '|','T', D04,BSQ, '4','9',/*A0-AF*/
+'v',D04, D04,D05, BSQ,BSQ, D05,D06, '+',D05, '{',D06, 'q',D06, D06,D07,/*B0-BF*/
+'_',':', ':',D04, ':',D04, D04,D05, ':',D04, D04,D05, ':',D05, D05,D06,/*C0-CF*/
+BSQ,D04, D04,D05, D04,'L', D05,'[', '<','Z', '/','Z', 'c','k', D06,'R',/*D0-DF*/
+',',D04, D04,D05, '>',BSQ, 'S','S', D04,D05, 'J',']', '>',D06, '1','9',/*E0-EF*/
+'o','b', 'd',D06, 'b','b', D06,'6', 'd',D06, 'd',D07, '#',D07, D07,D08 /*F0-FF*/
+    };
+
+
+
+int
+main( argc, argv )
+int argc;
+char* argv[];
+    {
+    FILE* ifp;
+    int argn, gridx, gridy, rows, cols, format;
+    int ccols, lastcol, row, subrow, subcol;
+    register int col, b;
+    bit* bitrow;
+    register bit* bP;
+    int* sig;
+    register int* sP;
+    char* line;
+    register char* lP;
+    char* carr;
+    const char* usage = "[-1x2|-2x4] [pbmfile]";
+
+    pbm_init( &argc, argv );
+
+    /* Set up default parameters. */
+    argn = 1;
+    gridx = 1;
+    gridy = 2;
+    carr = carr1x2;
+
+    /* Check for flags. */
+    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
+	{
+	if ( pm_keymatch( argv[argn], "-1x2", 2 ) )
+	    {
+	    gridx = 1;
+	    gridy = 2;
+	    carr = carr1x2;
+	    }
+	else if ( pm_keymatch( argv[argn], "-2x4", 2 ) )
+	    {
+	    gridx = 2;
+	    gridy = 4;
+	    carr = carr2x4;
+	    }
+	else
+	    pm_usage( usage );
+	++argn;
+	}
+
+    if ( argn < argc )
+	{
+	ifp = pm_openr( argv[argn] );
+	++argn;
+	}
+    else
+	ifp = stdin;
+    
+    if ( argn != argc )
+        pm_usage( usage );
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+    ccols = ( cols + gridx - 1 ) / gridx;
+    bitrow = pbm_allocrow( cols );
+    sig = (int*) pm_allocrow( ccols, sizeof(int) );
+    line = (char*) pm_allocrow( ccols + 1, sizeof(char) );
+
+    for ( row = 0; row < rows; row += gridy )
+	{
+	/* Get a character-row's worth of sigs. */
+	for ( col = 0; col < ccols; ++col )
+	    sig[col] = 0;
+	b = 1;
+	for ( subrow = 0; subrow < gridy; ++subrow )
+	    {
+	    if ( row + subrow < rows )
+		{
+		pbm_readpbmrow( ifp, bitrow, cols, format );
+		for ( subcol = 0; subcol < gridx; ++subcol )
+		    {
+		    for ( col = subcol, bP = &(bitrow[subcol]), sP = sig;
+			  col < cols;
+			  col += gridx, bP += gridx, ++sP )
+			if ( *bP == PBM_BLACK )
+			    *sP |= b;
+		    b <<= 1;
+		    }
+		}
+	    }
+	/* Ok, now remove trailing blanks.  */
+	for ( lastcol = ccols - 1; lastcol >= 0; --lastcol )
+	    if ( carr[sig[lastcol]] != ' ' )
+		break;
+	/* Copy chars to an array and print. */
+	for ( col = 0, sP = sig, lP = line; col <= lastcol; ++col, ++sP, ++lP )
+	    *lP = carr[*sP];
+	*lP++ = '\0';
+	puts( line );
+	}
+
+    pm_close( ifp );
+    pbm_freerow( bitrow );
+    pm_freerow( (char*) sig );
+    pm_freerow( (char*) line );
+
+    /* If the program failed, it previously aborted with nonzero completion
+       code, via various function calls.
+    */
+    return 0;
+    }
diff --git a/converter/pbm/pbmtoatk.c b/converter/pbm/pbmtoatk.c
new file mode 100644
index 00000000..de7adf63
--- /dev/null
+++ b/converter/pbm/pbmtoatk.c
@@ -0,0 +1,188 @@
+/* pbmtoatk.c - convert portable bitmap to Andrew Toolkit raster object
+**
+** Copyright (C) 1991 by Bill Janssen.
+**
+** 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 <string.h>
+
+#include "pm_c_util.h"
+#include "nstring.h"
+#include "pbm.h"
+
+#define DEFAULTSCALE (1<<16)
+#define RASTERVERSION 2
+
+
+static void
+write_atk_bytes(FILE *        const file, 
+                unsigned char const curbyte, 
+                unsigned int  const startcount) {
+
+    /* codes for data stream */
+    static unsigned char const whitezero   = 'f';
+    static unsigned char const whitetwenty = 'z';
+    static unsigned char const blackzero   = 'F';
+    static unsigned char const blacktwenty = 'Z';
+    static unsigned char const otherzero   = 0x1F;
+
+    #define WHITEBYTE 0x00
+    #define BLACKBYTE 0xFF
+
+    /* WriteRow table for conversion of a byte value to two character
+       hex representation 
+    */
+
+    static unsigned char hex[16] = {
+    '0', '1', '2', '3', '4', '5', '6', '7',
+    '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+    };
+
+    unsigned int curcount;
+
+    curcount = startcount;  /* initial value */
+
+    switch (curbyte) {
+    case WHITEBYTE:
+        while (curcount > 20) {
+            fputc(whitetwenty, file);
+            curcount -= 20;
+        }
+        fputc(whitezero + curcount, file);
+        break;
+    case BLACKBYTE:
+        while (curcount > 20) {
+            fputc(blacktwenty, file);
+            curcount -= 20;
+        }
+        fputc(blackzero + curcount, file);
+        break;
+    default:
+        while (curcount > 16) {
+            fputc(otherzero + 16, file);
+            fputc(hex[curbyte / 16], file);
+            fputc(hex[curbyte & 15], file);
+            curcount -= 16;
+        }
+        if (curcount > 1)
+            fputc(otherzero + curcount, file);
+        else ;  /* the byte written will represent a single instance */
+        fputc(hex[curbyte / 16], file);
+        fputc(hex[curbyte & 15], file);
+    }
+}
+
+
+
+static void
+process_atk_byte(int *           const pcurcount, 
+                 unsigned char * const pcurbyte, 
+                 FILE *          const file, 
+                 unsigned char   const newbyte, 
+                 int             const eolflag) {
+
+    int curcount;
+    unsigned char curbyte;
+
+    curcount = *pcurcount;  /* initial value */
+    curbyte  = *pcurbyte;  /* initial value */
+    
+    if (curcount < 1) {
+        *pcurbyte = curbyte = newbyte;
+        *pcurcount = curcount = 1;
+    } else if (newbyte == curbyte) {
+        *pcurcount = (curcount += 1);
+    }
+
+    if (curcount > 0 && newbyte != curbyte) {
+        write_atk_bytes (file, curbyte, curcount);
+        *pcurcount = 1;
+        *pcurbyte = newbyte;
+    }
+
+    if (eolflag) {
+        write_atk_bytes (file, *pcurbyte, *pcurcount);
+        fprintf(file, " |\n");
+        *pcurcount = 0;
+        *pcurbyte = 0;
+    }
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    FILE *ifd;
+    bit *bitrow;
+    register bit *bP;
+    int rows, cols, format, row;
+    int col;
+    char name[100], *cp;
+    unsigned char curbyte, newbyte;
+    int curcount, gather;
+
+    pbm_init ( &argc, argv );
+
+    if (argc-1 > 1)
+        pm_error("Too many arguments.  Only argument is file name");
+
+    else if (argc-1 == 1) {
+        ifd = pm_openr( argv[1] );
+        strcpy(name, argv[1]);
+        if (STREQ( name, "-"))
+            strcpy(name, "noname");
+        
+        if ((cp = strchr(name, '.')) != 0)
+            *cp = '\0';
+    } else {
+        ifd = stdin;
+        strcpy( name, "noname" );
+    }
+
+    pbm_readpbminit(ifd, &cols, &rows, &format);
+    bitrow = pbm_allocrow(cols);
+
+    printf ("\\begindata{raster,%d}\n", 1);
+    printf ("%d %d %d %d ", RASTERVERSION, 0, DEFAULTSCALE, DEFAULTSCALE);
+    printf ("%d %d %d %d\n", 0, 0, cols, rows); /* subraster */
+    printf ("bits %d %d %d\n", 1, cols, rows);
+
+    for (row = 0; row < rows; ++row) {
+        pbm_readpbmrow(ifd, bitrow, cols, format);
+        bP = bitrow;
+        gather = 0;
+        newbyte = 0;
+        curbyte = 0;
+        curcount = 0;
+        col = 0;
+        while (col < cols) {
+            if (gather > 7) {
+                process_atk_byte (&curcount, &curbyte, stdout, newbyte, FALSE);
+                gather = 0;
+                newbyte = 0;
+            }
+            newbyte = (newbyte << 1) | (*bP++);
+            gather += 1;
+            col += 1;
+        }
+
+        if (gather > 0) {
+            newbyte = (newbyte << (8 - gather));
+            process_atk_byte (&curcount, &curbyte, stdout, newbyte, TRUE);
+        }
+    }
+
+    pm_close( ifd );
+    
+    printf ("\\enddata{raster, %d}\n", 1);
+
+    return 0;
+}
diff --git a/converter/pbm/pbmtobbnbg.c b/converter/pbm/pbmtobbnbg.c
new file mode 100644
index 00000000..e97ef4f2
--- /dev/null
+++ b/converter/pbm/pbmtobbnbg.c
@@ -0,0 +1,152 @@
+/* pbmtobg.c - read a portable bitmap and produce BitGraph graphics
+**
+** Copyright 1989 by Mike Parker.
+**
+** 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.
+*/
+  
+/*
+** Changed to take advantage of negative Packed Pixed Data values and
+** supply ANSI-standard string terminator.  Paul Milazzo, 28 May 1990.
+*/
+
+#include "pbm.h"
+
+static void write16 ARGS(( unsigned int	));
+
+static int nco;
+
+int
+main(argc,argv)
+int argc;
+char **argv;
+{
+ int rows;
+ int cols;
+ int format;
+ bit *bitrow;
+ int row;
+ unsigned int sixteen;
+ int i;
+ unsigned int mask;
+ int op;
+ int x;
+ int y;
+
+
+ pbm_init( &argc, argv );
+
+ op = 3;
+ switch (argc)
+  { case 1:
+       break;
+    case 2:
+       op = atoi(argv[1]);
+       break;
+    case 3:
+       x = atoi(argv[1]);
+       y = atoi(argv[2]);
+       printf("\33:%d;%dm",x,y);
+       break;
+    case 4:
+       op = atoi(argv[1]);
+       x = atoi(argv[2]);
+       y = atoi(argv[3]);
+       printf("\33:%d;%dm",x,y);
+       break;
+  }
+ nco = 0;
+ pbm_readpbminit(stdin,&cols,&rows,&format);
+ printf("\33P:%d;%d;%ds\n",op,cols,rows);
+ bitrow = pbm_allocrow(cols);
+ for (row=0;row<rows;row++)
+  { pbm_readpbmrow(stdin,bitrow,cols,format);
+    sixteen = 0;
+    mask = 0x8000;
+    for (i=0;i<cols;i++)
+     { if (bitrow[i]==PBM_BLACK) sixteen |= mask;
+       mask >>= 1;
+       if (mask == 0)
+	{ mask = 0x8000;
+	  write16(sixteen);
+	  sixteen = 0;
+	}
+     }
+    if (mask != 0x8000)
+     { write16(sixteen);
+     }
+  }
+ puts("\033\\");
+ exit(0);
+}
+
+#ifdef POSITIVE_VALUES_ONLY
+static void
+write16(sixteen)
+unsigned int sixteen;
+{
+ if (nco > 75)
+  { putchar('\n');
+    nco = 0;
+  }
+ if (sixteen & 0xfc00)
+  { putchar(0100+(sixteen>>10));
+    nco ++;
+  }
+ if (sixteen & 0xfff0)
+  { putchar(0100+((sixteen>>4)&0x3f));
+    nco ++;
+  }
+ putchar(060+(sixteen&0xf));
+ nco ++;
+}
+#else
+/*
+ *  This version of "write16" uses negative Packed Pixel Data values to
+ *  represent numbers in the range 0x7fff--0xffff; negative values will
+ *  require fewer characters as they approach the upper end of that range.
+ */
+static void
+write16 (word)
+unsigned int	word;
+{
+    int		high;
+    int		mid;
+    int		low;
+    int		signChar;
+
+    if (nco > 75) {
+	putchar ('\n');
+	nco = 0;
+    }
+
+    if (word > 0x7fff) {
+	word = (unsigned int) (0x10000L - (long) word);
+	signChar = ' ';
+    }
+    else
+	signChar = '0';
+
+    high = (word >> 10) + '@';
+    mid	= ((word & 0x3f0) >> 4) + '@';
+    low	= (word & 0xf) + signChar;
+
+    if (high != '@') {
+	printf ("%c%c%c", high, mid, low);
+	nco += 3;
+    }
+    else if (mid != '@') {
+	printf ("%c%c", mid, low);
+	nco += 2;
+    }
+    else {
+	putchar (low);
+	nco++;
+    }
+}
+#endif
diff --git a/converter/pbm/pbmtocmuwm.c b/converter/pbm/pbmtocmuwm.c
new file mode 100644
index 00000000..64d7af40
--- /dev/null
+++ b/converter/pbm/pbmtocmuwm.c
@@ -0,0 +1,117 @@
+/* pbmtocmuwm.c - read a portable bitmap and produce a CMU window manager bitmap
+**
+** 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 "pbm.h"
+#include "cmuwm.h"
+
+static void putinit ARGS(( int rows, int cols ));
+static void putbit ARGS(( bit b ));
+static void putrest ARGS(( void ));
+static void putitem ARGS(( void ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, format, padright, row, col;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[pbmfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+    bitrow = pbm_allocrow( cols );
+    
+    /* Round cols up to the nearest multiple of 8. */
+    padright = ( ( cols + 7 ) / 8 ) * 8 - cols;
+
+    putinit( rows, cols );
+    for ( row = 0; row < rows; row++ )
+	{
+	pbm_readpbmrow( ifp, bitrow, cols, format );
+        for ( col = 0, bP = bitrow; col < cols; col++, bP++ )
+	    putbit( *bP );
+	for ( col = 0; col < padright; col++ )
+	    putbit( 0 );
+        }
+
+    pm_close( ifp );
+
+    putrest( );
+
+    exit( 0 );
+    }
+
+static unsigned char item;
+static int bitsperitem, bitshift;
+
+static void
+putinit( rows, cols )
+    int rows, cols;
+    {
+    if ( pm_writebiglong( stdout, CMUWM_MAGIC ) == -1 )
+	pm_error( "write error" );
+    if ( pm_writebiglong( stdout, cols ) == -1 )
+	pm_error( "write error" );
+    if ( pm_writebiglong( stdout, rows ) == -1 )
+	pm_error( "write error" );
+    if ( pm_writebigshort( stdout, (short) 1 ) == -1 )
+	pm_error( "write error" );
+
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 7;
+    }
+
+#if __STDC__
+static void
+putbit( bit b )
+#else /*__STDC__*/
+static void
+putbit( b )
+    bit b;
+#endif /*__STDC__*/
+    {
+    if ( bitsperitem == 8 )
+	putitem( );
+    if ( b == PBM_WHITE )
+	item += 1 << bitshift;
+    bitsperitem++;
+    bitshift--;
+    }
+
+static void
+putrest( )
+    {
+    if ( bitsperitem > 0 )
+	putitem( );
+    }
+
+static void
+putitem( )
+    {
+    (void) putc( item, stdout );
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 7;
+    }
diff --git a/converter/pbm/pbmtodjvurle.c b/converter/pbm/pbmtodjvurle.c
new file mode 100644
index 00000000..dbe96f31
--- /dev/null
+++ b/converter/pbm/pbmtodjvurle.c
@@ -0,0 +1,140 @@
+/*
+   Convert a PBM image into the DjVu Bitonal RLE format
+   described in the csepdjvu(1) documentation
+  
+   Copyright (c) 2004 Scott Pakin <scott+pbm@pakin.org>
+
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+   
+   1. Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+   2. Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+   3. The name of the author may not be used to endorse or promote products
+      derived from this software without specific prior written permission.
+   
+   THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+   IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "pbm.h"
+
+
+/* Write a byte to a file and check for errors. */
+static void
+writebyte(FILE *        const ofP,
+          unsigned char const c) {
+
+    if (fputc (c, ofP) == EOF)
+        pm_error ("failed to write to the RLE file.  Errno=%d (%s)",
+                  errno, strerror(errno));
+}
+
+
+/* Write a run length to the RLE file. */
+static void 
+write_rle (FILE *rlefile, uint32n tally)
+{
+  do {
+    /* Output a single run. */
+    if (tally < 192) {
+      /* Single-byte runs */
+      writebyte (rlefile, tally);
+      tally >>= 8;
+    }
+    else {
+      /* Two-byte runs */
+      writebyte (rlefile, ((tally>>8)&0x3F) + 0xC0);
+      writebyte (rlefile, tally&0xFF);
+      tally >>= 14;
+    }
+
+    /* Very large runs need to be split into smaller runs.  We
+     * therefore need to toggle back to the same color we had for the
+     * previous smaller run. */
+    if (tally > 0)
+      writebyte (rlefile, 0);
+  }
+  while (tally > 0);
+}
+
+
+
+int 
+main (int argc, char *argv[])
+{
+  FILE * const rlefile = stdout;    /* Generated Bitonal RLE file */
+
+  FILE *pbmfile;             /* PBM file to convert */
+  int numcols, numrows;      /* Width and height in pixels of the PBM file */
+  int format;                /* Original image type before conversion to PBM */
+  bit *pbmrow;               /* One row of the PBM file */
+  uint32n pixeltally = 0;    /* Run length of the current color */
+  int row, col;              /* Row and column loop variables */
+  const char * pbmfilename;  /* Name of input file */
+
+  /* Parse the command line. */
+  pbm_init (&argc, argv);
+
+  if (argc-1 < 1)
+      pbmfilename = "-";
+  else if (argc-1 == 1)
+      pbmfilename = argv[1];
+  else
+      pm_error("Program takes at most 1 argument -- the input file name.  "
+               "You specified %d", argc-1);
+
+  pbmfile = pm_openr(pbmfilename);
+
+  /* Write an RLE header. */
+  pbm_readpbminit (pbmfile, &numcols, &numrows, &format);
+  fprintf (rlefile, "R4\n");
+  fprintf (rlefile, "%d %d\n", numcols, numrows);
+
+  /* Write the RLE data. */
+  pbmrow = pbm_allocrow (numcols);
+  for (row=0; row<numrows; row++) {
+    bit prevpixel;        /* Previous pixel seen */
+
+    pbm_readpbmrow (pbmfile, pbmrow, numcols, format);
+    prevpixel = PBM_WHITE;   /* Bitonal RLE rows always start with white */
+    pixeltally = 0;
+    for (col=0; col<numcols; col++) {
+      bit newpixel = pbmrow[col];      /* Current pixel color */
+
+      if (newpixel == prevpixel)
+        pixeltally++;
+      else {
+        write_rle (rlefile, pixeltally);
+        pixeltally = 1;
+        prevpixel = newpixel;
+      }
+    }
+    write_rle (rlefile, pixeltally);
+  }
+
+  /* Finish up cleanly. */
+  pbm_freerow (pbmrow);
+  if (rlefile != stdout)
+    pm_close (rlefile);
+  if (pbmfile != stdin)
+    pm_close (pbmfile);
+  exit (0);
+}
diff --git a/converter/pbm/pbmtoepsi.c b/converter/pbm/pbmtoepsi.c
new file mode 100644
index 00000000..fc8cee7d
--- /dev/null
+++ b/converter/pbm/pbmtoepsi.c
@@ -0,0 +1,252 @@
+/* pbmtoepsi.c
+**
+**    by Doug Crabill, based heavily on pbmtoascii
+**
+**    Converts a pbm file to an encapsulated PostScript style bitmap.
+**    Note that it does NOT covert the pbm file to PostScript, only to
+**    a bitmap to be added to a piece of PostScript generated elsewhere.
+**
+** Copyright (C) 1988 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 "pbm.h"
+#include "shhopt.h"
+
+#if !defined(MAXINT)
+#define MAXINT (0x7fffffff)
+#endif
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *inputFilespec;  /* Filespecs of input files */
+
+    unsigned int dpiX;     /* horiz component of DPI option */
+    unsigned int dpiY;     /* vert component of DPI option */
+    unsigned int bbonly;
+
+    unsigned int verbose;
+};
+
+
+
+static void
+parse_dpi(char * const dpiOpt, 
+          unsigned int * const dpiXP, unsigned int * const dpiYP) {
+
+    char *dpistr2;
+    unsigned int dpiX, dpiY;
+
+    dpiX = strtol(dpiOpt, &dpistr2, 10);
+    if (dpistr2 == dpiOpt) 
+        pm_error("Invalid value for -dpi: '%s'.  Must be either number "
+                 "or NxN ", dpiOpt);
+    else {
+        if (*dpistr2 == '\0') {
+            *dpiXP = dpiX;
+            *dpiYP = dpiX;
+        } else if (*dpistr2 == 'x') {
+            char * dpistr3;
+
+            dpistr2++;  /* Move past 'x' */
+            dpiY = strtol(dpistr2, &dpistr3, 10);        
+            if (dpistr3 != dpistr2 && *dpistr3 == '\0') {
+                *dpiXP = dpiX;
+                *dpiYP = dpiY;
+            } else {
+                pm_error("Invalid value for -dpi: '%s'.  Must be either "
+                         "number or NxN", dpiOpt);
+            }
+        }
+    }
+}
+
+
+static void
+parseCommandLine(int argc, char ** const 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 = malloc(100*sizeof(optEntry));
+        /* Instructions to OptParseOptions2 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    char * dpiOpt;
+    unsigned int dpiOptSpec;
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "bbonly",     OPT_FLAG,   NULL, &cmdlineP->bbonly,        0);
+    OPTENT3(0, "verbose",    OPT_FLAG,   NULL, &cmdlineP->verbose,       0);
+    OPTENT3(0, "dpi",        OPT_STRING, &dpiOpt,         &dpiOptSpec,   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 (dpiOptSpec)
+        parse_dpi(dpiOpt, &cmdlineP->dpiX, &cmdlineP->dpiY);
+    else
+        cmdlineP->dpiX = cmdlineP->dpiY = 72;
+    
+    if ((argc-1) > 1)
+        pm_error("Too many arguments (%d).  Only argument is input filespec",
+                 argc-1);
+    
+    if (argc-1 == 0)
+        cmdlineP->inputFilespec = "-";
+    else
+        cmdlineP->inputFilespec = argv[1];
+}
+
+
+
+static void
+findPrincipalImage(bit ** const bits, 
+                   int    const rows,
+                   int    const cols,
+                   int *  const topP,
+                   int *  const bottomP,
+                   int *  const leftP,
+                   int *  const rightP) {
+
+    int top, bottom, left, right;
+    int row;
+
+    /* Initial values */
+    top = MAXINT;
+    bottom = -MAXINT;
+    left = MAXINT;
+    right = -MAXINT;
+ 
+    for (row = 0; row < rows; row++) {
+        int col;
+        for (col = 0; col < cols; col++) {
+            if (bits[row][col] == PBM_BLACK) {
+                if (row < top) 
+                    top = row;
+                if (row > bottom) 
+                    bottom = row;
+                if (col < left) 
+                    left = col;
+                if (col > right) 
+                    right = col;
+            }
+        }
+    }
+    *topP = top;
+    *bottomP = bottom;
+    *leftP = left;
+    *rightP = right;
+}
+
+
+
+static void
+outputBoundingBox(int const top, int const bottom,
+                  int const left, int const right,
+                  int const rows,
+                  unsigned int const dpiX, unsigned int const dpiY) {
+
+    float const xScale = 72.0 / dpiX;
+    float const yScale = 72.0 / dpiY;
+
+    printf("%%%%BoundingBox: %d %d %d %d\n", 
+           ROUND(left*xScale),  ROUND((rows - bottom)*yScale), 
+           ROUND(right*xScale), ROUND((rows - top)*yScale));
+}
+
+
+
+static unsigned char
+eightPixels(bit ** const bits,
+            int    const row,
+            int    const col,
+            int    const cols) {
+/*----------------------------------------------------------------------------
+  Compute a byte that represents the 8 pixels starting at Column 'col' of
+  row 'row' of the raster 'bits'.  The most significant bit of the result
+  represents the leftmost pixel, with 1 meaning black.
+
+  The row is 'cols' columns wide, so fill on the right with white if there
+  are not eight pixels in the row starting with Column 'col'.
+-----------------------------------------------------------------------------*/
+    unsigned int bitPos;
+    unsigned char octet;
+
+    octet = 0;  /* initial value */
+
+    for (bitPos = 0; bitPos < 8; ++bitPos) {
+        octet <<= 1;
+        if (col + bitPos < cols)
+            if (bits[row][col + bitPos] == PBM_BLACK)
+                octet += 1;
+    }
+    return(octet);
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE *ifP;
+    bit **bits;
+    int rows, cols;
+    int top, bottom, left, right;
+        /* boundaries of principal part of image -- i.e. excluding white
+           borders
+        */
+
+    pbm_init( &argc, argv );
+    
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+    bits = pbm_readpbm( ifP, &cols, &rows );
+    pm_close(ifP);
+
+    findPrincipalImage(bits, rows, cols, &top, &bottom, &left, &right);
+
+    printf("%%!PS-Adobe-2.0 EPSF-1.2\n");
+
+    outputBoundingBox(top, bottom, left, right, rows, 
+                      cmdline.dpiX, cmdline.dpiY);
+
+    if (!cmdline.bbonly) {
+        int row;
+        printf("%%%%BeginPreview: %d %d 1 %d\n", 
+               right - left + 1, bottom - top + 1, bottom - top + 1);
+
+        for (row = top; row <= bottom; row++) {
+            int col;
+
+            printf("%% ");
+
+            for (col = left; col <= right; col += 8) 
+                printf("%02x", eightPixels(bits, row, col, cols));
+
+            printf("\n");
+        }
+        printf("%%%%EndImage\n");
+        printf("%%%%EndPreview\n");
+    }
+    exit(0);
+}
diff --git a/converter/pbm/pbmtoepson.c b/converter/pbm/pbmtoepson.c
new file mode 100644
index 00000000..d26734d4
--- /dev/null
+++ b/converter/pbm/pbmtoepson.c
@@ -0,0 +1,338 @@
+/* pbmtoeps.c - read a PBM image and produce Epson graphics
+**
+** Copyright (C) 1990 by John Tiller (tiller@galois.msfc.nasa.gov)
+**			 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.
+*/
+
+#define _BSD_SOURCE    /* Make sure strcasecmp() is in string.h */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "shhopt.h"
+#include "mallocvar.h"
+
+#include "pbm.h"
+
+
+static char const esc = 033;
+
+enum epsonProtocol {ESCP9, ESCP};
+
+enum adjacence {ADJACENT_ANY, ADJACENT_YES, ADJACENT_NO};
+
+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 dpi;  /* zero means "any" */
+    enum adjacence adjacence;
+    enum epsonProtocol protocol;
+};
+
+
+
+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;
+
+    char * protocol;
+    unsigned int adjacentSpec, nonadjacentSpec;
+    unsigned int dpiSpec, protocolSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "protocol",   OPT_UINT,   &protocol,
+            &protocolSpec,                    0);
+    OPTENT3(0, "dpi",        OPT_UINT,   &cmdlineP->dpi,
+            &dpiSpec,                    0);
+    OPTENT3(0, "adjacent",   OPT_FLAG,   NULL,
+            &adjacentSpec,                    0);
+    OPTENT3(0, "nonadjacent",   OPT_FLAG,   NULL,
+            &nonadjacentSpec,                    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 (!dpiSpec)
+        cmdlineP->dpi = 0;
+    else {
+        if (cmdlineP->dpi == 0)
+            pm_error("-dpi must be positive");
+    }
+
+    if (!protocolSpec)
+        cmdlineP->protocol = ESCP9;
+    else {
+        if (strcasecmp(protocol, "escp9") == 0)
+            cmdlineP->protocol = ESCP9;
+        else if (strcasecmp(protocol, "escp") == 0)
+            cmdlineP->protocol = ESCP;
+        else if (strcasecmp(protocol, "escp2") == 0)
+            pm_error("This program cannot do ESC/P2.  Try Pbmtoescp2.");
+        else
+            pm_error("Unrecognized value '%s' for -protocol.  "
+                     "Only recognized values are 'escp9' and 'escp'",
+                     protocol);
+    }
+    
+    if (adjacentSpec && nonadjacentSpec)
+        pm_error("You can't specify both -adjacent and -nonadjacent");
+    else if (adjacentSpec)
+        cmdlineP->adjacence = ADJACENT_YES;
+    else if (nonadjacentSpec)
+        cmdlineP->adjacence = ADJACENT_NO;
+    else
+        cmdlineP->adjacence = ADJACENT_ANY;
+
+    if (argc-1 < 1)
+        cmdlineP->inputFilespec = "-";
+    else {
+        cmdlineP->inputFilespec = argv[1];
+        if (argc-1 > 1)
+            pm_error("Too many arguments (%d).  The only non-option argument "
+                     "is the file name", argc-1);
+    }
+}
+
+
+
+static unsigned int
+lineWidth(const bit ** const stripeBits,
+          unsigned int const cols,
+          unsigned int const stripeRows) {
+/*----------------------------------------------------------------------------
+   Return the column number just past the rightmost column of the stripe
+   stripeBits[] that contains at least some black.
+
+   The stripe is 'cols' wide by 'stripeRows' high.
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+    unsigned int endSoFar;
+    
+    endSoFar = 0;
+
+    for (col = 0; col < cols; ++ col) {
+        unsigned int stripeRow;  /* row number within stripe */
+
+        for (stripeRow = 0; stripeRow < stripeRows; ++stripeRow) {
+            if (stripeBits[stripeRow][col] == PBM_BLACK)
+                endSoFar = col+1;
+        }
+    }
+    return endSoFar;
+}
+
+
+
+static void
+printStripe(const bit ** const stripeBits,
+            unsigned int const cols,
+            unsigned int const stripeRows,
+            char         const m) {
+/*----------------------------------------------------------------------------
+   Print one stripe (a group of rows printed with one pass of the print
+   head.  The stripe is cols columns wide by stripeRows high.
+   stripeBits[row][col] is the pixel value for Row row, Column col within
+   the stripe.
+
+   'm' is the "m" parameter for the Select Bit Image command.  It controls
+   such things as the horizontal density.
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+
+    /* Print header of Select Bit Image command */
+    printf("%c%c%c%c%c", esc, '*', m, cols % 256, cols / 256);
+    
+    /* Print the data part of the Select Bit Image command */
+    for (col = 0; col < cols; ++col) {
+        unsigned int stripeRow;
+        int val;
+        
+        val = 0;
+        for (stripeRow = 0; stripeRow < stripeRows; ++stripeRow) 
+            if (stripeBits[stripeRow][col] == PBM_BLACK)
+                val |= (1 << (8-1-stripeRow));
+        putchar(val);
+    }
+}
+
+
+
+static void
+computeM(enum epsonProtocol const protocol,
+         unsigned int       const dpi,
+         enum adjacence     const adjacence,
+         char *             const mP) {
+/*----------------------------------------------------------------------------
+   Compute the "m" parameter for the Select Bit Image command.
+-----------------------------------------------------------------------------*/
+    switch (dpi) {
+    case 0:
+        /* Special value meaning "any dpi you feel is appropriate" */
+        if (adjacence == ADJACENT_NO)
+            *mP = 2;
+        else {
+            switch (protocol) {
+            case ESCP9: *mP = 5; break;
+            case ESCP:  *mP = 6; break;
+            }
+        }
+        break;
+    case 60: 
+        if (adjacence == ADJACENT_NO)
+            pm_error("You can't print at %u dpi "
+                     "with adjacent dot printing", dpi);
+        *mP = 0;
+        break;
+    case 120:
+        *mP = adjacence == ADJACENT_NO ? 2 : 1;
+        break;
+    case 240:
+        if (adjacence == ADJACENT_YES)
+            pm_error("You can't print at %u dpi "
+                     "without adjacent dot printing", dpi);
+        *mP = 3;
+        break;
+    case 80:
+        if (adjacence == ADJACENT_NO)
+            pm_error("You can't print at %u dpi "
+                     "with adjacent dot printing", dpi);
+        *mP = 4;
+        break;
+    case 72:
+        if (protocol != ESCP9)
+            pm_error("%u dpi is possible only with the ESC/P 9-pin protocol", 
+                     dpi);
+        if (adjacence == ADJACENT_NO)
+            pm_error("You can't print at %u dpi "
+                     "with adjacent dot printing", dpi);
+        *mP = 5;
+        break;
+    case 90:
+        if (adjacence == ADJACENT_NO)
+            pm_error("You can't print at %u dpi "
+                     "with adjacent dot printing", dpi);
+        *mP = 6;
+        break;
+    case 144:
+        if (protocol != ESCP9)
+            pm_error("%u dpi is possible only with the ESC/P 9-pin protocol", 
+                     dpi);
+        if (adjacence == ADJACENT_NO)
+            pm_error("You can't print at %u dpi "
+                     "with adjacent dot printing", dpi);
+        *mP = 7;
+        break;
+    default:
+        pm_error("Invalid DPI value: %u.  This program knows only "
+                 "60, 72, 80, 90, 120, 144, and 240.", dpi);
+    }
+}
+
+
+
+static void
+convertToEpson(const bit **       const bits,
+               int                const cols,
+               int                const rows,
+               enum epsonProtocol const protocol,
+               unsigned int       const dpi,
+               enum adjacence     const adjacence) {
+    
+    unsigned int const rowsPerStripe = 8;
+    unsigned int const stripes = (rows + rowsPerStripe-1) / rowsPerStripe;
+
+    unsigned int stripe;
+    char m;
+    
+    computeM(protocol, dpi, adjacence, &m);
+
+    /* Change line spacing to 8/72 inches. */
+    printf("%c%c%c", esc, 'A', 8);
+
+    /* Write out the rows, one stripe at a time.  A stripe is 8 rows --
+       the amount written in one pass of the print head.  The bottommost
+       stripe can be fewer than 8 rows.
+    */
+
+    for (stripe = 0; stripe < stripes; ++stripe) {
+        const bit ** const stripeBits = &bits[stripe*rowsPerStripe];
+        unsigned int const stripeRows = 
+            MIN(rowsPerStripe, rows - stripe * rowsPerStripe);
+            /* Number of rows in this stripe (8 for all but bottom stripe) */
+        
+        unsigned int const endcol = lineWidth(stripeBits, cols, stripeRows);
+            /* Column where right margin (contiguous white area at right
+               end of stripe) begins.  Zero if entire stripe is white.
+            */
+
+        if (endcol > 0)
+            printStripe(stripeBits, endcol, stripeRows, m);
+
+        putchar('\n');
+    }
+    putchar('\f');
+
+    /* Restore normal line spacing. */
+    printf("%c%c", esc, '@');
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE* ifP;
+    const bit** bits;
+    int rows, cols;
+
+    pbm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    bits = (const bit **)pbm_readpbm(ifP, &cols, &rows);
+
+    pm_close(ifP);
+
+    convertToEpson(bits, cols, rows, 
+                   cmdline.protocol, cmdline.dpi, cmdline.adjacence);
+
+    pbm_freearray(bits, rows);
+
+    return 0;
+}
diff --git a/converter/pbm/pbmtoescp2.c b/converter/pbm/pbmtoescp2.c
new file mode 100644
index 00000000..787f7027
--- /dev/null
+++ b/converter/pbm/pbmtoescp2.c
@@ -0,0 +1,186 @@
+/* pbmtoescp2.c - read a portable bitmap and produce Epson ESC/P2 raster
+**                 graphics output data for Epson Stylus printers
+**
+** Copyright (C) 2003 by Ulrich Walcher (u.walcher@gmx.de)
+**                       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.
+*/
+
+/* I used the Epson ESC/P Reference Manual (1997) in writing this. */
+
+#include <string.h>
+
+#include "pbm.h"
+#include "shhopt.h"
+
+static char const esc = 033;
+
+struct cmdlineInfo {
+    const char * inputFileName;
+    unsigned int resolution;
+    unsigned int compress;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo *cmdlineP) {
+
+    optStruct3 opt;
+    unsigned int option_def_index = 0;
+    optEntry *option_def = malloc(100*sizeof(optEntry));
+
+    unsigned int compressSpec, resolutionSpec;
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;
+    opt.allowNegNum = FALSE;
+    OPTENT3(0, "compress",     OPT_UINT,    &cmdlineP->compress,    
+            &compressSpec, 0);
+    OPTENT3(0, "resolution",   OPT_UINT,    &cmdlineP->resolution,  
+            &resolutionSpec, 0);
+    
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    
+    if (argc-1 > 1)
+        pm_error("Too many arguments: %d.  "
+                 "Only argument is the filename", argc-1);
+
+    if (compressSpec) {
+        if (cmdlineP->compress != 0 && cmdlineP->compress != 1)
+            pm_error("Invalid -compress value: %u.  Only 0 and 1 are valid.",
+                     cmdlineP->compress);
+    } else
+        cmdlineP->compress = 1;
+
+    if (resolutionSpec) {
+        if (cmdlineP->resolution != 360 && cmdlineP->resolution != 180)
+            pm_error("Invalid -resolution value: %u.  "
+                     "Only 180 and 360 are valid.", cmdlineP->resolution);
+    } else
+        cmdlineP->resolution = 360;
+
+    if (argc-1 == 1)
+        cmdlineP->inputFileName = argv[1];
+    else
+        cmdlineP->inputFileName = "-";
+}
+
+
+
+static unsigned int
+enc_epson_rle(unsigned int          const l, 
+              const unsigned char * const src, 
+              unsigned char *       const dest) {
+/*----------------------------------------------------------------------------
+  compress l data bytes from src to dest and return the compressed
+  length
+-----------------------------------------------------------------------------*/
+    unsigned int i;      /* index */
+    unsigned int state;  /* run state */
+    unsigned int pos;    /* source position */
+    unsigned int dpos;   /* destination position */
+
+    pos = dpos = state  = 0;
+    while ( pos < l )
+    {
+        for (i=0; i<128 && pos+i<l; i++)
+            /* search for begin of a run, smallest useful run is 3
+               equal bytes 
+            */
+            if(src[pos+i]==src[pos+i+1] && src[pos+i]==src[pos+i+2])
+            {
+                state=1;                       /* set run state */
+                break;
+            }
+	if(i)
+	{
+        /* set counter byte for copy through */
+        dest[dpos] = i-1;       
+        /* copy data bytes before run begin or end cond. */
+        memcpy(dest+dpos+1,src+pos,i);    
+        pos+=i; dpos+=i+1;                 /* update positions */
+	}
+    if (state)
+    {
+        for (i=0; src[pos+i]==src[pos+i+1] && i<128 && pos+i<l; i++);
+        /* found the runlength i */
+        dest[dpos]   = 257-i;           /* set counter for byte repetition */
+        dest[dpos+1] = src[pos];        /* set byte to be repeated */
+        pos+=i; dpos+=2; state=0;       /* update positions, reset run state */
+        }
+    }
+    return dpos;
+}
+
+
+
+int
+main(int argc, char* argv[]) {
+
+    FILE* ifP;
+    int rows, cols;
+    int format;
+    unsigned int row, idx, len;
+    unsigned int h, v;
+    unsigned char *bytes, *cprbytes;
+    struct cmdlineInfo cmdline;
+    
+    pbm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+
+    bytes = malloc(24*pbm_packed_bytes(cols)+2);
+    cprbytes = malloc(2*24*pbm_packed_bytes(cols));
+    if (bytes == NULL || cprbytes == NULL)
+        pm_error("Cannot allocate memory");
+
+    h = v = 3600/cmdline.resolution;
+
+    /* Set raster graphic mode. */
+    printf("%c%c%c%c%c%c", esc, '(', 'G', 1, 0, 1);
+
+    /* Set line spacing in units of 1/360 inches. */
+    printf("%c%c%c", esc, '+', 24*h/10);
+
+    /* Write out raster stripes 24 rows high. */
+    for (row = 0; row < rows; row += 24) {
+        unsigned int const linesThisStripe = (rows-row<24) ? rows%24 : 24;
+        printf("%c%c%c%c%c%c%c%c", esc, '.', cmdline.compress, 
+               v, h, linesThisStripe, 
+               cols%256, cols/256);
+        /* Read pbm rows, each padded to full byte */
+        for (idx = 0; idx < 24 && row+idx < rows; ++idx)
+            pbm_readpbmrow_packed(ifP,bytes+idx*pbm_packed_bytes(cols),
+                                  cols,format);
+        /* Write raster data. */
+        if (cmdline.compress != 0) {
+            /* compressed */
+            len = enc_epson_rle(linesThisStripe * pbm_packed_bytes(cols), 
+                                bytes, cprbytes);
+            fwrite(cprbytes,len,1,stdout);
+        } else
+            /* uncompressed */
+            fwrite(bytes, pbm_packed_bytes(cols), linesThisStripe, stdout);    
+
+        if (rows-row >= 24) putchar('\n');
+    }
+    free(bytes); free(cprbytes);
+    pm_close(ifP);
+
+    /* Reset printer. */
+    printf("%c%c", esc, '@');
+
+    return 0;
+}
diff --git a/converter/pbm/pbmtog3.c b/converter/pbm/pbmtog3.c
new file mode 100644
index 00000000..10db3afe
--- /dev/null
+++ b/converter/pbm/pbmtog3.c
@@ -0,0 +1,485 @@
+/* pbmtog3.c - read a PBM image and produce a Group 3 FAX file
+**
+** Copyright (C) 1989 by Paul Haeberli <paul@manray.sgi.com>.
+**
+** 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.
+*/
+
+/*
+   For specifications for Group 3 (G3) fax MH coding see ITU-T T.4
+   This program generates only MH.  It is coded with future expansion for
+   MR and MMR in mind.
+*/
+
+#include <assert.h>
+
+#include "pbm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "bitreverse.h"
+#include "wordaccess.h"
+#include "g3.h"
+
+#define TC_MC 64
+
+static bool const pbmtorl = 
+#ifdef PBMTORL
+    TRUE;
+#else
+    FALSE;
+#endif
+
+
+struct bitString {
+    /* A string of bits, up to as many fit in a word. */
+    unsigned int bitCount;
+        /* The length of the bit string */
+    wordint intBuffer;
+        /* The bits are in the 'bitCount' least significant bit positions 
+           of this number.  The rest of the bits of this number are always 
+           zero.
+        */
+
+    /* Example:  The bit string 010100, on a machine with a 32 bit word,
+       would be represented by bitCount = 6, intBuffer = 20
+       (N.B. 20 = 00000000 00000000 00000000 00010100 in binary)
+    */
+};
+
+
+
+struct outStream {
+    struct bitString buffer;
+    
+    bool reverseBits;
+};
+
+/* This is a global variable for speed. */
+static struct outStream out;
+
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;
+    unsigned int reversebits;
+    unsigned int nofixedwidth;
+    unsigned int verbose;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** const 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 OptParseOptions2 on how to parse our options.  */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0,   "reversebits",      OPT_FLAG,  NULL, &cmdlineP->reversebits,
+            0);
+    OPTENT3(0,   "nofixedwidth",     OPT_FLAG,  NULL, &cmdlineP->nofixedwidth,
+            0);
+    OPTENT3(0,   "verbose",          OPT_FLAG,  NULL, &cmdlineP->verbose, 
+            0);
+
+    /* TODO
+       Explicit fixed widths: -A4 -B4 -A3
+    */
+
+    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 */
+
+    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];
+}
+
+
+
+static void
+reversebuffer(unsigned char * const p, 
+              unsigned int    const n) {
+
+    unsigned int i;
+    for (i = 0; i < n; ++i)
+        p[i] = bitreverse[p[i]];
+}
+
+
+
+static struct bitString
+makeBs(wordint      const bits, 
+       unsigned int const bitCount) {
+
+    struct bitString retval;
+    retval.intBuffer = bits;
+    retval.bitCount  = bitCount;
+
+    return retval;
+}
+
+    
+
+static __inline__ void
+putbits(struct bitString const newBits) {
+/*----------------------------------------------------------------------------
+   Push the bits 'newBits' onto the right end of output buffer
+   out.buffer (moving the bits already in the buffer left).
+
+   Flush the buffer to stdout as necessary to make room.
+
+   'newBits' must be shorter than a whole word.
+   
+   N.B. the definition of struct bitString requires upper bits to be zero.
+-----------------------------------------------------------------------------*/
+    unsigned int const spaceLeft = 
+        sizeof(out.buffer.intBuffer)*8 - out.buffer.bitCount;
+        /* Number of bits of unused space (at the high end) in buffer */
+
+    assert(newBits.bitCount < sizeof(out.buffer.intBuffer) * 8);
+    assert(newBits.intBuffer >> newBits.bitCount == 0);
+
+    if (spaceLeft > newBits.bitCount) {
+        /* New bits fit with bits to spare */
+        out.buffer.intBuffer = 
+            out.buffer.intBuffer << newBits.bitCount | newBits.intBuffer;
+        out.buffer.bitCount += newBits.bitCount;
+    } else { 
+        /* New bits fill buffer.  We'll have to flush the buffer to stdout
+           and put the rest of the bits in the new buffer.
+        */
+        unsigned int const nextBufBitCount = newBits.bitCount - spaceLeft;
+
+        wordintBytes outbytes;
+        size_t rc;
+
+        wordintToBytes(&outbytes, 
+                       (out.buffer.intBuffer << spaceLeft) 
+                       | (newBits.intBuffer >> nextBufBitCount));
+        if (out.reverseBits)
+            reversebuffer(outbytes, sizeof(outbytes));
+            
+        rc = fwrite(outbytes, 1, sizeof(outbytes), stdout);
+        if (rc != sizeof(outbytes))
+            pm_error("Output error.  Unable to fwrite() to stdout");
+        
+        out.buffer.intBuffer = newBits.intBuffer & ((1<<nextBufBitCount) - 1); 
+        out.buffer.bitCount = nextBufBitCount;
+    }
+}
+
+
+
+static void 
+initOutStream(bool const reverseBits) {
+    out.buffer.intBuffer = 0;
+    out.buffer.bitCount  = 0;
+    out.reverseBits = reverseBits;
+}
+
+
+
+static __inline__ void
+putcode(unsigned int const clr, 
+        unsigned int const ix) {
+
+    /* Note that this requires ttable to be aligned white entry, black
+       entry, white, black, etc.  
+    */
+    putbits(makeBs(ttable[ix * 2 + clr].code, ttable[ix * 2 + clr].length));
+}
+
+
+
+static __inline__ void
+putcode2(int const clr,
+         int const ix) {
+/*----------------------------------------------------------------------------
+   Output Make-up code and Terminating code at once.
+
+   For run lengths above TC_MC threshold (usually 64).
+
+   The codes are combined here to avoid calculations in putbits()
+   wordint is usually wide enough, with 32 or 64 bits.
+   Provisions are made for 16 bit wordint (for debugging).
+
+   Terminating code is max 12 bits, Make-up code is max 13 bits.
+   (See ttable, mtable entries in pbmtog3.h)
+
+   Also reduces object code size when putcode is compiled inline.
+-----------------------------------------------------------------------------*/
+    unsigned int const loIndex = ix % 64 * 2 + clr;
+    unsigned int const hiIndex = ix / 64 * 2 + clr;
+
+    if (sizeof(wordint) * 8 > 24) {
+        unsigned int const l1 = ttable[loIndex].length;
+        
+        putbits(
+            makeBs(mtable[hiIndex].code << l1 | ttable[loIndex].code,
+                   mtable[hiIndex].length + l1)
+            );
+    } else { /* typically 16 bit wordint used for debugging */
+        putbits(makeBs(mtable[hiIndex].code, mtable[hiIndex].length));
+        putbits(makeBs(ttable[loIndex].code, ttable[loIndex].length));
+    }
+}
+
+
+
+static __inline__ void
+putspan_normal(bit          const color, 
+               unsigned int const len) {
+
+    if (len < TC_MC)
+        putcode(color, len);
+    else if (len < 2624)
+        putcode2(color, len);
+    else {  /* len >= 2624 : rare */
+        unsigned int remainingLen;
+
+        for (remainingLen = len;
+             remainingLen >= 2624;
+             remainingLen -= 2623) {
+
+            putcode2(color, 2560+63);
+            putcode(!color, 0);
+        }
+        if (remainingLen < TC_MC)
+            putcode(color, remainingLen);
+        else  /* TC_MC <= len < 2624 */
+            putcode2(color, remainingLen);
+    }
+}
+
+
+
+static __inline__ void
+putspan(bit          const color, 
+        unsigned int const len) {
+/*----------------------------------------------------------------------------
+   Put a span of 'len' pixels of color 'color' in the output.
+-----------------------------------------------------------------------------*/
+    if (pbmtorl) {
+        if (len > 0) 
+            printf("%c %d\n", color == PBM_WHITE ? 'W' : 'B', len);
+    } else 
+        putspan_normal(color, len);
+}
+
+
+
+static void
+puteol(void) {
+
+    if (pbmtorl)
+        puts("EOL");
+    else {
+        struct bitString const eol = {12, 1};
+            
+        putbits(eol);
+    }
+}
+
+
+
+/*
+  PBM raw bitrow to inflection point array
+
+  Write inflection (=color change) points into array milepost[].  
+  It is easy to calculate run length from this.
+
+  In milepost, a white-to-black (black-to-white) inflection point
+  always has an even (odd) index.  A line starting with black is
+  indicated by bitrow[0] == 0.
+
+  WWWWWWWBBWWWWWWW ... = 7,2,7, ...
+  BBBBBWBBBBBWBBBB ... = 0,5,1,5,1,4, ...
+
+  Return the number of milepost elements written.
+  Note that max number of entries into milepost = cols+1 .
+
+  The inflection points are calculated like this:
+
+   r1: 00000000000111111110011111000000
+   r2: c0000000000011111111001111100000 0->carry
+  xor: ?0000000000100000001010000100000
+
+  The 1 bits in the xor above are the inflection points.
+*/
+
+static __inline__ void
+convertRowToRunLengths(unsigned char * const bitrow, 
+                       int             const cols, 
+                       unsigned int *  const milepost,
+                       unsigned int *  const lengthP) {
+
+    unsigned int   const bitsPerWord  = sizeof(wordint) * 8;
+    wordint      * const bitrowByWord = (wordint *) bitrow;
+    int            const wordCount    = (cols + bitsPerWord - 1)/bitsPerWord; 
+        /* Number of full and partial words in the row */
+        
+
+    if (cols % bitsPerWord != 0) {
+        /* Clean final word in row.  For loop simplicity */
+        wordint r1;
+        r1 = bytesToWordint((unsigned char *)&bitrowByWord[wordCount - 1]);
+        r1 >>= bitsPerWord - cols % bitsPerWord;
+        r1 <<= bitsPerWord - cols % bitsPerWord;
+        wordintToBytes((wordintBytes *)&bitrowByWord[wordCount - 1], r1);
+    }
+    {
+
+        wordint carry;
+        wordint r1, r2;
+        unsigned int n;
+        int i,c,k;
+
+        for (i = carry = n = 0; i < wordCount; ++i) {
+            r1 = r2 = bytesToWordint((unsigned char *)&bitrowByWord[i]);
+            r2 = r1 ^ (carry << (bitsPerWord-1) | r2 >> 1);
+            carry = r1 & 0x1;  k = 0;
+            while (r2 != 0) {
+                /* wordintClz(r2) reports most significant "1" bit of r2
+                   counting from MSB = position 0.
+                */
+                c = wordintClz(r2);
+                milepost[n++] = i * bitsPerWord + k + c;
+                r2 <<= c++; r2 <<= 1;  k += c; 
+            } 
+        }
+        if (milepost[n - 1] != cols) 
+            milepost[n++] = cols;
+        *lengthP = n;
+    }
+}
+
+
+
+static void
+padToDesiredWidth(unsigned int * const milepost,
+                  unsigned int * const nRunP,
+                  int            const existingCols,
+                  int            const desiredCols) {
+
+    if (existingCols < desiredCols) {
+        /* adjustment for narrow input in fixed width mode
+           nRun % 2 == 1 (0) means last (=rightmost) pixel is white (black)
+           if white, extend the last span to outwidth
+           if black, fill with a white span len (outwidth - readcols)
+        */
+        if (*nRunP % 2 == 0)
+            ++*nRunP;
+        milepost[*nRunP - 1] = desiredCols;
+    }
+}
+
+
+
+int
+main(int    argc,
+     char * argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    unsigned char * bitrow;
+       /* This is the bits of the current row, as read from the input and
+           modified various ways at various points in the program.  It has
+           a word of zero padding on the high (right) end for the convenience
+           of code that accesses this buffer in word-size bites.
+        */
+     
+    int rows;
+    int cols;
+    int readcols;
+    int outwidth;
+    int format;
+    int row;
+    unsigned int * milepost;
+
+    pbm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+     
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+    if (cmdline.nofixedwidth)
+        readcols = outwidth = cols;
+    else {
+        readcols = MIN(cols, 1728);
+        outwidth = 1728;
+    }
+
+    MALLOCARRAY_NOFAIL(bitrow, pbm_packed_bytes(cols) + sizeof(wordint));
+
+    MALLOCARRAY_NOFAIL(milepost, readcols + 1);
+
+    initOutStream(cmdline.reversebits);
+    puteol();
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int nRun;  /* Number of runs in milepost[] */
+        unsigned int p;
+        unsigned int i;
+
+        pbm_readpbmrow_packed(ifP, bitrow, cols, format);
+        
+        convertRowToRunLengths(bitrow, readcols, milepost, &nRun);
+        
+        padToDesiredWidth(milepost, &nRun, readcols, outwidth);
+
+        for (i = p = 0; i < nRun; p = milepost[i++])
+            putspan(i%2 == 0 ? PBM_WHITE : PBM_BLACK, milepost[i] - p);
+        /* TODO 2-dimensional coding MR, MMR */
+        puteol();
+    }
+
+    free(milepost);
+    {
+        unsigned int i;  
+        for( i = 0; i < 6; ++i)
+            puteol();
+    }
+    if (out.buffer.bitCount > 0) {
+        /* flush final partial buffer */
+        unsigned int const bytesToWrite = (out.buffer.bitCount+7)/8;
+        
+        unsigned char outbytes[sizeof(wordint)];
+        size_t rc;
+        wordintToBytes(&outbytes, 
+                       out.buffer.intBuffer << (sizeof(out.buffer.intBuffer)*8 
+                                                - out.buffer.bitCount));
+        if (out.reverseBits)
+            reversebuffer(outbytes, bytesToWrite);
+        rc = fwrite(outbytes, 1, bytesToWrite, stdout);
+        if (rc != bytesToWrite)
+            pm_error("Output error");
+    }
+    pm_close(ifP);
+
+    return 0;
+}
diff --git a/converter/pbm/pbmtog3.test b/converter/pbm/pbmtog3.test
new file mode 100644
index 00000000..9ca45079
--- /dev/null
+++ b/converter/pbm/pbmtog3.test
@@ -0,0 +1,23 @@
+echo Test 1.  Should print 3697098186 144
+./pbmtog3 ../../testgrid.pbm | cksum
+echo Test 2.  Should print 1248301383 122
+./pbmtog3 -nofixedwidth ../../testgrid.pbm | cksum
+echo Test 3.  Should print 686713716 144
+./pbmtog3 -reverse ../../testgrid.pbm | cksum
+echo Test 4.  Should print 215463240 122
+./pbmtog3 -nofixedwidth -reverse ../../testgrid.pbm | cksum
+echo Test 5.  Should print 28792587 47
+pbmmake -w 10 10 | ./pbmtog3 | cksum
+echo Test 6.  Should print 277456854 32
+pbmmake -w 10 10 | ./pbmtog3 -nofixedwidth | cksum
+echo Test 7.  Should print 28792587 47
+pbmmake -w 10000 10 | ./pbmtog3 | cksum
+echo Test 8.  Should print 871281767 162
+pbmmake -w 10000 10 | ./pbmtog3 -nofixedwidth | cksum
+echo Test 9.  Should print 3736247115 62
+pbmmake -b 10 10 | ./pbmtog3 | cksum
+echo Test 10.  Should print 2820255307 2191856
+pbmmake -g 1700 2286 | ./pbmtog3 | cksum
+echo Test 11.  Should print 4159089282 2226575
+pbmmake -g 1800 2286 | ./pbmtog3 | cksum
+echo Tests done.
diff --git a/converter/pbm/pbmtogem.c b/converter/pbm/pbmtogem.c
new file mode 100644
index 00000000..59f2b9cf
--- /dev/null
+++ b/converter/pbm/pbmtogem.c
@@ -0,0 +1,252 @@
+/* pbmtogem.c - read a portable bitmap and produce a GEM .img file
+**
+** Author: David Beckemeyer (bdt!david)
+**
+** Much of the code for this program was taken from other
+** pbmto* programs.  I just modified the code to produce
+** a .img header and generate .img "Bit Strings".
+**
+** Thanks to Diomidis D. Spinellis for the .img header format.
+**
+** Copyright (C) 1988 by David Beckemeyer (bdt!david) and Jef Poskanzer.
+**
+** Modified by Johann Haider to produce Atari ST compatible files
+**
+** 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.
+*/
+/*
+*  92/07/11 jh
+*
+*  Changes made to this program:
+*  changed header length to count words to conform with Atari ST documentation
+*  removed rounding of the imagewidth to the next word boundary
+*  removed arbitrary limit to imagewidth
+*  changed pattern length to 1 to simplify locating of compressable parts
+*	in real world images
+*  add solid run and pattern run compression
+*
+*  Deficiencies:
+*  Compression of repeated scanlines not added
+*  
+*	Johann Haider (jh@fortec.tuwien.ac.at)
+*
+* 94/01/31 Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de)
+* Changed to remove architecture dependencies
+* Added compression of repeated scanlines
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include "pbm.h"
+
+#define SOLID_0 0
+#define SOLID_1 0xff
+#define MINRUN 4
+#define putsolid(v,c) putc((v&0x80)|c, stdout)
+#define putpattern(v,c) putc(0, stdout);putc(c, stdout);putc(v, stdout)
+
+static void putinit ARGS ((int rows, int cols));
+static void putbit ARGS(( bit b ));
+static void putitem ARGS(( void ));
+static void putrow ARGS(( void ));
+static void flushrow ARGS ((void));
+static void putstring ARGS((register unsigned char *p, register int n));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, format, row, col;
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[pbmfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+
+    bitrow = pbm_allocrow( cols );
+
+    putinit (rows, cols);
+    for ( row = 0; row < rows; ++row )
+	{
+#ifdef DEBUG
+	fprintf (stderr, "row %d\n", row);
+#endif
+	pbm_readpbmrow( ifp, bitrow, cols, format );
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+	    putbit( *bP );
+        putrow( );
+        }
+    flushrow ();
+
+    pm_close( ifp );
+
+
+    exit( 0 );
+    }
+
+static short item;
+static int outcol, outmax;
+static short bitsperitem, bitshift;
+static short linerepeat;
+static unsigned char *outrow, *lastrow;
+
+static void
+putinit (rows, cols)
+     int rows, cols;
+{
+  if (pm_writebigshort (stdout, (short) 1) == -1 /* Image file version */
+      || pm_writebigshort (stdout, (short) 8) == -1 /* Header length */
+      || pm_writebigshort (stdout, (short) 1) == -1 /* Number of planes */
+      || pm_writebigshort (stdout, (short) 1) == -1 /* Pattern length */
+      || pm_writebigshort (stdout, (short) 372) == -1 /* Pixel width */
+      || pm_writebigshort (stdout, (short) 372) == -1 /* Pixel height */
+      || pm_writebigshort (stdout, (short) cols) == -1
+      || pm_writebigshort (stdout, (short) rows) == -1)
+    pm_error ("write error");
+  item = 0;
+  bitsperitem = 0;
+  bitshift = 7;
+  outcol = 0;
+  outmax = (cols + 7) / 8;
+  outrow = (unsigned char *) pm_allocrow (outmax, sizeof (unsigned char));
+  lastrow = (unsigned char *) pm_allocrow (outmax, sizeof (unsigned char));
+  linerepeat = -1;
+}
+
+#if __STDC__
+static void
+putbit( bit b )
+#else /*__STDC__*/
+static void
+putbit( b )
+    bit b;
+#endif /*__STDC__*/
+    {
+    if ( bitsperitem == 8 )
+	putitem( );
+    ++bitsperitem;
+    if ( b == PBM_BLACK )
+	item += 1 << bitshift;
+    --bitshift;
+    }
+
+static void
+putitem( )
+    {
+    outrow[outcol++] = item;
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 7;
+    }
+
+static void
+putstring (p, n)
+register unsigned char *p;
+register int n;
+{
+#ifdef DEBUG
+    fprintf (stderr, "Bitstring, length: %d, pos %d\n", n, outcol);
+#endif
+    (void) putc((char) 0x80, stdout);     /* a Bit string */
+    (void) putc(n, stdout);	/* count */
+    fwrite( p, n, 1, stdout );
+}
+
+static void
+putrow( )
+{
+  if (bitsperitem > 0)
+    putitem ();
+  outcol = 0;
+  if (linerepeat == -1 || linerepeat == 255
+      || memcmp (outrow, lastrow, outmax) != 0)
+    {
+      unsigned char *temp;
+      if (linerepeat != -1) /* Unless first line */
+	flushrow ();
+      /* Swap the pointers */
+      temp = outrow; outrow = lastrow; lastrow = temp;
+      linerepeat = 1;
+    }
+  else
+    /* Repeated line */
+    linerepeat++;
+}
+
+static void
+flushrow( )
+    {
+    register unsigned char *outp, *p, *q;
+    register int count;
+    int col = outmax;
+
+    if (linerepeat > 1)
+      {
+	/* Put out line repeat count */
+	fwrite ("\0\0\377", 3, 1, stdout);
+	putchar (linerepeat);
+      }
+    for (outp = p = lastrow; col > 0;)
+    {
+	    for (q = p, count=0; (count < col) && (*q == *p); q++,count++);
+	    if (count > MINRUN)
+	    {
+		if (p > outp)
+		{
+		    putstring (outp, p-outp);
+		    outp = p;
+		}
+		col -= count;
+		switch (*p)
+		{
+		case SOLID_0:
+#ifdef DEBUG
+/*			if (outcol > 0) */
+			fprintf (stderr, "Solid run 0, length: %d\n", count);
+#endif
+			putsolid (SOLID_0, count);
+			break;
+
+		case SOLID_1:
+#ifdef DEBUG
+			fprintf (stderr, "Solid run 1, length: %d, pos %d\n", count, outcol);
+#endif
+			putsolid (SOLID_1, count);
+			break;
+		default:
+#ifdef DEBUG
+			fprintf (stderr, "Pattern run, length: %d\n", count);
+#endif
+			putpattern (*p, count);
+			break;
+		}
+		outp = p = q;
+	    }
+	    else
+	    {
+		p++;
+		col--;
+	    }
+    }		
+    if (p > outp)
+         putstring (outp, p-outp);
+    if (ferror (stdout))
+      pm_error ("write error");
+}
+
diff --git a/converter/pbm/pbmtogo.c b/converter/pbm/pbmtogo.c
new file mode 100644
index 00000000..b7c12373
--- /dev/null
+++ b/converter/pbm/pbmtogo.c
@@ -0,0 +1,309 @@
+/* pbmtogo.c - read a PBM image and produce a GraphOn terminal raster file
+**	
+**	Rev 1.1 was based on pbmtolj.c
+**
+**	Bo Thide', Swedish Institute of Space Physics, bt@irfu.se
+**				   
+**
+** $Log:	pbmtogo.c,v $
+ * Revision 1.5  89/11/25  00:24:12  00:24:12  root (Bo Thide)
+ * Bug found: The byte after 64 repeated bytes sometimes lost. Fixed.
+ * 
+ * Revision 1.4  89/11/24  14:56:04  14:56:04  root (Bo Thide)
+ * Fixed the command line parsing since pbmtogo now always uses 2D
+ * compression.  Added a few comments to the source.
+ * 
+ * Revision 1.3  89/11/24  13:43:43  13:43:43  root (Bo Thide)
+ * Added capability for > 63 repeated bytes and > 62 repeated lines in
+ * the 2D compression scheme.
+ * 
+ * Revision 1.2  89/11/15  01:04:47  01:04:47  root (Bo Thide)
+ * First version that works reasonably well with GraphOn 2D runlength
+ * encoding/compression.
+ * 
+ * Revision 1.1  89/11/02  23:25:25  23:25:25  root (Bo Thide)
+ * Initial revision
+ * 
+**
+** Copyright (C) 1988, 1989 by Jef Poskanzer, Michael Haberler, and Bo Thide'.
+**
+** 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 <string.h>
+
+#include "pm_c_util.h"
+#include "pbm.h"
+
+#define BUFSIZE 132	/* GraphOn has 132 byte/1056 bit wide raster lines */
+#define REPEAT_CURRENT_LINE_MASK	0x00 
+#define SKIP_AND_PLOT_MASK		0x40 
+#define REPEAT_PLOT_MASK		0x80 
+#define PLOT_ARBITRARY_DATA_MASK	0xc0 
+#define MAX_REPEAT 64
+
+static unsigned char *scanlineptr;		/* Pointer to current scan line byte */
+
+static void putinit ARGS(( void ));
+static void putbit ARGS(( bit b ));
+static void putrest ARGS(( void ));
+static void putitem ARGS(( void ));
+
+int
+main( argc, argv )
+     int argc;
+     char* argv[];
+{
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int argn, rows, cols, format, rucols, padright, row, col;
+    int nbyte, bytesperrow, ecount, ucount, nout, i, linerepeat;
+    int	olditem;
+    unsigned char oldscanline[BUFSIZE];
+    unsigned char newscanline[BUFSIZE];
+    unsigned char diff[BUFSIZE];
+    unsigned char buffer[BUFSIZE];
+    unsigned char outbuffer[2*(BUFSIZE+1)];	/* Worst case.  Should malloc */
+    const char* usage = "[-c] [pbmfile]";
+
+
+    pbm_init( &argc, argv );
+
+    argn = 2;
+
+    /* Check for flags. */
+    if (argc > argn + 1)
+      pm_usage(usage);
+
+    if (argc == argn)
+      ifp = pm_openr( argv[argn-1] );
+    else
+      ifp = stdin;
+
+    pbm_readpbminit(ifp, &cols, &rows, &format);
+    bitrow = pbm_allocrow(cols);
+
+    /* Round cols up to the nearest multiple of 8. */
+    rucols = ( cols + 7 ) / 8;
+    bytesperrow = rucols;       /* GraphOn uses bytes */
+    rucols = rucols * 8;
+    padright = rucols - cols;
+
+    for (i = 0; i < BUFSIZE; ++i )
+      buffer[i] = 0;
+    putinit();
+
+    /* Start donwloading screen raster */
+    printf("\033P0;1;0;4;1;%d;%d;1!R1/", rows, rucols);
+
+    linerepeat = 63; /*  63 means "Start new picture" */
+    nout = 0;  /* To prevent compiler warning */
+    for (row = 0; row < rows; row++) {
+        /* Store scan line data in the new scan line vector */
+        scanlineptr = newscanline;
+        pbm_readpbmrow(ifp, bitrow, cols, format);
+        /* Transfer raster graphics */
+        for (col = 0, bP = bitrow; col < cols; col++, bP++)
+          putbit(*bP);
+        for (col = 0; col < padright; col++)
+          putbit(0);
+        
+        /* XOR data from the new scan line with data from old scan line */
+        for (i = 0; i < bytesperrow; i++)
+          diff[i] = oldscanline[i]^newscanline[i];
+        
+        /*
+         ** If the difference map is different from current internal buffer,
+         ** encode the difference and put it in the output buffer. 
+         ** Else, increase the counter for the current buffer by one.
+         */
+        
+        if ((memcmp(buffer, diff, bytesperrow) != 0) || (row == 0)) {
+            /*    
+             **Since the data in the buffer has changed, send the
+             **scan line repeat count to cause the old line(s) to
+             **be plotted on the screen, copy the new data into
+             **the internal buffer, and reset the counters.  
+             */
+              
+            putchar(linerepeat);
+            for (i = 0; i < bytesperrow; ++i)
+              buffer[i] = diff[i];
+            nbyte = 0;          /* Internal buffer byte counter */
+            nout = 0;           /* Output buffer byte counter */
+              
+            /* Run length encode the new internal buffr (= difference map) */
+            while (TRUE) {
+                ucount = 0;     /* Unique items counter */
+                do              /* Find unique patterns */
+                  {
+                      olditem = buffer[nbyte++];
+                      ucount++;
+                  } while ((olditem != buffer[nbyte])
+                           && (ucount < MIN(bytesperrow, MAX_REPEAT)));
+                  
+                if ((ucount != MAX_REPEAT) && (nbyte != bytesperrow)) {
+                    /* Back up to the last truly unique pattern */
+                    ucount--;
+                    nbyte--;
+                }
+
+                if (ucount > 0) { 
+                    /* Output the unique patterns */
+                    outbuffer[nout++] = 
+                      (ucount-1) | PLOT_ARBITRARY_DATA_MASK;
+                    for (i = nbyte-ucount; i < nbyte; i++)
+                      outbuffer[nout++] = buffer[i];
+                }
+
+                /*
+                 ** If we already are at the end of the current scan
+                 ** line, skip the rest of the encoding and start
+                 ** with a new scan line.  
+                 */
+
+                if (nbyte >= bytesperrow)
+                  goto nextrow;
+                  
+                ecount = 0;     /* Equal items counter */
+                do              /* Find equal patterns */
+                  {
+                      olditem = buffer[nbyte++];
+                      ecount++;
+                  } while ((olditem == buffer[nbyte])
+                           && (ecount < MIN(bytesperrow, MAX_REPEAT)));
+                  
+                if (ecount > 1) {
+                    /* More than 1 equal pattern */
+                    if (olditem == '\0') {
+                        /* White patterns */
+                        if (nbyte >= bytesperrow-1) {
+                            /* No more valid data ahead */
+                            outbuffer[nout++] = 
+                              (ecount-2) | SKIP_AND_PLOT_MASK;
+                            outbuffer[nout++] = buffer[nbyte-1];
+                        } 
+                        else { 
+                            /* More valid data ahead */
+                            outbuffer[nout++] = 
+                              (ecount-1) | SKIP_AND_PLOT_MASK;
+                            outbuffer[nout++] = buffer[nbyte++];
+                        } 
+                    }
+                    else { 
+                        /* Non-white patterns */
+                        outbuffer[nout++] = (ecount-1) | REPEAT_PLOT_MASK;
+                        outbuffer[nout++] = olditem;
+                    } /* if (olditem == '\0') */
+                } /* if (ecount > 1) */
+                else
+                  nbyte--;      /* No equal items found */
+                  
+                if (nbyte >= bytesperrow)
+                  goto nextrow;
+            } /* while (TRUE) */
+              
+          nextrow: printf("%d/", nout+1); /* Total bytes to xfer = nout+1 */
+            fflush(stdout);
+
+            /* Output the plot data */
+            write(1, outbuffer, nout);
+
+            /* Reset the counters */
+            linerepeat = 0;
+        } else {
+            linerepeat++;
+            if (linerepeat == 62) /* 62 lines max, then restart */
+              {
+                  putchar(linerepeat);
+                  printf("%d/", nout+1);
+                  fflush(stdout);
+                  write(1, outbuffer, nout);
+                  linerepeat = 0;
+              }
+        }
+        
+        /* Now we are ready for a new scan line */
+        for (i = 0; i < bytesperrow; ++i)
+          oldscanline[i] = newscanline[i];
+    }
+    putchar(linerepeat);        /* For the last line(s) to be plotted */
+    pm_close(ifp);
+    putrest();
+    exit(0);
+}
+
+
+
+static int item, bitsperitem, bitshift;
+
+static void
+putinit()
+{
+  /* Enter graphics window */
+  printf("\0331");
+
+  /* Erase graphics window */
+  printf("\033\014");
+
+  /* Set graphics window in raster mode */
+  printf("\033r");
+
+  /* Select standard Tek coding **/
+  printf("\033[=11l");
+
+  bitsperitem = 1;
+  item = 0;
+  bitshift = 7;
+}
+
+#if __STDC__
+static void
+putbit(bit b)
+#else /*__STDC__*/
+static void
+putbit(b)
+bit b;
+#endif /*__STDC__*/
+{
+  if (b == PBM_BLACK)
+    item += 1 << bitshift;
+  bitshift--;
+  if (bitsperitem == 8)
+  {
+    putitem();
+    bitshift = 7;
+  }
+  bitsperitem++;
+}
+
+static void
+putrest()
+{
+  if (bitsperitem > 1)
+      putitem();
+
+  /* end raster downloading */
+  printf("\033\134");
+
+  /* Exit raster mode */
+  printf("\033t");
+
+  /* Exit graphics window
+  printf("\0332"); */
+}
+
+static void
+putitem()
+  {
+  *scanlineptr++ = item;
+  bitsperitem = 0;
+  item = 0;
+  }
diff --git a/converter/pbm/pbmtoibm23xx.c b/converter/pbm/pbmtoibm23xx.c
new file mode 100644
index 00000000..973f7de0
--- /dev/null
+++ b/converter/pbm/pbmtoibm23xx.c
@@ -0,0 +1,213 @@
+/*
+ * pbmtoibm23xx -- print pbm file on IBM 23XX printers
+ * Copyright (C) 2004  Jorrit Fahlke <jorrit@jorrit.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/*
+ * This prgram is primarily based on the description of Brothers PPDS
+ * emulation (see
+ * http://www.brother.de/download/send_file.cfm?file_name=guide_ibmpro.pdf).
+ * However, there are some differences.  Their document states that
+ * ESC J does linefeed in terms of 1/216" -- my printer clearly does
+ * it in terms of 1/240".  Also, the quick and the slow mode for
+ * double density printing really makes a difference on my printer,
+ * the result of printing tiger.ps in quick double density mode was
+ * worse than printing it in single density mode.
+ *
+ * If anyone Knows of any better documentation of the language used by
+ * the IBM 23XX or PPDS in general, please send a mail to
+ * Jö Fahlke <jorrit@jorrit.de>.
+ *
+ * All the graphics modes of the printer differ only in the resolution
+ * in x they provide (and how quick they do their job).  They print a
+ * line of 8 pixels height and variable widths.  The bitlines within
+ * the line are 1/60" apart, so that is the resolution you can
+ * normally achieve in y.  But the printer is able to do line feeds in
+ * terms of 1/240", so the trick to print in higher resolutions is to
+ * print in several interleaved passes, and do a line feed of 1/240"
+ * or 1/120" inbetween.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pbm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+
+struct cmdlineInfo {
+    unsigned char graph_mode;
+    unsigned int passes;
+    unsigned int nFiles;
+    const char ** inputFile;
+};
+
+
+bool sent_xon; /* We have send x-on to enable the printer already */
+
+
+
+
+static void
+parseCommandLine(int argc, char ** const argv,
+                 struct cmdlineInfo * const cmdlineP) {
+
+    optStruct3 opt;
+    optEntry option_def[100];
+
+    unsigned int option_def_index = 0;
+    unsigned int xresSpec, yresSpec;
+    unsigned int xres, yres;
+    unsigned int slowMode;
+
+    OPTENT3(0, "xres", OPT_UINT, &xres, &xresSpec, 0);
+    OPTENT3(0, "yres", OPT_UINT, &yres, &yresSpec, 0);
+    OPTENT3(0, "slow", OPT_FLAG, NULL,  &slowMode, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = 0;
+    opt.allowNegNum = 0;
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!xresSpec)
+        pm_error("You must specify the -xres option");
+    if (!yresSpec)
+        pm_error("You must specify the -yres option");
+
+    switch (xres) {
+    case  60: cmdlineP->graph_mode = 'K';                  break;
+    case 120: cmdlineP->graph_mode = slowMode ? 'L' : 'Y'; break;
+    case 240: cmdlineP->graph_mode = 'Z';                  break;
+    default:
+        pm_error("Please specify 60, 120, or 240 for -xres");
+    }
+
+    if (yres != 60 && yres != 120 && yres != 240)
+        pm_error("Please specify 60, 120, or 240 for -yres");
+
+    cmdlineP->passes = yres / 60;
+
+    cmdlineP->nFiles = MAX(argc-1, 1);
+    MALLOCARRAY_NOFAIL(cmdlineP->inputFile, cmdlineP->nFiles);
+
+    if (argc-1 < 1)
+        cmdlineP->inputFile[0] = "-";
+    else {
+        unsigned int i;
+        for (i = 0; i < argc-1; ++i)
+            cmdlineP->inputFile[i] = argv[i+1];
+    }
+}
+
+
+
+/* Read all pbm images from a filehandle and print them */
+static void 
+process_handle(FILE *        const fh,
+               unsigned char const graph_mode,
+               unsigned int  const passes) {
+    int eof;
+
+    while(pbm_nextimage(fh, &eof), eof == 0) {
+        /* pbm header dats */
+        int cols, rows, format;
+        /* iteration variables */
+        unsigned int x, y;
+        unsigned int bitline; /* pixel line within a sigle printing line */
+        unsigned int pass;
+        /* here we build the to-be-printed data */
+        unsigned char *output;  /* for reading one row from the file */
+        bit *row;
+
+        /* Enable printer in case it is disabled, do it only once */
+        if(!sent_xon) {
+            putchar(0x11);
+            sent_xon = TRUE;
+        }
+
+        pbm_readpbminit(fh, &cols, &rows, &format);
+
+        output = malloc(sizeof(*output) * cols * passes);
+        if(output == NULL)
+            pm_error("Out of memory");
+        row = pbm_allocrow(cols);
+
+        for(y = 0; y < rows; y += 8 * passes) {
+            memset(output, 0, sizeof(*output) * cols * passes);
+            for(bitline = 0; bitline < 8; ++bitline)
+                for(pass = 0; pass < passes; ++pass)
+                    /* don't read beyond the end of the image if
+                       height is not a multiple of passes 
+                    */
+                    if(y + bitline * passes + pass < rows) {
+                        pbm_readpbmrow(fh, row, cols, format);
+                        for(x = 0; x < cols; ++x)
+                            if(row[x] == PBM_BLACK)
+                                output[cols * pass + x] |= 1 << (7 - bitline);
+                    }
+            for(pass = 0; pass < passes; ++pass){
+                /* write graphics data */
+                putchar(0x1b); putchar(graph_mode);
+                putchar(cols & 0xff); putchar((cols >> 8) & 0xff);
+                fwrite(output + pass * cols, sizeof(*output), cols, stdout);
+
+                /* Carriage return */
+                putchar('\r');
+
+                /* move one pixel down */
+                putchar(0x1b); putchar('J'); putchar(4 / passes);
+            }
+
+            /* move one line - passes pixel down */
+            putchar(0x1b); putchar('J'); putchar(24 - 4);
+        }
+        putchar(0x0c); /* Form-feed */
+
+        pbm_freerow(row);
+        free(output);
+    }
+}
+
+
+
+int 
+main(int argc,char **argv) {
+
+  struct cmdlineInfo cmdline;
+  unsigned int i;
+
+  pbm_init(&argc, argv);
+
+  parseCommandLine(argc, argv, &cmdline);
+
+  sent_xon = FALSE;
+
+  for (i = 0; i < cmdline.nFiles; ++i) {
+      FILE *ifP;
+      pm_message("opening '%s'", cmdline.inputFile[i]);
+      ifP = pm_openr(cmdline.inputFile[i]);
+      process_handle(ifP, cmdline.graph_mode, cmdline.passes);
+      pm_close(ifP);
+  }
+
+  free(cmdline.inputFile);
+
+  return 0;
+}
diff --git a/converter/pbm/pbmtoicon.c b/converter/pbm/pbmtoicon.c
new file mode 100644
index 00000000..0e21c202
--- /dev/null
+++ b/converter/pbm/pbmtoicon.c
@@ -0,0 +1,134 @@
+/* pbmtoicon.c - read a portable bitmap and produce a Sun icon file
+**
+** Copyright (C) 1988 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 "pbm.h"
+
+static void putinit ARGS(( void ));
+static void putbit ARGS(( bit b ));
+static void putrest ARGS(( void ));
+static void putitem ARGS(( void ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, format, pad, padleft, padright, row, col;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[pbmfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+    bitrow = pbm_allocrow( cols );
+    
+    /* Round cols up to the nearest multiple of 16. */
+    pad = ( ( cols + 15 ) / 16 ) * 16 - cols;
+    padleft = pad / 2;
+    padright = pad - padleft;
+
+    printf( "/* Format_version=1, Width=%d, Height=%d", cols + pad, rows );
+    printf( ", Depth=1, Valid_bits_per_item=16\n */\n" );
+
+    putinit( );
+    for ( row = 0; row < rows; ++row )
+	{
+	pbm_readpbmrow( ifp, bitrow, cols, format );
+	for ( col = 0; col < padleft; ++col )
+	    putbit( 0 );
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+	    putbit( *bP );
+	for ( col = 0; col < padright; ++col )
+	    putbit( 0 );
+        }
+
+    pm_close( ifp );
+
+    putrest( );
+
+    exit( 0 );
+    }
+
+static int item, bitsperitem, bitshift, itemsperline, firstitem;
+
+static void
+putinit( )
+    {
+    itemsperline = 0;
+    bitsperitem = 0;
+    item = 0;
+    bitshift = 15;
+    firstitem = 1;
+    }
+
+#if __STDC__
+static void
+putbit( bit b )
+#else /*__STDC__*/
+static void
+putbit( b )
+bit b;
+#endif /*__STDC__*/
+    {
+    if ( bitsperitem == 16 )
+	putitem( );
+    ++bitsperitem;
+    if ( b == PBM_BLACK )
+	item += 1 << bitshift;
+    --bitshift;
+    }
+
+static void
+putrest( )
+    {
+    if ( bitsperitem > 0 )
+	putitem( );
+    putchar( '\n' );
+    }
+
+static void
+putitem( )
+    {
+    const char* hexits = "0123456789abcdef";
+
+    if ( firstitem )
+	firstitem = 0;
+    else
+	putchar( ',' );
+    if ( itemsperline == 8 )
+	{
+	putchar( '\n' );
+	itemsperline = 0;
+	}
+    if ( itemsperline == 0 )
+	putchar( '\t' );
+    putchar( '0' );
+    putchar( 'x' );
+    putchar( hexits[item >> 12] );
+    putchar( hexits[( item >> 8 ) & 15] );
+    putchar( hexits[( item >> 4 ) & 15] );
+    putchar( hexits[item & 15] );
+    ++itemsperline;
+    bitsperitem = 0;
+    item = 0;
+    bitshift = 15;
+    }
diff --git a/converter/pbm/pbmtolj.c b/converter/pbm/pbmtolj.c
new file mode 100644
index 00000000..e8373050
--- /dev/null
+++ b/converter/pbm/pbmtolj.c
@@ -0,0 +1,564 @@
+/* pbmtolj.c - read a portable bitmap and produce a LaserJet bitmap file
+**  
+**  based on pbmtops.c
+**
+**  Michael Haberler HP Vienna mah@hpuviea.uucp
+**                 mcvax!tuvie!mah
+**  misfeatures: 
+**      no positioning
+**
+**      Bug fix Dec 12, 1988 :
+**              lines in putbit() reshuffled 
+**              now runs OK on HP-UX 6.0 with X10R4 and HP Laserjet II
+**      Bo Thide', Swedish Institute of Space Physics, Uppsala <bt@irfu.se>
+**
+**  Flags added December, 1993:
+**      -noreset to suppress printer reset code
+**      -float to suppress positioning code (such as it is)
+**  Wim Lewis, Seattle <wiml@netcom.com>
+**
+** Copyright (C) 1988 by Jef Poskanzer and Michael Haberler.
+**
+** 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 "pbm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include <string.h>
+#include <assert.h>
+
+static char *rowBuffer, *prevRowBuffer, *packBuffer, *deltaBuffer;
+static int rowBufferSize, rowBufferIndex, prevRowBufferIndex;
+static int packBufferSize, packBufferIndex;
+static int deltaBufferSize, deltaBufferIndex;
+
+static int item, bitsperitem, bitshift;
+
+
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *inputFilename;
+    unsigned int dpi;
+    unsigned int copies;     /* number of copies */
+    unsigned int floating;   /* suppress the ``ESC & l 0 E'' ? */
+    unsigned int noreset;
+    unsigned int pack;       /* use TIFF packbits compression */
+    unsigned int delta;      /* use row-delta compression */
+};
+
+
+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;
+    unsigned int dpiSpec, copiesSpec, compressSpec;
+
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0,   "resolution",  OPT_UINT, &cmdlineP->dpi, 
+            &dpiSpec, 0);
+    OPTENT3(0,   "copies",      OPT_UINT, &cmdlineP->copies,
+            &copiesSpec, 0);
+    OPTENT3(0,   "float",       OPT_FLAG, NULL,
+            &cmdlineP->floating, 0);
+    OPTENT3(0,   "noreset",     OPT_FLAG, NULL,
+            &cmdlineP->noreset, 0);
+    OPTENT3(0,   "packbits",    OPT_FLAG, NULL,
+            &cmdlineP->pack, 0);
+    OPTENT3(0,   "delta",       OPT_FLAG, NULL,
+            &cmdlineP->delta, 0);
+    OPTENT3(0,   "compress",    OPT_FLAG, NULL,
+            &compressSpec, 0);
+
+    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 (!dpiSpec)
+        cmdlineP->dpi = 75;
+    if (!copiesSpec)
+        cmdlineP->copies = 1;
+    if (compressSpec) {
+        cmdlineP->pack = 1;
+        cmdlineP->delta = 1;
+    }
+}
+
+
+
+static void
+allocateBuffers(unsigned int const cols) {
+
+    rowBufferSize = (cols + 7) / 8;
+    packBufferSize = rowBufferSize + (rowBufferSize + 127) / 128 + 1;
+    deltaBufferSize = rowBufferSize + rowBufferSize / 8 + 10;
+
+    MALLOCARRAY_NOFAIL(prevRowBuffer, rowBufferSize);
+    MALLOCARRAY_NOFAIL(rowBuffer, rowBufferSize);
+    MALLOCARRAY_NOFAIL(packBuffer, packBufferSize);
+    MALLOCARRAY_NOFAIL(deltaBuffer, deltaBufferSize);
+}
+
+
+
+static void
+freeBuffers(void) {
+
+    free(deltaBuffer);
+    free(packBuffer);
+    free(rowBuffer);
+    free(prevRowBuffer);
+}
+
+
+
+static void
+putinit(struct cmdlineInfo const cmdline) {
+    if (!cmdline.noreset) {
+        /* Printer reset. */
+        printf("\033E");
+    }
+
+    if (cmdline.copies > 1) {
+        /* number of copies */
+        printf("\033&l%dX", cmdline.copies);
+    }
+    if (!cmdline.floating) {
+        /* Ensure top margin is zero */
+        printf("\033&l0E");
+    }
+
+    /* Set raster graphics resolution */
+    printf("\033*t%dR", cmdline.dpi);
+
+    /* Start raster graphics, relative adressing */
+    printf("\033*r1A");
+
+    bitsperitem = 1;
+    item = 0;
+    bitshift = 7;
+}
+
+
+
+static void
+putitem(void) {
+    assert(rowBufferIndex < rowBufferSize);
+    rowBuffer[rowBufferIndex++] = item;
+    bitsperitem = 0;
+    item = 0;
+}
+
+
+
+static void
+putbit(bit const b) {
+
+    if (b == PBM_BLACK)
+        item += 1 << bitshift;
+
+    --bitshift;
+
+    if (bitsperitem == 8) {
+        putitem();
+        bitshift = 7;
+    }
+    ++bitsperitem;
+}
+
+
+
+static void
+putflush(void) {
+    if (bitsperitem > 1)
+        putitem();
+}
+
+
+
+static void
+putrest(bool const reset) {
+    /* end raster graphics */
+    printf("\033*rB");
+
+    if (reset) {
+        /* Printer reset. */
+        printf("\033E");
+    }
+}
+
+
+
+static void
+packbits(void) {
+    int ptr, litStart, runStart, thisByte, startByte, chew, spit;
+    packBufferIndex = 0;
+    ptr = 0;
+    while (ptr < rowBufferIndex) {
+        litStart = ptr;
+        runStart = ptr;
+        startByte = rowBuffer[ptr];
+        ++ptr;
+        while (ptr < rowBufferIndex) {
+            thisByte = rowBuffer[ptr];
+            if (thisByte != startByte) {
+                if (ptr - runStart > 3) {
+                    /* found literal after nontrivial run */
+                    break;
+                }
+                startByte = thisByte;
+                runStart = ptr;
+            }
+            ++ptr;
+        }
+        /*
+          We drop out here after having found a [possibly empty]
+          literal, followed by a [possibly degenerate] run of repeated
+          bytes.  Degenerate runs can occur at the end of the scan line...
+          there may be a "repeat" of 1 byte (which can't actually be 
+          represented as a repeat) so we simply fold it into the previous
+          literal.
+        */
+        if (runStart == rowBufferIndex - 1) {
+            runStart = rowBufferIndex;
+        }
+        /*
+          Spit out the leading literal if it isn't empty
+        */
+        chew = runStart - litStart;
+        while (chew > 0) {
+            spit = (chew > 127) ? 127 : chew;
+            packBuffer[packBufferIndex++] = (char) (spit - 1);
+            memcpy(packBuffer+packBufferIndex, rowBuffer+litStart, spit);
+            packBufferIndex += spit;
+            litStart += spit;
+            chew -= spit;
+        }
+        /*
+          Spit out the repeat, if it isn't empty
+        */
+        chew = ptr - runStart;
+        while (chew > 0) {
+            spit = (chew > 128) ? 128 : chew;
+            if (chew == spit + 1) {
+                spit--; /* don't leave a degenerate run at the end */
+            }
+            if (spit == 1) {
+                fprintf(stderr, "packbits created a degenerate run!\n");
+            }
+            packBuffer[packBufferIndex++] = (char) -(spit - 1);
+            packBuffer[packBufferIndex++] = startByte;
+            chew -= spit;
+        }
+    }
+}
+
+
+
+static void
+deltarow(void) {
+    int burstStart, burstEnd, burstCode, mustBurst, ptr, skip, skipped, code;
+    deltaBufferIndex = 0;
+    if (memcmp(rowBuffer, prevRowBuffer, rowBufferIndex) == 0) {
+        return; /* exact match, no deltas required */
+    }
+    ptr = 0;
+    skipped = 0;
+    burstStart = -1;
+    burstEnd = -1;
+    mustBurst = 0;
+    while (ptr < rowBufferIndex) {
+        skip = 0;
+        if (ptr == 0 || skipped == 30 || rowBuffer[ptr] != prevRowBuffer[ptr]
+            || (burstStart != -1 && ptr == rowBufferIndex - 1)) {
+            /* we want to output this byte... */
+            if (burstStart == -1) {
+                burstStart = ptr;
+            }
+            if (ptr - burstStart == 7 || ptr == rowBufferIndex - 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;
+            deltaBuffer[deltaBufferIndex++] = (char) code;
+            memcpy(deltaBuffer+deltaBufferIndex, rowBuffer+burstStart, 
+                   burstCode + 1);
+            deltaBufferIndex += burstCode + 1;
+            burstStart = -1;
+            burstEnd = -1;
+            mustBurst = 0;
+            skipped = 0;
+        }
+        if (skip) {
+            ++skipped;
+        }
+        ++ptr;
+    }
+}
+
+
+
+static void
+findRightmostBlackCol(const bit *    const bitrow,
+                      unsigned int   const cols,
+                      bool *         const allWhiteP,
+                      unsigned int * const blackColP) {
+
+    int i;
+
+    for (i = cols - 1; i >= 0 && bitrow[i] == PBM_WHITE; --i);
+
+    if (i < 0)
+        *allWhiteP = TRUE;
+    else {
+        *allWhiteP = FALSE;
+        *blackColP = i;
+    }
+}
+
+
+
+static void
+convertRow(const bit *    const bitrow,
+           unsigned int   const cols,
+           bool           const pack,
+           bool           const delta,
+           bool *         const rowIsBlankP) {
+
+    unsigned int rightmostBlackCol;
+        
+    findRightmostBlackCol(bitrow, cols, rowIsBlankP, &rightmostBlackCol);
+
+    if (!*rowIsBlankP) {
+        unsigned int const nzcol = rightmostBlackCol + 1;
+            /* Number of columns excluding white right margin */
+        unsigned int const rucols = ((nzcol + 7) / 8) * 8;
+            /* 'nzcol' rounded up to nearest multiple of 8 */
+        
+        unsigned int col;
+
+        memset(rowBuffer, 0, rowBufferSize);
+
+        rowBufferIndex = 0;
+
+        /* Generate the unpacked data */
+
+        for (col = 0; col < nzcol; ++col)
+            putbit(bitrow[col]);
+
+        /* Pad out to a full byte with white */
+        for (col = nzcol; col < rucols; ++col)
+            putbit(0);
+
+        putflush();
+
+        /* Try optional compression algorithms */
+
+        if (pack)
+            packbits();
+        else
+            packBufferIndex = rowBufferIndex + 999;
+
+        if (delta) {
+            /* May need to temporarily bump the row buffer index up to
+               whatever the previous line's was - if this line is shorter 
+               than the previous would otherwise leave dangling cruft.
+            */
+            unsigned int const savedRowBufferIndex = rowBufferIndex;
+
+            if (rowBufferIndex < prevRowBufferIndex)
+                rowBufferIndex = prevRowBufferIndex;
+
+            deltarow();
+
+            rowBufferIndex = savedRowBufferIndex;
+        } else
+            deltaBufferIndex = packBufferIndex + 999;
+    }
+}
+
+
+    
+static void
+printBlankRows(unsigned int const count) {
+
+    if (count > 0) {
+        unsigned int x;
+        /* The code used to be this, but Charles Howes reports that
+           this escape sequence does not exist on his HP Laserjet IIP
+           plus, so we use the following less elegant code instead.
+           
+           printf("\033*b%dY", (*blankRowsP)); 
+        */
+        for (x = 0; x < count; ++x) 
+            printf("\033*b0W");
+        
+        memset(prevRowBuffer, 0, rowBufferSize);
+    }
+}
+
+
+
+static void
+establishMode(int const newMode) {
+
+    static int mode = -1;
+
+    if (mode != newMode) {
+        printf("\033*b%uM", newMode);
+        mode = newMode;
+    }
+}
+
+
+
+static void
+printRow(void) {
+
+    if (deltaBufferIndex < packBufferIndex &&
+        deltaBufferIndex < rowBufferIndex) {
+        assert(deltaBufferIndex <= deltaBufferSize);
+        /*
+          It's smallest when delta'ed
+        */
+        establishMode(3);
+
+        printf("\033*b%dW", deltaBufferIndex);
+        fwrite(deltaBuffer, 1, deltaBufferIndex, stdout);
+    } else if (rowBufferIndex <= packBufferIndex) {
+        assert (rowBufferIndex <= rowBufferSize);
+        /*
+          It didn't pack - send it unpacked
+        */
+        establishMode(0);
+
+        printf("\033*b%dW", rowBufferIndex);
+        fwrite(rowBuffer, 1, rowBufferIndex, stdout);
+    } else {
+        assert (packBufferIndex <= packBufferSize);
+        /*
+          It's smaller when packed
+        */
+        establishMode(2);
+
+        printf("\033*b%dW", packBufferIndex);
+        fwrite(packBuffer, 1, packBufferIndex, stdout);
+    }
+    memcpy(prevRowBuffer, rowBuffer, rowBufferSize);
+    prevRowBufferIndex = rowBufferIndex;
+}
+
+
+
+static void
+doPage(FILE *             const ifP, 
+       struct cmdlineInfo const cmdline) {
+
+    bit * bitrow;
+    int rows, cols, format, row;
+    unsigned int blankRows;
+    bool rowIsBlank;
+
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+
+    bitrow = pbm_allocrow(cols);
+
+    allocateBuffers(cols);
+
+    putinit(cmdline);
+
+    blankRows = 0;
+    prevRowBufferIndex = 0;
+    memset(prevRowBuffer, 0, rowBufferSize);
+
+    for (row = 0; row < rows; ++row) {
+        pbm_readpbmrow(ifP, bitrow, cols, format);
+
+        convertRow(bitrow, cols, cmdline.pack, cmdline.delta,
+                   &rowIsBlank);
+
+        if (rowIsBlank)
+            ++blankRows;
+        else {
+            printBlankRows(blankRows);
+            blankRows = 0;
+            
+            printRow();
+        }
+    }    
+    printBlankRows(blankRows);
+    blankRows = 0;
+
+    putrest(!cmdline.noreset);
+
+    freeBuffers();
+    pbm_freerow(bitrow);
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    bool eof;
+
+    pbm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilename);
+
+    eof = FALSE;
+    while (!eof) {
+        doPage(ifP, cmdline);
+        pbm_nextimage(ifP, &eof);
+    }
+
+    pm_close(ifP);
+
+    return 0;
+}
diff --git a/converter/pbm/pbmtoln03.c b/converter/pbm/pbmtoln03.c
new file mode 100644
index 00000000..07c8629f
--- /dev/null
+++ b/converter/pbm/pbmtoln03.c
@@ -0,0 +1,260 @@
+/*
+From tim@deakin.edu.au Fri May  7 00:18:57 1993
+From: Tim Cook <tim@deakin.edu.au>
+Date: Fri, 7 May 1993 15:18:34 -0500
+X-Mailer: Mail User's Shell (7.2.4 2/2/92)
+To: dyson@sunfish.Physics.UIowa.Edu
+Subject: Re: DEC LN03+ printer (not postscript) under SunOS (on SS10) ?
+Content-Length: 5893
+
+In a message dated 6 May,  9:32, dyson@sunfish.Physics.UIowa.Edu
+(Richard L. Dyson) wrote:
+> > Just in case anyone is interested, I have a pbmtoln03 utility I wrote
+> > when I was mucking about with an LN03+.  If you are interested in
+> > printing your bitmaps at 300x300dpi, ask me for the source.
+>
+> I would be interested.  We still only have LN03+ printers on our VMS
+> machines here...
+
+Ok, here goes.  Note that you will need the source from the pbmplus
+utilities, because my pbmtoln03 utility uses the library routines that
+are a part of pbmplus to read a PBM file (I linked it with libpbm.a).
+I have not tested this utility on VMS, but it looks like it should
+work.
+*/
+
+/* pbmtoln03.c -        Converts a PBM bitmap to DEC LN03 SIXEL bitmap
+ *
+ * SYNOPSIS
+ *      pbmtoln03 [pbm-file]
+ *
+ * OPTIONS
+ *      -l nn   Use "nn" as value for left margin (default 0).
+ *      -r nn   Use "nn" as value for right margin (default 2400).
+ *      -t nn   Use "nn" as value for top margin (default 0).
+ *      -b nn   Use "nn" as value for bottom margin (default 3400).
+ *      -f nn   Use "nn" as value for form length (default 3400).
+ *
+ * Tim Cook, 26 Feb 1992
+ * changed option parsing to PBM standards  - Ingo Wilken, 13 Oct 1993
+ */
+
+#include <stdio.h>
+#include "pbm.h"
+
+FILE *input ;
+
+#ifndef print
+#define print(s)        fputs (s, stdout)
+#define fprint(f, s)    fputs (s, f)
+#endif
+
+
+static void 
+output_sixel_record (unsigned char * record, int width) {
+
+   int i, j, k ;
+   unsigned char last_char ;
+   int start_repeat = 0 ;
+   int repeated ;
+   char repeated_str[16] ;
+   char *p ;
+
+   /* Do RLE */
+   last_char = record[0] ;
+   j = 0 ;
+
+   /* This will make the following loop complete */
+   record[width] = '\0' ;
+
+   for (i = 1 ; i <= width ; i++) {
+
+      repeated = i - start_repeat ;
+
+      if (record[i] != last_char || repeated >= 32766) {
+
+         /* Repeat has ended */
+
+         if (repeated > 3) {
+
+            /* Do an encoding */
+            record[j++] = '!' ;
+            sprintf (repeated_str, "%d", i - start_repeat) ;
+            for (p = repeated_str ; *p ; p++)
+               record[j++] = *p ;
+               record[j++] = last_char ; }
+
+         else {
+            for (k = 0 ; k < repeated ; k++)
+               record[j++] = last_char ; }
+
+         start_repeat = i ;
+         last_char = record[i] ; }
+      }
+
+   fwrite ((char *) record, j, 1, stdout) ;
+   putchar ('-') ;      /* DECGNL (graphics new-line) */
+   putchar ('\n') ;
+   }
+
+
+static void 
+convert (int width, int height, int format) {
+
+   int i ;
+   unsigned char *sixel ;               /* A row of sixels */
+   int sixel_row ;
+
+   bit *row = pbm_allocrow (width) ;
+
+   sixel = (unsigned char *) malloc (width + 2) ;
+
+   sixel_row = 0 ;
+   while (height--) {
+      pbm_readpbmrow (input, row, width, format) ;
+      switch (sixel_row) {
+         case 0 :
+           for (i = 0 ; i < width ; i++)
+              sixel[i] = row[i] ;
+           break ;
+         case 1 :
+           for (i = 0 ; i < width ; i++)
+              sixel[i] += row[i] << 1 ;
+           break ;
+         case 2 :
+           for (i = 0 ; i < width ; i++)
+              sixel[i] += row[i] << 2 ;
+           break ;
+         case 3 :
+           for (i = 0 ; i < width ; i++)
+              sixel[i] += row[i] << 3 ;
+           break ;
+         case 4 :
+           for (i = 0 ; i < width ; i++)
+              sixel[i] += row[i] << 4 ;
+           break ;
+         case 5 :
+           for (i = 0 ; i < width ; i++)
+              sixel[i] += (row[i] << 5) + 077 ;
+           output_sixel_record (sixel, width) ;
+           break ; }
+      if (sixel_row == 5)
+         sixel_row = 0 ;
+      else
+         sixel_row++ ;
+      }
+
+   if (sixel_row > 0) {
+      /* Incomplete sixel record needs to be output */
+      for (i = 0 ; i < width ; i++)
+         sixel[i] += 077 ;
+      output_sixel_record (sixel, width) ; }
+   }
+
+
+
+int
+main (int argc, char **argv) {
+   int argc_copy = argc ;
+   char **argv_copy = argv ;
+   int argn;
+   const char * const usage = "[-left <nn>] [-right <nn>] [-top <nn>] "
+       "[-bottom <nn>] [-formlength <nn>] [pbmfile]";
+
+   /* Options */
+   /* These defaults are for a DEC LN03 with A4 paper (2400x3400 pixels) */
+   const char *opt_left_margin = "0";
+   const char *opt_top_margin = opt_left_margin;
+   const char *opt_right_margin = "2400";
+   const char *opt_bottom_margin = "3400";
+   const char *opt_form_length = opt_bottom_margin;
+
+   int width, height, format ;
+
+   pbm_init (&argc_copy, argv_copy) ;
+
+   argn = 1;
+   while( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) {
+      if( pm_keymatch(argv[argn], "-left", 2) ) {
+         if( ++argn >= argc )
+            pm_usage(usage);
+         opt_left_margin = argv[argn];
+      }
+      else
+      if( pm_keymatch(argv[argn], "-right", 2) ) {
+         if( ++argn >= argc )
+            pm_usage(usage);
+         opt_right_margin = argv[argn];
+      }
+      else
+      if( pm_keymatch(argv[argn], "-top", 2) ) {
+         if( ++argn >= argc )
+            pm_usage(usage);
+         opt_top_margin = argv[argn];
+      }
+      else
+      if( pm_keymatch(argv[argn], "-bottom", 2) ) {
+         if( ++argn >= argc )
+            pm_usage(usage);
+         opt_bottom_margin = argv[argn];
+      }
+      else
+      if( pm_keymatch(argv[argn], "-formlength", 2) ) {
+         if( ++argn >= argc )
+            pm_usage(usage);
+         opt_form_length = argv[argn];
+      }
+      else
+         pm_usage(usage);
+      ++argn;
+   }
+
+   if( argn < argc ) {
+      input = pm_openr( argv[argn] );
+      argn++;
+   }
+   else
+      input = stdin;
+
+   if( argn != argc )
+      pm_usage(usage);
+
+
+   /* Initialize pbm file */
+   pbm_readpbminit (input, &width, &height, &format) ;
+
+   if (format != PBM_FORMAT && format != RPBM_FORMAT)
+      pm_error ("input not in PBM format") ;
+
+/*
+ * In explanation of the sequence below:
+ *      <ESC>[!p        DECSTR  soft terminal reset
+ *      <ESC>[11h       PUM     select unit of measurement
+ *      <ESC>[7 I       SSU     select pixel as size unit
+ *      <ESC>[?52l      DECOPM  origin is corner of printable area
+ *      <ESC>[%s;%ss    DECSLRM left and right margins
+ *      <ESC>[%s;%sr    DECSTBM top and bottom margins
+ *      <ESC>[%st       DECSLPP form length
+ *      <ESC>P0;0;1q            select sixel graphics mode
+ *      "1;1            DECGRA  aspect ratio (1:1)
+ */
+
+   /* Initialize sixel file */
+   printf ("\033[!p\033[11h\033[7 I\033[?52l\033[%s;%ss\033"
+           "[%s;%sr\033[%st\033P0;0;1q\"1;1",
+      opt_left_margin, opt_right_margin, opt_top_margin, opt_bottom_margin,
+      opt_form_length);
+
+   /* Convert data */
+   convert (width, height, format) ;
+
+   /* Terminate sixel data */
+   print ("\033\\\n") ;
+
+   /* If the program failed, it previously aborted with nonzero completion
+      code, via various function calls.
+   */
+   return 0;
+}
+
+
diff --git a/converter/pbm/pbmtolps.c b/converter/pbm/pbmtolps.c
new file mode 100644
index 00000000..13a14e2b
--- /dev/null
+++ b/converter/pbm/pbmtolps.c
@@ -0,0 +1,181 @@
+/*
+ * pbmtolps -- convert a Portable BitMap into Postscript.  The
+ * output Postscript uses lines instead of the image operator to
+ * generate a (device dependent) picture which will be imaged
+ * much faster.
+ *
+ * The Postscript path length is constrained to be less that 1000
+ * points so that no limits are overrun on the Apple Laserwriter
+ * and (presumably) no other printers.
+ *
+ * To do:
+ *      make sure encapsulated format is correct
+ *      repitition of black-white strips
+ *      make it more device independent (is this possible?)
+ *
+ * Author:
+ *      George Phillips <phillips@cs.ubc.ca>
+ *      Department of Computer Science
+ *      University of British Columbia
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include "nstring.h"
+#include "pbm.h"
+
+
+static int prev_white = -1;
+static int prev_black = -1;
+static char cmd = '\0';
+static int pointcount = 2;
+
+#ifdef RUN
+static int run = 1;
+#endif
+
+static char 
+morepoints(char cmd, int howmany_pbmtolps) {
+    pointcount += 2;
+    if (pointcount > 1000) {
+        pointcount = 2;
+        cmd += 'm' - 'a';
+    }
+    return(cmd);
+}
+
+
+
+static void 
+addstrip(int const white, 
+         int const black) {
+
+    if (cmd) {
+#ifdef RUN
+        if (white == prev_white && black == prev_black)
+            run++;
+        else {
+            if (run == 1)
+#endif
+                printf("%d %d %c ", 
+                       prev_black, prev_white, morepoints(cmd, 2));
+#ifdef RUN
+            else
+                                /* of course, we need to give a new command */
+                printf("%d %d %d %c ",
+                       prev_white, prev_black, run,
+                       morepoints(cmd + 'f' - 'a', 2 * run));
+            run = 1;
+        }
+#endif
+    }
+
+    prev_white = white;
+    prev_black = black;
+    cmd = 'a';
+}
+
+
+
+static void 
+nextline(void) {
+    /* need to check run, should have an outcommand */
+    if (cmd)
+        printf("%d %d %c\n", prev_black, prev_white, morepoints('c', 3));
+    else
+        printf("%c\n", morepoints('b', 1));
+    cmd = '\0';
+}
+
+
+
+int
+main(int argc, char ** argv) {
+    FILE*   fp;
+    bit*    bits;
+    int             row;
+    int             col;
+    int         rows;
+    int             cols;
+    int             format;
+    int             white;
+    int             black;
+    const char*   name;
+    float   dpi = 300.0;
+    float   sc_rows;
+    float   sc_cols;
+    int             i;
+    const char*   const usage = "[ -dpi n ] [ pbmfile ]";
+
+
+	pbm_init(&argc, argv);
+
+    i = 1;
+    if (i < argc && STREQ(argv[i], "-dpi")) {
+        if (i == argc - 1)
+            pm_usage(usage);
+        sscanf(argv[i + 1], "%f", &dpi);
+        i += 2;
+    }
+
+    if (i < argc - 1)
+        pm_usage(usage);
+
+    if (i == argc) {
+        name = "noname";
+        fp = stdin;
+    } else {
+        name = argv[i];
+        fp = pm_openr(name);
+    }
+    pbm_readpbminit(fp, &cols, &rows, &format);
+    bits = pbm_allocrow(cols);
+
+    sc_rows = (float)rows / dpi * 72.0;
+    sc_cols = (float)cols / dpi * 72.0;
+
+    puts("%!PS-Adobe-2.0 EPSF-2.0");
+    puts("%%Creator: pbmtolps");
+    printf("%%%%Title: %s\n", name);
+    printf("%%%%BoundingBox: %d %d %d %d\n",
+           (int)(305.5 - sc_cols / 2.0),
+           (int)(395.5 - sc_rows / 2.0),
+           (int)(306.5 + sc_cols / 2.0),
+           (int)(396.5 + sc_rows / 2.0));
+    puts("%%EndComments");
+    puts("%%EndProlog");
+    puts("gsave");
+
+    printf("%f %f translate\n", 306.0 - sc_cols / 2.0, 396.0 + sc_rows / 2.0);
+    printf("72 %f div dup neg scale\n", dpi);
+    puts("/a { 0 rmoveto 0 rlineto } def");
+    puts("/b { 0 row 1 add dup /row exch def moveto } def");
+    puts("/c { a b } def");
+    puts("/m { currentpoint stroke newpath moveto a } def");
+    puts("/n { currentpoint stroke newpath moveto b } def");
+    puts("/o { currentpoint stroke newpath moveto c } def");
+    puts("/row 0 def");
+    puts("newpath 0 0 moveto");
+
+    for (row = 0; row < rows; row++) {
+        pbm_readpbmrow(fp, bits, cols, format);
+        /* output white-strip+black-strip sequences */
+        for (col = 0; col < cols; ) {
+            for (white = 0; col < cols && bits[col] == PBM_WHITE; col++)
+                white++;
+            for (black = 0; col < cols && bits[col] == PBM_BLACK; col++)
+                black++;
+
+            if (black != 0)
+                addstrip(white, black);
+        }
+        nextline();
+    }
+    puts("stroke grestore showpage");
+    puts("%%Trailer");
+
+    pm_close(fp);
+
+    exit(0);
+}
diff --git a/converter/pbm/pbmtomacp.c b/converter/pbm/pbmtomacp.c
new file mode 100644
index 00000000..82b55904
--- /dev/null
+++ b/converter/pbm/pbmtomacp.c
@@ -0,0 +1,301 @@
+/* pbmtomacp.c - read a portable bitmap and produce a MacPaint bitmap file
+**
+** Copyright (C) 1988 by Douwe vand der Schaaf.
+**
+** 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 "pbm.h"
+#include "macp.h"
+
+#define TRUE		1
+#define FALSE		0
+#define EQUAL		1
+#define UNEQUAL		0
+
+static void fillbits ARGS(( bit **bits, bit **bitsr, int top, int left, int bottom, int right ));
+static void writemacp ARGS(( bit **bits ));
+static int packit ARGS(( bit *pb, bit *bits ));
+static void filltemp ARGS(( bit *dest, bit *src ));
+static void sendbytes ARGS(( bit *pb, register int npb ));
+static void header ARGS(( void ));
+
+static FILE *fdout;
+
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{ FILE *ifp;
+  register bit **bits, **bitsr;
+  int argn, rows, cols;
+  int left,bottom,right,top;
+  int lflg, rflg, tflg, bflg;
+  char name[100];
+  const char * const usage = "[-l left] [-r right] [-b bottom] [-t top] [pbmfile]";
+
+
+  pbm_init( &argc, argv );
+
+  argn = 1;
+  fdout = stdout;
+  lflg = rflg = tflg = bflg = 0;
+  left = right = top = bottom = 0;  /* To quiet compiler warning */
+
+  while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
+  { switch ( argv[argn][1] )
+    { case 'l':
+      lflg++;
+      argn++;
+      left = atoi( argv[argn] );
+      break;
+
+      case 'r':
+      rflg++;
+      argn++;
+      right = atoi( argv[argn] );
+      break;
+
+      case 't':
+      tflg++;
+      argn++;
+      top = atoi( argv[argn] );
+      break;
+
+      case 'b':
+      bflg++;
+      argn++;
+      bottom = atoi( argv[argn] );
+      break;
+
+      case '?':
+      default:
+      pm_usage( usage );
+    }
+    ++argn;
+  }
+
+  if ( argn == argc )
+  { ifp = stdin;
+    strcpy( name, "noname" );
+  }
+  else
+  { ifp = pm_openr( argv[argn] );
+    strcpy( name, argv[argn] );
+    ++argn;
+  }
+
+  if ( argn != argc )
+    pm_usage( usage );
+
+  bitsr = pbm_readpbm( ifp, &cols, &rows );
+
+  pm_close( ifp );
+
+  bits = pbm_allocarray( MAX_COLS, MAX_LINES );
+
+  if( !lflg )
+    left = 0;
+
+  if( rflg )
+  { if( right - left >= MAX_COLS )
+      right = left + MAX_COLS - 1;
+  }
+  else
+    right = ( left + MAX_COLS > cols ) ? ( cols - 1 ) : ( left + MAX_COLS - 1 );
+
+  if( !tflg )
+    top = 0;
+
+  if( bflg )
+  { if( bottom - top >= MAX_LINES )
+      bottom = top + MAX_LINES - 1;
+  }
+  else
+    bottom = ( top + MAX_LINES > rows ) ?
+		   ( rows - 1 ) : ( top + MAX_LINES - 1 );
+  
+    if( right <= left || left < 0 || right - left + 1 > MAX_COLS )
+      pm_error("error in right (= %d) and/or left (=%d)",right,left );
+    if( bottom <= top || top < 0 || bottom - top + 1 > MAX_LINES )
+      pm_error("error in bottom (= %d) and/or top (=%d)",bottom,top );
+
+  fillbits( bits, bitsr, top, left, bottom, right );
+
+  writemacp( bits );
+
+  exit( 0 );
+
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* centreer het over te zenden plaatje in het MacPaint document
+ *
+ * Het plaatje wordt vanaf al of niet opgegeven (left, bottom)
+ * in een pbm bitmap van de juist macpaint afmetingen gezet,
+ * en eventueel afgekapt.
+ */
+static void
+fillbits( bits, bitsr, top, left, bottom, right )
+bit **bits, **bitsr;
+int top, left, bottom, right;
+{ register bit *bi, *bir;
+  register int i, j;
+  register int bottomr, leftr, topr, rightr;
+  int width, height;
+
+  width = right - left + 1;
+  leftr = (MAX_COLS - width) / 2;
+  rightr = leftr + width - 1;
+
+  height = bottom - top + 1;
+  topr = ( MAX_LINES - height ) / 2;
+  bottomr = topr + height - 1;
+
+  for( i = 0; i < topr; i++ )
+  { bi = bits[i];
+    for( j = 0; j < MAX_COLS; j++ )
+      *bi++ = 0;
+  }
+
+  for( i = topr; i <= bottomr; i++ )
+  { bi = bits[i];
+    { for( j = 0; j < leftr; j++ )
+	*bi++ = 0;
+      bir = bitsr[ i - topr + top ];
+      for( j = leftr; j <= rightr; j++ )
+	*bi++ = bir[j - leftr + left];
+      for( j = rightr + 1; j < MAX_COLS; j++ )
+	*bi++ = 0;
+  } }
+
+  for( i = bottomr + 1; i < MAX_LINES; i++ )
+  { bi = bits[i];
+    for( j = 0; j < MAX_COLS; j++ )
+      *bi++ = 0;
+  }
+} /* fillbits */
+      
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+static void
+writemacp( bits )
+bit **bits;
+{ register int i;
+  bit pb[MAX_COLS * 2];
+  int npb;
+
+  header();
+  for( i=0; i < MAX_LINES; i++ )
+  { npb = packit( pb, bits[i] );
+    sendbytes( pb, npb );
+  }
+} /* writemacp */
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* pack regel van MacPaint doc in Apple's format
+ * return value = # of bytes in pb 
+ */
+static int
+packit( pb, bits )
+     bit *pb, *bits;
+{ register int charcount, npb, newcount, flg;
+  bit temp[72];
+  bit *count, *srcb, *destb, save;
+
+  srcb = bits; destb = temp;
+  filltemp( destb, srcb );
+  srcb = temp;
+  destb = pb;
+  npb = 0;
+  charcount = BYTES_WIDE;
+  flg = EQUAL;
+  while( charcount ) { 
+      save = *srcb++;
+      charcount--;
+      newcount = 1;
+      while( (*srcb == save) && charcount ) { 
+          srcb++;
+          newcount++;
+          charcount--;
+      }
+      if( newcount > 2 ) { 
+          count = destb++;
+          *count = 257 - newcount;
+          *destb++ = save;
+          npb += 2;
+          flg = EQUAL;
+      } else { 
+          if( flg == EQUAL ) { 
+              count = destb++;
+              *count = newcount - 1;
+              npb++;
+          } else
+            *count += newcount;
+          while( newcount-- ) { 
+              *destb++ = save;
+              npb++;
+          }
+          flg = UNEQUAL;
+      } 
+  }
+  return npb;
+} /* packit */
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+static void
+filltemp( dest, src )
+bit *dest, *src;
+{ register unsigned char ch, zero, acht;
+  register int i, j;
+
+  zero = '\0';
+  acht = 8;
+  i = BYTES_WIDE;
+  while( i-- )
+  { ch = zero; 
+    j = acht;
+    while( j-- )
+    { ch <<= 1;
+      if( *src++ )
+	ch++;
+    }
+    *dest++ = ch;
+  }
+} /* filltemp */
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+static void
+sendbytes( pb, npb )
+bit *pb;
+register int npb;
+{ register bit *b;
+
+  b = pb;
+  while( npb-- )
+    (void) putc( *b++, fdout );
+} /* sendbytes */
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+static void
+header()
+{ register int i;
+  register char ch;
+
+  /* header contains nothing ... */
+  ch = '\0';
+  for(i = 0; i < HEADER_LENGTH; i++ )
+    (void) putc( ch, fdout );
+} /* header */
diff --git a/converter/pbm/pbmtomatrixorbital.c b/converter/pbm/pbmtomatrixorbital.c
new file mode 100644
index 00000000..79347978
--- /dev/null
+++ b/converter/pbm/pbmtomatrixorbital.c
@@ -0,0 +1,87 @@
+#include "pbm.h"
+
+/* By Bryan Henderson, San Jose CA 2003.09.06.
+
+   Contributed to the public domain by its author.
+
+   This is a replacement of Joseph Sheedy's (hbarover2@yahoo.com) Perl
+   program of the same name, distributed in his Pbmtomatrixorbital
+   package.  This version uses Netpbm libraries and is fully
+   consistent with other Netpbm programs.
+*/
+
+
+static void
+generateMo(FILE * const ofP, 
+           bit ** const bits,
+           int    const cols,
+           int    const rows) {
+
+    unsigned int col;
+
+    fputc(cols, ofP);
+    fputc(rows, ofP);
+
+    for (col = 0; col < cols; ++col) {
+        unsigned int row;
+        unsigned int outbitpos;
+        unsigned char outchar;
+        
+        outbitpos = 0;  /* Start at 1st bit of 1st output byte */
+
+        for (row = 0; row < rows; ++row) {
+            if (outbitpos == 0)
+                /* We're starting a new byte; initialize it to zeroes */
+                outchar = 0;
+
+            outchar |= bits[row][col] << outbitpos;
+
+            if (outbitpos == 7) 
+                /* We filled up a byte.  Output it. */
+                fputc(outchar, ofP);
+
+            outbitpos = (outbitpos + 1) % 8;
+        }
+        if (outbitpos != 0)
+            /* Our last byte is partial, so must be output now. */
+            fputc(outchar, ofP);
+    }
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    FILE* ifp;
+    bit** bits;
+    int rows, cols;
+    const char * inputFilename;
+
+    pbm_init(&argc, argv);
+
+    if (argc-1 > 0)
+        pm_error("Too many arguments (%d).  The only valid argument is an "
+                 "input file name.", argc-1);
+    else if (argc-1 == 1) 
+        inputFilename = argv[1];
+    else
+        inputFilename = "-";
+
+    ifp = pm_openr(inputFilename);
+    
+    bits = pbm_readpbm(ifp, &cols, &rows);
+
+    if (rows > 255)
+        pm_error("Image is too high:  %d rows.  Max height: 255 rows", rows);
+    if (cols > 255)
+        pm_error("Image is too wide:  %d cols.  Max width: 255 cols", cols);
+
+    generateMo(stdout, bits, cols, rows);
+    
+    pm_close(ifp);
+
+    pbm_freearray(bits, rows);
+
+    exit(0);
+}
diff --git a/converter/pbm/pbmtomda.c b/converter/pbm/pbmtomda.c
new file mode 100644
index 00000000..3ad51499
--- /dev/null
+++ b/converter/pbm/pbmtomda.c
@@ -0,0 +1,201 @@
+
+/***************************************************************************
+
+    PBMTOMDA: Convert portable bitmap to Microdesign area
+    Copyright (C) 1999,2004 John Elliott <jce@seasip.demon.co.uk>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+******************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pbm.h"
+#include "mallocvar.h"
+
+/* I'm being somewhat conservative in the PBM -> MDA translation. I output 
+ * only the MD2 format and don't allow RLE over the ends of lines.
+ */
+
+typedef unsigned char mdbyte;
+
+static FILE *infile;
+static mdbyte header[128];
+static int bInvert = 0;
+static int bScale  = 0;
+
+/* Encode 8 pixels as a byte */
+
+static mdbyte 
+encode(bit ** const bits, int const row, int const col)
+{
+    int n;
+    int mask;
+    mdbyte b;
+
+    mask = 0x80;   /* initial value */
+    b = 0;  /* initial value */
+
+    for (n = 0; n < 8; n++) {
+        if (bits[row][col+n] == PBM_BLACK) b |= mask;
+        mask = mask >> 1;
+    }
+    return b;
+}
+
+/* Translate a pbm to MD2 format, one row at a time */
+
+static void 
+do_translation(bit ** const bits, 
+               int    const nOutCols, 
+               int    const nOutRows,
+               int    const nInRows)
+{
+    int row;
+    mdbyte *mdrow;  /* malloc'ed */
+
+    int const step = bScale ? 2 : 1;
+
+    MALLOCARRAY(mdrow, nOutCols);
+
+    if (mdrow == NULL)
+        pm_error("Not enough memory for conversion.");
+
+    for (row = 0; row < nOutRows; row+=step)
+    {
+        int col;
+        int x1;
+
+        /* Encode image into non-compressed bitmap */
+        for (col = 0; col < nOutCols; ++col) {
+            mdbyte b;
+
+            if (row < nInRows)
+                b = encode(bits, row, col*8);
+            else
+                b = 0xff;  /* All black */
+
+            mdrow[col] = bInvert ? b : ~b;
+        }
+
+        /* Encoded. Now RLE it */
+        for (col = 0; col < nOutCols; )
+        {
+            mdbyte const b = mdrow[col];
+
+            if (b != 0xFF && b != 0) /* Normal byte */
+            {
+                putchar(b);
+                ++col;
+            }
+            else    /* RLE a run of 0s or 0xFFs */
+            {
+                for (x1 = col; x1 < nOutCols; x1++)
+                {
+                    if (mdrow[x1] != b) break;
+                    if (x1 - col > 256) break;
+                }
+                x1 -= col;    /* x1 = no. of repeats */
+                if (x1 == 256) x1 = 0;
+                putchar(b);
+                putchar(x1);
+                col += x1;        
+            }   
+        }
+    }
+    free(mdrow);
+}
+
+
+static void usage(char *s)
+{        
+    printf("pbmtomda v1.01, Copyright (C) 1999,2004 John Elliott <jce@seasip.demon.co.uk>\n"
+         "This program is redistributable under the terms of the GNU General Public\n"
+                 "License, version 2 or later.\n\n"
+                 "Usage: %s [ -d ] [ -i ] [ -- ] [ infile ]\n\n"
+                 "-d: Halve height (to compensate for the PCW aspect ratio)\n"
+                 "-i: Invert colors\n"
+                 "--: No more options (use if filename begins with a dash)\n",
+        s);
+
+    exit(0);
+}
+
+int main(int argc, char **argv)
+{
+    int nOutRowsUnrounded;  /* Before rounding up to multiple of 4 */
+    int nOutCols, nOutRows;
+    int nInCols, nInRows;
+    bit **bits;
+    int rc;
+
+    int n, optstop = 0;
+    char *fname = NULL;
+
+    pbm_init(&argc, argv);
+
+    /* Output v2-format MDA images. Simulate MDA header...
+     * 2004-01-11: Hmm. Apparently some (but not all) MDA-reading 
+     * programs insist on the program identifier being exactly 
+     * 'MicroDesignPCW'. The spec does not make this clear. */
+    strcpy((char*) header, ".MDAMicroDesignPCWv1.00\r\npbm2mda\r\n");
+
+    for (n = 1; n < argc; n++)
+    {
+        if (argv[n][0] == '-' && !optstop)
+        {   
+            if (argv[n][1] == 'd' || argv[n][1] == 'D') bScale = 1;
+            if (argv[n][1] == 'i' || argv[n][1] == 'I') bInvert = 1;
+            if (argv[n][1] == 'h' || argv[n][1] == 'H') usage(argv[0]);
+            if (argv[n][1] == '-' && argv[n][2] == 0 && !fname)     /* "--" */
+            {
+                optstop = 1;
+            }
+            if (argv[n][1] == '-' && (argv[n][2] == 'h' || argv[n][2] == 'H')) usage(argv[0]);
+        }
+        else if (argv[n][0] && !fname)  /* Filename */
+        {
+            fname = argv[n];
+        }
+    }
+
+    if (fname) infile = pm_openr(fname);
+    else       infile = stdin;
+
+    bits = pbm_readpbm(infile, &nInCols, &nInRows);
+    
+    nOutRowsUnrounded = bScale ? nInRows/2 : nInRows;
+
+    nOutRows = ((nOutRowsUnrounded + 3) / 4) * 4;
+        /* MDA wants rows a multiple of 4 */   
+    nOutCols = nInCols / 8;
+
+    rc = fwrite(header, 1, 128, stdout);
+    if (rc < 128)
+        pm_error("Unable to write header to output file.  errno=%d (%s)",
+                 errno, strerror(errno));
+
+    pm_writelittleshort(stdout, nOutRows);
+    pm_writelittleshort(stdout, nOutCols);
+
+    do_translation(bits, nOutCols, nOutRows, nInRows);
+
+    pm_close(infile);
+    fflush(stdout);
+    pbm_freearray(bits, nInRows);
+    
+    return 0;
+}
diff --git a/converter/pbm/pbmtomgr.c b/converter/pbm/pbmtomgr.c
new file mode 100644
index 00000000..7a6e7fc1
--- /dev/null
+++ b/converter/pbm/pbmtomgr.c
@@ -0,0 +1,120 @@
+/* pbmtomgr.c - read a portable bitmap and produce a MGR bitmap
+**
+** 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 "pbm.h"
+#include "mgr.h"
+
+static void putinit ARGS(( int rows, int cols ));
+static void putbit ARGS(( bit b ));
+static void putrest ARGS(( void ));
+static void putitem ARGS(( void ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, format, padright, row, col;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[pbmfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+    bitrow = pbm_allocrow( cols );
+    
+    /* Round cols up to the nearest multiple of 8. */
+    padright = ( ( cols + 7 ) / 8 ) * 8 - cols;
+
+    putinit( rows, cols );
+    for ( row = 0; row < rows; ++row )
+	{
+	pbm_readpbmrow( ifp, bitrow, cols, format );
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+	    putbit( *bP );
+	for ( col = 0; col < padright; ++col )
+	    putbit( 0 );
+        }
+
+    pm_close( ifp );
+
+    putrest( );
+
+    exit( 0 );
+    }
+
+static unsigned char item;
+static int bitsperitem, bitshift;
+
+static void
+putinit( rows, cols )
+    int rows, cols;
+    {
+    struct b_header head;
+
+    head.magic[0] = 'y';
+    head.magic[1] = 'z';
+    head.h_wide = ( ( cols >> 6 ) & 0x3f ) + ' ';
+    head.l_wide = ( cols & 0x3f ) + ' ';
+    head.h_high = ( ( rows >> 6 ) & 0x3f ) + ' ';
+    head.l_high = ( rows & 0x3f ) + ' ';
+    head.depth = ( 1 & 0x3f ) + ' ';
+    head._reserved = ' ';
+    fwrite( &head, sizeof(head), 1, stdout );
+
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 7;
+    }
+
+#if __STDC__
+static void
+putbit( bit b )
+#else /*__STDC__*/
+static void
+putbit( b )
+    bit b;
+#endif /*__STDC__*/
+    {
+    if ( bitsperitem == 8 )
+	putitem( );
+    ++bitsperitem;
+    if ( b == PBM_BLACK )
+	item += 1 << bitshift;
+    --bitshift;
+    }
+
+static void
+putrest( )
+    {
+    if ( bitsperitem > 0 )
+	putitem( );
+    }
+
+static void
+putitem( )
+    {
+    fwrite( &item, sizeof(item), 1, stdout );
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 7;
+    }
diff --git a/converter/pbm/pbmtomrf.c b/converter/pbm/pbmtomrf.c
new file mode 100644
index 00000000..186e95f5
--- /dev/null
+++ b/converter/pbm/pbmtomrf.c
@@ -0,0 +1,338 @@
+/* pbmtomrf - convert pbm to mrf
+ * public domain by RJM
+ *
+ * Adapted to Netpbm by Bryan Henderson 2003.08.09.  Bryan got his copy from
+ * ftp://ibiblio.org/pub/linux/apps/convert, dated 1998.03.03.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "pm_c_util.h"
+#include "pbm.h"
+
+static int bitbox;
+static int bitsleft;
+
+static FILE *bit_out;
+
+
+static void 
+bit_init(FILE * const out) {
+    bitbox = 0; 
+    bitsleft = 8;
+    bit_out = out;
+}
+
+
+
+static void 
+bit_output(int const bit) {
+    --bitsleft;
+    bitbox |= (bit << bitsleft);
+    if (bitsleft == 0) {
+        fputc(bitbox, bit_out);
+        bitbox = 0;
+        bitsleft = 8;
+    }
+}
+
+
+
+static void 
+bit_flush(void) {
+    /* there are never 0 bits left outside of bit_output, but
+     * if 8 bits are left here there's nothing to flush, so
+     * only do it if bitsleft!=8.
+     */
+    if (bitsleft != 8) {
+        bitsleft = 1;
+        bit_output(0);    /* yes, really. This will always work. */
+    }
+}
+
+
+
+static void 
+doSquare(unsigned char * const image,
+         int             const ox,
+         int             const oy,
+         int             const w,
+         int             const size) {
+
+    unsigned int y;
+    unsigned int t;
+
+    /* check square to see if it's all black or all white. */
+
+    t = 0;
+    for (y = 0; y < size; ++y) {
+        unsigned int x;
+        for (x = 0; x < size; ++x)
+            t += image[(oy+y)*w + ox + x];
+    }        
+    /* if the total's 0, it's black. if it's size*size, it's white. */
+    if (t == 0 || t == size*size) {
+        if (size != 1)     /* (it's implicit when size is 1, of course) */
+            bit_output(1);  /* all same color */
+        bit_output(t?1:0);
+        return;
+    }
+    
+    /* otherwise, if our square is greater than 1x1, we need to recurse. */
+    if(size > 1) {
+        bit_output(0);    /* not all same */
+        doSquare(image, ox,      oy,      w, size>>1);
+        doSquare(image, ox+size, oy,      w, size>>1);
+        doSquare(image, ox,      oy+size, w, size>>1);
+        doSquare(image, ox+size, oy+size, w, size>>1);
+    }
+}
+    
+
+
+static void
+fiddleRightEdge(unsigned char * const image,
+                unsigned int    const w,
+                unsigned int    const h,
+                unsigned int    const pw,
+                bool *          const flippedP) {
+
+    unsigned int row;
+    unsigned int t;
+
+    for (row = t = 0; row < h; ++row)
+        t += image[row*pw + w - 1];
+
+    if (t*2 > h) {
+        unsigned int row;
+
+        *flippedP = TRUE;
+        for (row = 0; row < h; ++row) {
+            unsigned int col;
+            for (col = w; col < pw; ++col)
+                image[row*pw + col] = 1;
+        }
+    } else
+        *flippedP = FALSE;
+}
+
+
+
+static void
+fiddleBottomEdge(unsigned char * const image,
+                 unsigned int    const w,
+                 unsigned int    const h,
+                 unsigned int    const pw,
+                 unsigned int    const ph,
+                 bool *          const flippedP) {
+    
+    unsigned int col;
+    unsigned int t;
+
+    for (col = t = 0; col < w; ++col)
+        t += image[(h-1)*pw + col];
+
+    if (t*2 > w) {
+        unsigned int row;
+        *flippedP = TRUE;
+        for (row = h; row < ph; ++row) {
+            unsigned int col;
+            for (col = 0; col < w; ++col)
+                image[row*pw + col] = 1;
+        }
+    } else
+        *flippedP = FALSE;
+}
+
+
+
+
+static void
+fiddleBottomRightCorner(unsigned char * const image,
+                        unsigned int    const w,
+                        unsigned int    const h,
+                        unsigned int    const pw,
+                        unsigned int    const ph) {
+    unsigned int row;
+
+    for (row = h; row < ph; ++row) {
+        unsigned int col;
+        
+        for (col = w; col < pw; ++col)
+                    image[row*pw + col] = 1;
+    }
+}
+
+
+
+static void 
+fiddleEdges(unsigned char * const image,
+            int             const cols,
+            int             const rows) {
+/* the aim of this routine is play around with the edges which
+ * are compressed into the mrf but thrown away when it's decompressed,
+ * such that we get the best compression possible.
+ * If you don't see why this is a good idea, consider the simple case
+ * of a 1x1 white pixel. Placed on a black 64x64 this takes several bytes
+ * to compress. On a white 64x64, it takes two bits.
+ * (Clearly most cases will be more complicated, but you should get the
+ * basic idea from that.)
+ */
+
+    /* there are many possible approaches to this problem, and this one's
+         * certainly not the best, but at least it's quick and easy, and it's
+         * better than nothing. :-)
+         *
+         * So, all we do is flip the runoff area of an edge to white
+         * if more than half of the pixels on that edge are
+         * white. Then for the bottom-right runoff square (if there is
+         * one), we flip it if we flipped both edges.  
+         */
+        
+    /* w64 is units-of-64-bits width, h64 same for height */
+    unsigned int const w64 = (cols + 63) / 64;
+    unsigned int const h64 = (rows + 63) / 64;
+
+    int const pw=w64*64;
+    int const ph=h64*64;
+
+    bool flippedRight, flippedBottom;
+
+    if (cols % 64 != 0) 
+        fiddleRightEdge(image, cols, rows, pw, &flippedRight);
+    else
+        flippedRight = FALSE;
+
+    if (rows % 64 != 0) 
+        fiddleBottomEdge(image, cols, rows, pw, ph, &flippedBottom);
+    else
+        flippedBottom = FALSE;
+
+    if (flippedRight && flippedBottom) 
+        fiddleBottomRightCorner(image, cols, rows, pw, ph);
+}
+
+
+
+static void
+readPbmImage(FILE *           const ifP, 
+             unsigned char ** const imageP,
+             int *            const colsP,
+             int *            const rowsP) {
+    
+
+    /* w64 is units-of-64-bits width, h64 same for height */
+    unsigned int w64, h64;
+
+    unsigned char * image;
+    int cols, rows, format;
+    unsigned int row;
+    bit * bitrow;
+    
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+
+    w64 = (cols + 63) / 64;
+    h64 = (rows + 63) / 64;
+
+    if (UINT_MAX/w64/64/h64/64 == 0)
+        pm_error("Ridiculously large, unprocessable image: %u cols x %u rows",
+                 cols, rows);
+
+    image = calloc(w64*h64*64*64,1);
+    if (image == NULL)
+        pm_error("Unable to get memory for raster");
+                 
+    /* get bytemap image rounded up into mod 64x64 squares */
+
+    bitrow = pbm_allocrow(cols);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        pbm_readpbmrow(ifP, bitrow, cols, format);
+
+        for (col =0; col < cols; ++col)
+            image[row*(w64*64) + col] = (bitrow[col] == PBM_WHITE ? 1 : 0);
+    }
+    pbm_freerow(bitrow);
+    *imageP = image;
+    *colsP = cols;
+    *rowsP = rows;
+}
+
+
+
+static void
+outputMrf(FILE *          const ofP, 
+          unsigned char * const image,
+          unsigned int    const cols,
+          unsigned int    const rows) {
+
+    unsigned int const w64 = (cols + 63) / 64;
+    unsigned int const h64 = (rows + 63) / 64;
+
+    unsigned int row;
+
+    fprintf(ofP, "MRF1");
+    fprintf(ofP, "%c%c%c%c", cols >> 24, cols >> 16, cols >> 8, cols >> 0);
+    fprintf(ofP, "%c%c%c%c", rows >> 24, rows >> 16, rows >> 8, rows >> 0);
+    fputc(0, ofP);   /* option byte, unused for now */
+    
+    /* now recursively check squares. */
+
+    bit_init(ofP);
+
+    for (row = 0; row < h64; ++row) {
+        unsigned int col;
+        for (col = 0; col < w64; ++col)
+            doSquare(image, col*64, row*64, w64*64, 64);
+    }
+    bit_flush();
+}
+
+
+
+int 
+main(int argc,char *argv[]) {
+
+    FILE * ifP;
+    FILE * ofP;
+    unsigned char *image;
+    int rows, cols;
+    
+    pbm_init(&argc, argv);
+
+    if (argc-1 > 1)
+        pm_error("Too many arguments: %d.  Only argument is input file", 
+                 argc-1);
+
+    if (argc-1 == 1)
+        ifP = pm_openr(argv[1]);
+    else
+        ifP = stdin;
+
+    ofP = stdout;
+ 
+    readPbmImage(ifP, &image, &cols, &rows);
+
+    pm_close(ifP);
+
+    /* if necessary, alter the unused outside area to aid compression of
+     * edges of image.
+     */
+
+    fiddleEdges(image, cols, rows);
+
+    outputMrf(ofP, image, cols, rows);
+
+    free(image);
+
+    return 0;
+}
+
+
+
+
diff --git a/converter/pbm/pbmtonokia.c b/converter/pbm/pbmtonokia.c
new file mode 100644
index 00000000..66678b7b
--- /dev/null
+++ b/converter/pbm/pbmtonokia.c
@@ -0,0 +1,225 @@
+/* pbmtonokia.c - convert a portable bitmap to Nokia Smart Messaging
+   Formats (NOL, NGG, HEX)
+
+** Copyright (C)2001 OMS Open Media System GmbH, Tim Rühsen
+** <tim.ruehsen@openmediasystem.de>.
+**
+** 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.
+
+History
+  07.06.2001 Created
+  20.11.2001 Handle Picture Messages
+             new option -txt to embed text into Picture Messages
+             new option -net to specify operator network code for 
+                Nokia Operator Logos
+
+Notes:
+  - limited to rows <= 255 and columns <= 255
+  - limited to b/w graphics, not animated
+
+Testing:
+  Testing was done with SwissCom SMSC (Switzerland) and IC3S SMSC (Germany).
+  The data was send with EMI/UCP protocol over TCP/IP.
+
+  - 7.6.2001: tested with Nokia 3210: 72x14 Operator Logo
+  - 7.6.2001: tested with Nokia 6210: 72x14 Operator Logo and 
+              72x14 Group Graphic
+
+Todo:
+  - more testing
+  - sendsms compatibility ?
+  - are -fmt NOL and -fmt NGG working ok?  */
+
+#define _BSD_SOURCE    /* Make sure strcasecmp() is in string.h */
+#include <string.h>
+
+#include "nstring.h"
+#include "pbm.h"
+
+#define FMT_HEX_NOL   1
+#define FMT_HEX_NGG   2
+#define FMT_HEX_NPM   3
+#define FMT_NOL       4
+#define FMT_NGG       5
+
+static void 
+usage(char *myname)
+{
+    pm_message("Copyright (C)2001 OMS GmbH");
+    pm_message("Contact: Tim Ruehsen <tim.ruehsen@openmediasystem.de>\n");
+    pm_usage("[options] [pbmfile]\n"
+             "  Options:\n"
+             "    -fmt <HEX_NOL|HEX_NGG|HEX_NPM|NOL|NGG>  "
+             "Output format (default=HEX_NOL)\n"
+             "    -net <network code>                     "
+             "Network code for NOL operator logos\n"
+             "    -txt <text message>                     "
+             "Text for NMP picture messages\n");
+
+    exit(1);
+}
+
+int 
+main(int argc, char *argv[])
+{
+    FILE    *fp;
+    bit    **image;
+    unsigned int    c;
+    int    argpos, output=FMT_HEX_NOL, rows, cols, row, col, p, it, len;
+    char    header[32], *myname;
+    char    network_code[6+1];
+    char    *text=NULL;
+
+    if ((myname=strrchr(argv[0],'/'))!=NULL) myname++; else myname=argv[0];
+
+    pbm_init(&argc, argv);
+
+    strcpy(network_code, "62F210"); /* default is German D1 net */
+
+    for(argpos=1;argpos<argc;argpos++) {
+        if (argv[argpos][0]=='-') {
+            if (argv[argpos][1]=='-') {
+                if (argc>argpos+1 && ISDIGIT(argv[argpos+1][0]))
+                    {argpos++;break;}
+            } else if (STREQ(argv[argpos],"-fmt") && argc>argpos+1) {
+                ++argpos;
+                if (!strcasecmp(argv[argpos],"HEX_NOL")) output=FMT_HEX_NOL;
+                else if (!strcasecmp(argv[argpos],"HEX_NGG")) 
+                    output=FMT_HEX_NGG;
+                else if (!strcasecmp(argv[argpos],"HEX_NPM")) 
+                    output=FMT_HEX_NPM;
+                else if (!strcasecmp(argv[argpos],"NOL")) output=FMT_NOL;
+                else if (!strcasecmp(argv[argpos],"NGG")) output=FMT_NGG;
+                else usage(myname);
+            } else if (STREQ(argv[argpos],"-net") && argc>argpos+1) {
+                char * const network_code_arg=argv[++argpos];
+                unsigned int it;
+                len=strlen(network_code_arg);
+                if (len!=6) 
+                    pm_error("Network code must be 6 hex-digits long");
+                for (it=0;it<strlen(network_code_arg);it++) {
+                    if (!ISXDIGIT(network_code_arg[it])) 
+                        pm_error("Network code must contain hex-digits only");
+                    network_code[it]=TOUPPER(network_code_arg[it]);
+                }
+                network_code[it] = '\0';
+            } else if (STREQ(argv[argpos],"-txt") && argc>argpos+1) {
+                text=argv[++argpos];
+            }
+            else usage(myname);
+        } else break;
+    }
+
+    if (argpos==argc) {
+        image = pbm_readpbm(stdin, &cols, &rows);
+    } else {
+        fp=pm_openr(argv[argpos]);
+        image = pbm_readpbm(fp, &cols, &rows);
+        pm_close(fp);
+    }
+
+    memset(header,0,sizeof(header));
+
+    switch (output) {
+    case FMT_HEX_NOL:
+        /* header */
+        printf("06050415820000%s00%02X%02X01",network_code,cols,rows);
+
+        /* image */
+        for (row=0;row<rows;row++) {
+            for (p=c=col=0;col<cols;col++) {
+                if (image[row][col]==PBM_BLACK) c|=0x80>>p;
+                if (++p==8) {
+                    printf("%02X",c);
+                    p=c=0;
+                }
+            }
+            if (p) printf("%02X",c);
+        }
+        break;
+    case FMT_HEX_NGG:
+        /* header */
+        printf("0605041583000000%02X%02X01",cols,rows);
+
+        /* image */
+        for (row=0;row<rows;row++) {
+            for (p=c=col=0;col<cols;col++) {
+                if (image[row][col]==PBM_BLACK) c|=0x80>>p;
+                if (++p==8) {
+                    printf("%02X",c);
+                    p=c=0;
+                }
+            }
+            if (p) printf("%02X",c);
+        }
+        break;
+    case FMT_HEX_NPM:
+        /* header */
+        printf("060504158A0000");
+
+        /* text */
+        if (text!=NULL) {
+            printf("00%04X",(len=strlen(text)));
+            for (it=0;it<len;it++) printf("%02X",text[it]);
+        }
+
+        /* image */
+        printf("02%04X00%02X%02X01",(cols*rows)/8+4,cols,rows);
+        for (row=0;row<rows;row++) {
+            for (p=c=col=0;col<cols;col++) {
+                if (image[row][col]==PBM_BLACK) c|=0x80>>p;
+                if (++p==8) {
+                    printf("%02X",c);
+                    p=c=0;
+                }
+            }
+            if (p) printf("%02X",c);
+        }
+        break;
+    case FMT_NOL:
+        /* header - this is a hack */
+        header[1]=header[4]=header[5]=header[11]=header[13]=1;
+        header[3]=4;
+        header[7]=cols;
+        header[9]=rows;
+        header[15]=0x53;
+        fwrite(header,17,1,stdout);
+
+        /* image */
+        for (row=0;row<rows;row++) {
+            for (p=c=col=0;col<cols;col++) {
+                if (image[row][col]==PBM_BLACK) putchar('1');
+                else putchar('0');
+            }
+        }
+        break;
+    case FMT_NGG:
+        /* header - this is a hack */
+        header[1]=header[7]=header[9]=1;
+        header[3]=cols;
+        header[5]=rows;
+        header[11]=0x4a;
+        fwrite(header,13,1,stdout);
+
+        /* image */
+        for (row=0;row<rows;row++) {
+            for (p=c=col=0;col<cols;col++) {
+                if (image[row][col]==PBM_BLACK) putchar('1');
+                else putchar('0');
+            }
+        }
+        break;
+    default:
+        pm_error("Output format %d not implemented!\n"
+                 "Contact Tim Ruehsen <tim.ruehsen@openmediasystem.de>\n",
+                 output);
+        return 1;
+    }
+    return 0;
+}
+
diff --git a/converter/pbm/pbmtopi3.c b/converter/pbm/pbmtopi3.c
new file mode 100644
index 00000000..06023d7a
--- /dev/null
+++ b/converter/pbm/pbmtopi3.c
@@ -0,0 +1,123 @@
+/* pbmtopi3.c - read a portable bitmap and produce a Atari Degas .pi3 file
+**
+** Module created from other pbmplus tools by David Beckemeyer.
+**
+** Copyright (C) 1988 by David Beckemeyer 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 <stdio.h>
+#include "pbm.h"
+
+static void putinit ARGS(( void ));
+static void putbit ARGS(( bit b ));
+static void putrest ARGS(( void ));
+static void putitem ARGS(( void ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, format, padright, row, col;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[pbmfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+    if (cols > 640)
+	cols = 640;
+    if (rows > 400)
+	rows = 400;
+    bitrow = pbm_allocrow( cols );
+    
+    /* Compute padding to round cols up to 640 */
+    padright = 640 - cols;
+
+    putinit( );
+    for ( row = 0; row < rows; ++row )
+	{
+	pbm_readpbmrow( ifp, bitrow, cols, format );
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+	    putbit( *bP );
+	for ( col = 0; col < padright; ++col )
+	    putbit( 0 );
+        }
+    while (row++ < 400)
+	for ( col = 0; col < 640; ++col)
+	    putbit( 0 );
+
+    pm_close( ifp );
+
+    putrest( );
+
+    exit( 0 );
+    }
+
+static char item;
+static short bitsperitem, bitshift;
+
+static void
+putinit( )
+    {
+    int i;
+    if (pm_writebigshort (stdout, (short) 2) == -1
+	|| pm_writebigshort (stdout, (short) 0x777) == -1)
+      pm_error ("write error");
+    for (i = 1; i < 16; i++)
+      if (pm_writebigshort (stdout, (short) 0) == -1)
+	pm_error ("write error");
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 7;
+    }
+
+#if __STDC__
+static void
+putbit( bit b )
+#else /*__STDC__*/
+static void
+putbit( b )
+    bit b;
+#endif /*__STDC__*/
+    {
+    if (bitsperitem == 8)
+	putitem( );
+    ++bitsperitem;
+    if ( b == PBM_BLACK )
+	item += 1 << bitshift;
+    --bitshift;
+    }
+
+static void
+putrest( )
+    {
+    if ( bitsperitem > 0 )
+	putitem( );
+    }
+
+static void
+putitem( )
+    {
+    putc (item, stdout);
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 7;
+    }
diff --git a/converter/pbm/pbmtopk.c b/converter/pbm/pbmtopk.c
new file mode 100644
index 00000000..b84818b1
--- /dev/null
+++ b/converter/pbm/pbmtopk.c
@@ -0,0 +1,976 @@
+/*
+  pbmtopk, adapted from "pxtopk.c by tomas rokicki" by AJCD 1/8/90
+  
+  compile with: cc -o pbmtopk pbmtopk.c -lm -lpbm
+*/
+
+#define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include <stdio.h>
+#include <math.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "pbm.h"
+#include "nstring.h"
+#include "mallocvar.h"
+
+#define MAXPKCHAR 256
+#define MAXOPTLINE 200
+#define MAXWIDTHTAB 256
+#define MAXHEIGHTTAB 16
+#define MAXDEPTHTAB 16
+#define MAXITALICTAB 64
+#define MAXPARAMS 30
+#define NAMELENGTH 80
+
+#define fixword(d) ((int)((double)(d)*1048576))
+#define unfixword(f) ((double)(f) / 1048576)
+#define fixrange(f) ((f) < 16777216 && (f) > -16777216)
+#define designunits(p) ((p)*72.27/(double)resolution/unfixword(designsize))
+
+/* character flags: in order of appearance in option files. */
+#define XOFFSET     1
+#define YOFFSET     2
+#define HORZESC     4
+#define VERTESC     8
+#define TFMWIDTH   16
+#define TFMHEIGHT  32
+#define TFMDEPTH   64
+#define TFMITALIC 128
+
+typedef int integer ;
+typedef char quarterword ;
+typedef char boolean ;
+typedef quarterword ASCIIcode ;
+typedef quarterword eightbits ;
+typedef unsigned char byte ;
+
+static integer resolution, designsize ;
+static char *filename[MAXPKCHAR] ;
+
+static integer xoffset[MAXPKCHAR] ;
+static integer yoffset[MAXPKCHAR] ;
+static integer horzesc[MAXPKCHAR] ;
+static integer vertesc[MAXPKCHAR] ;
+
+static byte tfmindex[MAXPKCHAR] ;
+static byte hgtindex[MAXPKCHAR] ;
+static byte depindex[MAXPKCHAR] ;
+static byte italindex[MAXPKCHAR] ;
+static byte charflags[MAXPKCHAR] ;
+
+static bit **bitmap ;
+static integer smallestch = MAXPKCHAR ;
+static integer largestch = -1;
+static integer emwidth  = 0;
+static integer checksum ;
+static const char *codingscheme = "GRAPHIC" ;
+static const char *familyname = "PBM" ;
+
+static integer widthtab[MAXWIDTHTAB] = {0}; /* TFM widths */
+static integer numwidth = 1;      /* number of entries in width table */
+static integer heighttab[MAXHEIGHTTAB]  = {0};
+static integer numheight = 1;
+static integer depthtab[MAXDEPTHTAB] = {0};
+static integer numdepth = 1;
+static integer italictab[MAXITALICTAB] = {0};
+static integer numitalic = 1;
+static integer parameters[MAXPARAMS] = {0};
+static integer numparam = 0;
+
+static ASCIIcode xord[128] ;
+static char xchr[256] = {
+    '?', '?', '?', '?', '?', '?', '?', '?',
+    '?', '?', '?', '?', '?', '?', '?', '?',
+    '?', '?', '?', '?', '?', '?', '?', '?',
+    '?', '?', '?', '?', '?', '?', '?', '?',
+    ' ', '!', '"', '#', '$', '%', '&', '\'',
+    '(', ')', '*', '+', ',', '-', '.', '/',
+    '0', '1', '2', '3', '4', '5', '6', '7',
+    '8', '9', ':', ';', '<', '=', '>', '?',
+    '@', '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', '[', '\\', ']', '^', '_',
+    '`', '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', '{', '|', '}', '~', '?' };
+
+static FILE *tfmfile, *pkfile ;
+static char tfmname[NAMELENGTH+1], pkname[NAMELENGTH+1] ;
+static integer pbmtopk_pkloc = 0 ;
+static integer bitweight ;
+static integer outputbyte ;
+static integer car ;
+static integer hppp ;
+static integer width ;
+static integer height ;
+
+/* check sum algorithm (in Pascal):
+
+compute_checksum()
+    begin
+        c0:=bc; c1:=ec; c2:=bc; c3:=ec;
+        for c:=bc to ec do if char_wd[c]>0 then begin
+            temp_width:=memory[char_wd[c]];
+            if design_units<>unity then
+               temp_width:=round((temp_width/design_units)*1048576.0);
+            temp_width:=temp_width + (c+4)*@'20000000; 
+                {this should be positive}
+            c0:=(c0+c0+temp_width) mod 255;
+            c1:=(c1+c1+temp_width) mod 253;
+            c2:=(c2+c2+temp_width) mod 251;
+            c3:=(c3+c3+temp_width) mod 247;
+            end;
+        header_bytes[check_sum_loc]:=c0;
+        header_bytes[check_sum_loc+1]:=c1;
+        header_bytes[check_sum_loc+2]:=c2;
+        header_bytes[check_sum_loc+3]:=c3;
+        end
+*/
+
+#define add_tfmwidth(v) (add_tfmtable(widthtab, &numwidth, v, MAXWIDTHTAB,\
+                                      "TFM width"))
+#define add_tfmheight(v) (add_tfmtable(heighttab, &numheight, v, MAXHEIGHTTAB,\
+                                       "TFM height"))
+#define add_tfmdepth(v) (add_tfmtable(depthtab, &numdepth, v, MAXDEPTHTAB,\
+                                      "TFM depth"))
+#define add_tfmitalic(v) (add_tfmtable(italictab, &numitalic, v, MAXITALICTAB,\
+                                       "Italic correction"))
+
+
+static byte
+add_tfmtable(int *        const table, 
+             int *        const count, 
+             int          const value, 
+             int          const max_count, 
+             const char * const name) {
+    
+    integer i;
+    for (i = 0; i < *count; i++) /* search for value in tfm table */
+        if (table[i] == value) return (byte)i;
+    if (*count >= max_count)
+        pm_error("too many values in %s table", name) ;
+    if (!fixrange(value))
+        pm_error("%s %f for char %d out of range",
+                 name, unfixword(value), car);
+    table[*count] = value ;
+    return (*count)++ ;
+}
+
+
+
+/* add a suffix to a filename in an allocated space */
+static void 
+pbmtopk_add_suffix(char * const name, 
+                   const char * const suffix) {
+
+    char *slash = strrchr(name, '/');
+    char *dot = strrchr(name, '.');
+
+    if ((dot && slash ? dot < slash : !dot) && !STREQ(name, "-"))
+        strcat(name, suffix);
+}
+
+
+
+/* initialize the PK parameters */
+static void 
+initialize_pk(void) {
+    integer i ;
+    pm_message("This is PBMtoPK, version 2.4") ;
+    for (i = 127 ; i <= 255 ; i ++) xchr[i] = '?' ;
+    for (i = 0 ; i <= 127 ; i ++) xord[i] = 32 ;
+    for (i = 32 ; i < 127 ; i ++) xord[(int)xchr[i]] = i ;
+    for (i = 0; i < MAXPKCHAR; i++) {
+        filename[i] = NULL;
+        charflags[i] = 0;
+    }
+    designsize = fixword(1.0) ;
+}
+
+
+
+/* write a single byte to the PK file */
+static void 
+pbmtopk_pkbyte(integer const b_in) {
+    integer b;
+
+    b = b_in;  /* initial value */
+
+    if (b < 0) 
+        b += 256 ;
+    putc(b, pkfile) ;
+    pbmtopk_pkloc++ ;
+}
+
+
+
+/* write two bytes to the PK file */
+static void 
+pkhalfword(integer const a_in) {
+    integer a;
+
+    a = a_in;
+
+    if (a < 0) 
+        a += 65536 ;
+    pbmtopk_pkbyte(a >> 8) ;
+    pbmtopk_pkbyte(a & 255) ;
+}
+
+
+
+/* write three bytes to the PK file */
+static void 
+pkthreebytes(integer const a) {
+
+    pbmtopk_pkbyte((a>>16) & 255) ;
+    pbmtopk_pkbyte((a>>8) & 255) ;
+    pbmtopk_pkbyte(a & 255) ;
+}
+
+
+
+/* write four bytes to the PK file */
+static void 
+pkword(integer const a) {
+    pbmtopk_pkbyte((a>>24) & 255) ;
+    pbmtopk_pkbyte((a>>16) & 255) ;
+    pbmtopk_pkbyte((a>>8) & 255) ;
+    pbmtopk_pkbyte(a & 255) ;
+}
+
+
+
+/* write a nibble to the PK file */
+static void 
+pknyb(integer const a) {
+
+    if (bitweight == 16) {
+        outputbyte = (a<<4) ;
+        bitweight = 1 ;
+    } else {
+        pbmtopk_pkbyte(outputbyte + a) ;
+        bitweight = 16 ;
+    }
+}
+
+
+
+/* write preamble to PK file */
+static void 
+writepreamble(void) {
+    integer i ;
+    const char * const comment = "PBMtoPK 2.4 output" ;
+   
+    pbmtopk_pkbyte(247) ;                /* PRE command */
+    pbmtopk_pkbyte(89) ;             /* PK file type */
+    pbmtopk_pkbyte(strlen(comment)) ;            /* output comment */
+    for (i = 0 ; i < strlen(comment); i++) 
+        pbmtopk_pkbyte(xord[(int)comment[i]]) ;
+    pkword(designsize) ;             /* write designsize */
+    pkword(checksum) ;       /* write checksum; calculate if possible */
+    pkword(hppp) ;               /* write H pixels per point */
+    pkword(hppp) ;               /* write V pixels per point */
+}
+
+
+
+/* write postamble to PK file, padded to word length */
+static void 
+writepostamble(void) {
+    pbmtopk_pkbyte(245) ;                /* POST command */
+    while (pbmtopk_pkloc % 4)
+        pbmtopk_pkbyte(246) ;             /* pad with no-ops */
+    pm_message("%d bytes written to packed file.", pbmtopk_pkloc) ;
+}
+
+
+
+/* write a byte to the TFM file */
+static void 
+tfmbyte(integer const b_in) {
+    integer b;
+
+    b = b_in;
+
+    if (b < 0) b += 256 ;
+    putc(b, tfmfile) ;
+}
+
+
+
+/* write a half word to the TFM file */
+static void 
+tfmhalfword(integer const a_in) {
+    
+    integer a;
+
+    a = a_in;
+
+    if (a < 0) a += 65536 ;
+    tfmbyte(a >> 8) ;
+    tfmbyte(a & 255) ;
+}
+
+
+
+/* write a word to the TFM file */
+static void 
+tfmword(integer const a) {
+    tfmbyte((a>>24) & 255) ;
+    tfmbyte((a>>16) & 255) ;
+    tfmbyte((a>>8) & 255) ;
+    tfmbyte(a & 255) ;
+}
+
+
+
+/* write the whole TFM file for the font */
+static void 
+writetfmfile(void) {
+    integer totallength ;
+    integer headersize = 17;
+    integer i ;
+   
+    if (largestch - smallestch < 0) {
+        largestch = 0;
+        smallestch = 1;
+    }
+    if (numparam < 7) /* set default parameters */
+        switch (numparam) {
+        case 0: /* slant */
+            parameters[numparam++] = 0 ;
+        case 1: /* space */
+            parameters[numparam++] = fixword(designunits(emwidth/3.0));
+        case 2: /* space_stretch */
+            parameters[numparam++] = fixword(unfixword(parameters[1])/2.0) ;
+        case 3: /* space_shrink */
+            parameters[numparam++] = fixword(unfixword(parameters[1])/3.0) ;
+        case 4: /* x_height */
+            parameters[numparam++] = fixword(0.45);
+        case 5: /* quad */
+            parameters[numparam++] = fixword(designunits(emwidth)) ;
+        case 6: /* extra_space */
+            parameters[numparam++] = fixword(unfixword(parameters[1])/3.0) ;
+        }
+    totallength = 6 + headersize + (largestch+1-smallestch) +
+        numwidth + numheight + numdepth + numitalic + numparam ;
+    /* lengths */
+    tfmhalfword(totallength) ;           /* write file TFM length */
+    tfmhalfword(headersize) ;            /* write TFM header length */
+    tfmhalfword(smallestch) ;            /* write lowest char index */
+    tfmhalfword(largestch) ;         /* write highest char index */
+    tfmhalfword(numwidth) ;          /* write number of widths */
+    tfmhalfword(numheight) ;         /* write number of heights */
+    tfmhalfword(numdepth) ;          /* write number of depths */
+    tfmhalfword(numitalic) ;         /* write number of italcorrs */
+    tfmhalfword(0) ;             /* lig/kern table */
+    tfmhalfword(0) ;             /* kern table */
+    tfmhalfword(0) ;             /* extensible char table */
+    tfmhalfword(numparam) ;          /* number of fontdimens */
+    /* header */
+    tfmword(checksum) ;              /* write checksum */
+    tfmword(designsize) ;            /* write designsize */
+    if (strlen(codingscheme) > 39) 
+        tfmbyte(39) ; /* write coding scheme len */
+    else 
+        tfmbyte(strlen(codingscheme)) ;
+    for (i = 0; i < 39; i++)         /* write coding scheme */
+        if 
+            (*codingscheme) tfmbyte(xord[(int)(*codingscheme++)]) ;
+        else
+            tfmbyte(0) ;
+    if (strlen(familyname) > 19) 
+        tfmbyte(19) ;   /* write family length */
+    else 
+        tfmbyte(strlen(familyname)) ;
+    for (i = 0; i < 19; i++)         /* write family */
+        if (*familyname) 
+            tfmbyte(xord[(int)(*familyname++)]) ;
+        else 
+            tfmbyte(0) ;
+    /* char_info */
+    for (car = smallestch; car <= largestch; car++)
+        if (filename[car]) {          /* write character info */
+            tfmbyte(tfmindex[car]) ;
+            tfmbyte((hgtindex[car]<<4) + depindex[car]) ;
+            tfmbyte(italindex[car]<<2) ;
+            tfmbyte(0) ;
+        } else
+            tfmword(0) ;
+    /* width table */
+    for (i = 0; i < numwidth; i++) tfmword(widthtab[i]) ;
+    /* height table */
+    for (i = 0; i < numheight; i++) tfmword(heighttab[i]) ;
+    /* depth table */
+    for (i = 0; i < numdepth; i++) tfmword(depthtab[i]) ;
+    /* italic correction table */
+    for (i = 0; i < numitalic; i++) tfmword(italictab[i]) ;
+    /* no lig_kern, kern, or exten tables */
+    /* fontdimen table */
+    for (i = 0; i < numparam; i++)
+        if (i && !fixrange(parameters[i]))
+            pm_error("parameter %d out of range (-p)", i);
+        else
+            tfmword(parameters[i]) ;
+    pm_message("%d bytes written to tfm file.", totallength*4) ;
+}
+
+
+
+/* read a character from a PBM file */
+static void readcharacter(void) {
+    FILE *fp;
+   
+    fp = pm_openr(filename[car]);
+    bitmap = pbm_readpbm(fp, &width, &height) ;
+    pm_close(fp) ;
+   
+    if ((charflags[car] & HORZESC) == 0) horzesc[car] = width ;
+    if ((charflags[car] & VERTESC) == 0) vertesc[car] = 0;
+    if ((charflags[car] & XOFFSET) == 0) xoffset[car] = 0;
+    if ((charflags[car] & YOFFSET) == 0) yoffset[car] = height-1;
+    if ((charflags[car] & TFMWIDTH) == 0)
+        tfmindex[car] = add_tfmwidth(fixword(designunits(width)));
+    if ((charflags[car] & TFMHEIGHT) == 0)
+        hgtindex[car] = add_tfmheight(fixword(designunits(yoffset[car]+1)));
+    if ((charflags[car] & TFMDEPTH) == 0)
+        depindex[car] = 
+            add_tfmdepth(fixword(designunits(height-1-yoffset[car])));
+    if ((charflags[car] & TFMITALIC) == 0) italindex[car] = 0;
+   
+    if (car < smallestch) smallestch = car;
+    if (car > largestch) largestch = car;
+    if (width > emwidth) emwidth = width ;
+}
+
+
+
+/* test if two rows of the PBM are the same */
+static int 
+equal(const bit * const row1, 
+      const bit * const row2) {
+
+    integer i ;
+   
+    for (i = 0; i < width; i++)
+        if (row1[i] != row2[i]) 
+            return (0) ;
+
+    return(1) ;
+}
+
+
+
+static void 
+shipcharacter(void) {
+
+    integer compsize ;
+    integer i, j, k ;
+    bit *zerorow, *onesrow ;
+    integer *repeatptr, *bitcounts ;
+    integer count ;
+    integer test ;
+    integer curptr, rowptr ;
+    integer bitval ;
+    integer repeatflag ;
+    integer colptr ;
+    integer currepeat ;
+    integer dynf ;
+    integer deriv[14] ;
+    integer bcompsize ;
+    boolean firston ;
+    integer flagbyte ;
+    boolean state ;
+    boolean on ;
+    integer hbit ;
+    integer pbit ;
+    boolean ron, son ;
+    integer rcount, scount ;
+    integer ri, si ;
+    integer max2 ;
+    integer predpkloc ;
+    integer buff ;
+   
+    integer tfwid = widthtab[tfmindex[car]] ;
+    integer hesc = horzesc[car] ;
+    integer vesc = vertesc[car] ;
+    integer xoff = xoffset[car] ;
+    integer yoff = yoffset[car] ;
+   
+    MALLOCARRAY(repeatptr, height + 1);
+    MALLOCARRAY(bitcounts, height * width);
+    if (repeatptr == NULL || bitcounts == NULL)
+        pm_error("out of memory while allocating bit counts");
+    zerorow = pbm_allocrow(width) ;      /* initialize plain rows */
+    onesrow = pbm_allocrow(width) ;
+    for (i = 0 ; i < width ; i++) {
+        zerorow[i] = PBM_WHITE ;
+        onesrow[i] = PBM_BLACK ;
+    }
+    for (i=0; i < height; i = k) {       /* set repeat pointers */
+        k = i + 1;
+        if (!equal(bitmap[i], zerorow) && !equal(bitmap[i], onesrow)) {
+            while (k < height && equal(bitmap[i], bitmap[k]))
+                k++;
+            repeatptr[i] = k - i - 1;
+        } else {
+            repeatptr[i] = 0;
+        }
+    }
+    repeatptr[height] = 0 ;
+    colptr = width - 1 ;
+    repeatflag = currepeat = curptr = count = rowptr = 0 ;
+    test = PBM_WHITE ;
+    do {
+        colptr++ ;
+        if (colptr == width) {            /* end of row, get next row */
+            colptr = 0 ;
+            rowptr = currepeat ;
+            if (repeatptr[currepeat] > 0) {
+                repeatflag = repeatptr[currepeat] ;
+                currepeat += repeatflag ;
+                rowptr += repeatflag ;
+            }
+            currepeat++ ;
+        }
+        if (rowptr >= height) bitval = -1 ;
+        else bitval = bitmap[rowptr][colptr] ;
+        if (bitval == test) count++ ;     /* count repeated pixels */
+        else {                    /* end of pixel run */
+            bitcounts[curptr++] = count ;
+            if (curptr+3 >= height*width)
+                pm_error("out of memory while saving character counts");
+            count = 1 ;
+            test = bitval ;
+            if (repeatflag > 0) {
+                bitcounts[curptr++] = -repeatflag ;
+                repeatflag = 0 ;
+            }
+        }
+    } while (test != -1) ;
+    bitcounts[curptr] = 0 ;
+    bitcounts[curptr + 1] = 0 ;
+    for (i = 1 ; i <= 13 ; i ++) deriv[i] = 0 ;
+    i = firston = (bitcounts[0] == 0) ;
+    compsize = 0 ;
+    while (bitcounts[i] != 0) {          /* calculate dyn_f */
+        j = bitcounts[i] ;
+        if (j == -1) compsize++ ;
+        else {
+            if (j < 0) {
+                compsize++ ;
+                j = -j ;
+            }
+            if (j < 209) compsize += 2 ;
+            else {
+                k = j - 193 ;
+                while (k >= 16) {
+                    k >>= 4 ;
+                    compsize += 2 ;
+                }
+                compsize++ ;
+            }
+            if (j < 14) (deriv[j])-- ;
+            else if (j < 209) (deriv[(223 - j) / 15])++ ;
+            else {
+                k = 16 ;
+                while (((k<<4) < j + 3)) k <<= 4 ;
+                if (j - k <= 192)
+                    deriv[(207 - j + k) / 15] += 2 ;
+            }
+        }
+        i++ ;
+    }
+    bcompsize = compsize ;
+    dynf = 0 ;
+    for (i = 1 ; i <= 13 ; i ++) {
+        compsize += deriv[i] ;
+        if (compsize <= bcompsize) {
+            bcompsize = compsize ;
+            dynf = i ;
+        }
+    }
+    compsize = ((bcompsize + 1)>>1) ;
+    if ((compsize > ((height*width+7)>>3)) || (height*width == 0)) {
+        compsize = ((height*width+7)>>3) ;
+        dynf = 14 ;
+    }
+    flagbyte = (dynf<<4) ;
+    if (firston) flagbyte |= 8 ;
+    if (tfwid > 16777215 || tfwid < 0 || hesc < 0 || vesc != 0 ||
+        compsize > 196579 || width > 65535 || height > 65535 ||
+        xoff > 32767 || yoff > 32767 || xoff < -32768 || yoff < -32768) {
+        flagbyte |= 7 ;               /* long form preamble */
+        pbmtopk_pkbyte(flagbyte) ;
+        compsize += 28 ;
+        pkword(compsize) ;            /* char packet size */
+        pkword(car) ;             /* character number */
+        predpkloc = pbmtopk_pkloc + compsize ;
+        pkword(tfwid) ;               /* TFM width */
+        pkword(hesc<<16) ;            /* horiz escapement */
+        pkword(vesc<<16) ;            /* vert escapement */
+        pkword(width) ;               /* bounding box width */
+        pkword(height) ;              /* bounding box height */
+        pkword(xoff) ;                /* horiz offset */
+        pkword(yoff) ;                /* vert offset */
+    } else if (hesc > 255 || width > 255 || height > 255 ||
+               xoff > 127 || yoff > 127 || xoff < -128 ||
+               yoff < -128 || compsize > 1016) {
+        compsize += 13 ;              /* extended short preamble */
+        flagbyte += (compsize>>16) + 4 ;
+        pbmtopk_pkbyte(flagbyte) ;
+        pkhalfword(compsize & 65535) ;        /* char packet size */
+        pbmtopk_pkbyte(car) ;             /* character number */
+        predpkloc = pbmtopk_pkloc + compsize ;
+        pkthreebytes(tfwid) ;         /* TFM width */
+        pkhalfword(hesc) ;            /* horiz escapement */
+        pkhalfword(width) ;           /* bounding box width */
+        pkhalfword(height) ;          /* bounding box height */
+        pkhalfword(xoff) ;            /* horiz offset */
+        pkhalfword(yoff) ;            /* vert offset */
+    } else {
+        compsize += 8 ;               /* short form preamble */
+        flagbyte = flagbyte + (compsize>>8) ;
+        pbmtopk_pkbyte(flagbyte) ;
+        pbmtopk_pkbyte(compsize & 255) ;          /* char packet size */
+        pbmtopk_pkbyte(car) ;             /* character number */
+        predpkloc = pbmtopk_pkloc + compsize ;
+        pkthreebytes(tfwid) ;         /* TFM width */
+        pbmtopk_pkbyte(hesc) ;                /* horiz escapement */
+        pbmtopk_pkbyte(width) ;               /* bounding box width */
+        pbmtopk_pkbyte(height) ;              /* bounding box height */
+        pbmtopk_pkbyte(xoff) ;                /* horiz offset */
+        pbmtopk_pkbyte(yoff) ;                /* vert offset */
+    }
+    if (dynf != 14) {                /* write packed character */
+        bitweight = 16 ;
+        max2 = 208 - 15 * dynf ;
+        i = firston ;
+        while (bitcounts[i] != 0) {
+            j = bitcounts[i] ;
+            if (j == - 1) pknyb(15) ;
+            else {
+                if (j < 0) {
+                    pknyb(14) ;
+                    j = -j ;
+                }
+                if (j <= dynf) pknyb(j) ;
+                else if (j <= max2) {
+                    j -= dynf + 1 ;
+                    pknyb((j >> 4) + dynf + 1) ;
+                    pknyb((j & 15)) ;
+                } else {
+                    j -= max2 - 15 ;
+                    k = 16 ;
+                    while (k <= j) {
+                        k <<= 4 ;
+                        pknyb(0) ;
+                    }
+                    while (k > 1) {
+                        k >>= 4 ;
+                        pknyb(j / k) ;
+                        j = j % k ;
+                    }
+                }
+            }
+            i++ ;
+        }
+        if (bitweight != 16) pbmtopk_pkbyte(outputbyte) ;
+    } else {                 /* write bitmap character */
+        buff = 0 ;
+        pbit = 8 ;
+        i = firston ;
+        hbit = width ;
+        on = ! firston ;
+        state = 0 ;
+        count = repeatflag = 0 ;
+        while ((bitcounts[i] != 0) || state || (count > 0)) {
+            if (state) {
+                count = rcount ;
+                i = ri ;
+                on = ron ;
+                repeatflag-- ;
+            } else {
+                rcount = count ;
+                ri = i ;
+                ron = on ;
+            }
+            do {
+                if (count == 0) {
+                    if (bitcounts[i] < 0) {
+                        if (! state) repeatflag = -bitcounts[i] ;
+                        i++ ;
+                    }
+                    count = bitcounts[i] ;
+                    i++ ;
+                    on = !on ;
+                }
+                if ((count >= pbit) && (pbit < hbit)) {
+                    if (on) buff += (1 << pbit) - 1 ;
+                    pbmtopk_pkbyte(buff) ;
+                    buff = 0 ;
+                    hbit -= pbit ;
+                    count -= pbit ;
+                    pbit = 8 ;
+                } else if ((count < pbit) && (count < hbit)) {
+                    if (on) buff += (1 << pbit) - (1 << (pbit - count)) ;
+                    pbit -=  count ;
+                    hbit -= count ;
+                    count = 0 ;
+                } else {
+                    if (on) buff += (1 << pbit) - (1 << (pbit - hbit)) ;
+                    count -= hbit ;
+                    pbit -= hbit ;
+                    hbit = width ;
+                    if (pbit == 0) {
+                        pbmtopk_pkbyte(buff) ;
+                        buff = 0 ;
+                        pbit = 8 ;
+                    }
+                }
+            } while (hbit != width) ;
+            if (state && (repeatflag == 0)) {
+                count = scount ;
+                i = si ;
+                on = son ;
+                state = 0 ;
+            } else if (! state && (repeatflag > 0)) {
+                scount = count ;
+                si = i ;
+                son = on ;
+                state = 1 ;
+            }
+        }
+        if (pbit != 8) pbmtopk_pkbyte(buff) ;
+    }
+    if (predpkloc != pbmtopk_pkloc)
+        pm_error("bad predicted character length: character %d", car);
+    pbm_freerow(zerorow); 
+    pbm_freerow(onesrow); 
+    free((char *)repeatptr);
+    free((char *)bitcounts);
+}
+
+
+
+/* check that character is in valid range */
+static void 
+checkchar(void) {
+    if (car < 0 || car >= MAXPKCHAR)
+        pm_error("character must be in range 0 to %d", MAXPKCHAR-1) ;
+}
+
+
+
+/* read character information from an option file */
+static void 
+optionfile(const char * const name) {
+
+    FILE *fp ;
+    char buffer[MAXOPTLINE] ;
+   
+    fp = pm_openr(name);
+    while (!feof(fp)) {
+        char *here = buffer;
+      
+        if (fgets(buffer, MAXOPTLINE, fp) == NULL) break ;
+        while (ISSPACE(*here)) here++ ;
+        if (*here && *here == '=') {
+            if (sscanf(here+1, "%d", &car) != 1)
+                pm_error("bad option file line %s", buffer) ;
+        } else if (*here && *here != '%' && *here != '#') {
+            char str[NAMELENGTH] ;
+            integer i, n;
+     
+            checkchar() ;
+            if (sscanf(here, "%s%n", str, &n) != 1)
+                pm_error("bad option file line %s", buffer) ;
+            filename[car] = strdup(str);
+            if (filename[car] == NULL)
+                pm_error("out of memory allocating filename %s", str);
+            for (i = 1; i < 256; i<<=1) {
+                here += n;
+                if (sscanf(here, "%s%n", str, &n) != 1) break ;
+                if (strcmp(str, "*")) {
+                    charflags[car] |= i ;
+                    switch (i) {
+                    case XOFFSET:
+                        xoffset[car] = atoi(str) ;
+                        break ;
+                    case YOFFSET:
+                        yoffset[car] = atoi(str) ;
+                        break ;
+                    case HORZESC:
+                        horzesc[car] = atoi(str) ;
+                        break ;
+                    case VERTESC:
+                        vertesc[car] = atoi(str) ;
+                        break ;
+                    case TFMWIDTH:
+                        tfmindex[car] = add_tfmwidth(fixword(atof(str))) ;
+                        break ;
+                    case TFMHEIGHT:
+                        hgtindex[car] = add_tfmheight(fixword(atof(str))) ;
+                        break ;
+                    case TFMDEPTH:
+                        depindex[car] = add_tfmdepth(fixword(atof(str))) ;
+                        break ;
+                    case TFMITALIC:
+                        italindex[car] = add_tfmitalic(fixword(atof(str))) ;
+                        break ;
+                    }
+                }
+            }
+            car++ ;
+        }
+    }
+    pm_close(fp) ;
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+    integer i, hesc, vesc, xoff, yoff, tfwid, tfdep, tfhgt, tfital ;
+    byte flags ;
+    const char * const usage = "pkfile[.pk] tfmfile[.tfm] dpi "
+        "[-s designsize] [-p num param...]\n"
+        "[-C codingscheme ] [-F family] [-c num | <char>]...\n"
+        "<char> is:\n"
+        "[-W tfmwidth] [-H tfmheight] [-D tfmdepth] [-I ital_corr] "
+        "[-h horiz]\n"
+        "[-v vert] [-x xoffset] [-y yoffset] file\n"
+        "or:\n"
+        "-f optfile\n" ;
+
+    pbm_init(&argc, argv);
+    initialize_pk() ;
+   
+    if (--argc < 1) pm_usage(usage) ;
+    strcpy(pkname, *++argv) ;
+    pbmtopk_add_suffix(pkname, ".pk") ;
+   
+    if (--argc < 1) pm_usage(usage) ;
+    strcpy(tfmname, *++argv) ;
+    pbmtopk_add_suffix(tfmname, ".tfm") ;
+   
+    if (--argc < 1) pm_usage(usage) ;
+    resolution = atoi(*++argv) ;
+    if (resolution < 1 || resolution > 32767)
+        pm_error("unlikely resolution %d dpi", resolution);
+   
+    car = flags = hesc = vesc = xoff = yoff = tfwid = 0;
+    while (++argv, --argc) {
+        if (argv[0][0] == '-' && argv[0][1]) {
+            char c, *p;
+            c = argv[0][1] ;
+            if (argv[0][2]) p = *argv + 2 ;    /* set argument pointer */
+            else if (++argv, --argc) p = *argv ;
+            else pm_usage(usage) ;
+            switch (c) {
+            case 'C':
+                codingscheme = p;
+                break ;
+            case 'F':
+                familyname = p;
+                break ;
+            case 'c':
+                car = atoi(p) ;
+                break ;
+            case 's':
+                designsize = fixword(atof(p));
+                if (designsize < 1048576)
+                    pm_error("design size %f out of range", 
+                             unfixword(designsize));
+            case 'h':
+                hesc = atoi(p) ;
+                flags |= HORZESC ;
+                break ;
+            case 'v':
+                vesc = atoi(p) ;
+                flags |= VERTESC ;
+                break ;
+            case 'x':
+                xoff = atoi(p) ;
+                flags |= XOFFSET ;
+                break ;
+            case 'y':
+                yoff = atoi(p) ;
+                flags |= YOFFSET ;
+                break ;
+            case 'W':
+                tfwid = fixword(atof(p)) ;
+                flags |= TFMWIDTH ;
+                break ;
+            case 'H':
+                tfhgt = fixword(atof(p)) ;
+                flags |= TFMHEIGHT ;
+                break ;
+            case 'D':
+                tfdep = fixword(atof(p)) ;
+                flags |= TFMDEPTH ;
+                break ;
+            case 'I':
+                tfital = fixword(atof(p)) ;
+                flags |= TFMITALIC ;
+                break ;
+            case 'f':
+                optionfile(p) ;
+                break ;
+            case 'p':
+                numparam = atoi(p);
+                if (numparam < 1 || numparam > MAXPARAMS)
+                    pm_error("parameter count %d out of range", numparam);
+                for (i=0; i<numparam; i++)
+                    if (++argv,--argc)
+                        parameters[i] = fixword(atof(*argv)) ;
+                    else
+                        pm_error("not enough parameters (-p)");
+                break ;
+            default:
+                pm_usage(usage) ;
+            }
+        } else  {
+            checkchar() ;
+            if (flags & TFMWIDTH)
+                tfmindex[car] = add_tfmwidth(tfwid);
+            if (flags & TFMDEPTH)
+                depindex[car] = add_tfmdepth(tfdep);
+            if (flags & TFMHEIGHT)
+                hgtindex[car] = add_tfmheight(tfhgt);
+            if (flags & TFMITALIC)
+                italindex[car] = add_tfmitalic(tfital);
+            horzesc[car] = hesc ;
+            vertesc[car] = vesc ;
+            xoffset[car] = xoff ;
+            yoffset[car] = yoff ;
+            filename[car] = *argv ;
+            charflags[car] = flags ;
+            car++ ;
+            flags = 0;
+        }
+    }
+    hppp = ROUND((resolution<<16) / 72.27) ;
+    pkfile = pm_openw(pkname);
+    tfmfile = pm_openw(tfmname);
+    writepreamble() ;
+    for (car = 0 ; car < MAXPKCHAR ; car++)
+        if (filename[car]) {
+            readcharacter() ;
+            shipcharacter() ;
+        }
+    writepostamble() ;
+    writetfmfile() ;
+    pm_close(pkfile) ;
+    pm_close(tfmfile) ;
+
+    return 0;
+}
+
diff --git a/converter/pbm/pbmtoplot.c b/converter/pbm/pbmtoplot.c
new file mode 100644
index 00000000..8c9075b2
--- /dev/null
+++ b/converter/pbm/pbmtoplot.c
@@ -0,0 +1,76 @@
+/* pbmtoplot.c - read a portable bitmap and produce a UNIX-format plot file.
+**
+** Copyright (C) 1990 by Arthur David Olson.
+**
+** 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 "pbm.h"
+
+static void puttwo ARGS((int i));
+static void
+puttwo( i )
+    int i;
+    {
+    (void) putchar(i);
+    (void) putchar(i >> 8);
+    }
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    register bit** bits;
+    register int row, col, scol;
+    int	rows, cols;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[pbmfile]" );
+
+    ifp = (argc == 2) ? pm_openr( argv[1] ) : stdin;
+
+    bits = pbm_readpbm( ifp, &cols, &rows );
+
+    pm_close( ifp );
+
+    (void) putchar( 's' );
+    puttwo( 0 );
+    puttwo( 0 );
+    puttwo( rows - 1 );
+    puttwo( cols - 1 );
+    for ( row = 0; row < rows; ++row )
+	{
+	for ( col = 0; col < cols; ++col )
+	    {
+	    if ( bits[row][col] == PBM_WHITE )
+		continue;
+	    scol = col;
+	    while ( ++col < cols && bits[row][col] == PBM_BLACK )
+		; /* nothing */
+	    --col;
+	    if ( col == scol )
+		(void) putchar( 'p' );
+	    else
+		{
+		(void) putchar( 'l' );
+		puttwo( scol );
+		puttwo( rows - 1 - row );
+		}
+	    puttwo( col );
+	    puttwo( rows - 1 - row );
+	    }
+	}
+
+    exit( 0 );
+    }
diff --git a/converter/pbm/pbmtoppa/CREDITS b/converter/pbm/pbmtoppa/CREDITS
new file mode 100644
index 00000000..007d9a09
--- /dev/null
+++ b/converter/pbm/pbmtoppa/CREDITS
@@ -0,0 +1,12 @@
+CREDITS
+-------
+
+This project would not be where it is without the help of the following
+people:
+
+Ben Boule - first contacted me about the 720 series and helped with testing
+
+Jim Peterson - spent hours modifying the code for the 720 and adding lots of
+features, including all the configurability options.
+
+Kirk Reiten - helped with testing the 1000 series code
diff --git a/converter/pbm/pbmtoppa/INSTALL-MORE b/converter/pbm/pbmtoppa/INSTALL-MORE
new file mode 100644
index 00000000..2565c740
--- /dev/null
+++ b/converter/pbm/pbmtoppa/INSTALL-MORE
@@ -0,0 +1,187 @@
+Installation of ppa-0.8.5 with S.u.S.E. Linux
+(Special Installation with Hp820 and paper size A4)
+___________________________________________________
+
+
+0. Introduction
+
+This text describes how to use the package pbm2ppa written by Tim Norman
+with the S.u.S.E Linux System. This program allows the use of GDI (Winows
+only) printers with Linux. The program pbm2ppa is actually a converter
+between the two formats pbm (an output format from ghostscript) and the
+format understood by the HP printers 720, 820 and 1000. So anyway we have to
+use ghostscript to produce pbm (or faster: pbmraw) files. To print ascii
+files there is an extra step invoking enscript to convert the ascii to
+postscript files.  I rather constructed two new printer spoolers in printcap
+from scratch then using apsfilter: one for postscript files and another for
+ascii files. I welcome solutions in combination with the apsfilter script.
+The installation is quiet easy - after seven steps you should be ready to
+print postscript and ascii files, but it may take some time to adjust the
+constants properly, don't despair.
+
+0.1. Modifications
+
+This was modified on October 18, 1998 by Tim Norman to conform to the new
+A4 paper support in version 0.8.5.
+
+
+1. Installation of program package ppa-0.8.5
+
+Get the packate at http://www.rpi.edu/~normat/technical/ppa/ and compile it
+with
+
+# make 820
+
+or put in your printer number (720, 820 or 1000) (see also INSTALL and README
+file).
+
+
+2. To adjust the paper size to DIN A4, use the -s a4 option to pbm2ppa or
+change your pbm2ppa.conf file to read "papersize a4" (see step 5).
+
+
+3. You can now calibrate the printer with 
+
+For US size paper:
+# pbmtpg | pbm2ppa > /dev/lp1 ( as root )
+For A4 size paper:
+# pbmtpg -a4 | pbm2ppa -s a4 > /dev/lp1 ( as root )
+
+or you try first printing some sample files and calibrate in step 5.
+
+
+4. Now you can print (postscript) files with a shell script like this:
+
+Contents of print:
+
+cat $1 | gs -sDEVICE=pbmraw -q -dNOPAUSE -r600 -sOutputFile=- - | \
+pbm2ppa - - >/dev/lp1
+
+After changing the file modes (i.e. chmod 755 print) you are able to print
+a postscript file invoking the shell script print like:
+
+# print filename.ps
+
+
+To print ascii files just extend the script print with the use of enscript:
+
+Contents of printascii:
+
+enscript -2rj -p- $1 | \
+gs -sDEVICE=pbmraw -q -dNOPAUSE -r600 -sOutputFile=- - | \
+pbm2ppa - - >/dev/lp1
+
+
+Check the manpage for enscript to adjust the options to your flavour.
+Now you can also print ascii files with
+
+# printascii filename.ascii
+
+
+5. It may be possible that you have to recalibrate your printer (see 3.)
+
+Here follow the results from my calibration after printing ascii files with the
+shell script printascii (see 4.). The program pbm2ppa takes the arguments in the
+following order:
+
+1. shell arguments
+2. config file /etc/pbm2ppa.conf
+3. Compiled options from default.h
+
+So whenever you invoke pbm2ppa without arguments the program uses the options
+stored in the file /etc/pbm2ppa.conf, so I suggest to leave there a copy of
+this file.
+
+# Sample configuration file for the HP820 and DIN A4 paper size
+#
+# This file will be automatically read upon startup if it's placed in
+# /etc/pbm2ppa.conf
+#
+
+version  820
+
+papersize	a4
+
+xoff      0 # \ Adjust these for your printer.
+yoff    -600 # / (see CALIBRATE)
+
+# 1/4 inch margins all around (at 600 DPI)
+top       50
+bottom    50
+left      50
+right     50
+
+
+6. To integrate the converter into the Linux system we create two printer
+spooler in /etc/printcap. One to print postscript files and another to print
+plain ascii files.
+
+Contents of /etc/printcap:
+
+lp:\
+        :lp=/dev/lp1:\
+        :sd=/var/spool/lpd/lp:\
+        :lf=/var/spool/lpd/lp/log:\
+        :af=/var/spool/lpd/lp/acct:\
+        :if=/usr/local/bin/ps.if:\
+        :la:mx#0:\
+        :sh:sf:
+
+ascii:\
+        :lp=/dev/lp1:\
+        :sd=/var/spool/lpd/ascii:\
+        :lf=/var/spool/lpd/ascii/log:\
+        :af=/var/spool/lpd/ascii/acct:\
+        :if=/usr/local/bin/ascii.if:\
+        :la:mx#0:\
+        :sh:sf:
+
+
+Here follow some explanations (for more information consult the printcap
+manpage). We use the lp1 device, have two spool directories
+/var/spool/lpd/ascii and /var/spool/lpd/lp (better you create them now) a log
+file (lf) an accounting file (af), suppress form feeds (sf), suppress printing
+of burst page header (sh) and the maximum file size is unlimited (mx#0). To
+integrate the converter pbm2ppa into the system we use two input filters. Maybe
+you have a better solution in combination with apsfilter but until then try
+this way. Actually, the two input filter files are almost identical with the
+shell scripts print and printascii we created in step 4.
+
+File /usr/local/bin/ascii.if:
+
+#! /bin/sh
+enscript -2rj -p- | \
+gs -sDEVICE=pbmraw -q -dNOPAUSE -r600 -sOutputFile=- - | \
+/usr/local/bin/pbm2ppa - -
+
+
+File /usr/local/bin/ps.if:
+
+#! /bin/sh
+gs -sDEVICE=pbmraw -q -dNOPAUSE -r600 -sOutputFile=- - | \
+/usr/local/bin/pbm2ppa - -
+
+
+7. Place pbm2ppa in the directory /usr/local/bin. Now you are ready to print
+files with
+
+# lpr filename.ps
+
+and
+
+# lpr -P ascii filename.ascii
+
+like you are used to it.
+
+Enjoy
+
+
+19. May 1998
+
+Michael Buehlmann
+Badenerstrasse 285
+8003 Zuerich
+Switzerland
+
+mbuehlma@stud.ee.ethz.ch
+
diff --git a/converter/pbm/pbmtoppa/LICENSE b/converter/pbm/pbmtoppa/LICENSE
new file mode 100644
index 00000000..b8512125
--- /dev/null
+++ b/converter/pbm/pbmtoppa/LICENSE
@@ -0,0 +1,342 @@
+

+                    GNU GENERAL PUBLIC LICENSE

+                       Version 2, June 1991

+

+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.

+                          675 Mass Ave, Cambridge, MA 02139, USA

+ Everyone is permitted to copy and distribute verbatim copies

+ of this license document, but changing it is not allowed.

+

+                            Preamble

+

+  The licenses for most software are designed to take away your

+freedom to share and change it.  By contrast, the GNU General Public

+License is intended to guarantee your freedom to share and change free

+software--to make sure the software is free for all its users.  This

+General Public License applies to most of the Free Software

+Foundation's software and to any other program whose authors commit to

+using it.  (Some other Free Software Foundation software is covered by

+the GNU Library General Public License instead.)  You can apply it to

+your programs, too.

+

+  When we speak of free software, we are referring to freedom, not

+price.  Our General Public Licenses are designed to make sure that you

+have the freedom to distribute copies of free software (and charge for

+this service if you wish), that you receive source code or can get it

+if you want it, that you can change the software or use pieces of it

+in new free programs; and that you know you can do these things.

+

+  To protect your rights, we need to make restrictions that forbid

+anyone to deny you these rights or to ask you to surrender the rights.

+These restrictions translate to certain responsibilities for you if you

+distribute copies of the software, or if you modify it.

+

+  For example, if you distribute copies of such a program, whether

+gratis or for a fee, you must give the recipients all the rights that

+you have.  You must make sure that they, too, receive or can get the

+source code.  And you must show them these terms so they know their

+rights.

+

+  We protect your rights with two steps: (1) copyright the software, and

+(2) offer you this license which gives you legal permission to copy,

+distribute and/or modify the software.

+

+  Also, for each author's protection and ours, we want to make certain

+that everyone understands that there is no warranty for this free

+software.  If the software is modified by someone else and passed on, we

+want its recipients to know that what they have is not the original, so

+that any problems introduced by others will not reflect on the original

+authors' reputations.

+

+  Finally, any free program is threatened constantly by software

+patents.  We wish to avoid the danger that redistributors of a free

+program will individually obtain patent licenses, in effect making the

+program proprietary.  To prevent this, we have made it clear that any

+patent must be licensed for everyone's free use or not licensed at all.

+

+  The precise terms and conditions for copying, distribution and

+modification follow.

+

+                    GNU GENERAL PUBLIC LICENSE

+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

+

+  0. This License applies to any program or other work which contains

+a notice placed by the copyright holder saying it may be distributed

+under the terms of this General Public License.  The "Program", below,

+refers to any such program or work, and a "work based on the Program"

+means either the Program or any derivative work under copyright law:

+that is to say, a work containing the Program or a portion of it,

+either verbatim or with modifications and/or translated into another

+language.  (Hereinafter, translation is included without limitation in

+the term "modification".)  Each licensee is addressed as "you".

+

+Activities other than copying, distribution and modification are not

+covered by this License; they are outside its scope.  The act of

+running the Program is not restricted, and the output from the Program

+is covered only if its contents constitute a work based on the

+Program (independent of having been made by running the Program).

+Whether that is true depends on what the Program does.

+

+  1. You may copy and distribute verbatim copies of the Program's

+source code as you receive it, in any medium, provided that you

+conspicuously and appropriately publish on each copy an appropriate

+copyright notice and disclaimer of warranty; keep intact all the

+notices that refer to this License and to the absence of any warranty;

+and give any other recipients of the Program a copy of this License

+along with the Program.

+

+You may charge a fee for the physical act of transferring a copy, and

+you may at your option offer warranty protection in exchange for a fee.

+

+  2. You may modify your copy or copies of the Program or any portion

+of it, thus forming a work based on the Program, and copy and

+distribute such modifications or work under the terms of Section 1

+above, provided that you also meet all of these conditions:

+

+    a) You must cause the modified files to carry prominent notices

+    stating that you changed the files and the date of any change.

+

+    b) You must cause any work that you distribute or publish, that in

+    whole or in part contains or is derived from the Program or any

+    part thereof, to be licensed as a whole at no charge to all third

+    parties under the terms of this License.

+

+    c) If the modified program normally reads commands interactively

+    when run, you must cause it, when started running for such

+    interactive use in the most ordinary way, to print or display an

+    announcement including an appropriate copyright notice and a

+    notice that there is no warranty (or else, saying that you provide

+    a warranty) and that users may redistribute the program under

+    these conditions, and telling the user how to view a copy of this

+    License.  (Exception: if the Program itself is interactive but

+    does not normally print such an announcement, your work based on

+    the Program is not required to print an announcement.)

+

+These requirements apply to the modified work as a whole.  If

+identifiable sections of that work are not derived from the Program,

+and can be reasonably considered independent and separate works in

+themselves, then this License, and its terms, do not apply to those

+sections when you distribute them as separate works.  But when you

+distribute the same sections as part of a whole which is a work based

+on the Program, the distribution of the whole must be on the terms of

+this License, whose permissions for other licensees extend to the

+entire whole, and thus to each and every part regardless of who wrote it.

+

+Thus, it is not the intent of this section to claim rights or contest

+your rights to work written entirely by you; rather, the intent is to

+exercise the right to control the distribution of derivative or

+collective works based on the Program.

+

+In addition, mere aggregation of another work not based on the Program

+with the Program (or with a work based on the Program) on a volume of

+a storage or distribution medium does not bring the other work under

+the scope of this License.

+

+  3. You may copy and distribute the Program (or a work based on it,

+under Section 2) in object code or executable form under the terms of

+Sections 1 and 2 above provided that you also do one of the following:

+

+    a) Accompany it with the complete corresponding machine-readable

+    source code, which must be distributed under the terms of Sections

+    1 and 2 above on a medium customarily used for software interchange; or,

+

+    b) Accompany it with a written offer, valid for at least three

+    years, to give any third party, for a charge no more than your

+    cost of physically performing source distribution, a complete

+    machine-readable copy of the corresponding source code, to be

+    distributed under the terms of Sections 1 and 2 above on a medium

+    customarily used for software interchange; or,

+

+    c) Accompany it with the information you received as to the offer

+    to distribute corresponding source code.  (This alternative is

+    allowed only for noncommercial distribution and only if you

+    received the program in object code or executable form with such

+    an offer, in accord with Subsection b above.)

+

+The source code for a work means the preferred form of the work for

+making modifications to it.  For an executable work, complete source

+code means all the source code for all modules it contains, plus any

+associated interface definition files, plus the scripts used to

+control compilation and installation of the executable.  However, as a

+special exception, the source code distributed need not include

+anything that is normally distributed (in either source or binary

+form) with the major components (compiler, kernel, and so on) of the

+operating system on which the executable runs, unless that component

+itself accompanies the executable.

+

+If distribution of executable or object code is made by offering

+access to copy from a designated place, then offering equivalent

+access to copy the source code from the same place counts as

+distribution of the source code, even though third parties are not

+compelled to copy the source along with the object code.

+

+  4. You may not copy, modify, sublicense, or distribute the Program

+except as expressly provided under this License.  Any attempt

+otherwise to copy, modify, sublicense or distribute the Program is

+void, and will automatically terminate your rights under this License.

+However, parties who have received copies, or rights, from you under

+this License will not have their licenses terminated so long as such

+parties remain in full compliance.

+

+  5. You are not required to accept this License, since you have not

+signed it.  However, nothing else grants you permission to modify or

+distribute the Program or its derivative works.  These actions are

+prohibited by law if you do not accept this License.  Therefore, by

+modifying or distributing the Program (or any work based on the

+Program), you indicate your acceptance of this License to do so, and

+all its terms and conditions for copying, distributing or modifying

+the Program or works based on it.

+

+  6. Each time you redistribute the Program (or any work based on the

+Program), the recipient automatically receives a license from the

+original licensor to copy, distribute or modify the Program subject to

+these terms and conditions.  You may not impose any further

+restrictions on the recipients' exercise of the rights granted herein.

+You are not responsible for enforcing compliance by third parties to

+this License.

+

+  7. If, as a consequence of a court judgment or allegation of patent

+infringement or for any other reason (not limited to patent issues),

+conditions are imposed on you (whether by court order, agreement or

+otherwise) that contradict the conditions of this License, they do not

+excuse you from the conditions of this License.  If you cannot

+distribute so as to satisfy simultaneously your obligations under this

+License and any other pertinent obligations, then as a consequence you

+may not distribute the Program at all.  For example, if a patent

+license would not permit royalty-free redistribution of the Program by

+all those who receive copies directly or indirectly through you, then

+the only way you could satisfy both it and this License would be to

+refrain entirely from distribution of the Program.

+

+If any portion of this section is held invalid or unenforceable under

+any particular circumstance, the balance of the section is intended to

+apply and the section as a whole is intended to apply in other

+circumstances.

+

+It is not the purpose of this section to induce you to infringe any

+patents or other property right claims or to contest validity of any

+such claims; this section has the sole purpose of protecting the

+integrity of the free software distribution system, which is

+implemented by public license practices.  Many people have made

+generous contributions to the wide range of software distributed

+through that system in reliance on consistent application of that

+system; it is up to the author/donor to decide if he or she is willing

+to distribute software through any other system and a licensee cannot

+impose that choice.

+

+This section is intended to make thoroughly clear what is believed to

+be a consequence of the rest of this License.

+

+  8. If the distribution and/or use of the Program is restricted in

+certain countries either by patents or by copyrighted interfaces, the

+original copyright holder who places the Program under this License

+may add an explicit geographical distribution limitation excluding

+those countries, so that distribution is permitted only in or among

+countries not thus excluded.  In such case, this License incorporates

+the limitation as if written in the body of this License.

+

+  9. The Free Software Foundation may publish revised and/or new versions

+of the General Public License from time to time.  Such new versions will

+be similar in spirit to the present version, but may differ in detail to

+address new problems or concerns.

+

+Each version is given a distinguishing version number.  If the Program

+specifies a version number of this License which applies to it and "any

+later version", you have the option of following the terms and conditions

+either of that version or of any later version published by the Free

+Software Foundation.  If the Program does not specify a version number of

+this License, you may choose any version ever published by the Free Software

+Foundation.

+

+  10. If you wish to incorporate parts of the Program into other free

+programs whose distribution conditions are different, write to the author

+to ask for permission.  For software which is copyrighted by the Free

+Software Foundation, write to the Free Software Foundation; we sometimes

+make exceptions for this.  Our decision will be guided by the two goals

+of preserving the free status of all derivatives of our free software and

+of promoting the sharing and reuse of software generally.

+

+                            NO WARRANTY

+

+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY

+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN

+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES

+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED

+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF

+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS

+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE

+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,

+REPAIR OR CORRECTION.

+

+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING

+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR

+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,

+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING

+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED

+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY

+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER

+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE

+POSSIBILITY OF SUCH DAMAGES.

+

+                     END OF TERMS AND CONDITIONS

+

+        Appendix: How to Apply These Terms to Your New Programs

+

+  If you develop a new program, and you want it to be of the greatest

+possible use to the public, the best way to achieve this is to make it

+free software which everyone can redistribute and change under these terms.

+

+  To do so, attach the following notices to the program.  It is safest

+to attach them to the start of each source file to most effectively

+convey the exclusion of warranty; and each file should have at least

+the "copyright" line and a pointer to where the full notice is found.

+

+    <one line to give the program's name and a brief idea of what it does.>

+    Copyright (C) 19yy  <name of author>

+

+    This program is free software; you can redistribute it and/or modify

+    it under the terms of the GNU General Public License as published by

+    the Free Software Foundation; either version 2 of the License, or

+    (at your option) any later version.

+

+    This program is distributed in the hope that it will be useful,

+    but WITHOUT ANY WARRANTY; without even the implied warranty of

+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+    GNU General Public License for more details.

+

+    You should have received a copy of the GNU General Public License

+    along with this program; if not, write to the Free Software

+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

+

+Also add information on how to contact you by electronic and paper mail.

+

+If the program is interactive, make it output a short notice like this

+when it starts in an interactive mode:

+

+    Gnomovision version 69, Copyright (C) 19yy name of author

+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.

+    This is free software, and you are welcome to redistribute it

+    under certain conditions; type `show c' for details.

+

+The hypothetical commands `show w' and `show c' should show the appropriate

+parts of the General Public License.  Of course, the commands you use may

+be called something other than `show w' and `show c'; they could even be

+mouse-clicks or menu items--whatever suits your program.

+

+You should also get your employer (if you work as a programmer) or your

+school, if any, to sign a "copyright disclaimer" for the program, if

+necessary.  Here is a sample; alter the names:

+

+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program

+  `Gnomovision' (which makes passes at compilers) written by James Hacker.

+

+  <signature of Ty Coon>, 1 April 1989

+  Ty Coon, President of Vice

+

+This General Public License does not permit incorporating your program into

+proprietary programs.  If your program is a subroutine library, you may

+consider it more useful to permit linking proprietary applications with the

+library.  If this is what you want to do, use the GNU Library General

+Public License instead of this License.

+

+

diff --git a/converter/pbm/pbmtoppa/Makefile b/converter/pbm/pbmtoppa/Makefile
new file mode 100644
index 00000000..c4be08b7
--- /dev/null
+++ b/converter/pbm/pbmtoppa/Makefile
@@ -0,0 +1,25 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/pbm/pbmtoppa
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+all: pbmtoppa
+
+BINARIES = pbmtoppa
+
+MERGEBINARIES = $(BINARIES)
+
+OBJECTS = pbmtoppa.o ppa.o pbm.o cutswath.o
+MERGE_OBJECTS = pbmtoppa.o2 ppa.o pbm.o cutswath.o
+
+include $(SRCDIR)/Makefile.common
+
+pbmtoppa: $(OBJECTS) $(NETPBMLIB) $(LIBOPT)
+	$(LD) $(LDFLAGS) -o pbmtoppa $(OBJECTS) \
+	  -lm $(shell $(LIBOPT) $(NETPBMLIB)) $(LDLIBS) \
+	  $(RPATH) $(LADD)
+
diff --git a/converter/pbm/pbmtoppa/README.Netpbm b/converter/pbm/pbmtoppa/README.Netpbm
new file mode 100644
index 00000000..46c781e2
--- /dev/null
+++ b/converter/pbm/pbmtoppa/README.Netpbm
@@ -0,0 +1,30 @@
+Pbmtoppa was integrated into the Netpbm package in May 2000 by Bryan
+Henderson.  He took it from Tim Norton's pbm2ppa-0.8.6 package dated
+October 1998.
+
+Tim licenses the subject package to the public as described in the
+LICENSE file.
+
+The difference between what's in Netpbm and what was distributed by 
+Tim is:
+
+  - Tim called it 'pbm2ppa' Netpbm calls it 'pbmtoppa', to fit Netpbm
+    naming conventions.
+
+  - Tim's package included the program Pbmtpg, but the Netpbm directory
+    doesn't include that.  The program was integrated separately into
+    Netpbm as Pbmpage.
+
+  - Tim's package generated 3 different versions of Pbmtoppa, one for
+    each of the 720, 820 or 1000 printer models.  (They only differed
+    in their default parameters).  The netpbm version has only one
+    Pbmtoppa, and the (existing) -v option selects defaults for the 
+    specified printer model.
+
+  - Tim didn't have a man page.  His package had several other
+    documentation files, though, which are not in the Netpbm package
+    because the information is either specific to Tim's packaging or
+    the information in in the Netpbm man page.
+
+  - Jozsef Marak's extension to handle draft (300 dpi) printing is 
+    included.
diff --git a/converter/pbm/pbmtoppa/README.REDHAT b/converter/pbm/pbmtoppa/README.REDHAT
new file mode 100644
index 00000000..3586aea2
--- /dev/null
+++ b/converter/pbm/pbmtoppa/README.REDHAT
@@ -0,0 +1,29 @@
+RedHat users may find the following tip from Panayotis Vryonis <vrypan@hol.gr>
+helpful!
+
+Here is a tip to intergrate HP720C support in RedHat's printtool:
+
+Install pbm2ppa. Copy pbm2ppa to /usr/bin.
+Edit "printerdb" (in my system it is found in
+/usr/lib/rhs/rhs-printfilters )
+and append the following lines:
+----------------------Cut here
+-------------------------------------------
+StartEntry: DeskJet720C
+  GSDriver: pbm
+  Description: {HP DeskJet 720C}
+  About: { \
+           This driver supports the HP DeskJet 720C inkjet printer. \
+           It does does not support color printing. \
+           IMPORTANT! Insert \
+                "- | pbm2ppa -" \
+           in the "Extra GS Otions" field.\
+         }
+  Resolution: {600} {600} {}
+EndEntry
+--------------------------------------------------------------------------
+
+Now you can add an HP720C printer just like any other, using printtool.
+
+[Author's Note: The same should work for the 820 and 1000, but it hasn't
+been tested.  Also, use the pbmraw GSDriver if you have it; it's faster. ]
diff --git a/converter/pbm/pbmtoppa/cutswath.c b/converter/pbm/pbmtoppa/cutswath.c
new file mode 100644
index 00000000..0d44ce45
--- /dev/null
+++ b/converter/pbm/pbmtoppa/cutswath.c
@@ -0,0 +1,360 @@
+/* cutswath.c
+ * functions to cut a swath of a PBM file for PPA printers
+ * Copyright (c) 1998 Tim Norman.  See LICENSE for details.
+ * 3-15-98
+ *
+ * Mar 15, 1998  Jim Peterson  <jspeter@birch.ee.vt.edu>
+ *
+ *     Structured to accommodate both the HP820/1000, and HP720 series.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ppa.h"
+#include "ppapbm.h"
+#include "cutswath.h"
+
+extern int Width;
+extern int Height;
+extern int Pwidth;
+
+/* sweep_data->direction must be set already */
+/* Upon successful completion, sweep_data->image_data and
+   sweep_data->nozzle_data have been set to pointers which this routine
+   malloc()'d. */
+/* Upon successful completion, all members of *sweep_data have been set
+   except direction, vertical_pos, and next. */
+/* Returns: 0 if unsuccessful
+            1 if successful, but with non-printing result (end of page)
+            2 if successful, with printing result */
+int 
+cut_pbm_swath(pbm_stat* pbm,ppa_stat* prn,int maxlines,ppa_sweep_data* sweep_data)
+{
+  unsigned char *data, *ppa, *place, *maxplace;
+  int p_width, width8, p_width8;
+  int i, j, left, right, got_nonblank, numlines;
+  int horzpos, hp2;
+  int shift;
+  ppa_nozzle_data nozzles[2];
+
+  /* shift = 6 if DPI==300  */
+  /* shift = 12 if DPI==600 */ 
+  shift = ( prn->DPI == 300 ? 6:12 ) ;
+  
+  /* safeguard against the user freeing these */
+  sweep_data->image_data=NULL;
+  sweep_data->nozzle_data=NULL;
+
+  /* read the data from the input file */
+  width8 = (pbm->width + 7) / 8;
+ 
+/* 
+  fprintf(stderr,"cutswath(): width=%u\n",pbm->width);
+  fprintf(stderr,"cutswath(): height=%u\n",pbm->height);
+*/
+
+  if ((data=malloc(width8*maxlines)) == NULL)
+  {
+    fprintf(stderr,"cutswath(): could not malloc data storage\n");
+    return 0;
+  }
+
+  /* ignore lines that are above the upper margin */
+  while(pbm->current_line < prn->top_margin)
+    if(!pbm_readline(pbm,data))
+    {
+      fprintf(stderr,"cutswath(): A-could not read top margin\n");
+      free(data);
+      return 0;
+    }
+
+  /* eat all lines that are below the lower margin */
+  if(pbm->current_line >= Height - prn->bottom_margin)
+  {
+    while(pbm->current_line < pbm->height)
+      if(!pbm_readline(pbm,data))
+      {
+	fprintf(stderr,"cutswath(): could not clear bottom margin\n");
+	free(data);
+	return 0;
+      }
+    free(data);
+    return 1;
+  }
+
+  left = Pwidth-prn->right_margin/8;
+  right = prn->left_margin/8;
+
+  /* eat all beginning blank lines and then up to maxlines or lower margin */
+  got_nonblank=numlines=0;
+  while( (pbm->current_line < Height-prn->bottom_margin) &&
+	 (numlines < maxlines) )
+  {
+    if(!pbm_readline(pbm,data+width8*numlines))
+    {
+      fprintf(stderr,"cutswath(): B-could not read next line\n");
+      free(data);
+      return 0;
+    }
+    if(!got_nonblank)
+      for(j=prn->left_margin/8; j<left; j++)
+	if(data[j])
+	{
+	  left = j;
+	  got_nonblank=1;
+	  break;
+	}
+    if(got_nonblank)
+      {
+	int newleft = left, newright = right;
+
+	/* find left-most nonblank */
+	for (i = prn->left_margin/8; i < left; i++)
+	  if (data[width8*numlines+i])
+	    {
+	      newleft = i;
+	      break;
+	    }
+	/* find right-most nonblank */
+	for (i = Pwidth-prn->right_margin/8-1; i >= right; i--)
+	  if (data[width8*numlines+i])
+	    {
+	      newright = i;
+	      break;
+	    }
+	numlines++;
+
+	if (newright < newleft)
+	  {
+	    fprintf (stderr, "Ack! newleft=%d, newright=%d, left=%d, right=%d\n",
+		     newleft, newright, left, right);
+	    free (data);
+	    return 0;
+	  }
+
+	/* if the next line might push us over the buffer size, stop here! */
+	/* ignore this test for the 720 right now.  Will add better */
+	/* size-guessing for compressed data in the near future! */
+	if (numlines % 2 == 1 && prn->version != HP720)
+	  {
+	    int l = newleft, r = newright, w;
+	    
+	    l--;
+	    r+=2;
+	    l*=8;
+	    r*=8;
+	    w = r-l;
+	    w = (w+7)/8;
+	    
+	    if ((w+2*shift)*numlines > prn->bufsize)
+	      {
+		numlines--;
+		pbm_unreadline (pbm, data+width8*numlines);
+		break;
+	      }
+	    else
+	      {
+		left = newleft;
+		right = newright;
+	      }
+	  }
+	else
+	  {
+	    left = newleft;
+	    right = newright;
+	  }
+      }
+  }
+
+  if(!got_nonblank)
+  {
+    /* eat all lines that are below the lower margin */
+    if(pbm->current_line >= Height - prn->bottom_margin)
+    {
+      while(pbm->current_line < pbm->height)
+	if(!pbm_readline(pbm,data))
+	{
+	  fprintf(stderr,"cutswath(): could not clear bottom margin\n");
+	  free(data);
+	  return 0;
+	}
+      free(data);
+      return 1;
+    }
+    free(data);
+    return 0; /* error, since didn't get to lower margin, yet blank */
+  }
+
+  /* make sure numlines is even and >= 2 (b/c we have to pass the printer 
+     HALF of the number of pins used */
+  if (numlines == 1)
+    {
+      /* there's no way that we only have 1 line and not enough memory, so
+	 we're safe to increase numlines here.  Also, the bottom margin should
+	 be > 0 so we have some lines to read */
+      if(!pbm_readline(pbm,data+width8*numlines))
+	{
+	  fprintf(stderr,"cutswath(): C-could not read next line\n");
+	  free(data);
+	  return 0;
+	}
+      numlines++;
+    }
+  if (numlines % 2 == 1)
+    {
+      /* decrease instead of increasing so we don't max out the buffer */
+      numlines--;
+      pbm_unreadline (pbm, data+width8*numlines);
+    }
+
+  /* calculate vertical position */
+  sweep_data->vertical_pos = pbm->current_line;
+
+  /* change sweep params */
+  left--;
+  right+=2;
+  left *= 8;
+  right *= 8;
+
+  /* construct the sweep data */
+  p_width = right - left;
+  p_width8 = (p_width + 7) / 8;
+
+  if ((ppa = malloc ((p_width8+2*shift) * numlines)) == NULL)
+    {
+      fprintf(stderr,"cutswath(): could not malloc ppa storage\n");
+      free (data);
+      return 0;
+    }
+
+  place = ppa;
+
+  /* place 0's in the first 12 columns */
+  memset (place, 0, numlines/2 * shift);
+  place += numlines/2 * shift;
+
+
+  if(sweep_data->direction == right_to_left)  /* right-to-left */
+  {
+    for (i = p_width8+shift-1; i >= 0; i--)
+    {
+      if (i >= shift)
+      {
+	for (j = 0; j < numlines/2; j++)
+	  *place++ = data[j*2*width8 + i + left/8-shift];
+      }
+      else
+      {
+	memset (place, 0, numlines/2);
+	place += numlines/2;
+      }
+
+      if (i < p_width8)
+      {
+	for (j = 0; j < numlines/2; j++)
+	  *place++ = data[(j*2+1)*width8 + i + left/8];
+      }
+      else
+      {
+	memset (place, 0, numlines/2);
+	place += numlines/2;
+      }
+    }
+  }
+  else /* sweep_data->direction == left_to_right */
+  {
+    for (i = 0; i < p_width8+shift; i++)
+    {
+      if (i < p_width8)
+      {
+	for (j = 0; j < numlines/2; j++)
+	  *place++ = data[(j*2+1)*width8 + i + left/8];
+      }
+      else
+      {
+	memset (place, 0, numlines/2);
+	place += numlines/2;
+      }
+
+      if (i >= shift)
+      {
+	for (j = 0; j < numlines/2; j++)
+	  *place++ = data[j*2*width8 + i + left/8 - shift];
+      }
+      else
+      {
+	memset (place, 0, numlines/2);
+	place += numlines/2;
+      }
+    }
+  }
+
+  /* done with data */
+  free(data);
+
+  /* place 0's in the last 12 columns */
+  memset (place, 0, numlines/2 * shift);
+  place += numlines/2 * shift;
+  maxplace = place;
+
+  /* create sweep data */
+  sweep_data->image_data = ppa;
+  sweep_data->data_size = maxplace-ppa;
+  sweep_data->in_color = False;
+
+  /*
+  horzpos = left*600/prn->DPI + (sweep_data->direction==left_to_right ? 0*600/prn->DPI : 0);
+  */
+  horzpos = left * 600/prn->DPI;
+
+  hp2 = horzpos + ( p_width8 + 2*shift )*8 * 600/prn->DPI;
+  
+ 
+  sweep_data->left_margin = horzpos;
+  sweep_data->right_margin = hp2 + prn->marg_diff;
+
+  
+  for (i = 0; i < 2; i++)
+  {
+    nozzles[i].DPI = prn->DPI;
+        
+    nozzles[i].pins_used_d2 = numlines/2;
+    nozzles[i].unused_pins_p1 = 301-numlines;
+    nozzles[i].first_pin = 1;
+    if (i == 0)
+    {
+      nozzles[i].left_margin = horzpos + prn->marg_diff;
+      nozzles[i].right_margin = hp2 + prn->marg_diff;
+      if(sweep_data->direction == right_to_left)
+       /* 0 */
+	nozzles[i].nozzle_delay=prn->right_to_left_delay[0];
+      else
+       /* 6 */
+	nozzles[i].nozzle_delay=prn->left_to_right_delay[0];
+    }
+    else
+    {
+      nozzles[i].left_margin = horzpos;
+      nozzles[i].right_margin = hp2;
+      if(sweep_data->direction == right_to_left)
+       /* 2 */
+	nozzles[i].nozzle_delay=prn->right_to_left_delay[1];
+      else
+       /* 0 */
+	nozzles[i].nozzle_delay=prn->left_to_right_delay[1];
+
+    }
+  }
+
+  sweep_data->nozzle_data_size = 2;
+  sweep_data->nozzle_data = malloc(sizeof(nozzles));
+  if(sweep_data->nozzle_data == NULL)
+    return 0;
+  memcpy(sweep_data->nozzle_data,nozzles,sizeof(nozzles));
+
+  return 2;
+}
+
+
diff --git a/converter/pbm/pbmtoppa/cutswath.h b/converter/pbm/pbmtoppa/cutswath.h
new file mode 100644
index 00000000..430558de
--- /dev/null
+++ b/converter/pbm/pbmtoppa/cutswath.h
@@ -0,0 +1,4 @@
+int 
+cut_pbm_swath(pbm_stat* pbm,ppa_stat* prn,int maxlines,
+              ppa_sweep_data* sweep_data);
+
diff --git a/converter/pbm/pbmtoppa/defaults.h b/converter/pbm/pbmtoppa/defaults.h
new file mode 100644
index 00000000..481b9585
--- /dev/null
+++ b/converter/pbm/pbmtoppa/defaults.h
@@ -0,0 +1,65 @@
+/* defaults.h
+ * Default printer values.  Edit these and recompile if so desired.
+ * [Note: a /etc/pbm2ppa.conf file will override these]
+ */
+#ifndef _DEFAULTS_H
+#define _DEFAULTS_H
+
+/* Refer to CALIBRATION file about these settings */
+
+#define HP1000_PRINTER        ( HP1000 )
+
+#define HP1000_MARG_DIFF      (  0x62 )
+#define HP1000_BUFSIZE        ( 100*1024 )
+
+#define HP1000_X_OFFSET       (   100 )
+#define HP1000_Y_OFFSET       (  -650 )
+
+#define HP1000_TOP_MARGIN     (   150 )
+#define HP1000_LEFT_MARGIN    (   150 )
+#define HP1000_RIGHT_MARGIN   (   150 )
+#define HP1000_BOTTOM_MARGIN  (   150 )
+
+
+#define HP720_PRINTER        ( HP720 )
+
+#define HP720_MARG_DIFF      (     2 )
+#define HP720_BUFSIZE        ( 200*1024 )
+
+#define HP720_X_OFFSET       (   169 )
+#define HP720_Y_OFFSET       (  -569 )
+
+#define HP720_TOP_MARGIN     (   150 )
+#define HP720_LEFT_MARGIN    (   150 )
+#define HP720_RIGHT_MARGIN   (   150 )
+#define HP720_BOTTOM_MARGIN  (   150 )
+
+
+#define HP820_PRINTER        ( HP820 )
+
+#define HP820_MARG_DIFF      (  0x62 )
+#define HP820_BUFSIZE        ( 100*1024 )
+
+#define HP820_X_OFFSET       (    75 )
+#define HP820_Y_OFFSET       (  -500 )
+
+#define HP820_TOP_MARGIN     (    80 )
+#define HP820_LEFT_MARGIN    (    80 )
+#define HP820_RIGHT_MARGIN   (    80 )
+#define HP820_BOTTOM_MARGIN  (   150 )
+
+
+
+#define DEFAULT_PRINTER        HP1000_PRINTER
+
+#define DEFAULT_X_OFFSET       HP1000_X_OFFSET
+#define DEFAULT_Y_OFFSET       HP1000_Y_OFFSET
+
+#define DEFAULT_TOP_MARGIN     HP1000_TOP_MARGIN
+#define DEFAULT_LEFT_MARGIN    HP1000_LEFT_MARGIN
+#define DEFAULT_RIGHT_MARGIN   HP1000_RIGHT_MARGIN
+#define DEFAULT_BOTTOM_MARGIN  HP1000_BOTTOM_MARGIN
+
+#define DEFAULT_DPI ( 600 )
+
+#endif
diff --git a/converter/pbm/pbmtoppa/pbm.c b/converter/pbm/pbmtoppa/pbm.c
new file mode 100644
index 00000000..5c9798f2
--- /dev/null
+++ b/converter/pbm/pbmtoppa/pbm.c
@@ -0,0 +1,135 @@
+/* pbm.c
+ * code for reading the header of an ASCII PBM file
+ * Copyright (c) 1998 Tim Norman.  See LICENSE for details
+ * 2-25-98
+ *
+ * Mar 18, 1998  Jim Peterson  <jspeter@birch.ee.vt.edu>
+ *
+ *     Restructured to encapsulate more of the PBM handling.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ppapbm.h"
+
+int make_pbm_stat(pbm_stat* pbm,FILE* fptr)
+{
+  char line[1024];
+
+  pbm->fptr=fptr;
+  pbm->version=none;
+  pbm->current_line=0;
+  pbm->unread = 0;
+
+  if (fgets (line, 1024, fptr) == NULL)
+    return 0;
+  line[strlen(line)-1] = 0;
+
+  if(!strcmp(line,"P1")) pbm->version=P1;
+  if(!strcmp(line,"P4")) pbm->version=P4;
+  if(pbm->version == none)
+  {
+    fprintf(stderr,"pbm_readheader(): unknown PBM magic '%s'\n",line);
+    return 0;
+  }
+
+  do
+    if (fgets (line, 1024, fptr) == NULL)
+      return 0;
+  while (line[0] == '#');
+
+  if (2 != sscanf (line, "%d %d", &pbm->width, &pbm->height))
+    return 0;
+
+  return 1;
+}
+
+static int getbytes(FILE *fptr,int width,unsigned char* data)
+{
+  unsigned char mask,acc,*place;
+  int num;
+
+  if(!width) return 0;
+  for(mask=0x80, acc=0, num=0, place=data; num<width; )
+  {
+    switch(getc(fptr))
+    {
+    case EOF:      
+      return 0;
+    case '1':
+      acc|=mask;
+      /* fall through */
+    case '0':
+      mask>>=1;
+      num++;
+      if(!mask) /* if(num%8 == 0) */
+      {
+	*place++ = acc;
+	acc=0;
+	mask=0x80;
+      }
+    }
+  }
+  if(width%8)
+    *place=acc;
+  return 1;
+}
+
+/* Reads a single line into data which must be at least (pbm->width+7)/8
+   bytes of storage */
+int pbm_readline(pbm_stat* pbm,unsigned char* data)
+{
+  int tmp,tmp2;
+
+  if(pbm->current_line >= pbm->height) return 0;
+
+  if (pbm->unread)
+    {
+      memcpy (data, pbm->revdata, (pbm->width+7)/8);
+      pbm->current_line++;
+      pbm->unread = 0;
+      free (pbm->revdata);
+      return 1;
+    }
+
+  switch(pbm->version)
+  {
+  case P1:
+    if(getbytes(pbm->fptr,pbm->width,data))
+    {
+      pbm->current_line++;
+      return 1;
+    }
+    return 0;
+
+  case P4:
+    tmp=(pbm->width+7)/8;
+    tmp2=fread(data,1,tmp,pbm->fptr);
+    if(tmp2 == tmp)
+    {
+      pbm->current_line++;
+      return 1;
+    }
+    fprintf(stderr,"pbm_readline(): error reading line data (%d)\n",tmp2);
+    return 0;
+
+  default:
+    fprintf(stderr,"pbm_readline(): unknown PBM version\n");
+    return 0;
+  }
+}
+
+/* push a line back into the buffer; we read too much! */
+void pbm_unreadline (pbm_stat *pbm, void *data)
+{
+  /* can only store one line in the unread buffer */
+  if (pbm->unread)
+    return;
+
+  pbm->unread = 1;
+  pbm->revdata = malloc ((pbm->width+7)/8);
+  memcpy (pbm->revdata, data, (pbm->width+7)/8);
+  pbm->current_line--;
+}
diff --git a/converter/pbm/pbmtoppa/pbmtoppa.c b/converter/pbm/pbmtoppa/pbmtoppa.c
new file mode 100644
index 00000000..85a98529
--- /dev/null
+++ b/converter/pbm/pbmtoppa/pbmtoppa.c
@@ -0,0 +1,449 @@
+/* pbmtoppa.c
+ * program to print a 600 dpi PBM file on the HP DJ820Cse or the HP720 series.
+ * Copyright (c) 1998 Tim Norman.  See LICENSE for details
+ * 2-24-98
+ */
+
+#define _BSD_SOURCE
+    /* This makes sure strcasecmp() is in string.h */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "pbm.h"
+#include "ppa.h"
+#include "ppapbm.h"
+#include "cutswath.h"
+
+#include "defaults.h"
+
+/* Paper sizes in 600ths of an inch. */
+
+/* US is 8.5 in by 11 in */
+
+#define USWIDTH  (5100)
+#define USHEIGHT (6600)
+
+/* A4 is 210 mm by 297 mm == 8.27 in by 11.69 in */
+
+#define A4WIDTH  (4960)
+#define A4HEIGHT (7016)
+
+int Width;      /* width and height in 600ths of an inch */
+int Height;
+int Pwidth;     /* width in bytes */
+
+#define MAX_LINES 300
+
+ppa_stat printer;
+
+static int 
+print_pbm(FILE * const in) {
+
+    char line[1024];
+    pbm_stat pbm;
+    int done_page, numpages = 0;
+    ppa_sweep_data sweeps[2];
+    int current_sweep, previous_sweep;
+
+    ppa_init_job(&printer);
+
+    while(make_pbm_stat(&pbm, in)) {
+        if (pbm.width != Width || pbm.height != Height) 
+            pm_error("print_pbm(): Input image is not the size "
+                    "of a page for Page %d.  "
+                    "The input is %dW x %dH, "
+                     "while a page is %dW x %dH pixels.\n"
+                    "Page size is controlled by options and the configuration "
+                    "file.",
+                     numpages+1, pbm.width, pbm.height, Width, Height);
+
+        ppa_init_page(&printer);
+        ppa_load_page(&printer);
+
+        sweeps[0].direction = right_to_left;
+        sweeps[0].next=&sweeps[1];
+        sweeps[1].direction = left_to_right;
+        sweeps[1].next=&sweeps[0];
+
+        current_sweep=0;
+        previous_sweep=-1;
+
+        done_page=0;
+        while(!done_page) {
+            int rc;
+            rc = cut_pbm_swath(&pbm, &printer, MAX_LINES,
+                               &sweeps[current_sweep]);
+            switch(rc) {
+            case 0:
+                pm_error("print_pbm(): cut_pbm_swath() failed.");
+                break;
+            case 1:
+                done_page=1;
+                break;
+            case 2:
+                if (previous_sweep >= 0) {
+                    ppa_print_sweep(&printer, &sweeps[previous_sweep]);
+                    free(sweeps[previous_sweep].image_data);
+                    free(sweeps[previous_sweep].nozzle_data);
+                }
+                previous_sweep=current_sweep;
+                current_sweep= current_sweep==0 ? 1 : 0;
+                break;
+            default:
+                pm_error("print_pbm(): unknown return code from "
+                         "cut_pbm_swath()");
+            }
+        }
+        if (previous_sweep >= 0) {
+            sweeps[previous_sweep].next=NULL;
+            ppa_print_sweep(&printer,&sweeps[previous_sweep]);
+        }
+
+        free(sweeps[0].image_data);
+        free(sweeps[0].nozzle_data);
+        free(sweeps[1].image_data);
+        free(sweeps[1].nozzle_data);
+
+        ppa_eject_page(&printer);
+
+        /* eat any remaining whitespace */
+        if(pbm.version==P1)
+            fgets (line, 1024, in);
+
+        ++numpages;
+    }
+
+    if (numpages == 0)
+        pm_error("No pages printed!");
+
+    ppa_end_print(&printer);
+
+    fclose (pbm.fptr);
+    fclose (printer.fptr);
+
+    return 0;
+}
+
+
+
+static void 
+set_printer_specific_defaults()
+{
+    switch(printer.version)
+    {
+    case HP720:
+        printer.marg_diff     = HP720_MARG_DIFF;
+        printer.bufsize       = HP720_BUFSIZE;
+
+        printer.x_offset      = HP720_X_OFFSET;
+        printer.y_offset      = HP720_Y_OFFSET;
+        printer.top_margin    = HP720_TOP_MARGIN;
+        printer.left_margin   = HP720_LEFT_MARGIN;
+        printer.right_margin  = HP720_RIGHT_MARGIN;
+        printer.bottom_margin = HP720_BOTTOM_MARGIN;
+        break;
+    case HP820:
+        printer.marg_diff     = HP820_MARG_DIFF;
+        printer.bufsize       = HP820_BUFSIZE;
+
+        printer.x_offset      = HP820_X_OFFSET;
+        printer.y_offset      = HP820_Y_OFFSET;
+        printer.top_margin    = HP820_TOP_MARGIN;
+        printer.left_margin   = HP820_LEFT_MARGIN;
+        printer.right_margin  = HP820_RIGHT_MARGIN;
+        printer.bottom_margin = HP820_BOTTOM_MARGIN;
+        break;
+    case HP1000:
+        printer.marg_diff     = HP1000_MARG_DIFF;
+        printer.bufsize       = HP1000_BUFSIZE;
+
+        printer.x_offset      = HP1000_X_OFFSET;
+        printer.y_offset      = HP1000_Y_OFFSET;
+        printer.top_margin    = HP1000_TOP_MARGIN;
+        printer.left_margin   = HP1000_LEFT_MARGIN;
+        printer.right_margin  = HP1000_RIGHT_MARGIN;
+        printer.bottom_margin = HP1000_BOTTOM_MARGIN;
+        break;
+    default:
+        pm_error("set_printer_defaults(): unknown printer version");
+    }
+}
+
+
+
+static void 
+show_usage(const char* const prog)
+{
+    printf("usage: %s [ options ] [ <infile> [ <outfile> ] ]\n\n",prog);
+    printf("  Prints a pbm- or pbmraw-format <infile> to HP720/820/1000-format <outfile>.\n\n");
+    printf("    -v <version>    printer version (720, 820, or 1000)\n");
+    printf("    -x <xoff>       vertical offset adjustment in 1\"/600\n");
+    printf("    -y <yoff>       horizontal offset adjustment in 1\"/600\n");
+    printf("    -t <topmarg>    top margin in 1\"/600    (default: 150 = 0.25\")\n");
+    printf("    -l <leftmarg>   left margin in 1\"/600   (default: 150 = 0.25\")\n");
+    printf("    -r <rightmarg>  right margin in 1\"/600  (default: 150 = 0.25\")\n");
+    printf("    -b <botmarg>    bottom margin in 1\"/600 (default: 150 = 0.25\")\n");
+    printf("    -s <paper>      paper size (us, a4, default: us)\n");
+    printf("    -f <cfgfile>    read <cfgfile> as parameters\n\n");
+    printf("  The -x and -y options accumulate.  The -v option resets the horizontal and\n");
+    printf("  vertical adjustments to an internal default.  <infile> and <outfile> default\n");
+    printf("  to stdin and stdout.  '-' is a synonym for stdin and stdout.\n\n");
+    printf("  Configuration files specified with the '-f' parameter have the following\n  format:\n\n");
+    printf("    # Comment\n");
+    printf("    <key1> <value1>\n");
+    printf("    <key2> <value2>\n");
+    printf("    [etc.]\n\n");
+    printf("  Valid keys are 'version', 'xoffset', 'yoffset', 'topmargin', 'leftmargin',\n");
+    printf("  'rightmargin', 'bottommargin', 'papersize', or any non-null truncated\n");
+    printf("  version of these words.  Valid values are the same as with the corresponding\n");
+    printf("  command-line parameters.  Parameters in the configuration file act as though\n");
+    printf("  the corresponding parameters were substituted, in order, for the '-f'\n");
+    printf("  parameter which specified the file.\n\n");
+    printf("  The file /etc/pbmtoppa.conf, if it exists, is processed as a configuration\n");
+    printf("  file before any command-line parameters are processed.\n\n");
+}
+
+
+
+static void 
+parm_version(char* arg)
+{
+    if(!strcasecmp(arg,"hp720") || !strcmp(arg,"720"))
+        printer.version=HP720;
+    else if(!strcasecmp(arg,"hp820") || !strcmp(arg,"820"))
+        printer.version=HP820;
+    else if(!strcasecmp(arg,"hp1000") || !strcmp(arg,"1000"))
+        printer.version=HP1000;
+    else
+        pm_error("parm_version(): unknown printer version '%s'",arg);
+    set_printer_specific_defaults();
+}
+
+
+
+static void 
+parm_iversion(int arg)
+{
+    switch(arg)
+    {
+    case 720:
+        printer.version=HP720;
+        break;
+    case 820:
+        printer.version=HP820;
+        break;
+    case 1000:
+        printer.version=HP1000;
+        break;
+    default:
+        pm_error("parm_iversion(): unknown printer version '%d'", arg);
+    }
+    set_printer_specific_defaults();
+}
+
+
+
+static void 
+dump_config()
+{
+    printf("version:  ");
+    switch(printer.version)
+    {
+    case HP710:  printf("HP710\n");  break;
+    case HP720:  printf("HP720\n");  break;
+    case HP820:  printf("HP820\n");  break;
+    case HP1000: printf("HP1000\n"); break;
+    }
+    printf("x-offset: %d\ny-offset: %d\nmargins:\n top:    %d\n"
+           " left:   %d\n right:  %d\n bottom: %d\n",printer.x_offset,
+           printer.y_offset,printer.top_margin,printer.left_margin,
+           printer.right_margin,printer.bottom_margin);
+    exit(0);
+}
+
+
+
+static void 
+read_config_file(const char* const fname)
+{
+    FILE* cfgfile=fopen(fname,"r");
+    char line[1024],key[14],buf[10];
+    int len,value,lineno=1;
+
+    if(!cfgfile)
+        pm_error("read_config_file(): couldn't open file '%s'", fname);
+
+    while(fgets(line,1024,cfgfile))
+    {
+        if(strchr(line,'#'))
+            *strchr(line,'#')=0;
+        switch(sscanf(line,"%13s%9s",key,buf))
+        {
+        case 2:
+            value=atoi(buf);
+            len=strlen(key);
+            if(!strncmp(key,"version",len))
+                parm_iversion(value);
+            else if(!strncmp(key,"xoffset",len))
+                printer.x_offset=value;
+            else if(!strncmp(key,"yoffset",len))
+                printer.y_offset=value;
+            else if(!strncmp(key,"topmargin",len))
+                printer.top_margin=value;
+            else if(!strncmp(key,"leftmargin",len))
+                printer.left_margin=value;
+            else if(!strncmp(key,"rightmargin",len))
+                printer.right_margin=value;
+            else if(!strncmp(key,"bottommargin",len))
+                printer.bottom_margin=value;
+            else if(!strncmp(key,"papersize",len))
+            {
+                if(!strcmp(buf,"us"))
+                {
+                    Width = USWIDTH;
+                    Height = USHEIGHT;
+                }
+                else if(!strcmp(buf,"a4"))
+                {
+                    Width = A4WIDTH;
+                    Height = A4HEIGHT;
+                }
+                else
+                    pm_error("read_config_file(): unknown paper size %s", buf);
+            }
+            else if(!strcmp(key,"dump"))
+                dump_config();
+            else 
+                pm_error("read_config_file(): unrecognized parameter '%s' "
+                         "(line %d)", key, lineno);
+        case EOF:
+        case 0: 
+            break;
+        default:
+            pm_error("read_config_file(): error parsing config file "
+                     "(line %d)", lineno);
+        }
+        lineno++;
+    }
+
+    if(feof(cfgfile))
+    {
+        fclose(cfgfile);
+        return;
+    }
+
+    pm_error("read_config_file(): error parsing config file");
+}
+
+
+
+const char* const defaultcfgfile="/etc/pbmtoppa.conf";
+
+
+
+int 
+main(int argc, char *argv[]) {
+
+    int argn;
+    int got_in=0, got_out=0, do_continue=1;
+    FILE *in=stdin, *out=stdout;
+    struct stat tmpstat;
+
+    pbm_init(&argc, argv);
+
+    printer.version       = DEFAULT_PRINTER;
+    printer.x_offset      = DEFAULT_X_OFFSET;
+    printer.y_offset      = DEFAULT_Y_OFFSET;
+    printer.top_margin    = DEFAULT_TOP_MARGIN;
+    printer.left_margin   = DEFAULT_LEFT_MARGIN;
+    printer.right_margin  = DEFAULT_RIGHT_MARGIN;
+    printer.bottom_margin = DEFAULT_BOTTOM_MARGIN;
+    printer.DPI           = DEFAULT_DPI;
+    Width = USWIDTH;
+    Height = USHEIGHT;
+    set_printer_specific_defaults();
+
+    if(!stat(defaultcfgfile,&tmpstat))
+        read_config_file(defaultcfgfile);
+
+    for(argn=1; argn<argc; argn++)
+    {
+        if(!strcmp(argv[argn],"-h"))
+        {
+            show_usage(*argv);
+            return 0;
+        }
+        else if(!strcmp(argv[argn],"-d"))
+            dump_config();
+        else if(argn+1<argc)
+        {
+            do_continue=1;
+            if(!strcmp(argv[argn],"-v"))
+                parm_version(argv[++argn]);
+            else if(!strcmp(argv[argn],"-x"))
+                printer.x_offset+=atoi(argv[++argn]);
+            else if(!strcmp(argv[argn],"-y"))
+                printer.y_offset+=atoi(argv[++argn]);
+            else if(!strcmp(argv[argn],"-t"))
+                printer.top_margin=atoi(argv[++argn]);
+            else if(!strcmp(argv[argn],"-l"))
+                printer.left_margin=atoi(argv[++argn]);
+            else if(!strcmp(argv[argn],"-r"))
+                printer.right_margin=atoi(argv[++argn]);
+            else if(!strcmp(argv[argn],"-b"))
+                printer.bottom_margin=atoi(argv[++argn]);
+            else if(!strcmp(argv[argn],"-d"))
+                printer.DPI=atoi(argv[++argn]);
+            else if(!strcmp(argv[argn],"-s"))
+            {
+                argn++;
+                if(!strcmp(argv[argn],"us"))
+                {
+                    Width = USWIDTH;
+                    Height = USHEIGHT;
+                }
+                else if(!strcmp(argv[argn],"a4"))
+                {
+                    Width = A4WIDTH;
+                    Height = A4HEIGHT;
+                }
+                else
+                    pm_error("unknown paper size %s",argv[argn]);
+            }
+            else if(!strcmp(argv[argn],"-f"))
+                read_config_file(argv[++argn]);
+            else do_continue=0;
+            if(do_continue) continue;
+        }
+
+        if(!got_in)
+        {
+            if (strcmp (argv[argn], "-") == 0)
+                in = stdin;
+            else if ((in = fopen (argv[argn], "rb")) == NULL)
+                pm_error("main(): couldn't open file '%s'", 
+                         argv[argn]);
+            got_in=1;
+        }
+        else if(!got_out)
+        {
+            if (strcmp (argv[argn], "-") == 0)
+                out = stdout;
+            else if ((out = fopen (argv[argn], "wb")) == NULL)
+                pm_error("main(): couldn't open file '%s'", 
+                         argv[argn]);
+            got_out=1;
+        }
+        else
+            pm_error("main(): unrecognized parameter '%s'", argv[argn]);
+    }
+
+    Pwidth=(Width+7)/8;
+    printer.fptr=out;
+
+    return print_pbm (in);
+}
+
diff --git a/converter/pbm/pbmtoppa/pbmtoppa.conf.hp1000 b/converter/pbm/pbmtoppa/pbmtoppa.conf.hp1000
new file mode 100644
index 00000000..a3ba3260
--- /dev/null
+++ b/converter/pbm/pbmtoppa/pbmtoppa.conf.hp1000
@@ -0,0 +1,18 @@
+# Sample configuration file for the HP720
+#
+# This file will be automatically read upon startup if it's placed in
+# /etc/pbm2ppa.conf
+#
+
+version  1000
+
+papersize	us
+
+xoff     100 # \ Adjust these for your printer.
+yoff    -650 # / (see CALIBRATE)
+
+# 1/4 inch margins all around (at 600 DPI)
+top      150
+bottom   150
+left     150
+right    150
diff --git a/converter/pbm/pbmtoppa/pbmtoppa.conf.hp720 b/converter/pbm/pbmtoppa/pbmtoppa.conf.hp720
new file mode 100644
index 00000000..b8975564
--- /dev/null
+++ b/converter/pbm/pbmtoppa/pbmtoppa.conf.hp720
@@ -0,0 +1,18 @@
+# Sample configuration file for the HP720
+#
+# This file will be automatically read upon startup if it's placed in
+# /etc/pbm2ppa.conf
+#
+
+version  720
+
+papersize	us
+
+xoff     169 # \ Adjust these for your printer.
+yoff    -569 # / (see CALIBRATE)
+
+# 1/4 inch margins all around (at 600 DPI)
+top      150
+bottom   150
+left     150
+right    150
diff --git a/converter/pbm/pbmtoppa/pbmtoppa.conf.hp820 b/converter/pbm/pbmtoppa/pbmtoppa.conf.hp820
new file mode 100644
index 00000000..55a8cb7d
--- /dev/null
+++ b/converter/pbm/pbmtoppa/pbmtoppa.conf.hp820
@@ -0,0 +1,18 @@
+# Sample configuration file for the HP820
+#
+# This file will be automatically read upon startup if it's placed in
+# /etc/pbm2ppa.conf
+#
+
+version  820
+
+papersize	us
+
+xoff      75 # \ Adjust these for your printer.
+yoff    -500 # / (see CALIBRATE)
+
+# 1/4 inch margins all around (at 600 DPI)
+top       80
+bottom   150
+left      80
+right     80
diff --git a/converter/pbm/pbmtoppa/ppa.c b/converter/pbm/pbmtoppa/ppa.c
new file mode 100644
index 00000000..8363d927
--- /dev/null
+++ b/converter/pbm/pbmtoppa/ppa.c
@@ -0,0 +1,526 @@
+/* ppa.c
+ * functions to handle PPA communications
+ * Copyright (c) 1998 Tim Norman.  See LICENSE for details.
+ * 2-25-98
+ *
+ * Mar 3, 1998  Jim Peterson  <jspeter@birch.ee.vt.edu>
+ *
+ *     Restructured to accommodate both the HP820/1000, and HP720 series.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pm_config.h"
+  /* pm_config.h is necessary for __inline__ for those compilers that don't
+     define __inline__ themselves
+     */
+#include "ppa.h"
+
+/*
+  VLink packet structure:
+
+    Bytes  Description
+    --------------------------------------------
+      1    Packet-start marker (always '$')
+      1    Channel (0: image data, 1: commands/responses(*), 2: autostatus(*), 128: pacing(*))
+      2    Data length (N)
+      N    [remaining data (e.g., SCP or SCP2 packet when Channel=1)]
+
+  (*): responses, autostatus, and pacing are communicated from the printer to
+       the computer, and may be safely ignored.
+*/
+static void 
+vlink_put(FILE *fptr, int channel, int length, void *data)
+{
+  fputc ('$', fptr);
+  fputc (channel, fptr);
+  fputc (length>>8, fptr);     fputc (length&0xFF, fptr);
+  fwrite (data, length, 1, fptr);
+}
+
+/*
+  SCP packet structure:
+
+    Bytes  Description
+    --------------------------------------------
+      2    Command specifier
+      2    Command reference number
+      1    Priority
+      1    padding? (always zero)
+      2    Data length (N)
+      N    [remaining data]
+
+    ComSpec  ComRef     Command
+    -------------------------------
+       35       1     Initialize1
+      101       2     Initialize2
+       21       1     Initialize3
+       19       1     Handle Media
+       18       1     Print Sweep
+*/
+static void 
+scp_put(FILE *fptr, int comspec, int comref, int priority,
+	     int length, void *data)
+{
+  /* encapsulate the vlink_put call in here, to avoid a memcpy */
+  fputc ('$', fptr);
+  fputc (1, fptr);
+  fputc ((length+8)>>8, fptr);     fputc ((length+8)&0xFF, fptr);
+
+  fputc (comspec>>8, fptr);  fputc (comspec&0xFF, fptr);
+  fputc (comref>>8, fptr);   fputc (comref&0xFF, fptr);
+  fputc (priority, fptr);
+  fputc (0, fptr);
+  fputc (length>>8, fptr);   fputc (length&0xFF, fptr);
+
+  fwrite (data, length, 1, fptr);
+}
+
+
+/*
+  SCP2 packet structure:
+
+    Bytes  Description
+    --------------------------------------------
+      2    Command specifier
+      2    Packet length (N)
+      1    Priority
+      1    padding? (always zero)
+      2    Command reference number
+      4    Channel 0 buffer increment
+      4    version number? (always 00 02 00 00)
+     N-16  [remaining data]
+
+    ComSpec  ComRef     Command
+    -------------------------------
+     0x186      1     Initialize1
+     0x18f      2     Initialize2
+     0x183      1     Initialize3
+     0x181      1     Handle Media
+     0x180      1     Print Sweep
+*/
+static void 
+scp2_put(FILE *fptr,unsigned short comspec,unsigned short pkt_len_s16,
+	      unsigned char priority,unsigned short comref,unsigned data_len,
+	      void *data)
+{
+  /* encapsulate the vlink_put call in here, to avoid a memcpy */
+  fputc ('$', fptr);
+  fputc (1, fptr);
+  fputc ((pkt_len_s16+16)>>8, fptr);  fputc ((pkt_len_s16+16), fptr);
+
+  fputc (comspec>>8, fptr);  fputc (comspec, fptr);
+  fputc ((pkt_len_s16+16)>>8, fptr);  fputc ((pkt_len_s16+16), fptr);
+  fputc (priority, fptr);
+  fputc (0, fptr);
+  fputc (comref>>8, fptr);
+  fputc (comref, fptr);
+  fputc (data_len>>24, fptr);   fputc (data_len>>16, fptr);
+  fputc (data_len>>8, fptr);    fputc (data_len, fptr);
+  fputc (0, fptr);
+  fputc (2, fptr);
+  fputc (0, fptr);
+  fputc (0, fptr);
+
+  fwrite (data, pkt_len_s16, 1, fptr);
+}
+
+
+/*
+  SCP3 packet structure:
+
+    Bytes  Description
+    --------------------------------------------
+      2    Command specifier
+      2    Packet length (N)
+      1    Priority
+      1    padding? (always zero)
+      2    Command reference number
+      4    Channel 0 buffer increment
+      4    version number? (always 01 04 00 00)
+     N-16  [remaining data]
+
+    ComSpec  ComRef     Command
+    -------------------------------
+     0x186      1     Initialize1
+     0x18C     16     Initialize Printer name
+     0x1A1      1     Initialize4?
+     0x18f      2     Initialize2
+     0x183      1     Initialize3
+     0x181      1     Handle Media
+     0x180      1     Print Sweep
+*/
+static void 
+scp3_put(FILE *fptr,unsigned short comspec,unsigned short pkt_len_s16,
+	      unsigned char priority,unsigned short comref,unsigned data_len,
+	      void *data)
+{
+  /* encapsulate the vlink_put call in here, to avoid a memcpy */
+  fputc ('$', fptr);
+  fputc (1, fptr);
+  fputc ((pkt_len_s16+16)>>8, fptr);  fputc ((pkt_len_s16+16), fptr);
+
+  fputc (comspec>>8, fptr);  fputc (comspec, fptr);
+  fputc ((pkt_len_s16+16)>>8, fptr);  fputc ((pkt_len_s16+16), fptr);
+  fputc (priority, fptr);
+  fputc (0, fptr);
+  fputc (comref>>8, fptr);
+  fputc (comref, fptr);
+  fputc (data_len>>24, fptr);   fputc (data_len>>16, fptr);
+  fputc (data_len>>8, fptr);    fputc (data_len, fptr);
+  fputc (1, fptr);
+  fputc (4, fptr);
+  fputc (0, fptr);
+  fputc (0, fptr);
+
+  fwrite (data, pkt_len_s16, 1, fptr);
+}
+
+
+void ppa_init_job(ppa_stat* prn)
+{
+  unsigned char init1[8] = { 0x00, 0x00, 0x01, 0xf4, 0x01, 0x00, 0x00, 0x00 };
+  unsigned char init2[4] = { 0xde, 0xad, 0xbe, 0xef };
+  unsigned char init3[8] = { 0xde, 0xad, 0xbe, 0xef, 0x02, 0x00, 0x00, 0x00 };
+  unsigned char init4[60] = "!!TAZ            \x81*HP DeskJet 1000C Prin (Copy 2)*FILE!!\x00\x00\x00"; /* plus 0 terminator */
+  unsigned char init5[4] = { 0x01, 0x01, 0x00, 0x00 };
+
+  switch(prn->version)
+  {
+  case HP820:
+    scp_put (prn->fptr, 35, 1, 7, sizeof(init1), init1);
+    vlink_put (prn->fptr, 0, sizeof(init2), init2);
+    scp_put (prn->fptr, 101, 2, 7, sizeof(init3), init3);
+    break;
+  case HP720:
+    scp2_put (prn->fptr, 0x0186, sizeof(init1), 7, 1, 0, init1);
+    vlink_put(prn->fptr, 0, sizeof(init2), init2);
+    scp2_put (prn->fptr, 0x018f, sizeof(init3), 7, 2, 4, init3);
+    break;
+  case HP1000:
+    scp3_put (prn->fptr, 0x0186, sizeof(init1), 7, 16, 0, init1);
+    scp3_put (prn->fptr, 0x018C, sizeof(init4), 7, 1, 0, init4);
+    scp3_put (prn->fptr, 0x01A1, sizeof(init5), 7, 1, 0, init5);
+    vlink_put (prn->fptr, 0, sizeof(init2), init2);
+    scp3_put (prn->fptr, 0x018f, sizeof(init3), 7, 2, 4, init3);
+    break;
+  default:
+    fprintf(stderr,"ppa_init_job(): unknown printer verson\n");
+  }
+}
+
+void ppa_end_print(ppa_stat* prn)
+{
+  unsigned char pageA[4] = { 0x05, 0x01, 0x03, 0x84 };
+
+  if (prn->version == HP1000)
+    scp3_put (prn->fptr, 0x0181, sizeof(pageA), 7, 2, 0, pageA);
+}
+
+void ppa_init_page(ppa_stat* prn)
+{
+  unsigned char pageA[16] = {0x28, 0x2d, 0x00, 0x41, 0x29, 0x2e, 0x00, 0x42,
+			     0x29, 0x2e, 0x00, 0x42, 0x29, 0x2e, 0x00, 0x42 };
+  unsigned char pageB[16] = {0x28, 0x2d, 0x00, 0x41, 0x2d, 0x32, 0x00, 0x46,
+			     0x2d, 0x32, 0x00, 0x46, 0x2d, 0x32, 0x00, 0x46 };
+
+  switch(prn->version)
+  {
+  case HP820:
+    scp_put (prn->fptr, 21, 1, 5, sizeof(pageA), pageA);
+    break;
+  case HP720:
+    scp2_put (prn->fptr, 0x0183, sizeof(pageB), 5, 1, 0, pageB);
+    break;
+  case HP1000:
+    scp3_put (prn->fptr, 0x0183, sizeof(pageA), 5, 1, 0, pageA);
+    break;
+  default:
+    fprintf(stderr,"ppa_init_page(): unknown printer verson\n");
+  }
+}
+
+void ppa_load_page(ppa_stat* prn)
+{
+  unsigned char loadA[4] = {0x01, 0x01, 0x09, 0x60 };
+  unsigned char loadB[4] = {0x01, 0x01, 0x12, 0xc0 };
+  unsigned char loadC[4] = {0x01, 0x01, 0x07, 0x08 };
+
+  switch(prn->version)
+  {
+  case HP820:
+    scp_put (prn->fptr, 19, 1, 7, sizeof(loadA), loadA);
+    break;
+  case HP720:
+    scp2_put (prn->fptr, 0x0181, sizeof(loadB), 7, 1, 0, loadB);
+    break;
+  case HP1000:
+    scp3_put (prn->fptr, 0x0181, sizeof(loadC), 7, 1, 0, loadC);
+    break;
+  default:
+    fprintf(stderr,"ppa_load_page(): unknown printer verson\n");
+  }
+}
+
+void ppa_eject_page(ppa_stat* prn)
+{
+  unsigned char loadA[4] = {0x02, 0x01, 0x09, 0x60 };
+  unsigned char loadB[4] = {0x02, 0x01, 0x12, 0xc0 };
+  unsigned char loadC[4] = {0x02, 0x01, 0x07, 0x08 };
+
+  switch(prn->version)
+  {
+  case HP820:
+    scp_put (prn->fptr, 19, 1, 7, sizeof(loadA), loadA);
+    break;
+  case HP720:
+    scp2_put (prn->fptr, 0x0181, sizeof(loadB), 7, 1, 0, loadB);
+    break;
+  case HP1000:
+    scp3_put (prn->fptr, 0x0181, sizeof(loadC), 7, 1, 0, loadC);
+    break;
+  default:
+    fprintf(stderr,"ppa_eject_page(): unknown printer verson\n");
+  }
+}
+
+
+
+static int 
+compress(unsigned char *in, int num_lines_d2, int final_len, 
+             unsigned char *iout)
+{
+  unsigned char* out = iout;
+  int I,len=num_lines_d2;
+  for(I=0; I<final_len; I+=num_lines_d2, in+=num_lines_d2)
+  {
+    int i = 0;
+    while (i < len) {
+      /* Find the size of duplicate values */
+      int dup_len = 0;
+      while ((i + dup_len < len)
+	     && (in[i + dup_len] == in[i])) {
+	dup_len++;
+      }
+      /* See if we have enough zeros to be worth compressing. */
+      /* I figure one is enough. */
+      if ((dup_len >= 1) && (in[i] == 0)) {
+	/* Output run of zeros. */
+	while (dup_len >= 128) {
+	  /* Max is 128 */
+	  *out++ = 0x00;
+	  i += 128;
+	  dup_len -= 128;
+	}
+	if (dup_len >= 1)
+	{
+	  *out++ = dup_len;
+	  i += dup_len;
+	}
+	/* See if we have enough non-zeros to be worth compressing. */
+	/* Here two should be enough. */
+      }
+      else if (dup_len >= 2)
+      {
+	/* Output run of duplicates. */
+	while (dup_len >= 64) {
+	  /* Max is 64 */
+	  *out++ = 0x80;
+	  *out++ = in[i];
+	  i += 64;
+	  dup_len -= 64;
+	}
+	if (dup_len >= 2)
+	{
+	  *out++ = dup_len + 0x80;
+	  *out++ = in[i];
+	  i += dup_len;
+	}
+      }
+      else
+      {
+	/* Look for two zeros, or three duplicates to end literal run. */
+	/* Note this is one more than the number to start a run. */
+	int lit_len = -1;
+	int add_more = 1;
+	while (add_more) {
+	  lit_len++;
+	  if (i + lit_len == len) add_more = 0;
+	  /* Always add more if we are near the very end. */
+	  if (i + lit_len < len - 3) {
+	    char a = in[i + lit_len + 0];
+	    char b = in[i + lit_len + 1];
+	    char c = in[i + lit_len + 2];
+	    /* See if there are enough zeros */
+	    if ((a == b) && (b == 0)) add_more = 0;
+	    /* See if there are enough duplicates */
+	    if ((a == b) && (b == c)) add_more = 0;
+	  }
+	}
+	/* Output run of literals. */
+	while (lit_len >= 64) {
+	  /* Max is 64 */
+	  int j;
+	  *out++ = 0xc0;
+	  for (j = i; j < i + 64; j++) {
+	    *out++ = in[j];
+	  }
+	  i += 64;
+	  lit_len -= 64;
+	} 
+	if (lit_len) {
+	  int j;
+	  *out++ = lit_len + 0xc0;
+	  for (j = i; j < i + lit_len; j++) {
+	    *out++ = in[j];
+	  }
+	  i += lit_len;
+	}
+      }
+    }
+  }
+  return out-iout;
+}
+
+static void __inline__ place_2bytes(int x,unsigned char* y)
+{ y[0]=x>>8; y[1]=x; }
+static void __inline__ place_4bytes(int x,unsigned char* y)
+{ place_2bytes(x>>16,y); place_2bytes(x,y+2); }
+
+#define do_compress_data (1)
+void ppa_print_sweep(ppa_stat* prn,ppa_sweep_data* data)
+{
+  unsigned char* pc, *tpc;
+  unsigned i,datasize=data->data_size;
+  unsigned char sweep_packet[144];
+  int sweep_packet_size;
+  unsigned short HP720constants[] = { 0x8ca0, 0x4650, 0x12c0 };
+  unsigned short HP820constants[] = { 0x4650, 0x1c20, 0x0960 };
+  unsigned short HP1000constants[] = { 0x4650, 0x2328, 0x0708 };
+  unsigned short* constants;
+  int nozzle_data_size;
+  int MF; /* Multiplicative Factor -- quick hack */
+
+  pc=data->image_data;
+
+  if(do_compress_data)
+  {
+    if(!(pc=malloc((datasize/64+1)*65)))
+    {
+      fprintf(stderr,"ppa_print_sweep(): could not malloc storage for compressed data\n");
+      exit(-1);
+    }
+    datasize=compress(data->image_data,data->nozzle_data->pins_used_d2,datasize,pc);
+  }
+
+  /* send image data 16k at a time */
+  for(i=0, tpc=pc; i<datasize; tpc+=16384, i+=16384)
+    vlink_put(prn->fptr, 0, datasize-i > 16384 ? 16384 : datasize-i, tpc);
+
+  /* memory leak fix courtesy of John McKown */
+  if (do_compress_data)
+    free (pc);
+
+  /* construct sweep packet */
+  switch(prn->version)
+  {
+  case HP820:
+    constants=HP820constants;
+    MF=1;
+    break;
+  case HP720:
+    constants=HP720constants;
+    MF=2;
+    break;
+  case HP1000:
+    constants=HP1000constants;
+    MF=1;
+    break;
+  default:
+    fprintf(stderr,"ppa_print_sweep(): unknown printer verson\n");
+    return;
+  }
+
+  sweep_packet[0]=0;
+  sweep_packet[1]= do_compress_data;
+  sweep_packet[2]= data->direction==right_to_left ? 1 : 2;
+  sweep_packet[3]= data->in_color ? 14 : 1;
+
+  place_4bytes(datasize,sweep_packet+4);
+
+  memset(sweep_packet+8,0,8);
+
+  place_4bytes(MF*(data->vertical_pos+prn->y_offset),sweep_packet+16);
+  place_2bytes(constants[0],sweep_packet+20);
+  place_2bytes(MF*(data->left_margin+prn->x_offset),sweep_packet+22);
+  place_2bytes(MF*(data->right_margin+prn->x_offset),sweep_packet+24);
+  place_2bytes(constants[1],sweep_packet+26);
+  place_2bytes(constants[2],sweep_packet+28);
+  place_2bytes(0x100,sweep_packet+30);
+
+  if(data->next)
+  {
+    sweep_packet[32]= data->next->direction==right_to_left ? 1 : 2;
+    sweep_packet[33]= data->next->in_color ? 14 : 1;
+    place_4bytes(MF*(data->next->vertical_pos+prn->y_offset),sweep_packet+34);
+    place_2bytes(MF*(data->next->left_margin+prn->x_offset),sweep_packet+38);
+    place_2bytes(MF*(data->next->right_margin+prn->x_offset),sweep_packet+40);
+    place_2bytes(constants[1],sweep_packet+42);
+    place_2bytes(constants[2],sweep_packet+44);
+  }
+  else
+    memset(sweep_packet+32,0,14);
+  sweep_packet[46]=8;
+
+  nozzle_data_size=data->nozzle_data_size;
+  if(nozzle_data_size>6)
+  {
+    fprintf(stderr,"ppa_print_sweep(): truncating nozzle data from %d rows to 6 rows\n",nozzle_data_size);
+    nozzle_data_size=6;
+  }
+  sweep_packet[47]=nozzle_data_size;
+
+  for(i=0; i<nozzle_data_size; ++i)
+  {
+    unsigned char * const pc = sweep_packet + 48 + i*16;
+
+    place_2bytes(data->nozzle_data[i].DPI,pc);
+    place_2bytes(data->nozzle_data[i].pins_used_d2,pc+2);
+    place_2bytes(data->nozzle_data[i].unused_pins_p1,pc+4);
+    place_2bytes(data->nozzle_data[i].first_pin,pc+6);
+    place_2bytes(data->nozzle_data[i].pins_used_d2,pc+8);
+    place_2bytes(MF*(data->nozzle_data[i].left_margin+prn->x_offset),pc+10);
+    place_2bytes(MF*(data->nozzle_data[i].right_margin+prn->x_offset),pc+12);
+    pc[14]=data->nozzle_data[i].nozzle_delay;
+    pc[15]=0;
+  }
+
+  sweep_packet_size = data->in_color ? 144 : 80;
+
+  /* send sweep packet */
+  switch(prn->version)
+  {
+  case HP820:
+    scp_put (prn->fptr, 18, 1, 7, sweep_packet_size, sweep_packet);
+    break;
+  case HP720:
+    scp2_put (prn->fptr, 0x0180, sweep_packet_size, 7, 1, datasize, sweep_packet);
+    break;
+  case HP1000:
+    scp3_put (prn->fptr, 0x0180, sweep_packet_size, 7, 1, datasize, sweep_packet);
+    break;
+  default:
+    fprintf(stderr,"ppa_print_sweep(): unknown printer version\n");
+    return;
+  }
+}
+
+
+void ppa_print_sweeps(ppa_stat* prn,ppa_sweep_data* data)
+{
+  ppa_sweep_data* current_sweep;
+  for(current_sweep=data; current_sweep; current_sweep=current_sweep->next)
+    ppa_print_sweep(prn,current_sweep);
+}
diff --git a/converter/pbm/pbmtoppa/ppa.h b/converter/pbm/pbmtoppa/ppa.h
new file mode 100644
index 00000000..1c7e6f18
--- /dev/null
+++ b/converter/pbm/pbmtoppa/ppa.h
@@ -0,0 +1,74 @@
+/* ppa.h
+ * Copyright (c) 1998 Tim Norman.  See LICENSE for details.
+ * 2-25-98
+ *
+ * Mar 3, 1998  Jim Peterson  <jspeter@birch.ee.vt.edu>
+ *
+ *     Restructured to accommodate both the HP820/1000, and HP720 series.
+ */
+#ifndef _PPA_H
+#define _PPA_H
+#include <stdio.h>
+
+typedef struct
+{
+  FILE* fptr;
+  enum { HP820, HP1000, HP720, HP710 } version;
+  /* 300 , 600 dpi */
+  int DPI;
+  /* nozzle delay */
+  int right_to_left_delay[2];
+  int left_to_right_delay[2];
+  /* direction of printing */
+  enum { RIGHT_ONLY, LEFT_ONLY, BOTH_DIR } direction;
+  int x_offset;
+  int y_offset;
+  int marg_diff;
+  int top_margin;
+  int left_margin;
+  int right_margin;
+  int bottom_margin;
+  int bufsize;
+} ppa_stat;
+
+typedef struct
+{ 
+  int DPI;
+  int right;
+  int left;
+} printer_delay;
+
+typedef struct
+{
+  unsigned short DPI;
+  unsigned short pins_used_d2;
+  unsigned short unused_pins_p1;
+  unsigned short first_pin;
+  unsigned short left_margin;
+  unsigned short right_margin;
+  unsigned char nozzle_delay;
+} ppa_nozzle_data;
+
+typedef struct ppa_sweep_data_s
+{
+  unsigned char* image_data;
+  unsigned data_size;
+  enum { False=0, True=1 } in_color;
+  enum { right_to_left, left_to_right } direction;
+  int vertical_pos;
+  unsigned short left_margin;
+  unsigned short right_margin;
+  unsigned char nozzle_data_size;
+  ppa_nozzle_data* nozzle_data;
+  struct ppa_sweep_data_s* next; /* NULL indicates last print sweep */
+} ppa_sweep_data;
+
+void ppa_init_job(ppa_stat*);
+void ppa_init_page(ppa_stat*);
+void ppa_load_page(ppa_stat*);
+void ppa_eject_page(ppa_stat*);
+void ppa_end_print(ppa_stat*);
+void ppa_print_sweep(ppa_stat*,ppa_sweep_data*);  /* prints one sweep */
+void ppa_print_sweeps(ppa_stat*,ppa_sweep_data*); /* prints a linked-list of sweeps */
+
+#endif
diff --git a/converter/pbm/pbmtoppa/ppapbm.h b/converter/pbm/pbmtoppa/ppapbm.h
new file mode 100644
index 00000000..1ffc093b
--- /dev/null
+++ b/converter/pbm/pbmtoppa/ppapbm.h
@@ -0,0 +1,29 @@
+/* pbm.h
+ * Copyright (c) 1998 Tim Norman.  See LICENSE for details
+ * 2-25-98
+ *
+ * Mar 18, 1998  Jim Peterson  <jspeter@birch.ee.vt.edu>
+ *
+ *     Restructured to encapsulate more of the PBM handling.
+ */
+#ifndef _PBM_H
+#define _PBM_H
+
+#include <stdio.h>
+
+typedef struct
+{
+  FILE* fptr;
+  enum { none, P1, P4 } version;
+  int width, height;
+  int current_line;
+  void *revdata;
+  int unread;
+} pbm_stat;
+
+int make_pbm_stat(pbm_stat*,FILE*);
+int pbm_readline(pbm_stat*,unsigned char*); 
+  /* reads a single line into char* */
+void pbm_unreadline(pbm_stat*,void*); /* pushes a single line back */
+
+#endif
diff --git a/converter/pbm/pbmtopsg3.c b/converter/pbm/pbmtopsg3.c
new file mode 100644
index 00000000..68b265f0
--- /dev/null
+++ b/converter/pbm/pbmtopsg3.c
@@ -0,0 +1,374 @@
+/* pbmtopsg3
+
+   Reads a series of PBM images and writes a Postscript program
+   containing these images as individual pages with Fax-G3 
+   (CCITT-Fiter) compression. (Useful for combining scanned pages into
+   a comfortably printable document.)
+
+   Copyright (C) 2001 Kristof Koehler 
+       <kristof@fachschaft.physik.uni-karlsruhe.de>
+
+   Netpbm adaptation by Bryan Henderson June 2001.
+ 
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+ 
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "pbm.h"
+#include "shhopt.h"
+
+struct cmdline_info {
+    /* 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 */
+    float        dpi;            /* requested resolution, dpi */
+    char *       title;          /* -title option.  NULL for none */
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdline_info *cmdlineP) {
+
+    optStruct3 opt;
+    unsigned int option_def_index = 0;
+    optEntry *option_def = malloc(100*sizeof(optEntry));
+
+    unsigned int dpiSpec, titleSpec;
+    float dpiOpt;
+    char * titleOpt;
+
+    OPTENT3(0, "dpi",      OPT_FLOAT,  &dpiOpt,   &dpiSpec,   0);
+    OPTENT3(0, "title",    OPT_STRING, &titleOpt, &titleSpec, 0);
+    
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;
+    opt.allowNegNum = FALSE;
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+
+    if (argc-1 == 0)
+        cmdlineP->inputFilespec = "-";
+    else {
+        cmdlineP->inputFilespec = argv[1];
+        if (argc-1 > 1)
+            pm_error("Too many arguments.  The only argument is the input "
+                     "file specification");
+    }
+
+    cmdlineP->dpi = dpiSpec ? dpiOpt : 72.0;
+    cmdlineP->title = titleSpec ? titleOpt : NULL;
+}
+
+    
+
+static void 
+write85 ( unsigned int bits, int *col )
+{
+	char buf[5] ;
+	if ( bits == 0 ) {
+		fputc ( 'z', stdout ) ;
+		*col += 1 ;
+	} else {
+		buf[4] = bits % 85 + '!' ;
+		bits /= 85 ;
+		buf[3] = bits % 85 + '!' ;
+		bits /= 85 ;
+		buf[2] = bits % 85 + '!' ;
+		bits /= 85 ;
+		buf[1] = bits % 85 + '!' ;
+		bits /= 85 ;
+		buf[0] = bits % 85 + '!' ;
+		fwrite ( buf, 1, 5, stdout ) ;
+		*col += 5 ;
+	}
+	if ( *col > 70 ) {
+		printf ( "\n" ) ;
+		*col = 0 ;
+	}
+}
+
+
+static void 
+writebits ( unsigned int *outbits, int *outbitsidx, int *col,
+            unsigned int bits, int n )
+{
+	int k, m ;
+	unsigned int usedbits ;
+	while ( n > 0 ) {
+		if ( *outbitsidx == 0 )
+			*outbits = 0 ;
+		k = 32 - *outbitsidx ;
+		m = n > k ? k : n ;
+		usedbits = (bits >> (n-m)) & ((1<<m)-1) ;
+		*outbits |= usedbits << (k-m) ;
+		*outbitsidx += m ;
+		n -= m ;
+		if ( *outbitsidx == 32 ) {
+			write85 ( *outbits, col ) ;
+			*outbitsidx = 0 ;
+		}
+	}
+}
+
+
+static void 
+flushbits ( unsigned int *outbits, int *outbitsidx, int *col )
+{
+	if ( *outbitsidx > 0 ) {
+		write85 ( *outbits, col ) ;
+		*outbitsidx = 0 ;
+	}
+}
+
+struct { unsigned int b, l ; } makeup[40][2] = {
+    { { 0x001b, 5 } /*         11011 */ , { 0x000f,10 } /*    0000001111 */  },
+    { { 0x0012, 5 } /*         10010 */ , { 0x00c8,12 } /*  000011001000 */  },
+    { { 0x0017, 6 } /*        010111 */ , { 0x00c9,12 } /*  000011001001 */  },
+    { { 0x0037, 7 } /*       0110111 */ , { 0x005b,12 } /*  000001011011 */  },
+    { { 0x0036, 8 } /*      00110110 */ , { 0x0033,12 } /*  000000110011 */  },
+    { { 0x0037, 8 } /*      00110111 */ , { 0x0034,12 } /*  000000110100 */  },
+    { { 0x0064, 8 } /*      01100100 */ , { 0x0035,12 } /*  000000110101 */  },
+    { { 0x0065, 8 } /*      01100101 */ , { 0x006c,13 } /* 0000001101100 */  },
+    { { 0x0068, 8 } /*      01101000 */ , { 0x006d,13 } /* 0000001101101 */  },
+    { { 0x0067, 8 } /*      01100111 */ , { 0x004a,13 } /* 0000001001010 */  },
+    { { 0x00cc, 9 } /*     011001100 */ , { 0x004b,13 } /* 0000001001011 */  },
+    { { 0x00cd, 9 } /*     011001101 */ , { 0x004c,13 } /* 0000001001100 */  },
+    { { 0x00d2, 9 } /*     011010010 */ , { 0x004d,13 } /* 0000001001101 */  },
+    { { 0x00d3, 9 } /*     011010011 */ , { 0x0072,13 } /* 0000001110010 */  },
+    { { 0x00d4, 9 } /*     011010100 */ , { 0x0073,13 } /* 0000001110011 */  },
+    { { 0x00d5, 9 } /*     011010101 */ , { 0x0074,13 } /* 0000001110100 */  },
+    { { 0x00d6, 9 } /*     011010110 */ , { 0x0075,13 } /* 0000001110101 */  },
+    { { 0x00d7, 9 } /*     011010111 */ , { 0x0076,13 } /* 0000001110110 */  },
+    { { 0x00d8, 9 } /*     011011000 */ , { 0x0077,13 } /* 0000001110111 */  },
+    { { 0x00d9, 9 } /*     011011001 */ , { 0x0052,13 } /* 0000001010010 */  },
+    { { 0x00da, 9 } /*     011011010 */ , { 0x0053,13 } /* 0000001010011 */  },
+    { { 0x00db, 9 } /*     011011011 */ , { 0x0054,13 } /* 0000001010100 */  },
+    { { 0x0098, 9 } /*     010011000 */ , { 0x0055,13 } /* 0000001010101 */  },
+    { { 0x0099, 9 } /*     010011001 */ , { 0x005a,13 } /* 0000001011010 */  },
+    { { 0x009a, 9 } /*     010011010 */ , { 0x005b,13 } /* 0000001011011 */  },
+    { { 0x0018, 6 } /*        011000 */ , { 0x0064,13 } /* 0000001100100 */  },
+    { { 0x009b, 9 } /*     010011011 */ , { 0x0065,13 } /* 0000001100101 */  },
+    { { 0x0008,11 } /*   00000001000 */ , { 0x0008,11 } /*   00000001000 */  },
+    { { 0x000c,11 } /*   00000001100 */ , { 0x000c,11 } /*   00000001100 */  },
+    { { 0x000d,11 } /*   00000001101 */ , { 0x000d,11 } /*   00000001101 */  },
+    { { 0x0012,12 } /*  000000010010 */ , { 0x0012,12 } /*  000000010010 */  },
+    { { 0x0013,12 } /*  000000010011 */ , { 0x0013,12 } /*  000000010011 */  },
+    { { 0x0014,12 } /*  000000010100 */ , { 0x0014,12 } /*  000000010100 */  },
+    { { 0x0015,12 } /*  000000010101 */ , { 0x0015,12 } /*  000000010101 */  },
+    { { 0x0016,12 } /*  000000010110 */ , { 0x0016,12 } /*  000000010110 */  },
+    { { 0x0017,12 } /*  000000010111 */ , { 0x0017,12 } /*  000000010111 */  },
+    { { 0x001c,12 } /*  000000011100 */ , { 0x001c,12 } /*  000000011100 */  },
+    { { 0x001d,12 } /*  000000011101 */ , { 0x001d,12 } /*  000000011101 */  },
+    { { 0x001e,12 } /*  000000011110 */ , { 0x001e,12 } /*  000000011110 */  },
+    { { 0x001f,12 } /*  000000011111 */ , { 0x001f,12 } /*  000000011111 */  }
+} ;
+
+struct { unsigned int b, l ; } term[64][2] = {
+    { { 0x0035, 8 } /*      00110101 */ , { 0x0037,10 } /*    0000110111 */  },
+    { { 0x0007, 6 } /*        000111 */ , { 0x0002, 3 } /*           010 */  },
+    { { 0x0007, 4 } /*          0111 */ , { 0x0003, 2 } /*            11 */  },
+    { { 0x0008, 4 } /*          1000 */ , { 0x0002, 2 } /*            10 */  },
+    { { 0x000b, 4 } /*          1011 */ , { 0x0003, 3 } /*           011 */  },
+    { { 0x000c, 4 } /*          1100 */ , { 0x0003, 4 } /*          0011 */  },
+    { { 0x000e, 4 } /*          1110 */ , { 0x0002, 4 } /*          0010 */  },
+    { { 0x000f, 4 } /*          1111 */ , { 0x0003, 5 } /*         00011 */  },
+    { { 0x0013, 5 } /*         10011 */ , { 0x0005, 6 } /*        000101 */  },
+    { { 0x0014, 5 } /*         10100 */ , { 0x0004, 6 } /*        000100 */  },
+    { { 0x0007, 5 } /*         00111 */ , { 0x0004, 7 } /*       0000100 */  },
+    { { 0x0008, 5 } /*         01000 */ , { 0x0005, 7 } /*       0000101 */  },
+    { { 0x0008, 6 } /*        001000 */ , { 0x0007, 7 } /*       0000111 */  },
+    { { 0x0003, 6 } /*        000011 */ , { 0x0004, 8 } /*      00000100 */  },
+    { { 0x0034, 6 } /*        110100 */ , { 0x0007, 8 } /*      00000111 */  },
+    { { 0x0035, 6 } /*        110101 */ , { 0x0018, 9 } /*     000011000 */  },
+    { { 0x002a, 6 } /*        101010 */ , { 0x0017,10 } /*    0000010111 */  },
+    { { 0x002b, 6 } /*        101011 */ , { 0x0018,10 } /*    0000011000 */  },
+    { { 0x0027, 7 } /*       0100111 */ , { 0x0008,10 } /*    0000001000 */  },
+    { { 0x000c, 7 } /*       0001100 */ , { 0x0067,11 } /*   00001100111 */  },
+    { { 0x0008, 7 } /*       0001000 */ , { 0x0068,11 } /*   00001101000 */  },
+    { { 0x0017, 7 } /*       0010111 */ , { 0x006c,11 } /*   00001101100 */  },
+    { { 0x0003, 7 } /*       0000011 */ , { 0x0037,11 } /*   00000110111 */  },
+    { { 0x0004, 7 } /*       0000100 */ , { 0x0028,11 } /*   00000101000 */  },
+    { { 0x0028, 7 } /*       0101000 */ , { 0x0017,11 } /*   00000010111 */  },
+    { { 0x002b, 7 } /*       0101011 */ , { 0x0018,11 } /*   00000011000 */  },
+    { { 0x0013, 7 } /*       0010011 */ , { 0x00ca,12 } /*  000011001010 */  },
+    { { 0x0024, 7 } /*       0100100 */ , { 0x00cb,12 } /*  000011001011 */  },
+    { { 0x0018, 7 } /*       0011000 */ , { 0x00cc,12 } /*  000011001100 */  },
+    { { 0x0002, 8 } /*      00000010 */ , { 0x00cd,12 } /*  000011001101 */  },
+    { { 0x0003, 8 } /*      00000011 */ , { 0x0068,12 } /*  000001101000 */  },
+    { { 0x001a, 8 } /*      00011010 */ , { 0x0069,12 } /*  000001101001 */  },
+    { { 0x001b, 8 } /*      00011011 */ , { 0x006a,12 } /*  000001101010 */  },
+    { { 0x0012, 8 } /*      00010010 */ , { 0x006b,12 } /*  000001101011 */  },
+    { { 0x0013, 8 } /*      00010011 */ , { 0x00d2,12 } /*  000011010010 */  },
+    { { 0x0014, 8 } /*      00010100 */ , { 0x00d3,12 } /*  000011010011 */  },
+    { { 0x0015, 8 } /*      00010101 */ , { 0x00d4,12 } /*  000011010100 */  },
+    { { 0x0016, 8 } /*      00010110 */ , { 0x00d5,12 } /*  000011010101 */  },
+    { { 0x0017, 8 } /*      00010111 */ , { 0x00d6,12 } /*  000011010110 */  },
+    { { 0x0028, 8 } /*      00101000 */ , { 0x00d7,12 } /*  000011010111 */  },
+    { { 0x0029, 8 } /*      00101001 */ , { 0x006c,12 } /*  000001101100 */  },
+    { { 0x002a, 8 } /*      00101010 */ , { 0x006d,12 } /*  000001101101 */  },
+    { { 0x002b, 8 } /*      00101011 */ , { 0x00da,12 } /*  000011011010 */  },
+    { { 0x002c, 8 } /*      00101100 */ , { 0x00db,12 } /*  000011011011 */  },
+    { { 0x002d, 8 } /*      00101101 */ , { 0x0054,12 } /*  000001010100 */  },
+    { { 0x0004, 8 } /*      00000100 */ , { 0x0055,12 } /*  000001010101 */  },
+    { { 0x0005, 8 } /*      00000101 */ , { 0x0056,12 } /*  000001010110 */  },
+    { { 0x000a, 8 } /*      00001010 */ , { 0x0057,12 } /*  000001010111 */  },
+    { { 0x000b, 8 } /*      00001011 */ , { 0x0064,12 } /*  000001100100 */  },
+    { { 0x0052, 8 } /*      01010010 */ , { 0x0065,12 } /*  000001100101 */  },
+    { { 0x0053, 8 } /*      01010011 */ , { 0x0052,12 } /*  000001010010 */  },
+    { { 0x0054, 8 } /*      01010100 */ , { 0x0053,12 } /*  000001010011 */  },
+    { { 0x0055, 8 } /*      01010101 */ , { 0x0024,12 } /*  000000100100 */  },
+    { { 0x0024, 8 } /*      00100100 */ , { 0x0037,12 } /*  000000110111 */  },
+    { { 0x0025, 8 } /*      00100101 */ , { 0x0038,12 } /*  000000111000 */  },
+    { { 0x0058, 8 } /*      01011000 */ , { 0x0027,12 } /*  000000100111 */  },
+    { { 0x0059, 8 } /*      01011001 */ , { 0x0028,12 } /*  000000101000 */  },
+    { { 0x005a, 8 } /*      01011010 */ , { 0x0058,12 } /*  000001011000 */  },
+    { { 0x005b, 8 } /*      01011011 */ , { 0x0059,12 } /*  000001011001 */  },
+    { { 0x004a, 8 } /*      01001010 */ , { 0x002b,12 } /*  000000101011 */  },
+    { { 0x004b, 8 } /*      01001011 */ , { 0x002c,12 } /*  000000101100 */  },
+    { { 0x0032, 8 } /*      00110010 */ , { 0x005a,12 } /*  000001011010 */  },
+    { { 0x0033, 8 } /*      00110011 */ , { 0x0066,12 } /*  000001100110 */  },
+    { { 0x0034, 8 } /*      00110100 */ , { 0x0067,12 } /*  000001100111 */  } 
+} ;
+
+
+static void
+writelength ( unsigned int *outbits, int *outbitsidx, int *col,
+              int bit, int length )
+{
+	while ( length >= 64 ) {
+		int m = length / 64 ;
+		if ( m > 40 )
+			m = 40 ;
+		writebits ( outbits, outbitsidx, col,
+			    makeup[m-1][bit].b, makeup[m-1][bit].l ) ;
+		length -= 64*m ;
+	}
+	writebits ( outbits, outbitsidx, col,
+		    term[length][bit].b, term[length][bit].l ) ;
+}
+
+
+
+static void
+doPage(FILE *       const ifP,
+       unsigned int const pageNum,
+       double       const dpi) {
+
+    int cols, rows, format;
+    bit * bitrow;
+    unsigned int row;
+    unsigned int outbits ;
+    int outbitsidx, col ;
+
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+        
+    bitrow = pbm_allocrow(cols);
+        
+    pm_message("[%u]\n", pageNum);
+
+    printf ("%%%%Page: %u %u\n", pageNum, pageNum);
+    printf ("%u %u 1 [ %f 0 0 %f 0 %u ]\n"
+            "{ currentfile /ASCII85Decode filter\n"
+            "  << /Columns %u /Rows %u /EndOfBlock false >> "
+                "/CCITTFaxDecode filter\n"
+            "  image } exec\n",
+            cols, rows, dpi/72.0, -dpi/72.0, rows, 
+            cols, rows) ;
+        
+    outbitsidx = col = 0 ;
+    for (row = 0 ; row < rows; ++row) {
+        int lastbit, cnt ;
+        unsigned int j;
+
+        pbm_readpbmrow(ifP, bitrow, cols, format);
+            
+        lastbit = cnt = 0 ;
+        for (j = 0; j < cols; ++j) {
+            if (bitrow[j] != lastbit) {
+                writelength(&outbits, &outbitsidx, &col, lastbit, cnt) ;
+                lastbit = 1 ^ lastbit ;
+                cnt = 0 ;
+            }
+            ++cnt;
+        }
+        writelength(&outbits, &outbitsidx, &col, lastbit, cnt);
+    }
+        
+    flushbits(&outbits, &outbitsidx, &col) ;
+    printf("~>\nshowpage\n") ;
+
+    pbm_freerow(bitrow);
+}
+
+
+
+static void 
+doPages(FILE *         const ifP,
+        unsigned int * const pagesP,
+        double         const dpi) {
+
+    bool eof;
+    unsigned int pagesDone;
+
+    eof = FALSE;
+    pagesDone = 0;
+
+    while (!eof) {
+        doPage(ifP, pagesDone + 1, dpi);
+        ++pagesDone;
+        pbm_nextimage(ifP, &eof);
+    }
+    *pagesP = pagesDone;
+}
+
+
+
+int
+main(int    argc,
+     char * argv[]) {
+
+    FILE *ifP;
+    unsigned int pages;
+    
+    struct cmdline_info cmdline;
+
+    pbm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    printf ("%%!PS-Adobe-3.0\n");
+    if (cmdline.title)
+        printf("%%%%Title: %s\n", cmdline.title) ;
+    printf ("%%%%Creator: pbmtopsg3, Copyright (C) 2001 Kristof Koehler\n"
+            "%%%%Pages: (atend)\n"
+            "%%%%EndComments\n") ;
+    
+    doPages(ifP, &pages, cmdline.dpi);
+
+    printf ("%%%%Trailer\n"
+            "%%%%Pages: %u\n"
+            "%%%%EOF\n",
+            pages);
+
+    pm_close(ifP);
+    pm_close(stdout);
+    
+    return 0;
+}
diff --git a/converter/pbm/pbmtoptx.c b/converter/pbm/pbmtoptx.c
new file mode 100644
index 00000000..5031efcb
--- /dev/null
+++ b/converter/pbm/pbmtoptx.c
@@ -0,0 +1,101 @@
+/* pbmtoptx.c - read a portable bitmap and produce a Printronix printer file
+**
+** Copyright (C) 1988 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 "pbm.h"
+
+static void putinit ARGS(( void ));
+static void putbit ARGS(( bit b ));
+static void putrest ARGS(( void ));
+static void putitem ARGS(( void ));
+
+int
+main( argc, argv )
+int argc;
+char *argv[];
+    {
+    FILE *ifp;
+    register bit *bitrow, *bP;
+    int rows, cols, format, row, col;
+    const char * const usage = "[pbmfile]";
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( usage );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+    bitrow = pbm_allocrow( cols );
+
+    putinit( );
+    for ( row = 0; row < rows; row++ )
+	{
+	pbm_readpbmrow( ifp, bitrow, cols, format );
+        for ( col = 0, bP = bitrow; col < cols; col++, bP++ )
+	    putbit( *bP );
+	putrest( );
+	putchar( 5 );
+	putchar( '\n' );
+        }
+
+    pm_close( ifp );
+    
+    exit( 0 );
+    }
+
+static char item;
+static int bitsperitem, bitshift;
+
+static void
+putinit( )
+    {
+    bitsperitem = 0;
+    item = 64;
+    bitshift = 0;
+    }
+
+#if __STDC__
+static void
+putbit( bit b )
+#else /*__STDC__*/
+static void
+putbit( b )
+    bit b;
+#endif /*__STDC__*/
+    {
+    if ( bitsperitem == 6 )
+	putitem( );
+    if ( b == PBM_BLACK )
+	item += 1 << bitshift;
+    bitsperitem++;
+    bitshift++;
+    }
+
+static void
+putrest( )
+    {
+    if ( bitsperitem > 0 )
+	putitem( );
+    }
+
+static void
+putitem( )
+    {
+    putchar( item );
+    bitsperitem = 0;
+    item = 64;
+    bitshift = 0;
+    }
diff --git a/converter/pbm/pbmtowbmp.c b/converter/pbm/pbmtowbmp.c
new file mode 100644
index 00000000..2907fd68
--- /dev/null
+++ b/converter/pbm/pbmtowbmp.c
@@ -0,0 +1,70 @@
+/* pbmtowbmp.c - convert a portable bitmap to a Wireless Bitmap file
+
+   This is derived for Netpbm from the pbmwbmp package from 
+   <http://www.looplab.com/wap/tools> on 2000.06.06.
+   
+   The specifications for the wbmp format are part of the Wireless 
+   Application Environment specification at
+   <http://www.wapforum.org/what/technical.htm>.
+
+** Copyright (C) 1999 Terje Sannum <terje@looplab.com>.
+**
+** 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 "pbm.h"
+
+static void 
+outputint(int i) {
+  int c = 1;
+  while(i & 0x7f << 7*c) c++;
+  while(c > 1) putchar(0x80 | ((i >> 7*--c) & 0xff));
+  putchar(i & 0x7f);
+}
+
+
+
+int 
+main(int argc, char *argv[]) {
+  FILE *f;
+  bit **image;
+  int rows, cols, row, col;
+  int c, p;
+
+  pbm_init(&argc, argv);
+  if(argc > 2) {
+    fprintf(stderr, "Copyright (C) 1999 Terje Sannum <terje@looplab.com>\n");
+    pm_usage("[pbmfile]");
+  }
+
+  f = argc == 2 ? pm_openr(argv[1]) : stdin;
+  image = pbm_readpbm(f, &cols, &rows);
+  pm_close(f);
+
+  /* Header */
+  putchar(0); /* Type 0: B/W, no compression */
+  putchar(0); /* FixHeaderField */
+  /* Geometry */
+  outputint(cols);
+  outputint(rows);
+  /* Image data */
+  for(row = 0; row < rows; row++) {
+    p = c = 0;
+    for(col = 0; col < cols; col++) {
+      if(image[row][col] == PBM_WHITE) c = c | (1 << (7-p));
+      if(++p == 8) {
+	putchar(c);
+	p = c = 0;
+      }
+    }
+    if(p) putchar(c);
+  }
+  
+  return 0;
+}
+
diff --git a/converter/pbm/pbmtox10bm.c b/converter/pbm/pbmtox10bm.c
new file mode 100644
index 00000000..ef31fb9b
--- /dev/null
+++ b/converter/pbm/pbmtox10bm.c
@@ -0,0 +1,120 @@
+/* pbmtox10bm.c - read a portable bitmap and produce an X10 bitmap file
+**
+** Copyright (C) 1988 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 "nstring.h"
+#include "pbm.h"
+
+int
+main(int argc, char * argv[]) {
+
+    FILE* ifp;
+    bit* bitrow;
+    bit * bP;
+    int rows, cols, format, padright, row;
+    int col;
+    char name[100];
+    char* cp;
+    int itemsperline;
+    int bitsperitem;
+    int item;
+    int firstitem;
+    const char* const hexchar = "0123456789abcdef";
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+        pm_usage( "[pbmfile]" );
+
+    if ( argc == 2 )
+	{
+        ifp = pm_openr( argv[1] );
+        strcpy( name, argv[1] );
+        if (STREQ( name, "-" ))
+            strcpy( name, "noname" );
+
+        if ( ( cp = strchr( name, '.' ) ) != 0 )
+            *cp = '\0';
+	}
+    else
+	{
+        ifp = stdin;
+        strcpy( name, "noname" );
+	}
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+    bitrow = pbm_allocrow( cols );
+
+    /* Compute padding to round cols up to the nearest multiple of 16. */
+    padright = ( ( cols + 15 ) / 16 ) * 16 - cols;
+
+    printf( "#define %s_width %d\n", name, cols );
+    printf( "#define %s_height %d\n", name, rows );
+    printf( "static short %s_bits[] = {\n", name );
+
+    itemsperline = 0;
+    bitsperitem = 0;
+    item = 0;
+    firstitem = 1;
+
+#define PUTITEM \
+    { \
+        if ( firstitem ) \
+	        firstitem = 0; \
+        else \
+	        putchar( ',' ); \
+    if ( itemsperline == 11 ) \
+	{ \
+	    putchar( '\n' ); \
+	    itemsperline = 0; \
+	} \
+    if ( itemsperline == 0 ) \
+	    putchar( ' ' ); \
+    ++itemsperline; \
+    putchar('0'); \
+    putchar('x'); \
+    putchar(hexchar[item >> 12]); \
+    putchar(hexchar[(item >> 8) & 15]); \
+    putchar(hexchar[(item >> 4) & 15]); \
+    putchar(hexchar[item & 15]); \
+    bitsperitem = 0; \
+    item = 0; \
+    }
+
+#define PUTBIT(b) \
+    { \
+    if ( bitsperitem == 16 ) \
+	    PUTITEM; \
+    if ( (b) == PBM_BLACK ) \
+	    item += 1 << bitsperitem; \
+    ++bitsperitem; \
+    }
+
+    for ( row = 0; row < rows; ++row )
+	{
+        pbm_readpbmrow( ifp, bitrow, cols, format );
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+            PUTBIT(*bP);
+        for ( col = 0; col < padright; ++col )
+            PUTBIT(0);
+    }
+
+    pm_close( ifp );
+    
+    if ( bitsperitem > 0 )
+        PUTITEM;
+    printf( "};\n" );
+
+    return 0;
+}
diff --git a/converter/pbm/pbmtoxbm.c b/converter/pbm/pbmtoxbm.c
new file mode 100644
index 00000000..96830a0c
--- /dev/null
+++ b/converter/pbm/pbmtoxbm.c
@@ -0,0 +1,165 @@
+/* pbmtoxbm.c - read a portable bitmap and produce an X11 bitmap file
+**
+** Copyright (C) 1988 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 "pbm.h"
+#include "nstring.h"
+
+
+static void
+generateName(char const filenameArg[], const char ** const nameP) {
+/*----------------------------------------------------------------------------
+   Generate a name for the image to put in the bitmap file.  Derive it from
+   the filename argument filenameArg[] and return it as a null-terminated
+   string in newly malloc'ed space at *nameP.
+
+   We take the part of the name after the rightmost slash
+   (i.e. filename without the directory path part), stopping before
+   any period.  We convert any punctuation to underscores.
+
+   If the argument is "-", meaning standard input, we return the name
+   "noname".  Also, if the argument is null or ends in a slash, we
+   return "noname".
+-----------------------------------------------------------------------------*/
+    if (STREQ(filenameArg, "-"))
+        *nameP = strdup("noname");
+    else {
+        int nameIndex, argIndex;
+        /* indices into the input and output buffers */
+
+        /* Start just after the rightmost slash, or at beginning if no slash */
+        if (strrchr(filenameArg, '/') == 0) 
+            argIndex = 0;
+        else argIndex = strrchr(filenameArg, '/') - filenameArg + 1;
+
+        if (filenameArg[argIndex] == '\0') 
+            *nameP = strdup("noname");
+        else {
+            char * name;
+            nameIndex = 0;  /* Start at beginning of name buffer */
+
+            name = malloc(strlen(filenameArg));
+    
+            while (filenameArg[argIndex] != '\0' 
+                   && filenameArg[argIndex] != '.') {
+                const char filenameChar = filenameArg[argIndex++];
+                name[nameIndex++] = 
+                    ISALNUM(filenameChar) ? filenameChar : '_';
+            }
+            name[nameIndex] = '\0';
+            *nameP = name;
+        }
+    }
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    FILE* ifp;
+    bit* bitrow;
+    int rows, cols, format;
+    int padright;
+    int row;
+    const char * inputFilename;
+    const char *name;
+    int itemsperline;
+    int bitsperitem;
+    int item;
+    int firstitem;
+    const char hexchar[] = "0123456789abcdef";
+
+    pbm_init(&argc, argv);
+
+    if (argc-1 > 1)
+        pm_error("Too many arguments (%d).  The only valid argument is an "
+                 "input file name.", argc-1);
+    else if (argc-1 == 1) 
+        inputFilename = argv[1];
+    else
+        inputFilename = "-";
+
+    generateName(inputFilename, &name);
+    ifp = pm_openr(inputFilename);
+    
+    pbm_readpbminit(ifp, &cols, &rows, &format);
+    bitrow = pbm_allocrow(cols);
+    
+    /* Compute padding to round cols up to the nearest multiple of 8. */
+    padright = ((cols + 7)/8) * 8 - cols;
+
+    printf("#define %s_width %d\n", name, cols);
+    printf("#define %s_height %d\n", name, rows);
+    printf("static char %s_bits[] = {\n", name);
+
+    itemsperline = 0;
+    bitsperitem = 0;
+    item = 0;
+    firstitem = 1;
+
+#define PUTITEM \
+    { \
+    if ( firstitem ) \
+        firstitem = 0; \
+    else \
+        putchar( ',' ); \
+    if ( itemsperline == 15 ) \
+        { \
+        putchar( '\n' ); \
+        itemsperline = 0; \
+        } \
+    if ( itemsperline == 0 ) \
+        putchar( ' ' ); \
+    ++itemsperline; \
+    putchar('0'); \
+    putchar('x'); \
+    putchar(hexchar[item >> 4]); \
+    putchar(hexchar[item & 15]); \
+    bitsperitem = 0; \
+    item = 0; \
+    }
+
+#define PUTBIT(b) \
+    { \
+    if ( bitsperitem == 8 ) \
+        PUTITEM; \
+    if ( (b) == PBM_BLACK ) \
+        item += 1 << bitsperitem; \
+    ++bitsperitem; \
+    }
+
+    for (row = 0; row < rows; ++row) {
+        int col;
+        pbm_readpbmrow(ifp, bitrow, cols, format);
+        for (col = 0; col < cols; ++col)
+            PUTBIT(bitrow[col]);
+        for (col = 0; col < padright; ++col)
+            PUTBIT(0);
+    }
+
+    pm_close(ifp);
+
+    if (bitsperitem > 0)
+        PUTITEM;
+    printf("};\n");
+
+    pbm_freerow(bitrow);
+
+    strfree(name);
+
+    exit(0);
+}
diff --git a/converter/pbm/pbmtoybm.c b/converter/pbm/pbmtoybm.c
new file mode 100644
index 00000000..1d2be3d9
--- /dev/null
+++ b/converter/pbm/pbmtoybm.c
@@ -0,0 +1,114 @@
+/* pbmtoybm.c - read a pbm and write a file for Bennet Yee's 'xbm' and 'face'
+** programs.
+**
+** Written by Jamie Zawinski based on code (C) 1988 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 <stdio.h>
+#include "pbm.h"
+
+#define YBM_MAGIC  ( ( '!' << 8 ) | '!' )
+
+static void putinit ARGS(( int cols, int rows ));
+static void putbit ARGS(( bit b ));
+static void putrest ARGS(( void ));
+static void putitem ARGS(( void ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, format, padright, row, col;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[pbmfile]" );
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+    bitrow = pbm_allocrow( cols );
+    
+    /* Compute padding to round cols up to the nearest multiple of 16. */
+    padright = ( ( cols + 15 ) / 16 ) * 16 - cols;
+
+    putinit( cols, rows );
+    for ( row = 0; row < rows; ++row )
+	{
+	pbm_readpbmrow( ifp, bitrow, cols, format );
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+	    putbit( *bP );
+	for ( col = 0; col < padright; ++col )
+	    putbit( 0 );
+        }
+
+    if ( ifp != stdin )
+	fclose( ifp );
+
+    putrest( );
+
+    exit( 0 );
+    }
+
+static long item;
+static int bitsperitem, bitshift;
+
+static void
+putinit( cols, rows )
+    int cols, rows;
+    {
+    pm_writebigshort( stdout, YBM_MAGIC );
+    pm_writebigshort( stdout, cols );
+    pm_writebigshort( stdout, rows );
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 0;
+    }
+
+#if __STDC__
+static void
+putbit( bit b )
+#else /*__STDC__*/
+static void
+putbit( b )
+    bit b;
+#endif /*__STDC__*/
+    {
+    if ( bitsperitem == 16 )
+	putitem( );
+    ++bitsperitem;
+    if ( b == PBM_BLACK )
+	item += 1 << bitshift;
+    ++bitshift;
+    }
+
+static void
+putrest( )
+    {
+    if ( bitsperitem > 0 )
+	putitem( );
+    }
+
+static void
+putitem( )
+    {
+    pm_writebigshort( stdout, item );
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 0;
+    }
diff --git a/converter/pbm/pbmtozinc.c b/converter/pbm/pbmtozinc.c
new file mode 100644
index 00000000..d39b71bc
--- /dev/null
+++ b/converter/pbm/pbmtozinc.c
@@ -0,0 +1,128 @@
+/* pbmtozinc.c - read a portable bitmap and produce an bitmap file
+**               in the format used by the Zinc Interface Library (v1.0)
+**               November 1990.
+**
+** Author: James Darrell McCauley
+**         Department of Agricultural Engineering
+**         Texas A&M University
+**         College Station, Texas 77843-2117 USA
+**
+** Copyright (C) 1988 by James Darrell McCauley (jdm5548@diamond.tamu.edu)
+**                    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 <stdio.h>
+#include <string.h>
+
+#include "nstring.h"
+#include "pbm.h"
+
+int
+main(int argc, char * argv[]) {
+
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, format, padright, row;
+    register int col;
+    char name[100];
+    char* cp;
+    int itemsperline;
+    register int bitsperitem;
+    register int item;
+    int firstitem;
+    const char * const hexchar = "084c2a6e195d3b7f";
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+        pm_usage( "[pbmfile]" );
+
+    if ( argc == 2 )
+	{
+        ifp = pm_openr( argv[1] );
+        strcpy( name, argv[1] );
+        if ( STREQ( name, "-" ) )
+            strcpy( name, "noname" );
+
+        if ( ( cp = strchr( name, '.' ) ) != 0 )
+            *cp = '\0';
+	}
+    else
+	{
+        ifp = stdin;
+        strcpy( name, "noname" );
+	}
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+    bitrow = pbm_allocrow( cols );
+
+    /* Compute padding to round cols up to the nearest multiple of 16. */
+    padright = ( ( cols + 15 ) / 16 ) * 16 - cols;
+
+    printf( "USHORT %s[] = {\n",name);
+    printf( "  %d\n", cols );
+    printf( "  %d\n", rows );
+
+    itemsperline = 0;
+    bitsperitem = 0;
+    item = 0;
+    firstitem = 1;
+
+#define PUTITEM \
+    { \
+    if ( firstitem ) \
+	firstitem = 0; \
+    else \
+	putchar( ',' ); \
+    if ( itemsperline == 11 ) \
+	{ \
+	putchar( '\n' ); \
+	itemsperline = 0; \
+	} \
+    if ( itemsperline == 0 ) \
+	putchar( ' ' ); \
+    ++itemsperline; \
+    putchar('0'); \
+    putchar('x'); \
+    putchar(hexchar[item & 15]); \
+    putchar(hexchar[(item >> 4) & 15]); \
+    putchar(hexchar[(item >> 8) & 15]); \
+    putchar(hexchar[item >> 12]); \
+    bitsperitem = 0; \
+    item = 0; \
+    }
+
+#define PUTBIT(b) \
+    { \
+    if ( bitsperitem == 16 ) \
+	PUTITEM; \
+    if ( (b) == PBM_BLACK ) \
+	item += 1 << bitsperitem; \
+    ++bitsperitem; \
+    }
+
+    for ( row = 0; row < rows; ++row )
+	{
+        pbm_readpbmrow( ifp, bitrow, cols, format );
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+            PUTBIT( *bP );
+        for ( col = 0; col < padright; ++col )
+            PUTBIT( 0 );
+    }
+
+    pm_close( ifp );
+    
+    if ( bitsperitem > 0 )
+        PUTITEM;
+    printf( "};\n" );
+
+    return 0;
+}
diff --git a/converter/pbm/pi3topbm.c b/converter/pbm/pi3topbm.c
new file mode 100644
index 00000000..8b3b21e3
--- /dev/null
+++ b/converter/pbm/pi3topbm.c
@@ -0,0 +1,112 @@
+/*
+ * Convert a ATARI Degas .pi3 file to a portable bitmap file.
+ *
+ * Author: David Beckemeyer
+ *
+ * This code was derived from the original gemtopbm program written
+ * by Diomidis D. Spinellis.
+ *
+ * (C) Copyright 1988 David Beckemeyer and Diomidis D. Spinellis.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include "pbm.h"
+
+int
+main(argc, argv)
+	int             argc;
+	char           *argv[];
+{
+	int             debug = 0;
+	FILE           *f;
+	int             x;
+	int             i, k;
+	int             c;
+	int		rows, cols;
+	bit		*bitrow;
+	short res;
+	int black, white;
+	const char * const usage = "[-debug] [pi3file]";
+	int argn = 1;
+
+	pbm_init( &argc, argv );
+
+	while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0')
+	  {
+	    if (pm_keymatch(argv[1], "-debug", 2))
+	      debug = 1;
+	    else
+	      pm_usage (usage);
+	    ++argn;
+	  }
+
+	if (argn == argc)
+	    f = stdin;
+	else
+	  {
+	    f = pm_openr (argv[argn]);
+	    ++argn;
+	  }
+
+	if (argn != argc)
+	  pm_usage (usage);
+
+	if (pm_readbigshort (f, &res) == -1)
+		pm_error ("EOF / read error");
+
+	if (debug)
+		pm_message ("resolution is %d", res);
+
+	/* only handles hi-rez 640x400 */
+	if (res != 2)
+		pm_error( "bad resolution" );
+
+	pm_readbigshort (f, &res);
+	if (res == 0)
+	  {
+	    black = PBM_WHITE;
+	    white = PBM_BLACK;
+	  }
+	else
+	  {
+	    black = PBM_BLACK;
+	    white = PBM_WHITE;
+	  }
+
+	for (i = 1; i < 16; i++)
+	  if (pm_readbigshort (f, &res) == -1)
+	    pm_error ("EOF / read error");
+
+	cols = 640;
+	rows = 400;
+	pbm_writepbminit( stdout, cols, rows, 0 );
+	bitrow = pbm_allocrow( cols );
+
+	for (i = 0; i < rows; ++i) {
+		x = 0;
+		while (x < cols) {
+			if ((c = getc(f)) == EOF)
+				pm_error( "end of file reached" );
+			for (k = 0x80; k; k >>= 1) {
+				bitrow[x] = (k & c) ? black : white;
+				++x;
+			}
+		}
+		pbm_writepbmrow( stdout, bitrow, cols, 0 );
+	}
+	pm_close( f );
+	pm_close( stdout );
+	exit(0);
+}
diff --git a/converter/pbm/pktopbm.c b/converter/pbm/pktopbm.c
new file mode 100644
index 00000000..32ed4fde
--- /dev/null
+++ b/converter/pbm/pktopbm.c
@@ -0,0 +1,442 @@
+/*
+  pktopbm, adapted from "pktopx in C by Tomas Rokicki" by AJCD 1/8/90
+  1998-09-22: jcn <janneke@gnu.org>
+     - lots of bugfixes:
+     * always read x/y offset bytes (3x)
+     * reset bmx, bmy to defaults for each char
+     * fix bitmap y placement of dynamically packed char
+     * skip char early if no output file allocated
+     - added debug output
+  
+  compile with: cc -lpbm -o pktopbm pktopbm.c
+  */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "nstring.h"
+#include "pbm.h"
+
+#define NAMELENGTH 80
+#define MAXROWWIDTH 3200
+#define MAXPKCHAR 256
+
+typedef int integer ;
+typedef unsigned char quarterword ;
+typedef char boolean ;
+typedef quarterword eightbits ;
+
+static FILE *pkfile ;
+static char pkname[NAMELENGTH+1] ;
+static integer pktopbm_pkloc = 0;
+static char *filename[MAXPKCHAR] ;
+static bit **bitmap = NULL ;
+static integer dynf ;
+static eightbits inputbyte ;
+static eightbits bitweight ;
+static integer repeatcount ;
+static integer flagbyte ;
+static integer debug=0;
+
+#define dprintf(s,d) if (debug) printf(s,d)
+#define dprintf0(s) if (debug) printf(s)
+
+/* add a suffix to a filename in an allocated space */
+static void
+pktopbm_add_suffix(char *       const name, 
+                   const char * const suffix) {
+
+    char * const slash = strrchr(name, '/');
+    char * const dot   = strrchr(name, '.');
+    
+    if ((dot && slash ? dot < slash : !dot) && !STREQ(name, "-"))
+        strcat(name, suffix);
+}
+
+
+
+/* get a byte from the PK file */
+static eightbits 
+pktopbm_pkbyte(void) {
+   pktopbm_pkloc++ ;
+   return(getc(pkfile)) ;
+}
+
+
+
+/* get a 16-bit half word from the PK file */
+static integer 
+get16(void) {
+   integer const a = pktopbm_pkbyte() ;
+   return((a<<8) + pktopbm_pkbyte()) ;
+}
+
+
+
+/* get a 32-bit word from the PK file */
+static integer get32(void) {
+    integer a;
+    a = get16() ;
+    if (a > 32767) a -= 65536 ;
+    return((a<<16) + get16()) ;
+}
+
+
+
+/* get a nibble from current input byte, or new byte if no current byte */
+static integer 
+getnyb(void) {
+    eightbits temp;
+    if (bitweight == 0) {
+        inputbyte = pktopbm_pkbyte() ;
+        bitweight = 16 ;
+    }
+    temp = inputbyte / bitweight ;
+    inputbyte -= temp * bitweight ;
+    bitweight >>= 4 ;
+    return(temp) ;
+}
+
+
+
+/* get a bit from the current input byte, or a new byte if no current byte */
+static bool
+getbit(void) {
+    bool temp ;
+    bitweight >>= 1 ;
+    if (bitweight == 0) {
+        inputbyte = pktopbm_pkbyte() ;
+        bitweight = 128 ;
+    }
+    temp = (inputbyte >= bitweight) ;
+    if (temp) inputbyte -= bitweight ;
+    return(temp) ;
+}
+
+
+
+/* unpack a dynamically packed number. dynf is dynamic packing threshold  */
+static integer 
+pkpackednum(void) {
+    integer i, j ;
+    i = getnyb() ;
+    if (i == 0) {           /* large run count, >= 3 nibbles */
+        do {
+            j = getnyb() ;          /* count extra nibbles */
+            i++ ;
+        } while (j == 0) ;
+        while (i > 0) {
+            j = (j<<4) + getnyb() ; /* add extra nibbles */
+            i-- ;
+        }
+        return (j - 15 +((13 - dynf)<<4) + dynf) ;
+    } else if (i <= dynf) return (i) ;  /* number > 0 and <= dynf */
+    else if (i < 14) return (((i - dynf - 1)<<4) + getnyb() + dynf + 1) ;
+    else {
+        if (i == 14) repeatcount = pkpackednum() ;  /* get repeat count */
+        else repeatcount = 1 ;      /* value 15 indicates repeat count 1 */
+        return(pkpackednum()) ;
+    }
+}
+
+
+
+/* skip specials in PK files, inserted by Metafont or some other program */
+static void
+skipspecials(void) {
+    integer i, j;
+    do {
+        flagbyte = pktopbm_pkbyte() ;
+        if (flagbyte >= 240)
+            switch(flagbyte) {
+            case 240:           /* specials of size 1-4 bytes */
+            case 241:
+            case 242:
+            case 243:
+                i = 0 ;
+                for (j = 240 ; j <= flagbyte ; ++j) 
+                    i = (i<<8) + pktopbm_pkbyte() ;
+                for (j = 1 ; j <= i ; ++j) 
+                    pktopbm_pkbyte() ;  /* ignore special */
+                break ;
+            case 244:           /* no-op, parameters to specials */
+                get32() ;
+            case 245:           /* start of postamble */
+            case 246:           /* no-op */
+                break ;
+            case 247:           /* pre-amble in wrong place */
+            case 248:
+            case 249:
+            case 250:
+            case 251:
+            case 252:
+            case 253:
+            case 254:
+            case 255:
+                pm_error("unexpected flag byte %d", flagbyte) ;
+            }
+    } while (!(flagbyte < 240 || flagbyte == 245)) ;
+}
+
+
+
+/* ignore character packet */
+static void
+ignorechar(integer const car, 
+           integer const endofpacket) {
+
+   while (pktopbm_pkloc != endofpacket) pktopbm_pkbyte() ;
+   if (car < 0 || car >= MAXPKCHAR)
+      pm_message("Character %d out of range", car) ;
+   skipspecials() ;
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+    integer x;
+    integer endofpacket ;
+    boolean turnon ;
+    integer i, j;
+    integer car ;
+    integer bmx=0, bmy=0;
+    integer set_bmx=0, set_bmy=0;
+    bit row[MAXROWWIDTH+1] ;
+    const char * const usage = 
+        "pkfile[.pk] [-d] [[-x width] [-y height] [-c num] pbmfile]...";
+   
+    pbm_init(&argc, argv);
+    for (i = 0 ; i < MAXPKCHAR ; i ++) filename[i] = NULL ;
+
+    pm_message("This is PKtoPBM, version 2.5") ;
+
+    if (--argc < 1) pm_usage(usage) ;
+
+    strcpy(pkname, *++argv) ;
+    pktopbm_add_suffix(pkname, ".pk") ;
+
+    car = 0 ;
+    /* urg: use getopt */
+    while (++argv, --argc) {
+        if (argv[0][0] == '-' && argv[0][1])
+            switch (argv[0][1]) {
+            case 'X':
+            case 'x':
+                if (argv[0][2]) bmx = atoi(*argv+2) ;
+                else if (++argv, --argc) set_bmx = atoi(*argv) ;
+                else pm_usage(usage) ;
+                continue ;
+            case 'Y':
+            case 'y':
+                if (argv[0][2]) bmy = atoi(*argv+2) ;
+                else if (++argv, --argc) set_bmy = atoi(*argv) ;
+                else pm_usage(usage) ;
+                continue ;
+            case 'C':
+            case 'c':
+                if (argv[0][2]) car = atoi(*argv+2) ;
+                else if (++argv, --argc) car = atoi(*argv) ;
+                else pm_usage(usage) ;
+                break ;
+            case 'd':
+                debug=1;
+                break ;
+            default:
+                pm_usage(usage) ;
+            } else if (car < 0 || car >= MAXPKCHAR) {
+                pm_error("character must be in range 0 to %d (-c)", 
+                         MAXPKCHAR-1) ;
+            } else filename[car++] = *argv ;
+    }
+
+    pkfile = pm_openr(pkname);
+    if (pktopbm_pkbyte() != 247)
+        pm_error("bad PK file (pre command missing)") ;
+    if (pktopbm_pkbyte() != 89)
+        pm_error("wrong version of packed file") ;
+    j = pktopbm_pkbyte() ;              /* get header comment size */
+    for (i = 1 ; i <= j ; i ++) pktopbm_pkbyte() ;  /* ignore header comment */
+    get32() ;                   /* ignore designsize */
+    get32() ;                   /* ignore checksum */
+    if (get32() != get32())         /* h & v pixels per point */
+        pm_message("Warning: aspect ratio not 1:1") ;
+    skipspecials() ;
+    while (flagbyte != 245) {           /* not at postamble */
+        integer cheight, cwidth ;
+        integer xoffs=0, yoffs=0;
+        FILE *ofp;
+
+        bmx=set_bmx;
+        bmy=set_bmy;
+        dynf = (flagbyte>>4) ;          /* get dynamic packing value */
+        flagbyte &= 15 ;
+        turnon = (flagbyte >= 8) ;      /* black or white initially? */
+        if (turnon) flagbyte &= 7 ;     /* long or short form */
+        if (flagbyte == 7) {            /* long form preamble */
+            integer packetlength = get32() ;    /* character packet length */
+            car = get32() ;         /* character number */
+            endofpacket = packetlength + pktopbm_pkloc;
+                /* calculate end of packet */
+            if ((car >= MAXPKCHAR) || !filename[car]) {
+                ignorechar(car, endofpacket);
+                continue;
+            }
+            dprintf0 ("flagbyte7\n");
+            dprintf ("car: %d\n", car);
+            get32() ;               /* ignore tfmwidth */
+            x=get32() ;             /* ignore horiz escapement */
+            x=get32() ;             /* ignore vert escapement */
+            dprintf ("horiz esc %d\n", x);
+            dprintf ("vert esc %d\n", x);
+            cwidth = get32() ;          /* bounding box width */
+            cheight = get32() ;         /* bounding box height */
+            dprintf ("cwidth %d\n", cwidth);
+            dprintf ("cheight %d\n", cheight);
+            if (cwidth < 0 || cheight < 0 || 
+                cwidth > 65535 || cheight > 65535) {
+                ignorechar(car, endofpacket);
+                continue;
+            }
+            xoffs= get32() ;              /* horiz offset */
+            yoffs= get32() ;              /* vert offset */
+            dprintf ("xoffs %d\n", xoffs);
+            dprintf ("yoffs %d\n", yoffs);
+        } else if (flagbyte > 3) {      /* extended short form */
+            integer packetlength = ((flagbyte - 4)<<16) + get16() ;
+            /* packet length */
+            car = pktopbm_pkbyte() ;            /* char number */
+            endofpacket = packetlength + pktopbm_pkloc ; 
+                /* calculate end of packet */
+            if ((car >= MAXPKCHAR) || !filename[car]) {
+                ignorechar(car, endofpacket);
+                continue;
+            }
+            dprintf0 ("flagbyte>3\n");
+            dprintf ("car: %d\n", car);
+            pktopbm_pkbyte() ;              /* ignore tfmwidth (3 bytes) */
+            get16() ;               /* ignore tfmwidth (3 bytes) */
+            get16() ;               /* ignore horiz escapement */
+            cwidth = get16() ;          /* bounding box width */
+            cheight = get16() ;         /* bounding box height */
+            dprintf ("cwidth %d\n", cwidth);
+            dprintf ("cheight %d\n", cheight);
+            xoffs=get16();                         /* horiz offset */
+            if (xoffs >= 32768)
+                xoffs-= 65536;
+            yoffs=get16();                         /* vert offset */
+            if (yoffs >= 32768)
+                yoffs-= 65536;
+            dprintf ("xoffs %d\n", xoffs);
+            dprintf ("yoffs %d\n", yoffs);
+        } else {                    /* short form preamble */
+            integer packetlength = (flagbyte<<8) + pktopbm_pkbyte() ;
+            /* packet length */
+            car = pktopbm_pkbyte() ;            /* char number */
+            endofpacket = packetlength + pktopbm_pkloc ;    
+                /* calculate end of packet */
+            if ((car >= MAXPKCHAR) || !filename[car]) {
+                ignorechar(car, endofpacket);
+                continue;
+            }
+            dprintf0 ("flagbyte<=3\n");
+            dprintf ("car: %d\n", car);
+            pktopbm_pkbyte() ;          /* ignore tfmwidth (3 bytes) */
+            get16() ;               /* ignore tfmwidth (3 bytes) */
+            x = pktopbm_pkbyte() ;  /* ignore horiz escapement */
+            dprintf ("horiz esc %d\n", x);
+            cwidth = pktopbm_pkbyte() ;            /* bounding box width */
+            cheight = pktopbm_pkbyte() ;           /* bounding box height */
+            dprintf ("cwidth %d\n", cwidth);
+            dprintf ("cheight %d\n", cheight);
+            xoffs=pktopbm_pkbyte ();               /* horiz offset */
+            if (xoffs >= 128)
+                xoffs-=256;
+            yoffs=pktopbm_pkbyte ();               /* vert offset */
+            if (yoffs >= 128)
+                yoffs-=256;
+            dprintf ("xoffs %d\n", xoffs);
+            dprintf ("yoffs %d\n", yoffs);
+        }
+        if (filename[car]) {
+            if (!bmx) bmx= cwidth;
+            if (!bmy) bmy= cheight;
+            bitmap = pbm_allocarray(bmx, bmy) ;
+            if (bitmap == NULL)
+                pm_error("out of memory allocating bitmap") ;
+        } else {
+            ignorechar(car, endofpacket);
+            continue;
+        }
+        bitweight = 0 ;
+        for (i = 0 ; i < bmy ; i ++)           /* make it blank */
+            for (j = 0 ; j < bmx ; j ++)
+                bitmap[i][j]= PBM_WHITE;
+        if (dynf == 14) {               /* bitmapped character */
+            dprintf ("bmy: %d\n ", bmy);
+            dprintf ("y: %d\n ", bmy-yoffs-1);
+            for (i = 0 ; i < cheight ; i ++) {
+                int yi= i+(bmy-yoffs-1);
+                for (j = 0 ; j < cwidth ; j ++) {
+                    int xj= j-xoffs;
+                    if (getbit() && 0<=xj && xj<bmx && 0<=yi && yi<bmy)
+                        bitmap[yi][xj] = PBM_BLACK ;
+                }
+            }
+        } else {                    /* dynamically packed char */
+            integer rowsleft = cheight ;
+            integer hbit = cwidth ;
+            integer rp = 0;
+            repeatcount = 0 ;
+            dprintf ("bmy: %d\n ", bmy);
+            dprintf ("y: %d\n", cheight-rowsleft+(bmy-2*yoffs-1));
+            while (rowsleft > 0) {
+                integer count = pkpackednum() ; /* get current color count */
+                while (count > 0) {
+                    if (count < hbit) {     /* doesn't extend past row */
+                        hbit -= count ;
+                        while (count--)
+                            row[rp++] = turnon ? PBM_BLACK : PBM_WHITE;
+                    } else {                /* reaches end of row */
+                        count -= hbit ;
+                        while (hbit--)
+                            row[rp++] = turnon ? PBM_BLACK : PBM_WHITE;
+                        for (i = 0; i <= repeatcount; i++) {  /* fill row */
+                            int yi= i+cheight-rowsleft-1;
+                            if (0<=yi && yi < bmy)
+                                for (j = 0; j < cwidth; j++) {
+                                    int xj= j-xoffs;
+                                    if (0<=xj && xj<bmx)
+                                        bitmap[yi][xj] = row[j] ;
+                                }
+                        }
+                        rowsleft -= repeatcount + 1;
+                        repeatcount = rp = 0 ;
+                        hbit = cwidth ;
+                    }
+                }
+                turnon = !turnon ;
+            }
+            if (rowsleft != 0 || hbit != cwidth)
+                pm_error("bad pk file (more bits than required)") ;
+        }
+        if (endofpacket != pktopbm_pkloc)
+            pm_error("bad pk file (bad packet length)") ;
+
+        ofp = pm_openw(filename[car]);
+        filename[car] = NULL;
+        pbm_writepbm(ofp, bitmap, bmx, bmy, 0) ;
+        pbm_freearray(bitmap, bmy) ;
+        pm_close(ofp) ;
+        skipspecials() ;
+    }
+    while (! feof(pkfile)) pktopbm_pkbyte() ;       /* skip trailing junk */
+    pm_close(pkfile);
+    for (car = 0; car < MAXPKCHAR; car++)
+        if (filename[car])
+            pm_message("Warning: No character in position %d (file %s).",
+                       car, filename[car]) ;
+    pm_message("%d bytes read from packed file.", pktopbm_pkloc-1) ;
+    return 0;
+}
diff --git a/converter/pbm/thinkjettopbm.c b/converter/pbm/thinkjettopbm.c
new file mode 100644
index 00000000..2f5f6fda
--- /dev/null
+++ b/converter/pbm/thinkjettopbm.c
@@ -0,0 +1,1792 @@
+/* This is the file 'lexheader'.  It contains extra stuff needed by
+   parsers generated by lex.
+
+   GNU Flex generates a parser that refers to the non-ansi C library 
+   subroutine fileno().  In order to let it compile without warnings,
+   we define _XOPEN_SOURCE to declare that fact.
+*/
+#define _XOPEN_SOURCE
+/* END OF lexheader */
+/* A lexical scanner generated by flex */
+
+/* Scanner skeleton version:
+ * $Header: /home/daffy/u0/vern/flex/RCS/flex.skl,v 2.91 96/09/10 16:58:48 vern Exp $
+ */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+
+#include <stdio.h>
+
+
+/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+
+
+#ifdef __cplusplus
+
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Use prototypes in function declarations. */
+#define YY_USE_PROTOS
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_PROTOS
+#define YY_USE_CONST
+
+#endif	/* __STDC__ */
+#endif	/* ! __cplusplus */
+
+#ifdef __TURBOC__
+ #pragma warn -rch
+ #pragma warn -use
+#include <io.h>
+#include <stdlib.h>
+#define YY_USE_CONST
+#define YY_USE_PROTOS
+#endif
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+
+#ifdef YY_USE_PROTOS
+#define YY_PROTO(proto) proto
+#else
+#define YY_PROTO(proto) ()
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#define YY_BUF_SIZE 16384
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+extern int yyleng;
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+/* The funky do-while in the following #define is used to turn the definition
+ * int a single C statement (which needs a semi-colon terminator).  This
+ * avoids problems with code like:
+ *
+ * 	if ( condition_holds )
+ *		yyless( 5 );
+ *	else
+ *		do_something_else();
+ *
+ * Prior to using the do-while the compiler would get upset at the
+ * "else" because it interpreted the "if" statement as being all
+ * done when it reached the ';' after the yyless() call.
+ */
+
+/* Return all but the first 'n' matched characters back to the input stream. */
+
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+		*yy_cp = yy_hold_char; \
+		YY_RESTORE_YY_MORE_OFFSET \
+		yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+		} \
+	while ( 0 )
+
+#define unput(c) yyunput( c, yytext_ptr )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+typedef unsigned int yy_size_t;
+
+
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via yyrestart()), so that the user can continue scanning by
+	 * just pointing yyin at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+	};
+
+static YY_BUFFER_STATE yy_current_buffer = 0;
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ */
+#define YY_CURRENT_BUFFER yy_current_buffer
+
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+
+static int yy_n_chars;		/* number of characters read into yy_ch_buf */
+
+
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 1;		/* whether we need to initialize */
+static int yy_start = 0;	/* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart YY_PROTO(( FILE *input_file ));
+
+void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer ));
+void yy_load_buffer_state YY_PROTO(( void ));
+YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size ));
+void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file ));
+void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer )
+
+YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size ));
+YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str ));
+YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len ));
+
+static void *yy_flex_alloc YY_PROTO(( yy_size_t ));
+static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t ));
+static void yy_flex_free YY_PROTO(( void * ));
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! yy_current_buffer ) \
+		yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+	yy_current_buffer->yy_is_interactive = is_interactive; \
+	}
+
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! yy_current_buffer ) \
+		yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+	yy_current_buffer->yy_at_bol = at_bol; \
+	}
+
+#define YY_AT_BOL() (yy_current_buffer->yy_at_bol)
+
+typedef unsigned char YY_CHAR;
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+typedef int yy_state_type;
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state YY_PROTO(( void ));
+static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state ));
+static int yy_get_next_buffer YY_PROTO(( void ));
+static void yy_fatal_error YY_PROTO(( yyconst char msg[] ));
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+	yytext_ptr = yy_bp; \
+	yyleng = (int) (yy_cp - yy_bp); \
+	yy_hold_char = *yy_cp; \
+	*yy_cp = '\0'; \
+	yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 10
+#define YY_END_OF_BUFFER 11
+static yyconst short int yy_accept[34] =
+    {   0,
+        0,    0,    0,    0,    0,    0,   11,    9,    9,   10,
+        4,   10,    1,    0,    0,    0,    0,    0,    0,    0,
+        0,    0,    0,    0,    8,    0,    3,    5,    5,    7,
+        6,    2,    0
+    } ;
+
+static yyconst int yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    2,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    3,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    4,    1,    1,
+        1,    5,    1,    1,    1,    6,    1,    7,    7,    7,
+        7,    7,    7,    7,    7,    7,    7,    1,    1,    1,
+        1,    1,    1,    1,    8,    9,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,   10,    1,    1,    1,   11,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,   12,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,   13,    1,    1,
+        1,    1,    1,   14,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst int yy_meta[15] =
+    {   0,
+        1,    2,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1
+    } ;
+
+static yyconst short int yy_base[38] =
+    {   0,
+       35,   34,    0,    5,    0,    0,   36,   39,    0,   39,
+       39,   30,   39,   21,    0,    1,   26,   25,    2,   24,
+       21,   15,    9,   11,   39,   12,   39,   39,   10,   39,
+       39,   39,   39,   23,   25,   27,    0
+    } ;
+
+static yyconst short int yy_def[38] =
+    {   0,
+       34,   34,   35,   35,   36,   36,   33,   33,   33,   33,
+       33,   33,   33,   33,   33,   33,   33,   33,   33,   33,
+       33,   37,   33,   33,   33,   33,   33,   33,   37,   33,
+       33,   33,    0,   33,   33,   33,   33
+    } ;
+
+static yyconst short int yy_nxt[54] =
+    {   0,
+       28,   11,   12,   14,   15,   11,   11,   12,   24,   25,
+       11,   18,   20,   19,   21,   23,   29,   24,   26,   30,
+       31,   29,   32,    8,    8,   10,   10,   13,   13,   27,
+       26,   23,   22,   17,   16,   33,    9,    9,    7,   33,
+       33,   33,   33,   33,   33,   33,   33,   33,   33,   33,
+       33,   33,   33
+    } ;
+
+static yyconst short int yy_chk[54] =
+    {   0,
+       37,    3,    3,    9,    9,    3,    4,    4,   19,   19,
+        4,   15,   16,   15,   16,   23,   29,   24,   26,   23,
+       24,   22,   26,   34,   34,   35,   35,   36,   36,   21,
+       20,   18,   17,   14,   12,    7,    2,    1,   33,   33,
+       33,   33,   33,   33,   33,   33,   33,   33,   33,   33,
+       33,   33,   33
+    } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "thinkjettopbm.l"
+#define INITIAL 0
+/*
+ * A user reported in January 2005 a problem building Thinkjettopbm
+ * in which the opening comment delimiter above for some reason did
+ * not make it into the Lex output in the Netpbm build of this.
+ * Needless to say, that would not compile.  This user was using
+ * 'lex -t' on Tru64.  We did not find it worthwhile to debug it.
+ *
+ * Simple FLEX scanner to convert HP ThinkJet graphics image
+ * to PBM format.
+ *
+ * Implements a small subset of ThinkJet commands.
+ *
+ * Copyright (C) 2001 by W. Eric Norum
+ *
+ * Department of Electrical Engineering
+ * University of Saskatchewan
+ * Saskatoon, Saskatchewan, CANADA
+ * eric.norum@usask.ca
+ *
+ *  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 2001.04.05 by Bryan Henderson for inclusion in the Netpbm
+ *  package.  Now uses Netpbm libraries and, for consistency with other
+ *  Netpbm programs, does not have PGM output option.
+ */
+#line 34 "thinkjettopbm.l"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include "pbm.h"
+#include "shhopt.h"
+
+/* The following macro definitions tell Lex what sort of code to generate.
+   GNU Flex does #if XXX for some of these, as opposed to #ifdef XXX, which
+   means that they properly have to be set to zero instead of just left
+   undefined.  (Simply leaving them undefined typically works anyway, but it
+   is a problem if you use compiler options that say to fail when someone
+   uses a macro he failed to define).
+*/
+#define YY_NO_UNPUT
+#define YY_STACK_USED 0
+#define YY_ALWAYS_INTERACTIVE 0
+#define YY_NEVER_INTERACTIVE 0
+#define YY_MAIN 0
+    /* Don't include the main() function.  We have our own */
+
+static int yylex(void);
+static int yywrap(void);
+/* This works, but generates a warning 
+static void yyrestart(FILE*);
+*/
+
+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 debug;
+};
+
+
+
+struct RowInfo {
+    int     length;    /* length, in bytes */
+    char    *bits;     /* Bitmap */
+};
+
+static int maxRowLength;
+static int rowCount;
+static int rowCapacity;
+static struct RowInfo *rows;
+
+static int column;
+
+int debugFlag;
+static void debug (const char *format, ...);
+
+#define RASTERMODE 1
+#define ROWMODE 2
+
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap YY_PROTO(( void ));
+#else
+extern int yywrap YY_PROTO(( void ));
+#endif
+#endif
+
+#ifndef YY_NO_UNPUT
+static void yyunput YY_PROTO(( int c, char *buf_ptr ));
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int ));
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen YY_PROTO(( yyconst char * ));
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+static int yyinput YY_PROTO(( void ));
+#else
+static int input YY_PROTO(( void ));
+#endif
+#endif
+
+#if YY_STACK_USED
+static int yy_start_stack_ptr = 0;
+static int yy_start_stack_depth = 0;
+static int *yy_start_stack = 0;
+#ifndef YY_NO_PUSH_STATE
+static void yy_push_state YY_PROTO(( int new_state ));
+#endif
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state YY_PROTO(( void ));
+#endif
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state YY_PROTO(( void ));
+#endif
+
+#else
+#define YY_NO_PUSH_STATE 1
+#define YY_NO_POP_STATE 1
+#define YY_NO_TOP_STATE 1
+#endif
+
+#ifdef YY_MALLOC_DECL
+YY_MALLOC_DECL
+#else
+#if __STDC__
+#ifndef __cplusplus
+#include <stdlib.h>
+#endif
+#else
+/* Just try to get by without declaring the routines.  This will fail
+ * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int)
+ * or sizeof(void*) != sizeof(int).
+ */
+#endif
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	if ( yy_current_buffer->yy_is_interactive ) \
+		{ \
+		int c = '*', n; \
+		for ( n = 0; n < max_size && \
+			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+			buf[n] = (char) c; \
+		if ( c == '\n' ) \
+			buf[n++] = (char) c; \
+		if ( c == EOF && ferror( yyin ) ) \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+		result = n; \
+		} \
+	else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \
+		  && ferror( yyin ) ) \
+		YY_FATAL_ERROR( "input in flex scanner failed" );
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL int yylex YY_PROTO(( void ))
+#endif
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+	YY_USER_ACTION
+
+YY_DECL
+	{
+	register yy_state_type yy_current_state;
+	register char *yy_cp, *yy_bp;
+	register int yy_act;
+
+#line 95 "thinkjettopbm.l"
+
+
+
+	if ( yy_init )
+		{
+		yy_init = 0;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! yy_start )
+			yy_start = 1;	/* first start state */
+
+		if ( ! yyin )
+			yyin = stdin;
+
+		if ( ! yyout )
+			yyout = stdout;
+
+		if ( ! yy_current_buffer )
+			yy_current_buffer =
+				yy_create_buffer( yyin, YY_BUF_SIZE );
+
+		yy_load_buffer_state();
+		}
+
+	while ( 1 )		/* loops until end-of-file is reached */
+		{
+		yy_cp = yy_c_buf_p;
+
+		/* Support of yytext. */
+		*yy_cp = yy_hold_char;
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = yy_start;
+yy_match:
+		do
+			{
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+			if ( yy_accept[yy_current_state] )
+				{
+				yy_last_accepting_state = yy_current_state;
+				yy_last_accepting_cpos = yy_cp;
+				}
+			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+				{
+				yy_current_state = (int) yy_def[yy_current_state];
+				if ( yy_current_state >= 34 )
+					yy_c = yy_meta[(unsigned int) yy_c];
+				}
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+			++yy_cp;
+			}
+		while ( yy_base[yy_current_state] != 39 );
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+		if ( yy_act == 0 )
+			{ /* have to back up */
+			yy_cp = yy_last_accepting_cpos;
+			yy_current_state = yy_last_accepting_state;
+			yy_act = yy_accept[yy_current_state];
+			}
+
+		YY_DO_BEFORE_ACTION;
+
+
+do_action:	/* This label is used only to access EOF actions. */
+
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+			case 0: /* must back up */
+			/* undo the effects of YY_DO_BEFORE_ACTION */
+			*yy_cp = yy_hold_char;
+			yy_cp = yy_last_accepting_cpos;
+			yy_current_state = yy_last_accepting_state;
+			goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 97 "thinkjettopbm.l"
+{
+                        rows[rowCount].bits[column++] = yytext[0]; 
+                        if (column >= rows[rowCount].length) {
+                            rowCount++;
+                            debug ("Done %d-byte row %d.\n", column, rowCount);
+                            BEGIN (RASTERMODE);
+                        }
+                        }
+	YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 106 "thinkjettopbm.l"
+{
+                            int l;
+                            if (rowCount >= rowCapacity) {
+                                rowCapacity += 100;
+                                rows = realloc (rows, rowCapacity * sizeof *rows);
+                                if (rows == NULL)
+                                    pm_error ("Out of memory.");
+                            }
+                            l = atoi (yytext+3);
+                            rows[rowCount].length = l;
+                            rows[rowCount].bits = malloc (l);
+                            if (rows[rowCount].bits == NULL)
+                                pm_error ("Out of memory.");
+                            if (l > maxRowLength)
+                                maxRowLength = l;
+                            debug ("Start %d-byte row.\n", l);
+                            column = 0;
+                            BEGIN (ROWMODE);
+                            }
+	YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 126 "thinkjettopbm.l"
+{
+                       debug ("Match <esc>*rB\n");
+                       BEGIN (0);
+                       }
+	YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 131 "thinkjettopbm.l"
+{ pm_error ("Unexpected character (%#x) in raster mode.\n", yytext[0]); }
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 133 "thinkjettopbm.l"
+{ debug ("Match <esc>&l\n"); }
+	YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 134 "thinkjettopbm.l"
+{ debug ("Match <esc>*r#S\n"); }
+	YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 135 "thinkjettopbm.l"
+{ debug ("Match <esc>*r#w\n"); }
+	YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 136 "thinkjettopbm.l"
+{
+                       debug ("Match <esc>*rA\n");
+                       BEGIN (RASTERMODE);
+                       }
+	YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 141 "thinkjettopbm.l"
+{ /* Silently consume all other characters */ }
+	YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 143 "thinkjettopbm.l"
+ECHO;
+	YY_BREAK
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(RASTERMODE):
+case YY_STATE_EOF(ROWMODE):
+	yyterminate();
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = yy_hold_char;
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed yyin at a new source and called
+			 * yylex().  If so, then we have to assure
+			 * consistency between yy_current_buffer and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			yy_n_chars = yy_current_buffer->yy_n_chars;
+			yy_current_buffer->yy_input_file = yyin;
+			yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state();
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+			yy_bp = yytext_ptr + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++yy_c_buf_p;
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = yy_c_buf_p;
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer() )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				yy_did_buffer_switch_on_eof = 0;
+
+				if ( yywrap() )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * yytext, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				yy_c_buf_p =
+					yytext_ptr + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state();
+
+				yy_cp = yy_c_buf_p;
+				yy_bp = yytext_ptr + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				yy_c_buf_p =
+				&yy_current_buffer->yy_ch_buf[yy_n_chars];
+
+				yy_current_state = yy_get_previous_state();
+
+				yy_cp = yy_c_buf_p;
+				yy_bp = yytext_ptr + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+	} /* end of yylex */
+
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+
+static int yy_get_next_buffer()
+	{
+	register char *dest = yy_current_buffer->yy_ch_buf;
+	register char *source = yytext_ptr;
+	register int number_to_move, i;
+	int ret_val;
+
+	if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( yy_current_buffer->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1;
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		yy_current_buffer->yy_n_chars = yy_n_chars = 0;
+
+	else
+		{
+		int num_to_read =
+			yy_current_buffer->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+#ifdef YY_USES_REJECT
+			YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+#else
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = yy_current_buffer;
+
+			int yy_c_buf_p_offset =
+				(int) (yy_c_buf_p - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				int new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					yy_flex_realloc( (void *) b->yy_ch_buf,
+							 b->yy_buf_size + 2 );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = 0;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = yy_current_buffer->yy_buf_size -
+						number_to_move - 1;
+#endif
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]),
+			yy_n_chars, num_to_read );
+
+		yy_current_buffer->yy_n_chars = yy_n_chars;
+		}
+
+	if ( yy_n_chars == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			yyrestart( yyin );
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			yy_current_buffer->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	yy_n_chars += number_to_move;
+	yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+	yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+	yytext_ptr = &yy_current_buffer->yy_ch_buf[0];
+
+	return ret_val;
+	}
+
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+static yy_state_type yy_get_previous_state()
+	{
+	register yy_state_type yy_current_state;
+	register char *yy_cp;
+
+	yy_current_state = yy_start;
+
+	for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp )
+		{
+		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 6);
+		if ( yy_accept[yy_current_state] )
+			{
+			yy_last_accepting_state = yy_current_state;
+			yy_last_accepting_cpos = yy_cp;
+			}
+		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+			{
+			yy_current_state = (int) yy_def[yy_current_state];
+			if ( yy_current_state >= 34 )
+				yy_c = yy_meta[(unsigned int) yy_c];
+			}
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+		}
+
+	return yy_current_state;
+	}
+
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+
+#ifdef YY_USE_PROTOS
+static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state )
+#else
+static yy_state_type yy_try_NUL_trans( yy_current_state )
+yy_state_type yy_current_state;
+#endif
+	{
+	register int yy_is_jam;
+	register char *yy_cp = yy_c_buf_p;
+
+	register YY_CHAR yy_c = 6;
+	if ( yy_accept[yy_current_state] )
+		{
+		yy_last_accepting_state = yy_current_state;
+		yy_last_accepting_cpos = yy_cp;
+		}
+	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+		{
+		yy_current_state = (int) yy_def[yy_current_state];
+		if ( yy_current_state >= 34 )
+			yy_c = yy_meta[(unsigned int) yy_c];
+		}
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+	yy_is_jam = (yy_current_state == 33);
+
+	return yy_is_jam ? 0 : yy_current_state;
+	}
+
+
+#ifndef YY_NO_UNPUT
+#ifdef YY_USE_PROTOS
+static void yyunput( int c, register char *yy_bp )
+#else
+static void yyunput( c, yy_bp )
+int c;
+register char *yy_bp;
+#endif
+	{
+	register char *yy_cp = yy_c_buf_p;
+
+	/* undo effects of setting up yytext */
+	*yy_cp = yy_hold_char;
+
+	if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+		{ /* need to shift things up to make room */
+		/* +2 for EOB chars. */
+		register int number_to_move = yy_n_chars + 2;
+		register char *dest = &yy_current_buffer->yy_ch_buf[
+					yy_current_buffer->yy_buf_size + 2];
+		register char *source =
+				&yy_current_buffer->yy_ch_buf[number_to_move];
+
+		while ( source > yy_current_buffer->yy_ch_buf )
+			*--dest = *--source;
+
+		yy_cp += (int) (dest - source);
+		yy_bp += (int) (dest - source);
+		yy_current_buffer->yy_n_chars =
+			yy_n_chars = yy_current_buffer->yy_buf_size;
+
+		if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+			YY_FATAL_ERROR( "flex scanner push-back overflow" );
+		}
+
+	*--yy_cp = (char) c;
+
+
+	yytext_ptr = yy_bp;
+	yy_hold_char = *yy_cp;
+	yy_c_buf_p = yy_cp;
+	}
+#endif	/* ifndef YY_NO_UNPUT */
+
+
+#ifdef __cplusplus
+static int yyinput()
+#else
+static int input()
+#endif
+	{
+	int c;
+
+	*yy_c_buf_p = yy_hold_char;
+
+	if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+			/* This was really a NUL. */
+			*yy_c_buf_p = '\0';
+
+		else
+			{ /* need more input */
+			int offset = yy_c_buf_p - yytext_ptr;
+			++yy_c_buf_p;
+
+			switch ( yy_get_next_buffer() )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					yyrestart( yyin );
+
+					/* fall through */
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( yywrap() )
+						return EOF;
+
+					if ( ! yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput();
+#else
+					return input();
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					yy_c_buf_p = yytext_ptr + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) yy_c_buf_p;	/* cast for 8-bit char's */
+	*yy_c_buf_p = '\0';	/* preserve yytext */
+	yy_hold_char = *++yy_c_buf_p;
+
+
+	return c;
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yyrestart( FILE *input_file )
+#else
+void yyrestart( input_file )
+FILE *input_file;
+#endif
+	{
+	if ( ! yy_current_buffer )
+		yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE );
+
+	yy_init_buffer( yy_current_buffer, input_file );
+	yy_load_buffer_state();
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+#else
+void yy_switch_to_buffer( new_buffer )
+YY_BUFFER_STATE new_buffer;
+#endif
+	{
+	if ( yy_current_buffer == new_buffer )
+		return;
+
+	if ( yy_current_buffer )
+		{
+		/* Flush out information for old buffer. */
+		*yy_c_buf_p = yy_hold_char;
+		yy_current_buffer->yy_buf_pos = yy_c_buf_p;
+		yy_current_buffer->yy_n_chars = yy_n_chars;
+		}
+
+	yy_current_buffer = new_buffer;
+	yy_load_buffer_state();
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (yywrap()) processing, but the only time this flag
+	 * is looked at is after yywrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	yy_did_buffer_switch_on_eof = 1;
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yy_load_buffer_state( void )
+#else
+void yy_load_buffer_state()
+#endif
+	{
+	yy_n_chars = yy_current_buffer->yy_n_chars;
+	yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos;
+	yyin = yy_current_buffer->yy_input_file;
+	yy_hold_char = *yy_c_buf_p;
+	}
+
+
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
+#else
+YY_BUFFER_STATE yy_create_buffer( file, size )
+FILE *file;
+int size;
+#endif
+	{
+	YY_BUFFER_STATE b;
+
+	b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	yy_init_buffer( b, file );
+
+	return b;
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yy_delete_buffer( YY_BUFFER_STATE b )
+#else
+void yy_delete_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+	{
+	if ( ! b )
+		return;
+
+	if ( b == yy_current_buffer )
+		yy_current_buffer = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		yy_flex_free( (void *) b->yy_ch_buf );
+
+	yy_flex_free( (void *) b );
+	}
+
+
+#ifndef YY_ALWAYS_INTERACTIVE
+#ifndef YY_NEVER_INTERACTIVE
+extern int isatty YY_PROTO(( int ));
+#endif
+#endif
+
+#ifdef YY_USE_PROTOS
+void yy_init_buffer( YY_BUFFER_STATE b, FILE *file )
+#else
+void yy_init_buffer( b, file )
+YY_BUFFER_STATE b;
+FILE *file;
+#endif
+
+
+	{
+	yy_flush_buffer( b );
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+#if YY_ALWAYS_INTERACTIVE
+	b->yy_is_interactive = 1;
+#else
+#if YY_NEVER_INTERACTIVE
+	b->yy_is_interactive = 0;
+#else
+	b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+#endif
+#endif
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yy_flush_buffer( YY_BUFFER_STATE b )
+#else
+void yy_flush_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+
+	{
+	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == yy_current_buffer )
+		yy_load_buffer_state();
+	}
+
+
+#ifndef YY_NO_SCAN_BUFFER
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size )
+#else
+YY_BUFFER_STATE yy_scan_buffer( base, size )
+char *base;
+yy_size_t size;
+#endif
+	{
+	YY_BUFFER_STATE b;
+
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return 0;
+
+	b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+	b->yy_buf_size = size - 2;	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = 0;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	yy_switch_to_buffer( b );
+
+	return b;
+	}
+#endif
+
+
+#ifndef YY_NO_SCAN_STRING
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str )
+#else
+YY_BUFFER_STATE yy_scan_string( yy_str )
+yyconst char *yy_str;
+#endif
+	{
+	int len;
+	for ( len = 0; yy_str[len]; ++len )
+		;
+
+	return yy_scan_bytes( yy_str, len );
+	}
+#endif
+
+
+#ifndef YY_NO_SCAN_BYTES
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len )
+#else
+YY_BUFFER_STATE yy_scan_bytes( bytes, len )
+yyconst char *bytes;
+int len;
+#endif
+	{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n;
+	int i;
+
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = len + 2;
+	buf = (char *) yy_flex_alloc( n );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+	for ( i = 0; i < len; ++i )
+		buf[i] = bytes[i];
+
+	buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = yy_scan_buffer( buf, n );
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+	}
+#endif
+
+
+#ifndef YY_NO_PUSH_STATE
+#ifdef YY_USE_PROTOS
+static void yy_push_state( int new_state )
+#else
+static void yy_push_state( new_state )
+int new_state;
+#endif
+	{
+	if ( yy_start_stack_ptr >= yy_start_stack_depth )
+		{
+		yy_size_t new_size;
+
+		yy_start_stack_depth += YY_START_STACK_INCR;
+		new_size = yy_start_stack_depth * sizeof( int );
+
+		if ( ! yy_start_stack )
+			yy_start_stack = (int *) yy_flex_alloc( new_size );
+
+		else
+			yy_start_stack = (int *) yy_flex_realloc(
+					(void *) yy_start_stack, new_size );
+
+		if ( ! yy_start_stack )
+			YY_FATAL_ERROR(
+			"out of memory expanding start-condition stack" );
+		}
+
+	yy_start_stack[yy_start_stack_ptr++] = YY_START;
+
+	BEGIN(new_state);
+	}
+#endif
+
+
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state()
+	{
+	if ( --yy_start_stack_ptr < 0 )
+		YY_FATAL_ERROR( "start-condition stack underflow" );
+
+	BEGIN(yy_start_stack[yy_start_stack_ptr]);
+	}
+#endif
+
+
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state()
+	{
+	return yy_start_stack[yy_start_stack_ptr - 1];
+	}
+#endif
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+#ifdef YY_USE_PROTOS
+static void yy_fatal_error( yyconst char msg[] )
+#else
+static void yy_fatal_error( msg )
+char msg[];
+#endif
+	{
+	(void) fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+	}
+
+
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+		yytext[yyleng] = yy_hold_char; \
+		yy_c_buf_p = yytext + n; \
+		yy_hold_char = *yy_c_buf_p; \
+		*yy_c_buf_p = '\0'; \
+		yyleng = n; \
+		} \
+	while ( 0 )
+
+
+/* Internal utility routines. */
+
+#ifndef yytext_ptr
+#ifdef YY_USE_PROTOS
+static void yy_flex_strncpy( char *s1, yyconst char *s2, int n )
+#else
+static void yy_flex_strncpy( s1, s2, n )
+char *s1;
+yyconst char *s2;
+int n;
+#endif
+	{
+	register int i;
+	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+	}
+#endif
+
+#ifdef YY_NEED_STRLEN
+#ifdef YY_USE_PROTOS
+static int yy_flex_strlen( yyconst char *s )
+#else
+static int yy_flex_strlen( s )
+yyconst char *s;
+#endif
+	{
+	register int n;
+	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+	}
+#endif
+
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_alloc( yy_size_t size )
+#else
+static void *yy_flex_alloc( size )
+yy_size_t size;
+#endif
+	{
+	return (void *) malloc( size );
+	}
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_realloc( void *ptr, yy_size_t size )
+#else
+static void *yy_flex_realloc( ptr, size )
+void *ptr;
+yy_size_t size;
+#endif
+	{
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return (void *) realloc( (char *) ptr, size );
+	}
+
+#ifdef YY_USE_PROTOS
+static void yy_flex_free( void *ptr )
+#else
+static void yy_flex_free( ptr )
+void *ptr;
+#endif
+	{
+	free( ptr );
+	}
+
+#if YY_MAIN
+int main()
+	{
+	yylex();
+	return 0;
+	}
+#endif
+#line 143 "thinkjettopbm.l"
+
+
+static void
+parseCommandLine(int argc, char ** const 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 = 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,   "debug",      OPT_FLAG, NULL, &cmdlineP->debug,    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 if (argc-1 > 1)
+        pm_error("Too many parameters.  There is at most one: the input "
+                 "file specification.  You specified %d", argc-1);
+    else
+        cmdlineP->inputFilespec = argv[1];
+}
+
+
+
+/*
+ * Application entry point
+ */
+int
+main (int argc, char **argv)
+{
+    struct cmdlineInfo cmdline;
+
+    pbm_init( &argc, argv );
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    if (strlen(cmdline.inputFilespec) > 0) {
+        FILE * ifP;
+        ifP = freopen(cmdline.inputFilespec, "rb", stdin);
+        if (ifP == NULL)
+            pm_error("Unable to open file '%s' as stdin.  errno=%d (%s)", 
+                     cmdline.inputFilespec, errno, strerror(errno));
+    }
+    debugFlag = cmdline.debug;
+    yylex ();
+    return 0;
+}
+
+/*
+ * Finish at end of file
+ */
+static int 
+yywrap (void)
+{
+    int row;
+    unsigned char * packed_bitrow;
+
+    debug ("Got %d rows, %d columns\n", rowCount, maxRowLength);
+
+    /*
+     * Quite simple since ThinkJet bit arrangement matches PBM
+     */
+    pbm_writepbminit(stdout, maxRowLength*8, rowCount, 0);
+
+    packed_bitrow = malloc(maxRowLength);
+    if (packed_bitrow == NULL) pm_error("Out of memory");
+
+    for (row = 0 ; row < rowCount ; row++) {
+        int col;
+        for (col = 0 ; col < rows[row].length ; col++) 
+            packed_bitrow[col] = rows[row].bits[col];
+        for (        ; col < maxRowLength;      col++)
+            packed_bitrow[col] = 0;
+        pbm_writepbmrow_packed(stdout, packed_bitrow, maxRowLength*8, 0);
+    }
+    free(packed_bitrow);
+    return 1;
+}
+
+/*
+ * Print debugging message
+ */
+static void
+debug (const char *format, ...)
+{
+    va_list args;
+
+    if (debugFlag) {
+        fprintf (stderr, "thinkjettopbm: ");
+        va_start (args, format);
+        vfprintf (stderr, format, args);
+        va_end (args);
+    }
+}
+
diff --git a/converter/pbm/thinkjettopbm.l b/converter/pbm/thinkjettopbm.l
new file mode 100644
index 00000000..a66ae07e
--- /dev/null
+++ b/converter/pbm/thinkjettopbm.l
@@ -0,0 +1,251 @@
+%pointer
+/*
+ * A user reported in January 2005 a problem building Thinkjettopbm
+ * in which the opening comment delimiter above for some reason did
+ * not make it into the Lex output in the Netpbm build of this.
+ * Needless to say, that would not compile.  This user was using
+ * 'lex -t' on Tru64.  We did not find it worthwhile to debug it.
+ *
+ * Simple FLEX scanner to convert HP ThinkJet graphics image
+ * to PBM format.
+ *
+ * Implements a small subset of ThinkJet commands.
+ *
+ * Copyright (C) 2001 by W. Eric Norum
+ *
+ * Department of Electrical Engineering
+ * University of Saskatchewan
+ * Saskatoon, Saskatchewan, CANADA
+ * eric.norum@usask.ca
+ *
+ *  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 2001.04.05 by Bryan Henderson for inclusion in the Netpbm
+ *  package.  Now uses Netpbm libraries and, for consistency with other
+ *  Netpbm programs, does not have PGM output option.
+ */
+
+%{
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include "pbm.h"
+#include "shhopt.h"
+
+/* The following macro definitions tell Lex what sort of code to generate.
+   GNU Flex does #if XXX for some of these, as opposed to #ifdef XXX, which
+   means that they properly have to be set to zero instead of just left
+   undefined.  (Simply leaving them undefined typically works anyway, but it
+   is a problem if you use compiler options that say to fail when someone
+   uses a macro he failed to define).
+*/
+#define YY_NO_UNPUT
+#define YY_STACK_USED 0
+#define YY_ALWAYS_INTERACTIVE 0
+#define YY_NEVER_INTERACTIVE 0
+#define YY_MAIN 0
+    /* Don't include the main() function.  We have our own */
+
+static int yylex(void);
+static int yywrap(void);
+/* This works, but generates a warning 
+static void yyrestart(FILE*);
+*/
+
+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 debug;
+};
+
+
+
+struct RowInfo {
+    int     length;    /* length, in bytes */
+    char    *bits;     /* Bitmap */
+};
+
+static int maxRowLength;
+static int rowCount;
+static int rowCapacity;
+static struct RowInfo *rows;
+
+static int column;
+
+int debugFlag;
+static void debug (const char *format, ...);
+
+%}
+
+DIG             [0-9]
+
+%x RASTERMODE ROWMODE
+
+%%
+
+<ROWMODE>[\0-\377]      {
+                        rows[rowCount].bits[column++] = yytext[0]; 
+                        if (column >= rows[rowCount].length) {
+                            rowCount++;
+                            debug ("Done %d-byte row %d.\n", column, rowCount);
+                            BEGIN (RASTERMODE);
+                        }
+                        }
+
+<RASTERMODE>\033\*b{DIG}+W  {
+                            int l;
+                            if (rowCount >= rowCapacity) {
+                                rowCapacity += 100;
+                                rows = realloc (rows, rowCapacity * sizeof *rows);
+                                if (rows == NULL)
+                                    pm_error ("Out of memory.");
+                            }
+                            l = atoi (yytext+3);
+                            rows[rowCount].length = l;
+                            rows[rowCount].bits = malloc (l);
+                            if (rows[rowCount].bits == NULL)
+                                pm_error ("Out of memory.");
+                            if (l > maxRowLength)
+                                maxRowLength = l;
+                            debug ("Start %d-byte row.\n", l);
+                            column = 0;
+                            BEGIN (ROWMODE);
+                            }
+
+<RASTERMODE>\033\*rB   {
+                       debug ("Match <esc>*rB\n");
+                       BEGIN (0);
+                       }
+
+<RASTERMODE>[.\0\n]    { pm_error ("Unexpected character (%#x) in raster mode.\n", yytext[0]); }
+
+\033\&l{DIG}+.         { debug ("Match <esc>&l\n"); }
+\033\*r{DIG}+S         { debug ("Match <esc>*r#S\n"); }
+\033\*b{DIG}+W         { debug ("Match <esc>*r#w\n"); }
+\033\*rA               {
+                       debug ("Match <esc>*rA\n");
+                       BEGIN (RASTERMODE);
+                       }
+
+[\0-\377]               { /* Silently consume all other characters */ }
+
+%%
+
+static void
+parseCommandLine(int argc, char ** const 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 = 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,   "debug",      OPT_FLAG, NULL, &cmdlineP->debug,    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 if (argc-1 > 1)
+        pm_error("Too many parameters.  There is at most one: the input "
+                 "file specification.  You specified %d", argc-1);
+    else
+        cmdlineP->inputFilespec = argv[1];
+}
+
+
+
+/*
+ * Application entry point
+ */
+int
+main (int argc, char **argv)
+{
+    struct cmdlineInfo cmdline;
+
+    pbm_init( &argc, argv );
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    if (strlen(cmdline.inputFilespec) > 0) {
+        FILE * ifP;
+        ifP = freopen(cmdline.inputFilespec, "rb", stdin);
+        if (ifP == NULL)
+            pm_error("Unable to open file '%s' as stdin.  errno=%d (%s)", 
+                     cmdline.inputFilespec, errno, strerror(errno));
+    }
+    debugFlag = cmdline.debug;
+    yylex ();
+    return 0;
+}
+
+/*
+ * Finish at end of file
+ */
+static int 
+yywrap (void)
+{
+    int row;
+    unsigned char * packed_bitrow;
+
+    debug ("Got %d rows, %d columns\n", rowCount, maxRowLength);
+
+    /*
+     * Quite simple since ThinkJet bit arrangement matches PBM
+     */
+    pbm_writepbminit(stdout, maxRowLength*8, rowCount, 0);
+
+    packed_bitrow = malloc(maxRowLength);
+    if (packed_bitrow == NULL) pm_error("Out of memory");
+
+    for (row = 0 ; row < rowCount ; row++) {
+        int col;
+        for (col = 0 ; col < rows[row].length ; col++) 
+            packed_bitrow[col] = rows[row].bits[col];
+        for (        ; col < maxRowLength;      col++)
+            packed_bitrow[col] = 0;
+        pbm_writepbmrow_packed(stdout, packed_bitrow, maxRowLength*8, 0);
+    }
+    free(packed_bitrow);
+    return 1;
+}
+
+/*
+ * Print debugging message
+ */
+static void
+debug (const char *format, ...)
+{
+    va_list args;
+
+    if (debugFlag) {
+        fprintf (stderr, "thinkjettopbm: ");
+        va_start (args, format);
+        vfprintf (stderr, format, args);
+        va_end (args);
+    }
+}
+
diff --git a/converter/pbm/wbmptopbm.c b/converter/pbm/wbmptopbm.c
new file mode 100644
index 00000000..a3ce7ec3
--- /dev/null
+++ b/converter/pbm/wbmptopbm.c
@@ -0,0 +1,112 @@
+/* wbmptopbm.c - convert a wbmp file to a portable bitmap
+
+   This is derived for Netpbm from the pbmwbmp package from 
+   <http://www.looplab.com/wap/tools> on 2000.06.06.
+   
+   The specifications for the wbmp format are part of the Wireless 
+   Application Environment specification at
+   <http://www.wapforum.org/what/technical.htm>.
+
+** Copyright (C) 1999 Terje Sannum <terje@looplab.com>.
+**
+** 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 "pbm.h"
+
+static int 
+readc(FILE *f) {
+  int c = fgetc(f);
+  if(c == EOF) pm_error("EOF / read error");
+  return c;
+}
+
+
+
+static int 
+readint(FILE *f) {
+  int c=0, pos=0, sum=0;
+  do {
+    c = readc(f);
+    sum = (sum << 7*pos++) | (c & 0x7f);
+  } while(c & 0x80);
+  return sum;
+}
+
+
+
+static void 
+readheader(int h, FILE *f) {
+  int c,i;
+  switch(h & 0x60) {
+  case 0x00:
+    /* Type 00: read multi-byte bitfield */
+    do c=readc(f); while(c & 0x80);
+    break;
+  case 0x60:
+    /* Type 11: read name/value pair */
+    for(i=0; i < ((h & 0x70) >> 4) + (h & 0x0f); i++) c=readc(f);
+    break;
+  }
+}
+
+
+
+static bit **
+readwbmp(FILE *f, int *cols, int *rows) {
+  int i,j,k,row,c;
+  bit **image;
+  /* Type */
+  c = readint(f);
+  if(c != 0) pm_error("Unrecognized WBMP type");
+  /* Headers */
+  c = readc(f); /* FixHeaderField */
+  while(c & 0x80) { /* ExtHeaderFields */
+    c = readc(f);
+    readheader(c, f);
+  }
+  /* Geometry */
+  *cols = readint(f);
+  *rows = readint(f);
+  image = pbm_allocarray(*cols, *rows);
+  /* read image */
+  row = *cols/8;
+  if(*cols%8) row +=1;
+  for(i=0; i<*rows; i++) {
+    for(j=0; j<row; j++) {
+      c=readc(f);
+      for(k=0; k<8 && j*8+k<*cols; k++) {
+	image[i][j*8+k] = c & (0x80 >> k) ? PBM_WHITE : PBM_BLACK;
+      }
+    }
+  }
+  return image;
+}
+
+
+
+int 
+main(int argc, char *argv[]) {
+  FILE *f;
+  bit **image;
+  int rows, cols;
+
+  pbm_init(&argc, argv);
+  if(argc > 2) {
+    fprintf(stderr, "Copyright (C) 1999 Terje Sannum <terje@looplab.com>\n");
+    pm_usage("[wbmpfile]");
+  }
+
+  f = argc == 2 ? pm_openr(argv[1]) : stdin;
+  image = readwbmp(f, &cols, &rows);
+  pm_close(f);
+  pbm_writepbm(stdout, image, cols, rows, 0);
+  pm_close(stdout);
+  return 0;
+}
+
diff --git a/converter/pbm/xbmtopbm.c b/converter/pbm/xbmtopbm.c
new file mode 100644
index 00000000..7779a9b5
--- /dev/null
+++ b/converter/pbm/xbmtopbm.c
@@ -0,0 +1,255 @@
+/* xbmtopbm.c - read an X bitmap file and produce a portable bitmap
+**
+** Copyright (C) 1988 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 "nstring.h"
+#include "pbm.h"
+
+#define TRUE 1
+#define FALSE 0
+
+static void ReadBitmapFile ARGS(( FILE* stream, int* widthP, int* heightP, char** dataP ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, row, col, charcount;
+    char* data;
+    char mask;
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+        pm_usage( "[bitmapfile]" );
+    
+    if ( argc == 2 )
+        ifp = pm_openr( argv[1] );
+    else
+        ifp = stdin;
+
+    ReadBitmapFile( ifp, &cols, &rows, &data );
+
+    pm_close( ifp );
+
+    pbm_writepbminit( stdout, cols, rows, 0 );
+    bitrow = pbm_allocrow( cols );
+
+    for ( row = 0; row < rows; ++row )
+        {
+        charcount = 0;
+        mask = 1;
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+            {
+            if ( charcount >= 8 )
+                {
+                ++data;
+                charcount = 0;
+                mask = 1;
+                }
+            *bP = ( *data & mask ) ? PBM_BLACK : PBM_WHITE;
+            ++charcount;
+            mask = mask << 1;
+            }
+        ++data;
+        pbm_writepbmrow( stdout, bitrow, cols, 0 );
+        }
+
+    pm_close( stdout );
+    exit( 0 );
+    }
+
+#define MAX_LINE 500
+
+static void
+ReadBitmapFile( stream, widthP, heightP, dataP )
+     FILE* stream;
+     int* widthP;
+     int* heightP;
+     char** dataP;
+{
+  char line[MAX_LINE], name_and_type[MAX_LINE];
+  char* ptr;
+  char* t;
+  int version10, raster_length, v;
+  register int bytes, bytes_per_line, padding;
+  register int c1, c2, value1, value2;
+  int hex_table[256];
+  int found_declaration;
+  /* In scanning through the bitmap file, we have found the first
+     line of the C declaration of the array (the "static char ..."
+     or whatever line)
+     */
+  int eof;
+  /* We've encountered end of file while searching file */
+
+  *widthP = *heightP = -1;
+
+  found_declaration = FALSE;    /* Haven't found it yet; haven't even looked*/
+  eof = FALSE;                  /* Haven't encountered end of file yet */
+
+  while (!found_declaration && !eof) {
+    if ( fgets( line, MAX_LINE, stream ) == NULL )
+      eof = TRUE;
+    else {
+      if ( strlen( line ) == MAX_LINE - 1 )
+        pm_error( "line too long" );
+
+      if ( sscanf( line, "#define %s %d", name_and_type, &v ) == 2 ) {
+        if ( ( t = strrchr( name_and_type, '_' ) ) == NULL )
+          t = name_and_type;
+        else
+          ++t;
+        if ( STREQ( "width", t ) )
+          *widthP = v;
+        else if ( STREQ( "height", t ) )
+          *heightP = v;
+        continue;
+      }
+        
+      if ( sscanf( line, "static short %s = {", name_and_type ) == 1 ) {
+        version10 = TRUE;
+        found_declaration = TRUE;
+      }
+      else if ( sscanf( line, "static char %s = {", name_and_type ) == 1 ) {
+        version10 = FALSE;
+        found_declaration = TRUE;
+      }
+      else if (sscanf(line, 
+                      "static unsigned char %s = {", name_and_type ) == 1 ) {
+        version10 = FALSE;
+        found_declaration = TRUE;
+      }
+    }
+  }
+ 
+  if (!found_declaration) 
+    pm_error("Unable to find a line in the file containing the start "
+             "of C array declaration (\"static char\" or whatever)");
+
+  if ( *widthP == -1 )
+    pm_error( "invalid width" );
+  if ( *heightP == -1 )
+    pm_error( "invalid height" );
+
+  padding = 0;
+  if ( ((*widthP % 16) >= 1) && ((*widthP % 16) <= 8) && version10 )
+    padding = 1;
+
+  bytes_per_line = (*widthP+7)/8 + padding;
+    
+  raster_length =  bytes_per_line * *heightP;
+  *dataP = (char*) malloc( raster_length );
+  if ( *dataP == (char*) 0 )
+    pm_error( "out of memory" );
+
+  /* Initialize hex_table. */
+  for ( c1 = 0; c1 < 256; ++c1 )
+    hex_table[c1] = 256;
+  hex_table['0'] = 0;
+  hex_table['1'] = 1;
+  hex_table['2'] = 2;
+  hex_table['3'] = 3;
+  hex_table['4'] = 4;
+  hex_table['5'] = 5;
+  hex_table['6'] = 6;
+  hex_table['7'] = 7;
+  hex_table['8'] = 8;
+  hex_table['9'] = 9;
+  hex_table['A'] = 10;
+  hex_table['B'] = 11;
+  hex_table['C'] = 12;
+  hex_table['D'] = 13;
+  hex_table['E'] = 14;
+  hex_table['F'] = 15;
+  hex_table['a'] = 10;
+  hex_table['b'] = 11;
+  hex_table['c'] = 12;
+  hex_table['d'] = 13;
+  hex_table['e'] = 14;
+  hex_table['f'] = 15;
+
+  if ( version10 )
+    for ( bytes = 0, ptr = *dataP; bytes < raster_length; bytes += 2 ) {
+      while ( ( c1 = getc( stream ) ) != 'x' )
+        if ( c1 == EOF )
+          pm_error( "EOF / read error" );
+      c1 = getc( stream );
+      c2 = getc( stream );
+      if ( c1 == EOF || c2 == EOF )
+        pm_error( "EOF / read error" );
+      value1 = ( hex_table[c1] << 4 ) + hex_table[c2];
+      if ( value1 >= 256 )
+        pm_error( "syntax error" );
+      c1 = getc( stream );
+      c2 = getc( stream );
+      if ( c1 == EOF || c2 == EOF )
+        pm_error( "EOF / read error" );
+      value2 = ( hex_table[c1] << 4 ) + hex_table[c2];
+      if ( value2 >= 256 )
+        pm_error( "syntax error" );
+      *ptr++ = value2;
+      if ( ( ! padding ) || ( ( bytes + 2 ) % bytes_per_line ) )
+        *ptr++ = value1;
+    }
+  else
+    for ( bytes = 0, ptr = *dataP; bytes < raster_length; ++bytes ) {
+      /*
+       ** Skip until digit is found.
+       */
+      for ( ; ; )
+        {
+          c1 = getc( stream );
+          if ( c1 == EOF )
+            pm_error( "EOF / read error" );
+          value1 = hex_table[c1];
+          if ( value1 != 256 )
+            break;
+        }
+      /*
+       ** Loop on digits.
+       */
+      for ( ; ; ) {
+        c2 = getc( stream );
+        if ( c2 == EOF )
+          pm_error( "EOF / read error" );
+        value2 = hex_table[c2];
+        if ( value2 != 256 ) {
+          value1 = (value1 << 4) | value2;
+          if ( value1 >= 256 )
+            pm_error( "syntax error" );
+        }
+        else if ( c2 == 'x' || c2 == 'X' )
+          if ( value1 == 0 )
+            continue;
+          else pm_error( "syntax error" );
+        else break;
+      }
+      *ptr++ = value1;
+    }
+}
+
+
+/*  CHANGE HISTORY:
+
+  99.09.08 bryanh    Recognize "static unsigned char" declaration.
+
+
+
+
+
+*/
diff --git a/converter/pbm/ybmtopbm.c b/converter/pbm/ybmtopbm.c
new file mode 100644
index 00000000..739e168a
--- /dev/null
+++ b/converter/pbm/ybmtopbm.c
@@ -0,0 +1,113 @@
+/* ybmtopbm.c - read a file from Bennet Yee's 'xbm' program and write a pbm.
+**
+** Written by Jamie Zawinski based on code (C) 1988 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 <stdio.h>
+#include "pbm.h"
+
+static void getinit ARGS(( FILE* file, short* colsP, short* rowsP, short* depthP, short* padrightP ));
+static bit getbit ARGS(( FILE* file ));
+
+#define YBM_MAGIC  ( ( '!' << 8 ) | '!' )
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    short rows, cols, padright, row, col;
+    short depth;
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[ybmfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    getinit( ifp, &cols, &rows, &depth, &padright );
+    if ( depth != 1 )
+	pm_error(
+	    "YBM file has depth of %d, must be 1",
+	    (int) depth );
+
+    pbm_writepbminit( stdout, cols, rows, 0 );
+    bitrow = pbm_allocrow( cols );
+
+    for ( row = 0; row < rows; ++row )
+	{
+	/* Get data. */
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+	    *bP = getbit( ifp );
+	/* Discard line padding */
+        for ( col = 0; col < padright; ++col )
+	    (void) getbit( ifp );
+	pbm_writepbmrow( stdout, bitrow, cols, 0 );
+	}
+
+    pm_close( ifp );
+    pm_close( stdout );
+
+    exit( 0 );
+    }
+
+static int item;
+static int bitsperitem, bitshift;
+
+static void
+getinit( file, colsP, rowsP, depthP, padrightP )
+    FILE* file;
+    short* colsP;
+    short* rowsP;
+    short* depthP;
+    short* padrightP;
+    {
+    short magic;
+
+    if ( pm_readbigshort( file, &magic ) == -1 )
+	pm_error( "EOF / read error" );
+    if ( magic != YBM_MAGIC )
+	pm_error( "bad magic number in YBM file" );
+    if ( pm_readbigshort( file, colsP ) == -1 )
+	pm_error( "EOF / read error" );
+      if ( pm_readbigshort( file, rowsP ) == -1 )
+	pm_error( "EOF / read error" );
+
+    *depthP = 1;
+    *padrightP = ( ( *colsP + 15 ) / 16 ) * 16 - *colsP;
+    bitsperitem = 0;
+    }
+
+static bit
+getbit( file )
+    FILE* file;
+    {
+    bit b;
+
+    if ( bitsperitem == 0 )
+	{
+	item = getc(file) | getc(file)<<8;
+	if ( item == EOF )
+	    pm_error( "EOF / read error" );
+	bitsperitem = 16;
+	bitshift = 0;
+	}
+    b = ( ( item >> bitshift) & 1 ) ? PBM_BLACK : PBM_WHITE;
+    --bitsperitem;
+    ++bitshift;
+    return b;
+    }
diff --git a/converter/pgm/Makefile b/converter/pgm/Makefile
new file mode 100644
index 00000000..f562fe92
--- /dev/null
+++ b/converter/pgm/Makefile
@@ -0,0 +1,23 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/pgm
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+PORTBINARIES =	asciitopgm bioradtopgm fstopgm hipstopgm \
+		lispmtopgm pgmtofs pgmtolispm pgmtopgm \
+	        psidtopgm spottopgm sbigtopgm
+MATHBINARIES =	rawtopgm
+BINARIES =	$(PORTBINARIES) $(MATHBINARIES)
+
+OBJECTS = $(BINARIES:%=%.o)
+MERGEBINARIES = $(BINARIES)
+MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2)
+
+.PHONY: all
+all: $(BINARIES)
+
+include $(SRCDIR)/Makefile.common
diff --git a/converter/pgm/asciitopgm.c b/converter/pgm/asciitopgm.c
new file mode 100644
index 00000000..b6b9c9a7
--- /dev/null
+++ b/converter/pgm/asciitopgm.c
@@ -0,0 +1,163 @@
+/* asciitopgm.c - read an ASCII graphics file and produce a portable graymap
+**
+** Copyright (C) 1989 by Wilson H. Bent, Jr
+**
+** - Based on fstopgm.c and other works which bear the following notice:
+** 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 "pm_c_util.h"
+#include "pgm.h"
+#include "mallocvar.h"
+#include "nstring.h"
+
+static char gmap [128] = {
+/*00 nul-bel*/  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/*08 bs -si */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/*10 dle-etb*/  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/*18 can-us */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/*20 sp - ' */  0x00, 0x21, 0x1b, 0x7f, 0x70, 0x25, 0x20, 0x0a,
+/*28  ( - / */  0x11, 0x11, 0x2a, 0x2b, 0x0b, 0x13, 0x04, 0x10,
+/*30  0 - 7 */  0x30, 0x28, 0x32, 0x68, 0x39, 0x35, 0x39, 0x16,
+/*38  8 - ? */  0x38, 0x39, 0x14, 0x15, 0x11, 0x1c, 0x11, 0x3f,
+/*40  @ - G */  0x40, 0x49, 0x52, 0x18, 0x44, 0x3c, 0x38, 0x38,
+/*48  H - O */  0x55, 0x28, 0x2a, 0x70, 0x16, 0x7f, 0x70, 0x14,
+/*50  P - W */  0x60, 0x20, 0x62, 0x53, 0x1a, 0x55, 0x36, 0x57,
+/*58  X - _ */  0x50, 0x4c, 0x5a, 0x24, 0x10, 0x24, 0x5e, 0x13,
+/*60  ` - g */  0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+/*68  h - o */  0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x2a,
+/*70  p - w */  0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+/*78  x -del*/  0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+};
+
+static gray maxval = 127;
+
+int
+main( argc, argv )
+    int argc;
+    char *argv[];
+{
+    FILE *ifd;
+    register gray **grays;
+    int argn, row;
+    register int c, i;
+    int rows = 0, cols = 0;
+    int divisor = 1;
+    bool warned;
+    int *obuf;
+    const char * const usage = "[-d <val>] height width [asciifile]";
+
+    pgm_init( &argc, argv );
+
+    warned = FALSE;
+
+    argn = 1;
+
+    if ( argc < 3 || argc > 6 )
+        pm_usage( usage );
+
+    if ( argv[argn][0] == '-' )
+    {
+        if ( STREQ( argv[argn], "-d" ) )
+        {
+            if ( argc == argn + 1 )
+                pm_usage( usage );
+            if ( sscanf( argv[argn+1], "%d", &divisor ) != 1 )
+                pm_usage( usage );
+            argn += 2;
+        }
+        else
+            pm_usage( usage );
+    }
+
+    if ( sscanf( argv[argn++], "%d", &rows ) != 1 )
+        pm_usage( usage );
+    if ( sscanf( argv[argn++], "%d", &cols ) != 1 )
+        pm_usage( usage );
+    if ( rows < 1 )
+        pm_error( "height is less than 1" );
+    if ( cols < 1 )
+        pm_error( "width is less than 1" );
+
+    if ( argc > argn + 1 )
+        pm_usage( usage );
+
+    if ( argc == argn + 1 )
+        ifd = pm_openr( argv[argn] );
+    else
+        ifd = stdin;
+
+    /* Here's where the work is done:
+     * - Usually, the graymap value of the input char is summed into grays
+     * - For a 'normal' newline, the current row is adjusted by the divisor
+     *   and the current row is incremented
+     * - If the first char in the input line is a '+', then the current row
+     *   stays the same to allow 'overstriking'
+     * NOTE that we assume the user specified a sufficiently large width!
+     */
+    MALLOCARRAY( obuf, cols );
+    if ( obuf == NULL )
+        pm_error( "Unable to allocate memory for %d columns.", cols);
+    else {
+        unsigned int col;
+        for (col = 0; col < cols; ++col) obuf[col] = 0;
+    }
+    grays = pgm_allocarray( cols, rows );
+    row = i = 0;
+    while ( row < rows )
+    {
+        switch (c = getc (ifd))
+        {
+        case EOF:
+            goto line_done;
+        case '\n':
+            if ((c = getc (ifd)) == EOF)
+                goto line_done;
+            if (c == '+')
+                i = 0;
+            else
+            {
+            line_done:
+                for (i = 0; i < cols; ++i)
+                    grays[row][i] = maxval - (obuf[i] / divisor);
+                {
+                    unsigned int col;
+                    for (col = 0; col < cols; ++col) obuf[col] = 0;
+                }
+                i = 0;
+                ++row;
+                if ( row >= rows )
+                    break;
+                if (c != EOF)
+                    obuf[i++] += gmap[c];
+            }
+            break;
+        default:
+            if (c > 0x7f)       /* !isascii(c) */
+            {
+                if (!warned)
+                {
+                    pm_message("Warning: non-ASCII char(s) in input");
+                    warned = TRUE;
+                }
+                c &= 0x7f;      /* toascii(c) */
+            }
+            obuf[i++] += gmap[c];
+            break;
+        }
+    }
+    pm_close( ifd );
+
+    pgm_writepgm( stdout, grays, cols, rows, maxval, 0 );
+
+    return 0;
+}
diff --git a/converter/pgm/bioradtopgm.c b/converter/pgm/bioradtopgm.c
new file mode 100644
index 00000000..e0bc3584
--- /dev/null
+++ b/converter/pgm/bioradtopgm.c
@@ -0,0 +1,152 @@
+/* bioradtopgm.c - convert a Biorad confocal image into a portable graymap
+**
+** Copyright (C) 1993 by Oliver Trepte, oliver@fysik4.kth.se
+**
+** Derived from the pbmplus 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.
+*/
+
+#include <ctype.h>
+
+#include "pgm.h"
+#include "nstring.h"
+
+#define BYTE unsigned char
+#define BIORAD_HEADER_LENGTH 76
+#define BYTE_TO_WORD(lsb,msb) (((BYTE) lsb) + (((BYTE) msb) << 8))
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    gray* grayrow;
+    register gray* gP;
+    int argn, row, i;
+    register int col, val, val2;
+    int rows=0, cols=0, image_num= -1, image_count, byte_word, check_word;
+    int maxval;
+    BYTE buf[BIORAD_HEADER_LENGTH];
+    const char* const usage = "[-image#] [Bioradfile]";
+
+
+    pgm_init( &argc, argv );
+
+    argn = 1;
+
+    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
+    {
+	if ( ISDIGIT( argv[argn][1] ))
+	{
+	    image_num = atoi( (argv[argn]+1) );
+	}
+	else
+	    pm_usage( usage );
+	++argn;
+    }
+
+    if ( argn < argc )
+	{
+	ifp = pm_openr( argv[argn] );
+	++argn;
+	}
+    else
+	ifp = stdin;
+
+    if ( argn != argc )
+	pm_usage( usage );
+
+    for ( i = 0; i < BIORAD_HEADER_LENGTH; ++i )
+    {
+	val = getc( ifp );
+	if ( val == EOF )
+	    pm_error( "EOF / read error" );
+	buf[ i ] = val;
+    }
+
+    cols = BYTE_TO_WORD(buf[0], buf[1]);
+    rows = BYTE_TO_WORD(buf[2], buf[3]);
+    image_count = BYTE_TO_WORD(buf[4], buf[5]);
+    byte_word = BYTE_TO_WORD(buf[14], buf[15]);
+    check_word = BYTE_TO_WORD(buf[54], buf[55]);
+
+    if ( check_word != 12345 )
+	pm_error( "Not a Biorad file" );
+
+    if ( cols <= 0 )
+	pm_error( "Strange image size, cols = %d", cols);
+
+    if ( rows <= 0 )
+	pm_error( "Strange image size, rows = %d", rows);
+
+    if ( image_count <= 0 )
+	pm_error( "Number of images in file is %d", image_count);
+
+    if ( byte_word )
+	maxval = 255;
+    else
+    {
+	maxval = 65535;   /* Perhaps this should be something else */
+
+    }
+
+    pm_message( "Image size: %d cols, %d rows", cols, rows);
+    pm_message( "%s",
+	       (byte_word) ? "Byte image (8 bits)" : "Word image (16 bits)");
+
+    if ( image_num < 0 )
+	pm_message( "Input contains %d image%c",
+		   image_count, (image_count > 1) ? 's' : '\0');
+    else
+    {
+	if ( image_num >= image_count )
+	    pm_error( "Cannot extract image %d, input contains only %d image%s",
+		     image_num, image_count, (image_count > 1) ? "s" : "" );
+	for ( i = (byte_word) ? image_num : image_num*2 ; i > 0 ; --i ) {
+	    for ( row = 0; row < rows; ++row)
+		for ( col = 0; col < cols; ++col )
+		{
+		    val = getc( ifp );
+		    if ( val == EOF ) {
+			pm_error( "EOF / read error" );
+		    }
+		}
+	}
+
+	pgm_writepgminit( stdout, cols, rows, (gray) maxval, 0 );
+	grayrow = pgm_allocrow( cols );
+
+	for ( row = 0; row < rows; ++row)
+	{
+	    for ( col = 0, gP = grayrow; col < cols; ++col )
+	    {
+		val = getc( ifp );
+		if ( val == EOF )
+		    pm_error( "EOF / read error" );
+		if (byte_word)
+		    *gP++ = val;
+		else
+		{
+		    val2 = getc( ifp );
+		    if ( val2 == EOF )
+			pm_error( "EOF / read error" );
+		    *gP++ = BYTE_TO_WORD(val, val2);
+		}
+	    }
+	    pgm_writepgmrow( stdout, grayrow, cols, (gray) maxval, 0 );
+	}
+
+	pm_close( ifp );
+	pm_close( stdout );
+
+    }
+    exit( 0 );
+}
diff --git a/converter/pgm/fstopgm.c b/converter/pgm/fstopgm.c
new file mode 100644
index 00000000..2c636f41
--- /dev/null
+++ b/converter/pgm/fstopgm.c
@@ -0,0 +1,138 @@
+/* fstopgm.c - read a Usenix FaceSaver(tm) file and produce a portable graymap
+**
+** 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 "pgm.h"
+
+static int gethexit ARGS(( FILE* ifp ));
+
+int
+main( argc, argv )
+int argc;
+char *argv[];
+    {
+    FILE *ifp;
+    register gray **grays, *gP;
+    int argn, row;
+    register int col;
+    int maxval;
+    int rows = 0, cols = 0, depth = 0, xrows = 0, xcols = 0, xdepth = 0;
+#define STRSIZE 1000
+    char buf[STRSIZE], firstname[STRSIZE], lastname[STRSIZE], email[STRSIZE];
+
+
+    pgm_init( &argc, argv );
+
+    argn = 1;
+
+    if ( argn < argc )
+	{
+	ifp = pm_openr( argv[argn] );
+	argn++;
+	}
+    else
+	ifp = stdin;
+
+    if ( argn != argc )
+	pm_usage( "[fsfile]" );
+
+    /* Read the FaceSaver(tm) header. */
+    for ( ; ; )
+	{
+	if ( fgets( buf, STRSIZE, ifp ) == (char *) 0 )
+	    pm_error( "error reading header" );
+
+	/* Blank line ends header. */
+	if ( strlen( buf ) == 1 )
+	    break;
+
+	if ( sscanf( buf, "FirstName: %[^\n]", firstname ) == 1 )
+	    ;
+	else if ( sscanf( buf, "LastName: %[^\n]", lastname ) == 1 )
+	    ;
+	else if ( sscanf( buf, "E-mail: %[^\n]", email ) == 1 )
+	    ;
+	else if ( sscanf( buf, "PicData: %d %d %d\n",
+			  &cols, &rows, &depth ) == 3 )
+	    {
+	    if ( depth != 8 )
+		pm_error(
+		    "can't handle 'PicData' depth other than 8" );
+	    }
+	else if ( sscanf( buf, "Image: %d %d %d\n",
+			  &xcols, &xrows, &xdepth ) == 3 )
+	    {
+	    if ( xdepth != 8 )
+		pm_error(
+		    "can't handle 'Image' depth other than 8" );
+	    }
+	}
+    if ( cols <= 0 || rows <= 0 )
+	pm_error( "invalid header" );
+    maxval = pm_bitstomaxval( depth );
+    if ( maxval > PGM_OVERALLMAXVAL )
+	pm_error( "depth %d is too large.  Our maximum is %d",
+              maxval, PGM_OVERALLMAXVAL);
+    if ( xcols != 0 && xrows != 0 && ( xcols != cols || xrows != rows ) )
+	{
+	float rowratio, colratio;
+
+	rowratio = (float) xrows / (float) rows;
+	colratio = (float) xcols / (float) cols;
+	pm_message(
+	    "warning, non-square pixels; to fix do a 'pamscale -%cscale %g'",
+	    rowratio > colratio ? 'y' : 'x',
+	    rowratio > colratio ? rowratio / colratio : colratio / rowratio );
+	}
+
+    /* Now read the hex bits. */
+    grays = pgm_allocarray( cols, rows );
+    for ( row = rows - 1; row >= 0; row--)
+	{
+	for ( col = 0, gP = grays[row]; col < cols; col++, gP++ )
+	    {
+	    *gP = gethexit( ifp ) << 4;
+	    *gP += gethexit( ifp );
+	    }
+	}
+    pm_close( ifp );
+
+    /* And write out the graymap. */
+    pgm_writepgm( stdout, grays, cols, rows, (gray) maxval, 0 );
+    pm_close( stdout );
+
+    exit( 0 );
+    }
+
+static int
+gethexit( ifp )
+FILE *ifp;
+    {
+    register int i;
+    register char c;
+
+    for ( ; ; )
+	{
+	i = getc( ifp );
+	if ( i == EOF )
+	    pm_error( "EOF / read error" );
+	c = (char) i;
+	if ( c >= '0' && c <= '9' )
+	    return c - '0';
+	else if ( c >= 'A' && c <= 'F' )
+	    return c - 'A' + 10;
+	else if ( c >= 'a' && c <= 'f' )
+	    return c - 'a' + 10;
+	/* Else ignore - whitespace. */
+	}
+    }
diff --git a/converter/pgm/hipstopgm.c b/converter/pgm/hipstopgm.c
new file mode 100644
index 00000000..826a8511
--- /dev/null
+++ b/converter/pgm/hipstopgm.c
@@ -0,0 +1,181 @@
+/* hipstopgm.c - read a HIPS file and produce a portable graymap
+**
+** 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 "nstring.h"
+#include "pgm.h"
+
+struct HIPS_Header {
+    char* orig_name;	/* An indication of the originator of this sequence. */
+    char* seq_name;	/* The sequence name. */
+    int num_frame;	/* The number of frames in this sequence. */
+    char* orig_date;	/* The date the sequence was originated. */
+    int rows;		/* The number of rows in each image, the height. */
+    int cols;		/* The number of columns in each image, the width. */
+    int bits_per_pixel;	/* The number of significant bits per pixel. */
+    int bit_packing;	/* Nonzero if the bits were packed such as to
+                           eliminate any unused bits resulting from a
+                           bits_per_pixel value which was not an even
+                           multiple of eight. */
+    int pixel_format;	/* An indication of the format of each pixel. */
+    char* seq_history;	/* A description of the sequence of transformations
+                           leading up to the current image. */
+    char* seq_desc;	/* A free form description of the contents of the
+                       sequence. */
+};
+#define HIPS_PFBYTE 0
+#define HIPS_PFSHORT 1
+#define HIPS_PFINT 2
+#define HIPS_PFFLOAT 3
+#define HIPS_PFCOMPLEX 4
+
+
+
+static void
+read_line(FILE * const fd,
+          char * const buf,
+          int    const size) {
+
+    if (fgets( buf, size, fd ) == NULL)
+        pm_error("error reading header");
+}
+
+
+
+
+static void
+read_hips_header( fd, hP )
+    FILE* fd;
+    struct HIPS_Header* hP;
+{
+    char buf[5000];
+
+    /* Read and toss orig_name. */
+    read_line( fd, buf, 5000 );
+
+    /* Read and toss seq_name. */
+    read_line( fd, buf, 5000 );
+
+    /* Read num_frame. */
+    read_line( fd, buf, 5000 );
+    hP->num_frame = atoi( buf );
+
+    /* Read and toss orig_date. */
+    read_line( fd, buf, 5000 );
+
+    /* Read rows. */
+    read_line( fd, buf, 5000 );
+    hP->rows = atoi( buf );
+
+    /* Read cols. */
+    read_line( fd, buf, 5000 );
+    hP->cols = atoi( buf );
+
+    /* Read bits_per_pixel. */
+    read_line( fd, buf, 5000 );
+    hP->bits_per_pixel = atoi( buf );
+
+    /* Read bit_packing. */
+    read_line( fd, buf, 5000 );
+    hP->bit_packing = atoi( buf );
+
+    /* Read pixel_format. */
+    read_line( fd, buf, 5000 );
+    hP->pixel_format = atoi( buf );
+
+    /* Now read and toss lines until we get one with just a period. */
+    do
+	{
+        read_line( fd, buf, 5000 );
+	}
+    while ( !STREQ( buf, ".\n" ) );
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    FILE* ifp;
+    gray* grayrow;
+    register gray* gP;
+    int argn, row;
+    register int col;
+    int maxval;
+    int rows, cols;
+    struct HIPS_Header h;
+
+
+    pgm_init( &argc, argv );
+
+    argn = 1;
+
+    if ( argn < argc )
+	{
+        ifp = pm_openr( argv[argn] );
+        argn++;
+	}
+    else
+        ifp = stdin;
+
+    if ( argn != argc )
+        pm_usage( "[hipsfile]" );
+
+    read_hips_header( ifp, &h );
+
+    cols = h.cols;
+    rows = h.rows * h.num_frame;
+
+    switch ( h.pixel_format )
+	{
+	case HIPS_PFBYTE:
+        if ( h.bits_per_pixel != 8 )
+            pm_error(
+                "can't handle unusual bits_per_pixel %d", h.bits_per_pixel );
+        if ( h.bit_packing != 0 )
+            pm_error( "can't handle bit_packing" );
+        maxval = 255;
+        break;
+
+	default:
+        pm_error( "unknown pixel format %d", h.pixel_format );
+	}
+
+    pgm_writepgminit( stdout, cols, rows, (gray) maxval, 0 );
+    grayrow = pgm_allocrow( cols );
+    for ( row = 0; row < rows; row++)
+	{
+        for ( col = 0, gP = grayrow; col < cols; col++, gP++ )
+	    {
+            int ich;
+
+            switch ( h.pixel_format )
+            {
+            case HIPS_PFBYTE:
+                ich = getc( ifp );
+                if ( ich == EOF )
+                    pm_error( "EOF / read error" );
+                *gP = (gray) ich;
+                break;
+
+            default:
+                pm_error( "can't happen" );
+            }
+	    }
+        pgm_writepgmrow( stdout, grayrow, cols, (gray) maxval, 0 );
+	}
+    pm_close( ifp );
+    pm_close( stdout );
+
+    return 0;
+}
diff --git a/converter/pgm/lispmtopgm.c b/converter/pgm/lispmtopgm.c
new file mode 100644
index 00000000..7b98ef00
--- /dev/null
+++ b/converter/pgm/lispmtopgm.c
@@ -0,0 +1,172 @@
+/* lispmtopgm.c - read a file written by the tv:write-bit-array-file function
+** of TI Explorer and Symbolics Lisp Machines, and write a PGM.
+**
+** Written by Jamie Zawinski based on code (C) 1988 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.
+**
+**   When one writes a multi-plane bitmap with tv:write-bit-array-file, it is
+**   usually a color image; but a color map is not written in the file, so we
+**   treat this as a graymap instead.  Since the pgm reader can also read pbms,
+**   this doesn't matter if you're using only single plane images.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "nstring.h"
+#include "pgm.h"
+
+#define LISPM_MAGIC  "This is a BitMap file"
+
+static void getinit ARGS(( FILE* file, short* colsP, short* rowsP, short* depthP, short* padrightP ));
+static int depth_to_word_size ARGS(( int depth ));
+static unsigned int getval ARGS(( FILE* file ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    gray* grayrow;
+    register gray* gP;
+    short rows, cols, padright, row, col;
+    short depth;
+    int maxval;
+
+
+    pgm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[lispmfile]" );
+
+    if ( argc == 2 )
+        ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    getinit( ifp, &cols, &rows, &depth, &padright );
+    maxval = 1 << depth;
+
+    if ( maxval > PGM_OVERALLMAXVAL )
+        pm_error( "depth (%d bits) is too large", depth);
+
+    pgm_writepgminit( stdout, cols, rows, (gray) maxval, 0 );
+    grayrow = pgm_allocrow( ( cols + 7 ) / 8 * 8 );
+
+    for ( row = 0; row < rows; ++row )
+	{
+        for ( col = 0, gP = grayrow; col < cols; ++col, ++gP )
+	    *gP = getval( ifp );
+	pgm_writepgmrow( stdout, grayrow, cols, (gray) maxval, 0 );
+	}
+    pm_close( ifp );
+    pm_close( stdout );
+    exit( 0 );
+    }
+
+static long item, bitmask;
+static unsigned int bitsperitem, maxbitsperitem, bitshift;
+
+static void
+getinit( file, colsP, rowsP, depthP, padrightP )
+    FILE* file;
+    short* colsP;
+    short* rowsP;
+    short* padrightP;
+    short* depthP;
+    {
+    short cols_32;
+    char magic[sizeof(LISPM_MAGIC)];
+    int i;
+
+    for ( i = 0; i < sizeof(magic)-1; ++i )
+        magic[i] = getc( file );
+    magic[i]='\0';
+    if (!STREQ(LISPM_MAGIC, magic))
+        pm_error( "bad id string in Lispm file" );
+    
+    if ( pm_readlittleshort( file, colsP ) == -1 )
+        pm_error( "EOF / read error" );
+    if ( pm_readlittleshort( file, rowsP ) == -1 )
+        pm_error( "EOF / read error" );
+    if ( pm_readlittleshort( file, &cols_32 ) == -1 )
+        pm_error( "EOF / read error" );
+    *depthP = getc( file );
+    
+    if ( *depthP == 0 )
+	*depthP = 1;	/* very old file */
+    
+    *padrightP = ( ( *colsP + 31 ) / 32 ) * 32 - *colsP;
+    
+    if ( *colsP != (cols_32 - *padrightP) ) {
+/*    pm_message( "inconsistent input: Width and Width(mod32) fields don't agree" );  */
+/*    *padrightP = cols_32 - *colsP;   */ /*    hmmmm....   */
+      /* This is a dilemma.  Usually the output is rounded up to mod32, but not always.
+       * For the Lispm code to not round up, the array size must be the same size as the
+       * portion being written - that is, the array itself must be an odd size, not just
+       * the selected portion.  Since arrays that are odd sizes can't be handed to bitblt,
+       * such arrays are probably not image data - so punt on it for now.
+       *
+       * Also, the lispm code for saving bitmaps has a bug, in that if you are writing a
+       * bitmap which is not mod32 across, the file may be up to 7 bits too short!  They
+       * round down instead of up.
+       *
+       * The code in 'pgmtolispm.c' always rounds up to mod32, which is totally reasonable.
+       */
+      }
+    bitsperitem = 0;
+    maxbitsperitem = depth_to_word_size( *depthP );
+    bitmask = ( 1 << maxbitsperitem ) - 1;		/* for depth=3, mask=00000111 */
+
+    for ( i = 0; i < 9; ++i )
+	getc( file );	/* discard bytes reserved for future use */
+    }
+
+static int
+depth_to_word_size (depth)	
+     int depth;			
+     /* Lispm architecture specific - if a bitmap is written    */
+     /* out with a depth of 5, it really has a depth of 8, and  */
+     /* is stored that way in the file.			   */
+{				
+    if (depth==0 || depth==1)	return ( 1);
+    else if (depth ==  2)	return ( 2);
+    else if (depth <=  4)	return ( 4);
+    else if (depth <=  8)	return ( 8);
+    else if (depth <= 16)	return (16);
+    else if (depth <= 32)	return (32);
+    else {
+      pm_error( "depth was %d, which is not in the range 1-32.", depth );
+      /* Should never reach here */
+      return(-1);
+  }
+}
+
+
+
+static unsigned int
+getval( file )
+    FILE* file;
+    {
+    unsigned int b;
+
+    if ( bitsperitem == 0 )
+	{
+	if ( pm_readlittlelong( file, &item ) == -1 )
+	    pm_error( "EOF / read error" );
+	bitsperitem = 32;
+	bitshift = 0;
+	item = ~item;
+	}
+    b = ( ( item >> bitshift ) & bitmask );
+    bitsperitem = bitsperitem - maxbitsperitem;
+    bitshift = bitshift + maxbitsperitem;
+    return b;
+    }
diff --git a/converter/pgm/pgmtofs.c b/converter/pgm/pgmtofs.c
new file mode 100644
index 00000000..b34d77c4
--- /dev/null
+++ b/converter/pgm/pgmtofs.c
@@ -0,0 +1,153 @@
+/* pgmtofs.c - convert portable graymap to Usenix FaceSaver(tm) format
+**
+** 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 "pgm.h"
+
+static void putinit ARGS(( int cols, int rows, int bps ));
+static void putitem ARGS(( void ));
+static void putgray ARGS(( gray g ));
+static void putrest ARGS(( void ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+{
+    FILE* ifp;
+    gray** grays;
+    register gray* gP;
+    int argn, rows, cols, bps, padright, row, col;
+    gray maxval, nmaxval;
+    const char* const usage = "[pgmfile]";
+
+
+    pgm_init( &argc, argv );
+
+    argn = 1;
+
+    if ( argn < argc )
+	{
+        ifp = pm_openr( argv[argn] );
+        ++argn;
+	}
+    else
+	{
+        ifp = stdin;
+	}
+
+    if ( argn != argc )
+        pm_usage( usage );
+
+    grays = pgm_readpgm( ifp, &cols, &rows, &maxval );
+    pm_close( ifp );
+
+    /* Figure out bps. */
+    bps = pm_maxvaltobits( (int) maxval );
+    if ( bps > 2 && bps < 4 )
+        bps = 4;
+    else if ( bps > 4 && bps < 8 )
+        bps = 8;
+    else if ( bps > 8 )
+        pm_error(
+            "maxval of %d is too large for FaceSaver(tm)", maxval );
+    nmaxval = pm_bitstomaxval( bps );
+    
+    /* Compute padding to round cols * bps up to the nearest multiple of 8. */
+    padright = ( ( cols * bps + 7 ) / 8 ) * 8 - cols * bps;
+
+    putinit( cols, rows, bps );
+    for ( row = rows - 1; row >= 0; --row )
+	{
+        for ( col = 0, gP = grays[row]; col < cols; ++col, ++gP )
+	    {
+            if ( maxval != nmaxval )
+                *gP = (int) *gP * nmaxval / maxval;
+            putgray( *gP );
+	    }
+        for ( col = 0; col < padright; ++col )
+            putgray( 0 );
+    }
+
+    putrest( );
+
+    exit( 0 );
+}
+
+
+static int bitspersample, item, bitsperitem, bitshift, itemsperline, items;
+
+static void
+putinit( cols, rows, bps )
+    int cols, rows, bps;
+{
+    printf( "FirstName: \n" );
+    printf( "LastName: \n" );
+    printf( "E-mail: \n" );
+    printf( "Telephone: \n" );
+    printf( "Company: \n" );
+    printf( "Address1: \n" );
+    printf( "Address2: \n" );
+    printf( "CityStateZip: \n" );
+    printf( "Date: \n" );
+    printf( "PicData: %d %d %d\n", cols, rows, bps );
+    printf( "Image: %d %d %d\n", cols, rows, bps );
+    printf( "\n" );
+
+    bitspersample = bps;
+    itemsperline = items = 0;
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 8 - bitspersample;
+}
+
+static void
+putitem( )
+{
+    const char* const hexits = "0123456789abcdef";
+
+    if ( itemsperline == 30 )
+	{
+        putchar( '\n' );
+        itemsperline = 0;
+	}
+    putchar( hexits[item >> 4] );
+    putchar( hexits[item & 15] );
+    ++itemsperline;
+    ++items;
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 8 - bitspersample;
+}
+
+#if __STDC__
+static void
+putgray( gray g )
+#else /*__STDC__*/
+    static void
+putgray( g )
+    gray g;
+#endif /*__STDC__*/
+{
+    if ( bitsperitem == 8 )
+        putitem( );
+    item += g << bitshift;
+    bitsperitem += bitspersample;
+    bitshift -= bitspersample;
+}
+
+static void
+putrest( )
+{
+    if ( bitsperitem > 0 )
+        putitem( );
+    printf( "\n" );
+}
diff --git a/converter/pgm/pgmtolispm.c b/converter/pgm/pgmtolispm.c
new file mode 100644
index 00000000..02f2fd1e
--- /dev/null
+++ b/converter/pgm/pgmtolispm.c
@@ -0,0 +1,148 @@
+/* pgmtolispm.c - read a pgm and write a file acceptable to the 
+** tv:read-bit-array-file function of TI Explorer and Symbolics Lisp Machines.
+**
+** Written by Jamie Zawinski based on code (C) 1988 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.
+**
+**   When one writes a multi-plane bitmap with tv:write-bit-array-file, it is
+**   usually a color image; but a color map is not written in the file, so we
+**   treat this as a graymap instead.  To convert a color image to Lispm 
+**   format, you must convert it to a pgm, and hand-edit a color map...  Ick.
+*/
+
+#include <stdio.h>
+#include "pgm.h"
+
+#define LISPM_MAGIC  "This is a BitMap file"
+
+static void putinit ARGS(( int cols, int rows, int depth ));
+static int depth_to_word_size ARGS(( int depth ));
+static void putval ARGS(( gray b ));
+static void putrest ARGS(( void ));
+static void putitem ARGS(( void ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    gray *grayrow;
+    register gray* gP;
+    int rows, cols, depth, format, padright, row, col;
+    gray maxval;
+
+
+    pgm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[pgmfile]" );
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    pgm_readpgminit( ifp, &cols, &rows, &maxval, &format );
+    grayrow = pgm_allocrow( cols );
+    depth = pm_maxvaltobits( maxval );
+
+    /* Compute padding to round cols up to the nearest multiple of 32. */
+    padright = ( ( cols + 31 ) / 32 ) * 32 - cols;
+
+    putinit( cols, rows, depth );
+    for ( row = 0; row < rows; ++row )
+	{
+	pgm_readpgmrow( ifp, grayrow, cols, maxval, format );
+        for ( col = 0, gP = grayrow; col < cols; ++col, ++gP )
+	    putval( *gP );
+	for ( col = 0; col < padright; ++col )
+	    putval( 0 );
+        }
+
+    pm_close( ifp );
+
+    putrest( );
+
+    exit( 0 );
+    }
+
+static unsigned int item;
+static unsigned int bitsperitem, maxbitsperitem, bitshift;
+
+static void
+putinit( cols, rows, depth )
+    int cols, rows, depth;
+    {
+    int i;
+    int cols32 = ( ( cols + 31 ) / 32 ) * 32;	/* Lispms are able to write bit files that are not mod32 wide, but we   */
+						/* don't.  This should be ok, since bit arrays which are not mod32 wide */
+    printf(LISPM_MAGIC);			/* are pretty useless on a lispm (can't hand them to bitblt).		*/
+    pm_writelittleshort( stdout, cols );
+    pm_writelittleshort( stdout, rows );
+    pm_writelittleshort( stdout, cols32 );
+    putchar(depth & 0xFF);
+
+    for ( i = 0; i < 9; ++i )
+	putchar( 0 );	/* pad bytes */
+
+    item = 0;
+    bitsperitem = 0;
+    maxbitsperitem = depth_to_word_size( depth );
+    bitshift = 0;
+    }
+
+static int
+depth_to_word_size (depth)	/* Lispm architecture specific - if a bitmap is written    */
+  int depth;			/* out with a depth of 5, it really has a depth of 8, and  */
+{				/* is stored that way in the file.			   */
+    if (depth==0 || depth==1)	return ( 1);
+    else if (depth ==  2)	return ( 2);
+    else if (depth <=  4)	return ( 4);
+    else if (depth <=  8)	return ( 8);
+    else if (depth <= 16)	return (16);
+    else if (depth <= 32)	return (32);
+    else {
+      pm_error( "depth was %d, which is not in the range 1-32", depth );
+      return(-1);  /* Should never reach here */
+  }
+}
+
+
+
+#if __STDC__
+static void
+putval( gray b )
+#else /*__STDC__*/
+static void
+putval( b )
+gray b;
+#endif /*__STDC__*/
+    {
+    if ( bitsperitem == 32 )
+	putitem( );
+    item = item | ( b << bitshift );
+    bitsperitem = bitsperitem + maxbitsperitem;
+    bitshift = bitshift + maxbitsperitem;
+    }
+
+static void
+putrest( )
+    {
+    if ( bitsperitem > 0 )
+	putitem( );
+    }
+
+static void
+putitem( )
+    {
+    pm_writelittlelong( stdout, ~item );
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 0;
+    }
diff --git a/converter/pgm/pgmtopgm.c b/converter/pgm/pgmtopgm.c
new file mode 100644
index 00000000..c65e98e0
--- /dev/null
+++ b/converter/pgm/pgmtopgm.c
@@ -0,0 +1,44 @@
+/*----------------------------------------------------------------------------
+                               pgmtopgm
+------------------------------------------------------------------------------
+  Part of the Netpbm package.
+
+  Copy PGM 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 "pgm.h"
+
+int
+main(int argc, char *argv[]) {
+    int format;
+    int rows, cols;
+    gray maxval;
+    int row;
+    gray* grayrow;
+    
+    pgm_init(&argc, argv);
+    
+    if (argc-1 != 0)
+        pm_error("Program takes no arguments.  Input is from Standard Input");
+
+    pgm_readpgminit(stdin, &cols, &rows, &maxval, &format);
+
+    pgm_writepgminit(stdout, cols, rows, maxval, 0);
+
+    grayrow = pgm_allocrow(cols);
+
+    for (row = 0; row < rows; row++) {
+        pgm_readpgmrow(stdin, grayrow, cols, maxval, format);
+        pgm_writepgmrow(stdout, grayrow, cols, maxval, 0);
+    }
+    pgm_freerow(grayrow);
+
+    pm_close(stdin);
+
+    return 0;
+}
diff --git a/converter/pgm/psidtopgm.c b/converter/pgm/psidtopgm.c
new file mode 100644
index 00000000..07417d1b
--- /dev/null
+++ b/converter/pgm/psidtopgm.c
@@ -0,0 +1,128 @@
+/* psidtopgm.c - convert PostScript "image" data into a portable graymap
+**
+** 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 "pgm.h"
+
+
+static int
+gethexit(FILE * const ifp) {
+
+    for ( ; ; ) {
+        int const i = getc(ifp);
+        if (i == EOF)
+            pm_error("EOF / read error");
+        else {
+            char const c = (char) i;
+            if (c >= '0' && c <= '9')
+                return c - '0';
+            else if (c >= 'A' && c <= 'F')
+                return c - 'A' + 10;
+            else if (c >= 'a' && c <= 'f')
+                return c - 'a' + 10;
+            /* Else ignore - whitespace. */
+        }
+    }
+}
+
+
+
+int
+main(int     argc,
+     char ** argv) {
+
+    FILE * ifP;
+    gray * grayrow;
+    int argn, row;
+    gray maxval;
+    int rows, cols, bitspersample;
+
+    pgm_init(&argc, argv);
+
+    argn = 1;
+    
+    if (argn + 3 > argc)
+        pm_error("Too few arguments");
+
+    cols = atoi(argv[argn++]);
+    rows = atoi(argv[argn++]);
+    bitspersample = atoi(argv[argn++]);
+    if (cols <= 0)
+        pm_error("Columns must be positive.  You specified %d",
+                 cols);
+    if (rows <= 0)
+        pm_error("Rows must be positive.  You specified %d",
+                 rows);
+    if (bitspersample <= 0)
+        pm_error("Bits per sample must be positive.  You specified %d",
+                 bitspersample);
+
+    if (argn < argc)
+        ifP = pm_openr(argv[argn++]);
+    else
+        ifP = stdin;
+
+    if (argn != argc)
+        pm_error("Too many arguments");
+
+    maxval = pm_bitstomaxval(bitspersample);
+    if (maxval > PGM_OVERALLMAXVAL)
+        pm_error("bits/sample (%d) is too large.", bitspersample);
+
+    pgm_writepgminit(stdout, cols, rows, maxval, 0);
+    grayrow = pgm_allocrow((cols + 7) / 8 * 8);
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        for (col = 0; col < cols; ) {
+            int val;
+            val = gethexit(ifP) << 4;
+            val += gethexit(ifP);
+            switch (bitspersample) {
+            case 1:
+                grayrow[col++] = val >> 7;
+                grayrow[col++] = ( val >> 6 ) & 0x1;
+                grayrow[col++] = ( val >> 5 ) & 0x1;
+                grayrow[col++] = ( val >> 4 ) & 0x1;
+                grayrow[col++] = ( val >> 3 ) & 0x1;
+                grayrow[col++] = ( val >> 2 ) & 0x1;
+                grayrow[col++] = ( val >> 1 ) & 0x1;
+                grayrow[col++] = val & 0x1;
+                break;
+
+            case 2:
+                grayrow[col++] = val >> 6;
+                grayrow[col++] = ( val >> 4 ) & 0x3;
+                grayrow[col++] = ( val >> 2 ) & 0x3;
+                grayrow[col++] = val & 0x3;
+                break;
+
+            case 4:
+                grayrow[col++] = val >> 4;
+                grayrow[col++] = val & 0xf;
+                break;
+
+            case 8:
+                grayrow[col++] = val;
+                break;
+
+            default:
+                pm_error("This program does not know how to interpret a"
+                         "%d bitspersample image", bitspersample );
+            }
+        }
+        pgm_writepgmrow(stdout, grayrow, cols, (gray) maxval, 0);
+    }
+    pgm_freerow(grayrow);
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
+}
diff --git a/converter/pgm/rawtopgm.c b/converter/pgm/rawtopgm.c
new file mode 100644
index 00000000..c91c6178
--- /dev/null
+++ b/converter/pgm/rawtopgm.c
@@ -0,0 +1,284 @@
+/* rawtopgm.c - convert raw grayscale bytes into a portable graymap
+**
+** 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 <math.h>
+#include "pgm.h"
+#include "shhopt.h"
+
+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 */
+    unsigned int headerskip;
+    float rowskip;
+    int bottomfirst;  /* the -bottomfirst/-bt option */
+    int autosize;  /* User wants us to figure out the size */
+    int width;
+    int height;
+    int bpp;
+      /* bytes per pixel in input format.  1 or 2 */
+    int littleendian;
+      /* logical: samples in input are least significant byte first */
+    int maxval;  /* -maxval option, or -1 if none */
+};
+
+
+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,   "bottomfirst",   OPT_FLAG,   &cmdline_p->bottomfirst,    0);
+    OPTENTRY(0,   "bt",            OPT_FLAG,   &cmdline_p->bottomfirst,    0);
+    OPTENTRY(0,   "topbottom",     OPT_FLAG,   &cmdline_p->bottomfirst,    0);
+    OPTENTRY(0,   "tb",            OPT_FLAG,   &cmdline_p->bottomfirst,    0);
+    OPTENTRY(0,   "headerskip",    OPT_UINT,   &cmdline_p->headerskip,     0);
+    OPTENTRY(0,   "rowskip",       OPT_FLOAT,  &cmdline_p->rowskip,        0);
+    OPTENTRY(0,   "bpp",           OPT_INT,    &cmdline_p->bpp,            0);
+    OPTENTRY(0,   "littleendian",  OPT_FLAG,   &cmdline_p->littleendian,   0);
+    OPTENTRY(0,   "maxval",        OPT_UINT,   &cmdline_p->maxval,         0);
+
+    /* Set the defaults */
+    cmdline_p->bottomfirst = FALSE;
+    cmdline_p->headerskip = 0;
+    cmdline_p->rowskip = 0.0;
+    cmdline_p->bpp = 1;
+    cmdline_p->littleendian = 0;
+    cmdline_p->maxval = -1;
+
+    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 */
+
+    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 = "-";
+        cmdline_p->autosize = TRUE;
+    } else if (argc-1 == 1) {
+        cmdline_p->input_filespec = argv[1];
+        cmdline_p->autosize = TRUE;
+    } else if (argc-1 == 2) {
+        cmdline_p->input_filespec = "-";
+        cmdline_p->autosize = FALSE;
+        cmdline_p->width = atoi(argv[1]);
+        cmdline_p->height = atoi(argv[2]);
+    } else if (argc-1 == 3) {
+        cmdline_p->input_filespec = argv[3];
+        cmdline_p->autosize = FALSE;
+        cmdline_p->width = atoi(argv[1]);
+        cmdline_p->height = atoi(argv[2]);
+    } else
+        pm_error("Program takes zero, one, two, or three arguments.  You "
+                 "specified %d", argc-1);
+
+    if (cmdline_p->bpp != 1 && cmdline_p->bpp != 2) 
+        pm_error("Bytes per pixel (-bpp) must be 1 or 2.  You specified %d.",
+                 cmdline_p->bpp);
+
+    if (cmdline_p->maxval == 0)
+        pm_error("Maxval (-maxval) may not be zero.");
+
+    if (cmdline_p->maxval > 255 && cmdline_p->bpp == 1)
+        pm_error("You have specified one byte per pixel, but a maxval "
+                 "too large to fit in one byte: %d", cmdline_p->maxval);
+    if (cmdline_p->maxval > 65535)
+        pm_error("Maxval must be less than 65536.  You specified %d.",
+                 cmdline_p->maxval);
+
+    if (cmdline_p->width <= 0) 
+        pm_error("Width must be a positive number.");
+    if (cmdline_p->height <= 0) 
+        pm_error("Height must be a positive number.");
+
+    if (cmdline_p->rowskip && cmdline_p->autosize)
+        pm_error("If you specify -rowskip, you must also give the image "
+                 "dimensions.");
+    if (cmdline_p->rowskip && cmdline_p->bottomfirst)
+        pm_error("You canot specify both -rowskip and -bottomfirst.  This is "
+                 "a limitation of this program.");
+
+}
+
+
+
+static void
+compute_image_size(const struct cmdline_info cmdline, const long nread,
+                   int * const rows_p, int * const cols_p) {
+
+    if (cmdline.autosize) {
+        int sqrt_trunc = 
+            (int) sqrt((double) (nread-cmdline.headerskip));
+        if (sqrt_trunc*sqrt_trunc+cmdline.headerskip != nread) 
+            pm_error( "You must specify the dimensions of the image unless "
+                      "it is a quadratic image.  This one is not quadratic: "
+                      "The number of "
+                      "pixels in the input is %ld, which is not a perfect "
+                      "square.", nread-cmdline.headerskip);
+        *rows_p = *cols_p = sqrt_trunc;
+        pm_message( "Image size: %d cols, %d rows", *cols_p, *rows_p);
+    } else {
+        *rows_p = cmdline.height;
+        *cols_p = cmdline.width;
+    }
+}
+
+
+
+static void
+skip_header(FILE *ifp, const int headerskip) {
+    int i;
+
+    for ( i = 0; i < headerskip; ++i ) {
+        /* Read a byte out of the file */
+        int val;
+        val = getc( ifp );
+        if ( val == EOF )
+            pm_error("EOF / read error reading Byte %d in the header", i );
+    }
+}
+
+
+
+static gray
+read_from_file(FILE *ifp, const int bpp, const int row, const int col,
+               const int littleendian) {
+/*----------------------------------------------------------------------------
+   Return the next sample value from the input file 'ifp', assuming the
+   input stream is 'bpp' bytes per pixel (1 or 2).  In the case of two
+   bytes, if 'littleendian', assume least significant byte is first.
+   Otherwise, assume MSB first.
+   
+   In error messages, say this is Column 'col', Row 'row'.  Exit program if
+   error.
+-----------------------------------------------------------------------------*/
+    gray retval;
+
+    if (bpp == 1) {
+        int val;
+        val = getc(ifp);
+        if (val == EOF)
+            pm_error( "EOF / read error at Row %d Column %d",
+                      row, col);
+        retval = (gray) val;
+    } else {
+        short val;
+        int rc;
+        rc = littleendian ? 
+            pm_readlittleshort(ifp, &val) : pm_readbigshort(ifp, &val);
+        if (rc != 0)
+            pm_error( "EOF / read error at Row %d Column %d",
+                      row, col);
+        retval = (gray) val;
+    }
+    return retval;
+}
+
+
+
+int
+main(int argc, char *argv[] ) {
+
+    struct cmdline_info cmdline;
+    FILE* ifp;
+    gray* grayrow;
+    int rows, cols;
+    gray maxval;
+    char* buf;
+    /* pixels_1 and pixels_2 are the array of pixels in the input buffer
+       (assuming we are using an input buffer).  pixels_1 is the array
+       as if the pixels are one byte each.  pixels_2 is the array as if
+       they are two bytes each.
+       */
+    unsigned char *pixels_1;  
+    unsigned short *pixels_2;
+    long nread;
+    int row;
+    float toskip;
+
+    pgm_init( &argc, argv );
+
+    parse_command_line(argc, argv, &cmdline);
+
+    ifp = pm_openr(cmdline.input_filespec);
+
+    if (cmdline.autosize || cmdline.bottomfirst) {
+        buf = pm_read_unknown_size( ifp, &nread );
+        pixels_1 = (unsigned char *) buf;
+        pixels_2 = (unsigned short *) buf;
+    } else
+        buf = NULL;
+
+    compute_image_size(cmdline, nread, &rows, &cols);
+
+    if (!buf)
+        skip_header(ifp, cmdline.headerskip);
+
+    toskip = 0.00001;
+
+    if (cmdline.maxval == -1)
+        maxval = (cmdline.bpp == 1 ? (gray) 255 : (gray) 65535);
+    else
+        maxval = (gray) cmdline.maxval;
+
+    pgm_writepgminit( stdout, cols, rows, maxval, 0 );
+    grayrow = pgm_allocrow( cols );
+
+    for ( row = 0; row < rows; ++row) {
+        int col;
+        unsigned int rowpos; /* index of this row in pixel array */
+        if (cmdline.bottomfirst)
+            rowpos = (rows-row-1) * cols;
+        else
+            rowpos = row * cols;
+
+        for ( col = 0; col < cols; ++col )
+            if (buf) {
+                if (cmdline.bpp == 1)
+                    grayrow[col] = pixels_1[rowpos+col];
+                else
+                    grayrow[col] = pixels_2[rowpos+col];
+            } else {
+                grayrow[col] = read_from_file(ifp, cmdline.bpp, 
+                                              row, col,
+                                              cmdline.littleendian);
+            }
+        for ( toskip += cmdline.rowskip; toskip >= 1.0; toskip -= 1.0 ) {
+            /* Note that if we're using a buffer, cmdline.rowskip is zero */
+            int val;
+            val = getc( ifp );
+            if ( val == EOF )
+                pm_error( "EOF / read error skipping bytes at the end "
+                          "of Row %d.", row);
+        }
+        pgm_writepgmrow( stdout, grayrow, cols, maxval, 0 );
+    }
+    
+    if (buf)
+        free(buf);
+    pm_close( ifp );
+    pm_close( stdout );
+
+    exit( 0 );
+}
diff --git a/converter/pgm/sbigtopgm.c b/converter/pgm/sbigtopgm.c
new file mode 100644
index 00000000..bb3cb6fe
--- /dev/null
+++ b/converter/pgm/sbigtopgm.c
@@ -0,0 +1,208 @@
+/*
+
+    sbigtopgm.c - read a Santa Barbara Instruments Group CCDOPS file
+
+    Note: All SBIG CCD astronomical cameras produce 14 bits or
+	  (the ST-4 and ST-5) or 16 bits (ST-6 and later) per pixel.
+
+		  Copyright (C) 1998 by John Walker
+		       http://www.fourmilab.ch/
+
+    If you find yourself having to add functionality included subsequent
+    to the implementation of this program, you can probably find
+    documentation of any changes to the SBIG file format on their
+    Web site: http://www.sbig.com/
+
+    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 "pgm.h"
+#include "nstring.h"
+
+#define SBIG_HEADER_LENGTH  2048      /* File header length */
+
+/*  looseCanon	--  Canonicalize a line from the file header so
+		    items more sloppily formatted than those
+		    written by CCDOPS are still accepted.  */
+
+static void looseCanon(cp)
+  char *cp;
+{
+    char *op = cp;
+    char c;
+    
+    while ((c = *cp++) != 0) {
+	if (!ISSPACE(c)) {
+	    if (ISUPPER(c)) {
+		c = tolower(c);
+	    }
+	    *op++ = c;
+	}
+    }
+    *op++ = 0;
+}
+
+int main(argc, argv)
+  int argc;
+  char* argv[];
+{
+    FILE *ifp;
+    gray *grayrow;
+    register gray *gP;
+    int argn, row;
+    register int col;
+    int maxval;
+    int comp, rows, cols;
+    char header[SBIG_HEADER_LENGTH];
+    char *hdr;
+    static char camera[80] = "ST-?";
+
+    pgm_init(&argc, argv);
+
+    argn = 1;
+
+    if (argn < argc) {
+	ifp = pm_openr(argv[argn]);
+	argn++;
+    } else {
+	ifp = stdin;
+    }
+
+    if (argn != argc)
+        pm_usage( "[sbigfile]" );
+
+    if (fread(header, SBIG_HEADER_LENGTH, 1, ifp) < 1) {
+        pm_error("error reading SBIG file header");
+    }
+
+    /*	Walk through the header and parse relevant parameters.	*/
+
+    comp = -1;
+    cols = -1;
+    rows = -1;
+
+    /*	The SBIG header specification equivalent to maxval is
+        "Sat_level", the saturation level of the image.  This
+	specification is optional, and was not included in files
+	written by early versions of CCDOPS. It was introduced when it
+	became necessary to distinguish 14-bit images with a Sat_level
+	of 16383 from 16-bit images which saturate at 65535.  In
+	addition, co-adding images or capturing with Track and
+	Accumulate can increase the saturation level.  Since files
+        which don't have a Sat_level line in the header were most
+	probably written by early drivers for the ST-4 or ST-5, it
+	might seem reasonable to make the default for maxval 16383,
+	the correct value for those cameras.  I chose instead to use
+	65535 as the default because the overwhelming majority of
+        cameras in use today are 16 bit, and it's possible some
+        non-SBIG software may omit the "optional" Sat_level
+	specification.	Also, no harm is done if a larger maxval is
+	specified than appears in the image--a simple contrast stretch
+	will adjust pixels to use the full 0 to maxval range.  The
+	converse, pixels having values greater than maxval, results in
+	an invalid file which may cause problems in programs which
+	attempt to process it.	*/
+
+    maxval = 65535;
+
+    hdr = header;
+
+    for (;;) {
+        char *cp = strchr(hdr, '\n');
+
+	if (cp == NULL) {
+            pm_error("malformed SBIG file header at character %d", hdr - header);
+	}
+	*cp = 0;
+        if (strncmp(hdr, "ST-", 3) == 0) {
+            char *ep = strchr(hdr + 3, ' ');
+
+	    if (ep != NULL) {
+		*ep = 0;
+		strcpy(camera, hdr);
+                *ep = ' ';
+	    }
+	}
+	looseCanon(hdr);
+        if (strncmp(hdr, "st-", 3) == 0) {
+            comp = strstr(hdr, "compressed") != NULL;
+        } else if (strncmp(hdr, "height=", 7) == 0) {
+	    rows = atoi(hdr + 7);
+        } else if (strncmp(hdr, "width=", 6) == 0) {
+	    cols = atoi(hdr + 6);
+        } else if (strncmp(hdr, "sat_level=", 10) == 0) {
+	    maxval = atoi(hdr + 10);
+        } else if (STREQ(hdr, "end")) {
+	    break;
+	}
+	hdr = cp + 1;
+    }
+
+    if ((comp == -1) || (rows == -1) || (cols == -1)) {
+        pm_error("required specification missing from SBIG file header");
+    }
+
+    fprintf(stderr, "SBIG %s %dx%d %s image, saturation level = %d.\n",
+        camera, cols, rows, comp ? "compressed" : "uncompressed", maxval);
+
+    if (maxval > PGM_OVERALLMAXVAL) {
+        pm_error("Saturation level (%d levels) is too large.\n"
+                 "This program's limit is %d.", maxval, PGM_OVERALLMAXVAL);
+    }
+
+    pgm_writepgminit(stdout, cols, rows, (gray) maxval, 0);
+    grayrow = pgm_allocrow(cols);
+
+#define DOSINT(fp) ((getc(fp) & 0xFF) | (getc(fp) << 8))
+
+    for (row = 0; row < rows; row++) {
+	int compthis = comp;
+
+	if (comp) {
+	    int rowlen = DOSINT(ifp); /* Compressed row length */
+
+	    /*	If compression results in a row length >= the uncompressed
+		row length, that row is output uncompressed.  We detect this
+		by observing that the compressed row length is equal to
+		that of an uncompressed row.  */
+
+	    if (rowlen == cols * 2) {
+		compthis = 0;
+	    }
+	}
+	for (col = 0, gP = grayrow; col < cols; col++, gP++) {
+	    gray g;
+
+	    if (compthis) {
+
+		if (col == 0) {
+		    g = DOSINT(ifp);
+		} else {
+		    int delta = getc(ifp);
+
+		    if (delta == 0x80) {
+			g = DOSINT(ifp);
+		    } else {
+			g += ((signed char) delta);
+		    }
+		}
+	    } else {
+		g = DOSINT(ifp);
+	    }
+	    *gP = g;
+	}
+	pgm_writepgmrow(stdout, grayrow, cols, (gray) maxval, 0);
+    }
+    pm_close(ifp);
+    pm_close(stdout);
+
+    return 0;
+}
diff --git a/converter/pgm/spottopgm.c b/converter/pgm/spottopgm.c
new file mode 100644
index 00000000..59240ba8
--- /dev/null
+++ b/converter/pgm/spottopgm.c
@@ -0,0 +1,231 @@
+/* Spottopgm: Convert a SPOT satellite image to PGM format.
+ *
+ * Usage: spottopgm [-1|2|3] [Firstcol Firstline Lastcol Lastline] inputfile
+ *
+ * Author: Warren Toomey, 1992.
+ */
+
+#include <stdio.h>
+#include "pgm.h"
+
+/* Global variables */
+
+static FILE *spotfile;          /* The input file */
+static char linebuf[12000];        /* The line buffer */
+
+static long Firstline = 0,     /* The rectangle the user wants */
+    Lastline = 3000,   /* cut out of the image */
+    Firstcol = 0,
+    Lastcol = 0;
+static long Diff = 0;          /* Firstcol - Lastcol */
+static char *Bufptr;           /* Pointer into the input image */
+static int Color = 1;          /* Either 1, 2 or 3 */
+static int Colbool = 0;        /* 1 if color */
+
+
+/* Get_image extracts the pixel data from one line
+ * (i.e one record) in the SPOT input file. A SPOT image
+ * record has a header, data and trailer. The data lengths
+ * are fixed at 3960, 5400, 8640 or 10980 bytes.
+ *
+ * When we arrive here we have read in 12 bytes of the record.
+ * We then read in the rest of the record. We find the trailer
+ * and from that determine the number of pixels on the line.
+ *
+ * If the image is really color i.e interleaved 3 colors, we
+ * convert a line if its spectral sequence is the same as the one
+ * requested by the user (i.e 1, 2 or 3). I could create a ppm file
+ * but I couldn't be bothered with the rearranging of the data.
+ */
+static int 
+get_image(long length)
+{
+    int cnt;
+    struct Linehdr        /* Each line begins with the 12 bytes */
+    {             /* we have already, plus these 20 bytes */
+        long linenum;       /* The line number of the record */
+        short recseq;       /* The record sequence number */
+        short spectseq;     /* The spectral number of the line */
+        long linetime;      /* Time it was recorded (in ms). */
+        long leftpixmar;        /* The pixel number of the 1st pixel */
+        long rightpixmar;       /* The pixel number of the last pixel */
+    } linehdr;
+    long numpixels;       /* Number of pixels on the line */
+
+    /* Get the details of this line */
+    if (pm_readbiglong (spotfile, &linehdr.linenum) == -1
+        || pm_readbigshort (spotfile, &linehdr.recseq) == -1
+        || pm_readbigshort (spotfile, &linehdr.spectseq) == -1
+        || pm_readbiglong (spotfile, &linehdr.linetime) == -1
+        || pm_readbiglong (spotfile, &linehdr.leftpixmar) == -1
+        || pm_readbiglong (spotfile, &linehdr.rightpixmar) == -1)
+        pm_error ("EOF / read error reading line header");
+
+    /* Now read in the line data */
+    cnt = length - 20 - 88;
+    cnt = fread(linebuf, 1, cnt, spotfile);
+
+    if (!Diff)
+    {
+        cnt += 28;
+        if (fseek (spotfile, 24, 1) == EOF)
+            pm_error ("seek error");
+        if (pm_readbiglong (spotfile, &numpixels) == -1)
+            pm_error ("EOF / read error reading line ender");
+    
+        /* Determine the picture size */
+        Bufptr = &linebuf[Firstcol];
+        if (Lastcol == 0 || Lastcol > numpixels)
+            Lastcol = numpixels;
+        Diff = Lastcol - Firstcol;
+        /* Print out the header */
+        printf("P5\n%ld %ld\n255\n", Diff, Lastline - Firstline);
+        /* Inform about the image size */
+        if (Colbool) fprintf(stderr, "Color image, ");
+        fprintf(stderr, "%ld pixels wide\n", numpixels);
+    }
+
+    /* Output the line */
+    if (linehdr.linenum >= Firstline && linehdr.linenum <= Lastline
+        && linehdr.spectseq == Color)
+        fwrite(Bufptr, 1, Diff, stdout);
+    if (linehdr.linenum > Lastline) exit(0);
+
+#ifdef DEBUG
+    fprintf(stderr,
+            "Line %4d, %3d, %3d, time %4d, l/r "
+            "pixmar %4d %4d len %d pixnum %d\n",
+            linehdr.linenum, linehdr.recseq, 
+            linehdr.spectseq, linehdr.linetime,
+            linehdr.leftpixmar, linehdr.rightpixmar, length, numpixels);
+#endif
+    /* And return the amount to seek */
+    return (length - 20 - cnt);
+}
+
+
+
+/* The image header tells us if the image is in monochrome or color, and
+ * if the latter, if the input colors are interleaved. If interleaved
+ * color, lines are interleaved R, G, B, R, G, B etc. Technically, some
+ * interleaving of infra-red, visible and ultra-violet.
+ *
+ * In the description field below,
+ *  element 0 == P --> monochrome
+ *  element 0 == X --> color
+ *  element 9 == S --> sequential (i.e only one color here)
+ *  element 9 == I --> interleaved (1 or more colors)
+ */
+static int 
+get_imghdr(int length)
+{
+    struct Imghdr
+    {
+        long linewidth;
+        char dummy1[36];
+        char description[16];   /* Type of image */
+    } header;
+
+    if (pm_readbiglong (spotfile, &header.linewidth) == -1)
+        pm_error ("EOF / read error reading header");
+#ifdef DEBUG
+    if (fread (header.dummy1, 1, 36, spotfile) != 36)
+        pm_error ("EOF / read error reading header");
+#else
+    if (fseek (spotfile, 36, 1) == EOF)
+        pm_error ("seek error");
+#endif
+    if (fread (header.description, 1, 16, spotfile) != 16)
+        pm_error ("EOF / read error reading header");
+
+    /* Determine mono or color */
+    if (header.description[0] == 'X' && header.description[9] == 'S')
+        Colbool = 1;
+    else Colbool = 0;
+
+#ifdef DEBUG
+    fprintf(stderr, "Dummy str is >%s<\n", header.dummy1);
+    fprintf(stderr, "Imghdr str is >%s<, col %d\n", 
+            header.description, Colbool);
+#endif
+    /* Return the amount to fseek */
+    return (length - 56);
+}
+
+
+static void
+usage()
+{
+    fprintf(stderr,
+            "Usage: spottopgm [-1|2|3] [Firstcol Firstline Lastcol Lastline] "
+            "input_file\n");
+    exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+    struct Record             /* A SPOT image is broken up into */
+    {                 /* records with the following fields */
+        long record;            /* The record number (1, 2, 3...) */
+        unsigned char sub1;         /* Record sub type 1 */
+        unsigned char type;         /* The record type */
+        unsigned char sub2;         /* Record sub type 2 */
+        unsigned char sub3;         /* Record sub type 3 */
+        long length;            /* Record length in bytes */
+    } arecord;
+
+    pgm_init( &argc, argv );
+   
+    switch (argc)
+    {
+    case 7:
+        Color= -(atoi(argv[1]));      /* Get the color to extract */
+        argv++;
+    case 6:
+        Firstcol = atoi(argv[1]);     /* Get the rectangle to extract */
+        Firstline = atoi(argv[2]);
+        Lastcol = atoi(argv[3]);
+        Lastline = atoi(argv[4]);
+        argv += 4;
+        goto openfile;            /* Yuk, a goto! */
+    case 3:
+        Color= -(atoi(argv[1]));      /* Get the color to extract */
+        argv++;
+    case 2:
+    openfile:
+    spotfile = fopen(argv[1], "rb");  /* Open the input file */
+    if (spotfile == NULL) { perror("fopen"); exit(1); }
+    break;
+    default:
+        usage();
+    }
+
+    while (1)             /* Get a record */
+    {
+        if (pm_readbiglong (spotfile, &arecord.record) == -1)
+            break;
+        arecord.sub1 = fgetc (spotfile);
+        arecord.type = fgetc (spotfile);
+        arecord.sub2 = fgetc (spotfile);
+        arecord.sub3 = fgetc (spotfile);
+        if (pm_readbiglong (spotfile, &arecord.length) == -1)
+            pm_error ("EOF / read error reading a record");
+
+        arecord.length -= 12;   /* Subtract header size as well */
+        if (arecord.type == 0355 && arecord.sub1 == 0355)
+            arecord.length = get_image(arecord.length);
+        else if (arecord.type == 0300 && arecord.sub1 == 077)
+            arecord.length = get_imghdr(arecord.length);
+#ifdef DEBUG
+        else
+            fprintf(stderr, 
+                    "Rcrd %3d, type %03o, stype %03o %03o %03o, length %d\n",
+                    arecord.record, arecord.type, arecord.sub1, arecord.sub2,
+                    (int) arecord.sub3 & 0xff, arecord.length);
+#endif
+        /* Seek to next record */
+        fseek(spotfile, arecord.length, 1);
+    }
+    exit (0);
+}
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);
+}
diff --git a/converter/tga.h b/converter/tga.h
new file mode 100644
index 00000000..fdbe4047
--- /dev/null
+++ b/converter/tga.h
@@ -0,0 +1,46 @@
+/* tga.h - header file for Targa files
+*/
+ 
+/* Header definition. */
+struct ImageHeader {
+    unsigned char IdLength;     /* length of Identifier String */
+    unsigned char CoMapType;        /* 0 = no map */
+    unsigned char ImgType;      /* image type (see below for values) */
+    unsigned char Index_lo, Index_hi;   /* index of first color map entry */
+    unsigned char Length_lo, Length_hi; /* number of entries in color map */
+    unsigned char CoSize;       /* size of color map entry (15,16,24,32) */
+    unsigned char X_org_lo, X_org_hi;   /* x origin of image */
+    unsigned char Y_org_lo, Y_org_hi;   /* y origin of image */
+    unsigned char Width_lo, Width_hi;   /* width of image */
+    unsigned char Height_lo, Height_hi; /* height of image */
+    unsigned char PixelSize;        /* pixel size (8,16,24,32) */
+    unsigned char AttBits;  /* 4 bits, number of attribute bits per pixel */
+    unsigned char Rsrvd;    /* 1 bit, reserved */
+    unsigned char OrgBit;   /* 1 bit, origin: 0=lower left, 1=upper left */
+    unsigned char IntrLve;  /* 2 bits, interleaving flag */
+    const char * Id;     /* Identifier String.  Length is IDLength, above */
+};
+
+#define IMAGEIDFIELDMAXSIZE 256
+
+typedef char ImageIDField[IMAGEIDFIELDMAXSIZE];
+
+#define TGA_MAXVAL 255
+
+/* Definitions for image types. */
+#define TGA_Null 0
+#define TGA_Map 1
+#define TGA_RGB 2
+#define TGA_Mono 3
+#define TGA_RLEMap 9
+#define TGA_RLERGB 10
+#define TGA_RLEMono 11
+#define TGA_CompMap 32
+#define TGA_CompMap4 33
+
+/* Definitions for interleave flag. */
+#define TGA_IL_None 0
+#define TGA_IL_Two 1
+#define TGA_IL_Four 2
+
+enum TGAbaseImageType {TGA_MAP_TYPE, TGA_MONO_TYPE, TGA_RGB_TYPE};